Browse Source

Precache dynamic permission and refactor AbpPermissionManagementDomainModule class.

pull/13644/head
Halil İbrahim Kalkan 4 years ago
parent
commit
41ba0aba7c
  1. 133
      modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo/Abp/PermissionManagement/AbpPermissionManagementDomainModule.cs
  2. 73
      modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo/Abp/PermissionManagement/DynamicPermissionDefinitionStore.cs

133
modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo/Abp/PermissionManagement/AbpPermissionManagementDomainModule.cs

@ -7,6 +7,7 @@ using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
using Polly; using Polly;
using Volo.Abp.Authorization; using Volo.Abp.Authorization;
using Volo.Abp.Authorization.Permissions;
using Volo.Abp.Caching; using Volo.Abp.Caching;
using Volo.Abp.DependencyInjection; using Volo.Abp.DependencyInjection;
using Volo.Abp.Domain; using Volo.Abp.Domain;
@ -23,62 +24,124 @@ namespace Volo.Abp.PermissionManagement;
[DependsOn(typeof(AbpJsonModule))] [DependsOn(typeof(AbpJsonModule))]
public class AbpPermissionManagementDomainModule : AbpModule public class AbpPermissionManagementDomainModule : AbpModule
{ {
private readonly CancellationTokenSource _cancellationTokenSource = new();
public override Task OnApplicationInitializationAsync(ApplicationInitializationContext context) public override Task OnApplicationInitializationAsync(ApplicationInitializationContext context)
{ {
if (context InitializeDynamicPermissions(context);
return Task.CompletedTask;
}
public override Task OnApplicationShutdownAsync(ApplicationShutdownContext context)
{
_cancellationTokenSource.Cancel();
return Task.CompletedTask;
}
private void InitializeDynamicPermissions(ApplicationInitializationContext context)
{
var options = context
.ServiceProvider .ServiceProvider
.GetRequiredService<IOptions<PermissionManagementOptions>>() .GetRequiredService<IOptions<PermissionManagementOptions>>()
.Value .Value;
.SaveStaticPermissionsToDatabase)
if (!options.SaveStaticPermissionsToDatabase && !options.IsDynamicPermissionStoreEnabled)
{ {
SaveStaticPermissionsToDatabase(context); return;
} }
return Task.CompletedTask;
}
private static void SaveStaticPermissionsToDatabase(ApplicationInitializationContext context)
{
var rootServiceProvider = context.ServiceProvider.GetRequiredService<IRootServiceProvider>(); var rootServiceProvider = context.ServiceProvider.GetRequiredService<IRootServiceProvider>();
var hostApplicationLifetime = context.ServiceProvider.GetService<IHostApplicationLifetime>();
var cancellationToken = hostApplicationLifetime?.ApplicationStopping ?? CancellationToken.None;
Task.Run(async () => Task.Run(async () =>
{ {
using var scope = rootServiceProvider.CreateScope(); using var scope = rootServiceProvider.CreateScope();
var applicationLifetime = scope.ServiceProvider.GetService<IHostApplicationLifetime>();
var cancellationTokenProvider = scope.ServiceProvider.GetRequiredService<ICancellationTokenProvider>(); var cancellationTokenProvider = scope.ServiceProvider.GetRequiredService<ICancellationTokenProvider>();
var cancellationToken = applicationLifetime?.ApplicationStopping ?? _cancellationTokenSource.Token;
try try
{ {
using (cancellationTokenProvider.Use(cancellationToken)) using (cancellationTokenProvider.Use(cancellationToken))
{ {
await Policy if (cancellationTokenProvider.Token.IsCancellationRequested)
.Handle<Exception>() {
.WaitAndRetryAsync(8, retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt) * 10)) return;
.ExecuteAsync(async _ => }
{
try await SaveStaticPermissionsToDatabaseAsync(options, scope, cancellationTokenProvider);
{
// ReSharper disable once AccessToDisposedClosure if (cancellationTokenProvider.Token.IsCancellationRequested)
await scope {
.ServiceProvider return;
.GetRequiredService<IStaticPermissionSaver>() }
.SaveAsync();
} await PreCacheDynamicPermissionsAsync(options, scope);
catch (Exception ex)
{
// ReSharper disable once AccessToDisposedClosure
scope.ServiceProvider
.GetService<ILogger<AbpPermissionManagementDomainModule>>()?
.LogException(ex);
throw; // Polly will catch it
}
}, cancellationTokenProvider.Token);
} }
} }
// ReSharper disable once EmptyGeneralCatchClause (No need to log since it is logged above) // ReSharper disable once EmptyGeneralCatchClause (No need to log since it is logged above)
catch { } catch { }
}); });
} }
private async static Task SaveStaticPermissionsToDatabaseAsync(
PermissionManagementOptions options,
IServiceScope scope,
ICancellationTokenProvider cancellationTokenProvider)
{
if (!options.SaveStaticPermissionsToDatabase)
{
return;
}
await Policy
.Handle<Exception>()
.WaitAndRetryAsync(8, retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt) * 10))
.ExecuteAsync(async _ =>
{
try
{
// ReSharper disable once AccessToDisposedClosure
await scope
.ServiceProvider
.GetRequiredService<IStaticPermissionSaver>()
.SaveAsync();
}
catch (Exception ex)
{
// ReSharper disable once AccessToDisposedClosure
scope.ServiceProvider
.GetService<ILogger<AbpPermissionManagementDomainModule>>()?
.LogException(ex);
throw; // Polly will catch it
}
}, cancellationTokenProvider.Token);
}
private async static Task PreCacheDynamicPermissionsAsync(PermissionManagementOptions options, IServiceScope scope)
{
if (!options.IsDynamicPermissionStoreEnabled)
{
return;
}
try
{
// Pre-cache permissions, so first request doesn't wait
await scope
.ServiceProvider
.GetRequiredService<IDynamicPermissionDefinitionStore>()
.GetGroupsAsync();
}
catch (Exception ex)
{
// ReSharper disable once AccessToDisposedClosure
scope
.ServiceProvider
.GetService<ILogger<AbpPermissionManagementDomainModule>>()?
.LogException(ex);
throw; // It will be cached in InitializeDynamicPermissions
}
}
} }

73
modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo/Abp/PermissionManagement/DynamicPermissionDefinitionStore.cs

@ -1,5 +1,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.Immutable;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.Extensions.Caching.Distributed; using Microsoft.Extensions.Caching.Distributed;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
@ -49,9 +50,12 @@ public class DynamicPermissionDefinitionStore : IDynamicPermissionDefinitionStor
{ {
return null; return null;
} }
await EnsureCacheIsUptoDateAsync(); using (await StoreCache.SyncSemaphore.LockAsync())
return StoreCache.GetPermissionOrNull(name); {
await EnsureCacheIsUptoDateAsync();
return StoreCache.GetPermissionOrNull(name);
}
} }
public virtual async Task<IReadOnlyList<PermissionDefinition>> GetPermissionsAsync() public virtual async Task<IReadOnlyList<PermissionDefinition>> GetPermissionsAsync()
@ -60,9 +64,12 @@ public class DynamicPermissionDefinitionStore : IDynamicPermissionDefinitionStor
{ {
return Array.Empty<PermissionDefinition>(); return Array.Empty<PermissionDefinition>();
} }
await EnsureCacheIsUptoDateAsync(); using (await StoreCache.SyncSemaphore.LockAsync())
return StoreCache.GetPermissions(); {
await EnsureCacheIsUptoDateAsync();
return StoreCache.GetPermissions().ToImmutableList();
}
} }
public virtual async Task<IReadOnlyList<PermissionGroupDefinition>> GetGroupsAsync() public virtual async Task<IReadOnlyList<PermissionGroupDefinition>> GetGroupsAsync()
@ -71,38 +78,38 @@ public class DynamicPermissionDefinitionStore : IDynamicPermissionDefinitionStor
{ {
return Array.Empty<PermissionGroupDefinition>(); return Array.Empty<PermissionGroupDefinition>();
} }
await EnsureCacheIsUptoDateAsync();
return StoreCache.GetGroups();
}
private async Task EnsureCacheIsUptoDateAsync()
{
using (await StoreCache.SyncSemaphore.LockAsync()) using (await StoreCache.SyncSemaphore.LockAsync())
{ {
if (StoreCache.LastCheckTime.HasValue && await EnsureCacheIsUptoDateAsync();
DateTime.Now.Subtract(StoreCache.LastCheckTime.Value).TotalSeconds < 30) return StoreCache.GetGroups().ToImmutableList();
{ }
/* We get the latest permission with a small delay for optimization */ }
return;
}
var stampInDistributedCache = await GetOrSetStampInDistributedCache();
if (stampInDistributedCache == StoreCache.CacheStamp)
{
StoreCache.LastCheckTime = DateTime.Now;
return;
}
await UpdateInMemoryStoreCache();
StoreCache.CacheStamp = stampInDistributedCache; protected virtual async Task EnsureCacheIsUptoDateAsync()
{
if (StoreCache.LastCheckTime.HasValue &&
DateTime.Now.Subtract(StoreCache.LastCheckTime.Value).TotalSeconds < 30)
{
/* We get the latest permission with a small delay for optimization */
return;
}
var stampInDistributedCache = await GetOrSetStampInDistributedCache();
if (stampInDistributedCache == StoreCache.CacheStamp)
{
StoreCache.LastCheckTime = DateTime.Now; StoreCache.LastCheckTime = DateTime.Now;
return;
} }
await UpdateInMemoryStoreCache();
StoreCache.CacheStamp = stampInDistributedCache;
StoreCache.LastCheckTime = DateTime.Now;
} }
private async Task UpdateInMemoryStoreCache() protected virtual async Task UpdateInMemoryStoreCache()
{ {
var permissionGroupRecords = await PermissionGroupRepository.GetListAsync(); var permissionGroupRecords = await PermissionGroupRepository.GetListAsync();
var permissionRecords = await PermissionRepository.GetListAsync(); var permissionRecords = await PermissionRepository.GetListAsync();
@ -110,7 +117,7 @@ public class DynamicPermissionDefinitionStore : IDynamicPermissionDefinitionStor
await StoreCache.FillAsync(permissionGroupRecords, permissionRecords); await StoreCache.FillAsync(permissionGroupRecords, permissionRecords);
} }
private async Task<string> GetOrSetStampInDistributedCache() protected virtual async Task<string> GetOrSetStampInDistributedCache()
{ {
var cacheKey = GetCommonStampCacheKey(); var cacheKey = GetCommonStampCacheKey();
@ -152,12 +159,12 @@ public class DynamicPermissionDefinitionStore : IDynamicPermissionDefinitionStor
return stampInDistributedCache; return stampInDistributedCache;
} }
private string GetCommonStampCacheKey() protected virtual string GetCommonStampCacheKey()
{ {
return $"{CacheOptions.KeyPrefix}_AbpInMemoryPermissionCacheStamp"; return $"{CacheOptions.KeyPrefix}_AbpInMemoryPermissionCacheStamp";
} }
private string GetCommonDistributedLockKey() protected virtual string GetCommonDistributedLockKey()
{ {
return $"{CacheOptions.KeyPrefix}_Common_AbpPermissionUpdateLock"; return $"{CacheOptions.KeyPrefix}_Common_AbpPermissionUpdateLock";
} }

Loading…
Cancel
Save