11 changed files with 320 additions and 362 deletions
@ -1,12 +1,34 @@ |
|||
namespace LINGYUN.Abp.Notifications; |
|||
using System; |
|||
|
|||
namespace LINGYUN.Abp.Notifications; |
|||
|
|||
public class AbpNotificationsManagementOptions |
|||
{ |
|||
public bool SaveStaticNotificationsToDatabase { get; set; } |
|||
public bool IsDynamicNotificationsStoreEnabled { get; set; } |
|||
/// <summary>
|
|||
/// 缓存刷新时间
|
|||
/// default: 30 seconds
|
|||
/// </summary>
|
|||
public TimeSpan NotificationsCacheRefreshInterval { get; set; } |
|||
/// <summary>
|
|||
/// 申请时间戳超时时间
|
|||
/// default: 2 minutes
|
|||
/// </summary>
|
|||
public TimeSpan NotificationsCacheStampTimeOut { get; set; } |
|||
/// <summary>
|
|||
/// 时间戳过期时间
|
|||
/// default: 30 days
|
|||
/// </summary>
|
|||
public TimeSpan NotificationsCacheStampExpiration { get; set; } |
|||
public AbpNotificationsManagementOptions() |
|||
{ |
|||
SaveStaticNotificationsToDatabase = true; |
|||
IsDynamicNotificationsStoreEnabled = true; |
|||
|
|||
NotificationsCacheRefreshInterval = TimeSpan.FromSeconds(30); |
|||
NotificationsCacheStampTimeOut = TimeSpan.FromMinutes(2); |
|||
// 30天过期重新刷新缓存
|
|||
NotificationsCacheStampExpiration = TimeSpan.FromDays(30); |
|||
} |
|||
} |
|||
|
|||
@ -1,326 +0,0 @@ |
|||
using Microsoft.Extensions.Caching.Distributed; |
|||
using Microsoft.Extensions.Caching.Memory; |
|||
using Microsoft.Extensions.Localization; |
|||
using System; |
|||
using System.Collections.Concurrent; |
|||
using System.Collections.Generic; |
|||
using System.Collections.Immutable; |
|||
using System.Globalization; |
|||
using System.Linq; |
|||
using System.Threading; |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp.Caching; |
|||
using Volo.Abp.DependencyInjection; |
|||
using Volo.Abp.Localization; |
|||
using Volo.Abp.Threading; |
|||
|
|||
namespace LINGYUN.Abp.Notifications; |
|||
|
|||
public class DynamicNotificationDefinitionCache : IDynamicNotificationDefinitionCache, ISingletonDependency |
|||
{ |
|||
private readonly static ConcurrentDictionary<string, NotificationGroupDefinition> _dynamicNotificationGroupL1Cache; |
|||
private readonly static ConcurrentDictionary<string, NotificationDefinition> _dynamicNotificationsL1Cache; |
|||
|
|||
private readonly static SemaphoreSlim _l2CacheSyncLock; |
|||
protected IMemoryCache DynamicNotificationL2Cache { get; } |
|||
|
|||
protected IDistributedCache<NotificationDefinitionGroupsCacheItem> DynamicNotificationGroupL3Cache { get; } |
|||
protected IDistributedCache<NotificationDefinitionsCacheItem> DynamicNotificationsL3Cache { get; } |
|||
|
|||
|
|||
protected INotificationDefinitionGroupRecordRepository NotificationDefinitionGroupRecordRepository { get; } |
|||
protected INotificationDefinitionRecordRepository NotificationDefinitionRecordRepository { get; } |
|||
protected ILocalizableStringSerializer LocalizableStringSerializer { get; } |
|||
protected IStringLocalizerFactory StringLocalizerFactory { get; } |
|||
|
|||
static DynamicNotificationDefinitionCache() |
|||
{ |
|||
_l2CacheSyncLock = new SemaphoreSlim(1, 1); |
|||
_dynamicNotificationsL1Cache = new ConcurrentDictionary<string, NotificationDefinition>(); |
|||
_dynamicNotificationGroupL1Cache = new ConcurrentDictionary<string, NotificationGroupDefinition>(); |
|||
} |
|||
|
|||
public DynamicNotificationDefinitionCache( |
|||
IMemoryCache dynamicNotificationL2Cache, |
|||
IDistributedCache<NotificationDefinitionGroupsCacheItem> dynamicNotificationGroupL3Cache, |
|||
IDistributedCache<NotificationDefinitionsCacheItem> dynamicNotificationsL3Cache, |
|||
INotificationDefinitionGroupRecordRepository notificationDefinitionGroupRecordRepository, |
|||
INotificationDefinitionRecordRepository notificationDefinitionRecordRepository, |
|||
ILocalizableStringSerializer localizableStringSerializer, |
|||
IStringLocalizerFactory stringLocalizerFactory) |
|||
{ |
|||
DynamicNotificationL2Cache = dynamicNotificationL2Cache; |
|||
DynamicNotificationGroupL3Cache = dynamicNotificationGroupL3Cache; |
|||
DynamicNotificationsL3Cache = dynamicNotificationsL3Cache; |
|||
NotificationDefinitionGroupRecordRepository = notificationDefinitionGroupRecordRepository; |
|||
NotificationDefinitionRecordRepository = notificationDefinitionRecordRepository; |
|||
LocalizableStringSerializer = localizableStringSerializer; |
|||
StringLocalizerFactory = stringLocalizerFactory; |
|||
} |
|||
|
|||
public async virtual Task<IReadOnlyList<NotificationGroupDefinition>> GetGroupsAsync() |
|||
{ |
|||
var cacheKey = NotificationDefinitionGroupsCacheItem.CalculateCacheKey(CultureInfo.CurrentCulture.Name); |
|||
|
|||
if (!_dynamicNotificationGroupL1Cache.Any()) |
|||
{ |
|||
var l2GroupCache = await GetGroupsFormL2CacheAsync(cacheKey); |
|||
var notifications = await GetNotificationsFormL2CacheAsync( |
|||
NotificationDefinitionsCacheItem.CalculateCacheKey(CultureInfo.CurrentCulture.Name)); |
|||
|
|||
foreach (var group in l2GroupCache.Groups) |
|||
{ |
|||
var groupGroup = NotificationGroupDefinition |
|||
.Create( |
|||
group.Name, |
|||
LocalizableStringSerializer.Deserialize(group.DisplayName), |
|||
group.AllowSubscriptionToClients); |
|||
|
|||
var notificationsInThisGroup = notifications.Notifications |
|||
.Where(p => p.GroupName == groupGroup.Name); |
|||
|
|||
foreach (var notification in notificationsInThisGroup) |
|||
{ |
|||
var notificationDefine = groupGroup.AddNotification( |
|||
notification.Name, |
|||
LocalizableStringSerializer.Deserialize(notification.DisplayName), |
|||
LocalizableStringSerializer.Deserialize(notification.Description), |
|||
notification.NotificationType, |
|||
notification.Lifetime, |
|||
notification.ContentType, |
|||
notification.AllowSubscriptionToClients); |
|||
|
|||
notificationDefine.Properties.AddIfNotContains(notification.Properties); |
|||
} |
|||
|
|||
_dynamicNotificationGroupL1Cache.TryAdd(group.Name, groupGroup); |
|||
} |
|||
|
|||
//NotificationGroupDefinition
|
|||
// .Create(g.Name, new FixedLocalizableString(g.DisplayName), g.AllowSubscriptionToClients)
|
|||
} |
|||
|
|||
return _dynamicNotificationGroupL1Cache.Values.ToImmutableList(); |
|||
} |
|||
|
|||
public async virtual Task<IReadOnlyList<NotificationDefinition>> GetNotificationsAsync() |
|||
{ |
|||
var cacheKey = NotificationDefinitionsCacheItem.CalculateCacheKey(CultureInfo.CurrentCulture.Name); |
|||
|
|||
if (!_dynamicNotificationsL1Cache.Any()) |
|||
{ |
|||
var l2cache = await GetNotificationsFormL2CacheAsync(cacheKey); |
|||
|
|||
foreach (var notification in l2cache.Notifications) |
|||
{ |
|||
var notificationDefinition = new NotificationDefinition( |
|||
notification.Name, |
|||
LocalizableStringSerializer.Deserialize(notification.DisplayName), |
|||
LocalizableStringSerializer.Deserialize(notification.Description), |
|||
notification.NotificationType, |
|||
notification.Lifetime, |
|||
notification.ContentType, |
|||
notification.AllowSubscriptionToClients); |
|||
|
|||
notificationDefinition.WithProviders(notification.Providers.ToArray()); |
|||
notificationDefinition.Properties.AddIfNotContains(notification.Properties); |
|||
|
|||
_dynamicNotificationsL1Cache.TryAdd(notification.Name, notificationDefinition); |
|||
} |
|||
} |
|||
|
|||
return _dynamicNotificationsL1Cache.Values.ToImmutableList(); |
|||
} |
|||
|
|||
public async virtual Task<NotificationDefinition> GetOrNullAsync(string name) |
|||
{ |
|||
if (_dynamicNotificationsL1Cache.Any()) |
|||
{ |
|||
return _dynamicNotificationsL1Cache.GetOrDefault(name); |
|||
} |
|||
var notifications = await GetNotificationsAsync(); |
|||
|
|||
return notifications.FirstOrDefault(n => n.Name.Equals(name)); |
|||
} |
|||
|
|||
protected async virtual Task<NotificationDefinitionGroupsCacheItem> GetGroupsFormL2CacheAsync(string cacheKey) |
|||
{ |
|||
var cacheItem = DynamicNotificationL2Cache.Get<NotificationDefinitionGroupsCacheItem>(cacheKey); |
|||
|
|||
if (cacheItem == null) |
|||
{ |
|||
using (await _l2CacheSyncLock.LockAsync()) |
|||
{ |
|||
var l2cache = await GetGroupsFormL3CacheAsync(cacheKey); |
|||
|
|||
var options = new MemoryCacheEntryOptions(); |
|||
options.SetAbsoluteExpiration(TimeSpan.FromMinutes(5)); |
|||
options.SetPriority(CacheItemPriority.High); |
|||
options.RegisterPostEvictionCallback((key, value, reason, state) => |
|||
{ |
|||
_dynamicNotificationGroupL1Cache.Clear(); |
|||
}); |
|||
|
|||
DynamicNotificationL2Cache.Set(cacheKey, l2cache, options); |
|||
|
|||
return l2cache; |
|||
} |
|||
} |
|||
|
|||
return cacheItem; |
|||
} |
|||
|
|||
protected async virtual Task<NotificationDefinitionGroupsCacheItem> GetGroupsFormL3CacheAsync(string cacheKey) |
|||
{ |
|||
var cacheItem = await DynamicNotificationGroupL3Cache.GetAsync(cacheKey); |
|||
|
|||
if (cacheItem == null) |
|||
{ |
|||
var records = await GetGroupsFormRepositoryAsync(); |
|||
var recordCaches = new List<NotificationDefinitionGroupCacheItem>(); |
|||
|
|||
foreach (var record in records) |
|||
{ |
|||
var displayName = record.DisplayName; |
|||
if (!displayName.IsNullOrWhiteSpace()) |
|||
{ |
|||
displayName = await LocalizableStringSerializer |
|||
.Deserialize(displayName) |
|||
.LocalizeAsync(StringLocalizerFactory); |
|||
} |
|||
|
|||
var description = record.Description; |
|||
if (!description.IsNullOrWhiteSpace()) |
|||
{ |
|||
description = await LocalizableStringSerializer |
|||
.Deserialize(description) |
|||
.LocalizeAsync(StringLocalizerFactory); |
|||
} |
|||
|
|||
recordCaches.Add(new NotificationDefinitionGroupCacheItem( |
|||
record.Name, |
|||
displayName, |
|||
description, |
|||
record.AllowSubscriptionToClients)); |
|||
} |
|||
|
|||
cacheItem = new NotificationDefinitionGroupsCacheItem(recordCaches.ToArray()); |
|||
|
|||
await DynamicNotificationGroupL3Cache.SetAsync( |
|||
cacheKey, |
|||
cacheItem, |
|||
new DistributedCacheEntryOptions |
|||
{ |
|||
AbsoluteExpirationRelativeToNow = TimeSpan.FromHours(2), |
|||
SlidingExpiration = TimeSpan.FromHours(1), |
|||
}); |
|||
} |
|||
|
|||
return cacheItem; |
|||
} |
|||
|
|||
protected async virtual Task<List<NotificationDefinitionGroupRecord>> GetGroupsFormRepositoryAsync() |
|||
{ |
|||
var records = await NotificationDefinitionGroupRecordRepository.GetListAsync(); |
|||
|
|||
return records; |
|||
} |
|||
|
|||
|
|||
protected async virtual Task<NotificationDefinitionsCacheItem> GetNotificationsFormL2CacheAsync(string cacheKey) |
|||
{ |
|||
var cacheItem = DynamicNotificationL2Cache.Get<NotificationDefinitionsCacheItem>(cacheKey); |
|||
|
|||
if (cacheItem == null) |
|||
{ |
|||
using (await _l2CacheSyncLock.LockAsync()) |
|||
{ |
|||
var l2cache = await GetNotificationsFormL3CacheAsync(cacheKey); |
|||
|
|||
var options = new MemoryCacheEntryOptions(); |
|||
options.SetAbsoluteExpiration(TimeSpan.FromMinutes(5)); |
|||
options.SetPriority(CacheItemPriority.High); |
|||
options.RegisterPostEvictionCallback((key, value, reason, state) => |
|||
{ |
|||
_dynamicNotificationsL1Cache.Clear(); |
|||
}); |
|||
|
|||
DynamicNotificationL2Cache.Set(cacheKey, l2cache, options); |
|||
|
|||
return l2cache; |
|||
} |
|||
} |
|||
|
|||
return cacheItem; |
|||
} |
|||
|
|||
protected async virtual Task<NotificationDefinitionsCacheItem> GetNotificationsFormL3CacheAsync(string cacheKey) |
|||
{ |
|||
var cacheItem = await DynamicNotificationsL3Cache.GetAsync(cacheKey); |
|||
|
|||
if (cacheItem == null) |
|||
{ |
|||
var records = await GetNotificationsFormRepositoryAsync(); |
|||
var recordCaches = new List<NotificationDefinitionCacheItem>(); |
|||
|
|||
foreach (var record in records) |
|||
{ |
|||
var displayName = record.DisplayName; |
|||
if (!displayName.IsNullOrWhiteSpace()) |
|||
{ |
|||
displayName = await LocalizableStringSerializer |
|||
.Deserialize(displayName) |
|||
.LocalizeAsync(StringLocalizerFactory); |
|||
} |
|||
|
|||
var description = record.Description; |
|||
if (!description.IsNullOrWhiteSpace()) |
|||
{ |
|||
description = await LocalizableStringSerializer |
|||
.Deserialize(description) |
|||
.LocalizeAsync(StringLocalizerFactory); |
|||
} |
|||
|
|||
var providers = new List<string>(); |
|||
if (!record.Providers.IsNullOrWhiteSpace()) |
|||
{ |
|||
providers = record.Providers.Split(';').ToList(); |
|||
} |
|||
|
|||
var recordCache = new NotificationDefinitionCacheItem( |
|||
record.Name, |
|||
record.GroupName, |
|||
displayName, |
|||
description, |
|||
record.NotificationLifetime, |
|||
record.NotificationType, |
|||
record.ContentType, |
|||
providers, |
|||
record.AllowSubscriptionToClients); |
|||
recordCache.Properties.AddIfNotContains(record.ExtraProperties); |
|||
|
|||
recordCaches.Add(recordCache); |
|||
} |
|||
|
|||
cacheItem = new NotificationDefinitionsCacheItem(recordCaches.ToArray()); |
|||
|
|||
await DynamicNotificationsL3Cache.SetAsync( |
|||
cacheKey, |
|||
cacheItem, |
|||
new DistributedCacheEntryOptions |
|||
{ |
|||
AbsoluteExpirationRelativeToNow = TimeSpan.FromHours(2), |
|||
SlidingExpiration = TimeSpan.FromHours(1), |
|||
}); |
|||
} |
|||
|
|||
return cacheItem; |
|||
} |
|||
|
|||
protected async virtual Task<List<NotificationDefinitionRecord>> GetNotificationsFormRepositoryAsync() |
|||
{ |
|||
var records = await NotificationDefinitionRecordRepository.GetListAsync(); |
|||
|
|||
return records; |
|||
} |
|||
} |
|||
@ -0,0 +1,113 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using System.Threading; |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp.DependencyInjection; |
|||
using Volo.Abp.Localization; |
|||
|
|||
namespace LINGYUN.Abp.Notifications; |
|||
|
|||
[ExposeServices( |
|||
typeof(IDynamicNotificationDefinitionStoreCache), |
|||
typeof(DynamicNotificationDefinitionInMemoryCache))] |
|||
public class DynamicNotificationDefinitionInMemoryCache : IDynamicNotificationDefinitionStoreCache, ISingletonDependency |
|||
{ |
|||
public string CacheStamp { get; set; } |
|||
|
|||
protected IDictionary<string, NotificationGroupDefinition> NotificationGroupDefinitions { get; } |
|||
protected IDictionary<string, NotificationDefinition> NotificationDefinitions { get; } |
|||
protected ILocalizableStringSerializer LocalizableStringSerializer { get; } |
|||
|
|||
public SemaphoreSlim SyncSemaphore { get; } = new(1, 1); |
|||
|
|||
public DateTime? LastCheckTime { get; set; } |
|||
|
|||
public DynamicNotificationDefinitionInMemoryCache( |
|||
ILocalizableStringSerializer localizableStringSerializer) |
|||
{ |
|||
LocalizableStringSerializer = localizableStringSerializer; |
|||
|
|||
NotificationGroupDefinitions = new Dictionary<string, NotificationGroupDefinition>(); |
|||
NotificationDefinitions = new Dictionary<string, NotificationDefinition>(); |
|||
} |
|||
|
|||
public Task FillAsync( |
|||
List<NotificationDefinitionGroupRecord> notificationGroupRecords, |
|||
List<NotificationDefinitionRecord> notificationRecords) |
|||
{ |
|||
NotificationGroupDefinitions.Clear(); |
|||
NotificationDefinitions.Clear(); |
|||
|
|||
var context = new NotificationDefinitionContext(); |
|||
|
|||
foreach (var notificationGroupRecord in notificationGroupRecords) |
|||
{ |
|||
var notificationGroup = context.AddGroup( |
|||
notificationGroupRecord.Name, |
|||
LocalizableStringSerializer.Deserialize(notificationGroupRecord.DisplayName), |
|||
LocalizableStringSerializer.Deserialize(notificationGroupRecord.Description), |
|||
notificationGroupRecord.AllowSubscriptionToClients |
|||
); |
|||
|
|||
NotificationGroupDefinitions[notificationGroup.Name] = notificationGroup; |
|||
|
|||
foreach (var property in notificationGroupRecord.ExtraProperties) |
|||
{ |
|||
notificationGroup[property.Key] = property.Value; |
|||
} |
|||
|
|||
var notificationRecordsInThisGroup = notificationRecords |
|||
.Where(p => p.GroupName == notificationGroup.Name); |
|||
|
|||
foreach (var notificationRecord in notificationRecordsInThisGroup) |
|||
{ |
|||
AddNotification(notificationGroup, notificationRecord); |
|||
} |
|||
} |
|||
|
|||
return Task.CompletedTask; |
|||
} |
|||
|
|||
public NotificationDefinition GetNotificationOrNull(string name) |
|||
{ |
|||
return NotificationDefinitions.GetOrDefault(name); |
|||
} |
|||
|
|||
public IReadOnlyList<NotificationDefinition> GetNotifications() |
|||
{ |
|||
return NotificationDefinitions.Values.ToList(); |
|||
} |
|||
|
|||
public IReadOnlyList<NotificationGroupDefinition> GetGroups() |
|||
{ |
|||
return NotificationGroupDefinitions.Values.ToList(); |
|||
} |
|||
|
|||
private void AddNotification( |
|||
NotificationGroupDefinition notificationGroup, |
|||
NotificationDefinitionRecord notificationRecord) |
|||
{ |
|||
var notification = notificationGroup.AddNotification( |
|||
notificationRecord.Name, |
|||
LocalizableStringSerializer.Deserialize(notificationRecord.DisplayName), |
|||
LocalizableStringSerializer.Deserialize(notificationRecord.Description), |
|||
notificationRecord.NotificationType, |
|||
notificationRecord.NotificationLifetime, |
|||
notificationRecord.ContentType, |
|||
notificationRecord.AllowSubscriptionToClients |
|||
); |
|||
|
|||
NotificationDefinitions[notification.Name] = notification; |
|||
|
|||
if (!notificationRecord.Providers.IsNullOrWhiteSpace()) |
|||
{ |
|||
notification.Providers.AddRange(notificationRecord.Providers.Split(',', ';')); |
|||
} |
|||
|
|||
foreach (var property in notificationRecord.ExtraProperties) |
|||
{ |
|||
notification[property.Key] = property.Value; |
|||
} |
|||
} |
|||
} |
|||
@ -1,49 +1,171 @@ |
|||
using Microsoft.Extensions.Options; |
|||
using Microsoft.Extensions.Caching.Distributed; |
|||
using Microsoft.Extensions.Options; |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Collections.Immutable; |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp; |
|||
using Volo.Abp.Caching; |
|||
using Volo.Abp.DependencyInjection; |
|||
using Volo.Abp.DistributedLocking; |
|||
using Volo.Abp.Threading; |
|||
|
|||
namespace LINGYUN.Abp.Notifications; |
|||
|
|||
[Dependency(ReplaceServices = true)] |
|||
public class DynamicNotificationDefinitionStore : IDynamicNotificationDefinitionStore, ITransientDependency |
|||
{ |
|||
private readonly AbpNotificationsManagementOptions _notificationsManagementOptions; |
|||
private readonly IDynamicNotificationDefinitionCache _dynamicNotificationDefinitionCache; |
|||
protected INotificationDefinitionGroupRecordRepository NotificationGroupRepository { get; } |
|||
protected INotificationDefinitionRecordRepository NotificationRepository { get; } |
|||
protected INotificationDefinitionSerializer NotificationDefinitionSerializer { get; } |
|||
protected IDynamicNotificationDefinitionStoreCache StoreCache { get; } |
|||
protected IDistributedCache DistributedCache { get; } |
|||
protected IAbpDistributedLock DistributedLock { get; } |
|||
protected AbpNotificationsManagementOptions NotificationManagementOptions { get; } |
|||
protected AbpDistributedCacheOptions CacheOptions { get; } |
|||
|
|||
public DynamicNotificationDefinitionStore( |
|||
IDynamicNotificationDefinitionCache dynamicNotificationDefinitionCache, |
|||
IOptions<AbpNotificationsManagementOptions> notificationsManagementOptions) |
|||
INotificationDefinitionGroupRecordRepository notificationGroupRepository, |
|||
INotificationDefinitionRecordRepository notificationRepository, |
|||
INotificationDefinitionSerializer notificationDefinitionSerializer, |
|||
IDynamicNotificationDefinitionStoreCache storeCache, |
|||
IDistributedCache distributedCache, |
|||
IOptions<AbpDistributedCacheOptions> cacheOptions, |
|||
IOptions<AbpNotificationsManagementOptions> notificationManagementOptions, |
|||
IAbpDistributedLock distributedLock) |
|||
{ |
|||
_dynamicNotificationDefinitionCache = dynamicNotificationDefinitionCache; |
|||
_notificationsManagementOptions = notificationsManagementOptions.Value; |
|||
NotificationGroupRepository = notificationGroupRepository; |
|||
NotificationRepository = notificationRepository; |
|||
NotificationDefinitionSerializer = notificationDefinitionSerializer; |
|||
StoreCache = storeCache; |
|||
DistributedCache = distributedCache; |
|||
DistributedLock = distributedLock; |
|||
NotificationManagementOptions = notificationManagementOptions.Value; |
|||
CacheOptions = cacheOptions.Value; |
|||
} |
|||
|
|||
public async virtual Task<IReadOnlyList<NotificationGroupDefinition>> GetGroupsAsync() |
|||
public async virtual Task<NotificationDefinition> GetOrNullAsync(string name) |
|||
{ |
|||
if (!_notificationsManagementOptions.IsDynamicNotificationsStoreEnabled) |
|||
if (!NotificationManagementOptions.IsDynamicNotificationsStoreEnabled) |
|||
{ |
|||
return Array.Empty<NotificationGroupDefinition>(); |
|||
return null; |
|||
} |
|||
|
|||
using (await StoreCache.SyncSemaphore.LockAsync()) |
|||
{ |
|||
await EnsureCacheIsUptoDateAsync(); |
|||
return StoreCache.GetNotificationOrNull(name); |
|||
} |
|||
return await _dynamicNotificationDefinitionCache.GetGroupsAsync(); |
|||
} |
|||
|
|||
public async virtual Task<IReadOnlyList<NotificationDefinition>> GetNotificationsAsync() |
|||
{ |
|||
if (!_notificationsManagementOptions.IsDynamicNotificationsStoreEnabled) |
|||
if (!NotificationManagementOptions.IsDynamicNotificationsStoreEnabled) |
|||
{ |
|||
return Array.Empty<NotificationDefinition>(); |
|||
} |
|||
return await _dynamicNotificationDefinitionCache.GetNotificationsAsync(); |
|||
|
|||
using (await StoreCache.SyncSemaphore.LockAsync()) |
|||
{ |
|||
await EnsureCacheIsUptoDateAsync(); |
|||
return StoreCache.GetNotifications().ToImmutableList(); |
|||
} |
|||
} |
|||
|
|||
public async virtual Task<NotificationDefinition> GetOrNullAsync(string name) |
|||
public async virtual Task<IReadOnlyList<NotificationGroupDefinition>> GetGroupsAsync() |
|||
{ |
|||
if (!_notificationsManagementOptions.IsDynamicNotificationsStoreEnabled) |
|||
if (!NotificationManagementOptions.IsDynamicNotificationsStoreEnabled) |
|||
{ |
|||
return null; |
|||
return Array.Empty<NotificationGroupDefinition>(); |
|||
} |
|||
|
|||
using (await StoreCache.SyncSemaphore.LockAsync()) |
|||
{ |
|||
await EnsureCacheIsUptoDateAsync(); |
|||
return StoreCache.GetGroups().ToImmutableList(); |
|||
} |
|||
} |
|||
|
|||
protected async virtual Task EnsureCacheIsUptoDateAsync() |
|||
{ |
|||
if (StoreCache.LastCheckTime.HasValue && |
|||
DateTime.Now.Subtract(StoreCache.LastCheckTime.Value) < NotificationManagementOptions.NotificationsCacheRefreshInterval) |
|||
{ |
|||
/* We get the latest Notification with a small delay for optimization */ |
|||
return; |
|||
} |
|||
|
|||
var stampInDistributedCache = await GetOrSetStampInDistributedCache(); |
|||
|
|||
if (stampInDistributedCache == StoreCache.CacheStamp) |
|||
{ |
|||
StoreCache.LastCheckTime = DateTime.Now; |
|||
return; |
|||
} |
|||
return await _dynamicNotificationDefinitionCache.GetOrNullAsync(name); |
|||
|
|||
await UpdateInMemoryStoreCache(); |
|||
|
|||
StoreCache.CacheStamp = stampInDistributedCache; |
|||
StoreCache.LastCheckTime = DateTime.Now; |
|||
} |
|||
|
|||
protected async virtual Task UpdateInMemoryStoreCache() |
|||
{ |
|||
var NotificationGroupRecords = await NotificationGroupRepository.GetListAsync(); |
|||
var NotificationRecords = await NotificationRepository.GetListAsync(); |
|||
|
|||
await StoreCache.FillAsync(NotificationGroupRecords, NotificationRecords); |
|||
} |
|||
|
|||
protected async virtual Task<string> GetOrSetStampInDistributedCache() |
|||
{ |
|||
var cacheKey = GetCommonStampCacheKey(); |
|||
|
|||
var stampInDistributedCache = await DistributedCache.GetStringAsync(cacheKey); |
|||
if (stampInDistributedCache != null) |
|||
{ |
|||
return stampInDistributedCache; |
|||
} |
|||
|
|||
await using (var commonLockHandle = await DistributedLock |
|||
.TryAcquireAsync(GetCommonDistributedLockKey(), NotificationManagementOptions.NotificationsCacheStampTimeOut)) |
|||
{ |
|||
if (commonLockHandle == null) |
|||
{ |
|||
/* This request will fail */ |
|||
throw new AbpException( |
|||
"Could not acquire distributed lock for Notification definition common stamp check!" |
|||
); |
|||
} |
|||
|
|||
stampInDistributedCache = await DistributedCache.GetStringAsync(cacheKey); |
|||
if (stampInDistributedCache != null) |
|||
{ |
|||
return stampInDistributedCache; |
|||
} |
|||
|
|||
stampInDistributedCache = Guid.NewGuid().ToString(); |
|||
|
|||
await DistributedCache.SetStringAsync( |
|||
cacheKey, |
|||
stampInDistributedCache, |
|||
new DistributedCacheEntryOptions |
|||
{ |
|||
SlidingExpiration = NotificationManagementOptions.NotificationsCacheStampExpiration |
|||
} |
|||
); |
|||
} |
|||
|
|||
return stampInDistributedCache; |
|||
} |
|||
|
|||
protected virtual string GetCommonStampCacheKey() |
|||
{ |
|||
return $"{CacheOptions.KeyPrefix}_AbpInMemoryNotificationCacheStamp"; |
|||
} |
|||
|
|||
protected virtual string GetCommonDistributedLockKey() |
|||
{ |
|||
return $"{CacheOptions.KeyPrefix}_Common_AbpNotificationUpdateLock"; |
|||
} |
|||
} |
|||
|
|||
@ -1,13 +0,0 @@ |
|||
using System.Collections.Generic; |
|||
using System.Threading.Tasks; |
|||
|
|||
namespace LINGYUN.Abp.Notifications; |
|||
|
|||
public interface IDynamicNotificationDefinitionCache |
|||
{ |
|||
Task<NotificationDefinition> GetOrNullAsync(string name); |
|||
|
|||
Task<IReadOnlyList<NotificationDefinition>> GetNotificationsAsync(); |
|||
|
|||
Task<IReadOnlyList<NotificationGroupDefinition>> GetGroupsAsync(); |
|||
} |
|||
@ -0,0 +1,25 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Threading; |
|||
using System.Threading.Tasks; |
|||
|
|||
namespace LINGYUN.Abp.Notifications; |
|||
|
|||
public interface IDynamicNotificationDefinitionStoreCache |
|||
{ |
|||
string CacheStamp { get; set; } |
|||
|
|||
SemaphoreSlim SyncSemaphore { get; } |
|||
|
|||
DateTime? LastCheckTime { get; set; } |
|||
|
|||
Task FillAsync( |
|||
List<NotificationDefinitionGroupRecord> webhookGroupRecords, |
|||
List<NotificationDefinitionRecord> webhookRecords); |
|||
|
|||
NotificationDefinition GetNotificationOrNull(string name); |
|||
|
|||
IReadOnlyList<NotificationDefinition> GetNotifications(); |
|||
|
|||
IReadOnlyList<NotificationGroupDefinition> GetGroups(); |
|||
} |
|||
Loading…
Reference in new issue