Skip to content

Commit

Permalink
Merge pull request #18412 from abpframework/Clear-Tenant-Cache
Browse files Browse the repository at this point in the history
Clear the tenant cache when creating/update/deleting.
  • Loading branch information
hikalkan authored Dec 30, 2023
2 parents eb75051 + dfac82c commit 3221e89
Show file tree
Hide file tree
Showing 11 changed files with 187 additions and 167 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,10 @@ namespace Volo.Abp.AspNetCore.Mvc.Client;

public class AbpAspNetCoreMvcClientCacheOptions
{
public TimeSpan TenantConfigurationCacheAbsoluteExpiration { get; set; }

public TimeSpan ApplicationConfigurationDtoCacheAbsoluteExpiration { get; set; }

public AbpAspNetCoreMvcClientCacheOptions()
{
TenantConfigurationCacheAbsoluteExpiration = TimeSpan.FromMinutes(5);
ApplicationConfigurationDtoCacheAbsoluteExpiration = TimeSpan.FromSeconds(300);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ public override void ConfigureServices(ServiceConfigurationContext context)
{
Configure<AbpAspNetCoreMvcClientCacheOptions>(options =>
{
options.TenantConfigurationCacheAbsoluteExpiration = TimeSpan.FromSeconds(5);
options.ApplicationConfigurationDtoCacheAbsoluteExpiration = TimeSpan.FromSeconds(5);
});
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
using System;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Caching.Distributed;
using Microsoft.Extensions.Options;
using Pages.Abp.MultiTenancy.ClientProxies;
using Volo.Abp.AspNetCore.Mvc.MultiTenancy;
using Volo.Abp.Caching;
using Volo.Abp.DependencyInjection;
using Volo.Abp.MultiTenancy;
Expand All @@ -16,13 +14,13 @@ public class MvcRemoteTenantStore : ITenantStore, ITransientDependency
{
protected AbpTenantClientProxy TenantAppService { get; }
protected IHttpContextAccessor HttpContextAccessor { get; }
protected IDistributedCache<TenantConfiguration> Cache { get; }
protected IDistributedCache<TenantConfigurationCacheItem> Cache { get; }
protected AbpAspNetCoreMvcClientCacheOptions Options { get; }

public MvcRemoteTenantStore(
AbpTenantClientProxy tenantAppService,
IHttpContextAccessor httpContextAccessor,
IDistributedCache<TenantConfiguration> cache,
IDistributedCache<TenantConfigurationCacheItem> cache,
IOptions<AbpAspNetCoreMvcClientCacheOptions> options)
{
TenantAppService = tenantAppService;
Expand All @@ -33,129 +31,101 @@ public MvcRemoteTenantStore(

public async Task<TenantConfiguration?> FindAsync(string name)
{
var cacheKey = CreateCacheKey(name);
var cacheKey = TenantConfigurationCacheItem.CalculateCacheKey(name);
var httpContext = HttpContextAccessor?.HttpContext;

if (httpContext != null && httpContext.Items[cacheKey] is TenantConfiguration tenantConfiguration)
if (httpContext != null && httpContext.Items[cacheKey] is TenantConfigurationCacheItem tenantConfigurationInHttpContext)
{
return tenantConfiguration;
return tenantConfigurationInHttpContext?.Value;
}

tenantConfiguration = (await Cache.GetOrAddAsync(
cacheKey,
async () => CreateTenantConfiguration(await TenantAppService.FindTenantByNameAsync(name))!,
() => new DistributedCacheEntryOptions
{
AbsoluteExpirationRelativeToNow = Options.TenantConfigurationCacheAbsoluteExpiration
}
))!;
var tenantConfiguration = await Cache.GetAsync(cacheKey);
if (tenantConfiguration == null)
{
await TenantAppService.FindTenantByNameAsync(name);
tenantConfiguration = await Cache.GetAsync(cacheKey);
}

if (httpContext != null)
{
httpContext.Items[cacheKey] = tenantConfiguration;
}

return tenantConfiguration;
return tenantConfiguration?.Value;
}

public async Task<TenantConfiguration?> FindAsync(Guid id)
{
var cacheKey = CreateCacheKey(id);
var cacheKey = TenantConfigurationCacheItem.CalculateCacheKey(id);
var httpContext = HttpContextAccessor?.HttpContext;

if (httpContext != null && httpContext.Items[cacheKey] is TenantConfiguration tenantConfiguration)
if (httpContext != null && httpContext.Items[cacheKey] is TenantConfigurationCacheItem tenantConfigurationInHttpContext)
{
return tenantConfiguration;
return tenantConfigurationInHttpContext?.Value;
}

tenantConfiguration = (await Cache.GetOrAddAsync(
cacheKey,
async () => CreateTenantConfiguration(await TenantAppService.FindTenantByIdAsync(id))!,
() => new DistributedCacheEntryOptions
{
AbsoluteExpirationRelativeToNow = Options.TenantConfigurationCacheAbsoluteExpiration
}
))!;
var tenantConfiguration = await Cache.GetAsync(cacheKey);
if (tenantConfiguration == null)
{
await TenantAppService.FindTenantByIdAsync(id);
tenantConfiguration = await Cache.GetAsync(cacheKey);
}

if (httpContext != null)
{
httpContext.Items[cacheKey] = tenantConfiguration;
}

return tenantConfiguration;
return tenantConfiguration?.Value;
}

public TenantConfiguration Find(string name)
public TenantConfiguration? Find(string name)
{
var cacheKey = CreateCacheKey(name);
var cacheKey = TenantConfigurationCacheItem.CalculateCacheKey(name);
var httpContext = HttpContextAccessor?.HttpContext;

if (httpContext != null && httpContext.Items[cacheKey] is TenantConfiguration tenantConfiguration)
if (httpContext != null && httpContext.Items[cacheKey] is TenantConfigurationCacheItem tenantConfigurationInHttpContext)
{
return tenantConfiguration;
return tenantConfigurationInHttpContext?.Value;
}

tenantConfiguration = Cache.GetOrAdd(
cacheKey,
() => AsyncHelper.RunSync(async () => CreateTenantConfiguration(await TenantAppService.FindTenantByNameAsync(name))!),
() => new DistributedCacheEntryOptions
{
AbsoluteExpirationRelativeToNow = Options.TenantConfigurationCacheAbsoluteExpiration
}
)!;
var tenantConfiguration = Cache.Get(cacheKey);
if (tenantConfiguration == null)
{
AsyncHelper.RunSync(async () => await TenantAppService.FindTenantByNameAsync(name));
tenantConfiguration = Cache.Get(cacheKey);
}

if (httpContext != null)
{
httpContext.Items[cacheKey] = tenantConfiguration;
}

return tenantConfiguration;
return tenantConfiguration?.Value;
}

public TenantConfiguration Find(Guid id)
public TenantConfiguration? Find(Guid id)
{
var cacheKey = CreateCacheKey(id);
var cacheKey = TenantConfigurationCacheItem.CalculateCacheKey(id);
var httpContext = HttpContextAccessor?.HttpContext;

if (httpContext != null && httpContext.Items[cacheKey] is TenantConfiguration tenantConfiguration)
if (httpContext != null && httpContext.Items[cacheKey] is TenantConfigurationCacheItem tenantConfigurationInHttpContext)
{
return tenantConfiguration;
return tenantConfigurationInHttpContext?.Value;
}

tenantConfiguration = Cache.GetOrAdd(
cacheKey,
() => AsyncHelper.RunSync(async () => CreateTenantConfiguration(await TenantAppService.FindTenantByIdAsync(id))!),
() => new DistributedCacheEntryOptions
{
AbsoluteExpirationRelativeToNow = Options.TenantConfigurationCacheAbsoluteExpiration
}
)!;

if (httpContext != null)
var tenantConfiguration = Cache.Get(cacheKey);
if (tenantConfiguration == null)
{
httpContext.Items[cacheKey] = tenantConfiguration;
AsyncHelper.RunSync(async () => await TenantAppService.FindTenantByIdAsync(id));
tenantConfiguration = Cache.Get(cacheKey);
}

return tenantConfiguration;
}

protected virtual TenantConfiguration? CreateTenantConfiguration(FindTenantResultDto tenantResultDto)
{
if (!tenantResultDto.Success || tenantResultDto.TenantId == null)
if (httpContext != null)
{
return null;
httpContext.Items[cacheKey] = tenantConfiguration;
}

return new TenantConfiguration(tenantResultDto.TenantId.Value, tenantResultDto.Name!);
}

protected virtual string CreateCacheKey(string tenantName)
{
return $"RemoteTenantStore_Name_{tenantName}";
}

protected virtual string CreateCacheKey(Guid tenantId)
{
return $"RemoteTenantStore_Id_{tenantId:N}";
return tenantConfiguration?.Value;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
using System;

namespace Volo.Abp.MultiTenancy;

[Serializable]
[IgnoreMultiTenancy]
public class TenantConfigurationCacheItem
{
private const string CacheKeyFormat = "i:{0},n:{1}";

public TenantConfiguration? Value { get; set; }

public TenantConfigurationCacheItem()
{

}

public TenantConfigurationCacheItem(TenantConfiguration? value)
{
Value = value;
}

public static string CalculateCacheKey(Guid? id, string? name)
{
if (id == null && name.IsNullOrWhiteSpace())
{
throw new AbpException("Both id and name can't be invalid.");
}
return string.Format(CacheKeyFormat,
id?.ToString() ?? "null",
(name.IsNullOrWhiteSpace() ? "null" : name));
}

public static string CalculateCacheKey(Guid id)
{
return string.Format(CacheKeyFormat, id.ToString(), "null" );
}

public static string CalculateCacheKey(string name)
{
return string.Format(CacheKeyFormat, "null", name);
}
}

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
using System;
using System.Threading.Tasks;
using Volo.Abp.Caching;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Domain.Entities.Events;
using Volo.Abp.EventBus;
using Volo.Abp.MultiTenancy;

namespace Volo.Abp.TenantManagement;


public class TenantConfigurationCacheItemInvalidator :
ILocalEventHandler<EntityChangedEventData<Tenant>>,
ILocalEventHandler<EntityDeletedEventData<Tenant>>, ITransientDependency
{
protected IDistributedCache<TenantConfigurationCacheItem> Cache { get; }

public TenantConfigurationCacheItemInvalidator(IDistributedCache<TenantConfigurationCacheItem> cache)
{
Cache = cache;
}

public virtual async Task HandleEventAsync(EntityChangedEventData<Tenant> eventData)
{
await ClearCacheAsync(eventData.Entity.Id, eventData.Entity.Name);
}

public virtual async Task HandleEventAsync(EntityDeletedEventData<Tenant> eventData)
{
await ClearCacheAsync(eventData.Entity.Id, eventData.Entity.Name);
}

protected virtual async Task ClearCacheAsync(Guid? id, string name)
{
await Cache.RemoveManyAsync(
new[]
{
TenantConfigurationCacheItem.CalculateCacheKey(id, null),
TenantConfigurationCacheItem.CalculateCacheKey(null, name),
});
}
}
Original file line number Diff line number Diff line change
@@ -1,17 +1,21 @@
using System;
using System.Threading.Tasks;
using Volo.Abp.Caching;
using Volo.Abp.Domain.Services;
using Volo.Abp.MultiTenancy;

namespace Volo.Abp.TenantManagement;

public class TenantManager : DomainService, ITenantManager
{
protected ITenantRepository TenantRepository { get; }
protected IDistributedCache<TenantConfigurationCacheItem> Cache { get; }

public TenantManager(ITenantRepository tenantRepository)
public TenantManager(ITenantRepository tenantRepository,
IDistributedCache<TenantConfigurationCacheItem> cache)
{
TenantRepository = tenantRepository;

Cache = cache;
}

public virtual async Task<Tenant> CreateAsync(string name)
Expand All @@ -28,6 +32,7 @@ public virtual async Task ChangeNameAsync(Tenant tenant, string name)
Check.NotNull(name, nameof(name));

await ValidateNameAsync(name, tenant.Id);
await Cache.RemoveAsync(TenantConfigurationCacheItem.CalculateCacheKey(tenant.Name));
tenant.SetName(name);
}

Expand Down
Loading

0 comments on commit 3221e89

Please sign in to comment.