Base solution for your next web application
Open Closed

Implementing AbpPerRequestRedisCache to improve performance #10717


User avatar
0
Ricavir created

ABP 6.4 Angular .NET 5

Hi,

I'm using zero for a while now and the journey for getting a performant app is quite hard :) As the trafic grows, I had to switch SignalR from self hosted to dedicated service. I also had to configure app scaling according to trafic load. I'm running on Azure and my app is currently scaling out to 2 or 3 instances.

Multi-instances means also cache issues... then I switch MemoryCache to RedisCache. Every thing works as expected on production but the app is, at least, two times slower when it comes to get information from cache. GetAll method access as increased a lot...

I've followed all previous post about Redis performance and AbpPerRequestRedisCache.

Should I use AbpPerRequestRedisCache for the entire project ? What happens if a tenant setting is changed in one instance ? The other instances will not be aware of it and will use invalid data no ?

I've tried to test it by following the docs : https://aspnetboilerplate.com/Pages/Documents/PerRequestRedisCache I was able to download nuget package and add [DependsOn(typeof(AbpAspNetCorePerRequestRedisCacheModule))] but I don't found the way to activate option usePerRequestRedisCache to true

Configuration.Caching.UseRedis(usePerRequestRedisCache: true);

How can I activate it ?

Sorry for all that questions but app performance is really sensitive for all users


6 Answer(s)
  • User Avatar
    0
    ismcagdas created
    Support Team

    Hi @ricavir

    Yes, for performance purposes, we suggest using AbpPerRequestRedisCache. This class only caches the values during a request. So, if the value of a cache item is changed in another instance. It will be used for the next HTTP reqeust, so it will not be a problem.

    UsePerRequestRedisCache is currently available in ABP 6.6 and 7.0-rc.1.

  • User Avatar
    0
    Ricavir created

    Hi @ismcagdas,

    Tks for your quick answer. I will do it.

  • User Avatar
    0
    Ricavir created

    Hi @ismcagdas,

    I've updated the project to Abp 6.6 to test redis usePerRequestRedisCache option. When I activate redis with this option, I'm getting an Internal Server Error :

    ERROR 2021-11-30 18:23:14,135 [36   ] Mvc.ExceptionHandling.AbpExceptionFilter - Unable to cast object of type 'Abp.MultiTenancy.TenantCacheItem' to type 'System.Collections.Generic.Dictionary`2[System.String,Abp.Localization.ApplicationLanguage]'.
    System.InvalidCastException: Unable to cast object of type 'Abp.MultiTenancy.TenantCacheItem' to type 'System.Collections.Generic.Dictionary`2[System.String,Abp.Localization.ApplicationLanguage]'.
       at Abp.Runtime.Caching.TypedCacheWrapper`2.Get(TKey key, Func`2 factory)
       at Abp.Runtime.Caching.TypedCacheExtensions.Get[TKey,TValue](ITypedCache`2 cache, TKey key, Func`1 factory)
       at Abp.Localization.ApplicationLanguageManager.GetLanguageDictionaryFromCache(Nullable`1 tenantId)
       at Abp.Localization.ApplicationLanguageManager.GetLanguageDictionary(Nullable`1 tenantId)
       at Abp.Localization.ApplicationLanguageManager.GetLanguages(Nullable`1 tenantId)
       at Abp.Localization.ApplicationLanguageManager.GetActiveLanguages(Nullable`1 tenantId)
       at Abp.Localization.ApplicationLanguageProvider.GetActiveLanguages()
       at Abp.Localization.LanguageManager.GetActiveLanguages()
       at Abp.Web.Configuration.AbpUserConfigurationBuilder.GetUserLocalizationConfig()
       at Abp.Web.Configuration.AbpUserConfigurationBuilder.GetAll()
       at Abp.AspNetCore.Mvc.Controllers.AbpUserConfigurationController.GetAll()
       at lambda_method1396(Closure , Object )
       at Microsoft.AspNetCore.Mvc.Infrastructure.ActionMethodExecutor.TaskOfActionResultExecutor.Execute(IActionResultTypeMapper mapper, ObjectMethodExecutor executor, Object controller, Object[] arguments)
       at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.<InvokeActionMethodAsync>g__Awaited|12_0(ControllerActionInvoker invoker, ValueTask`1 actionResultValueTask)
       at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.&lt;InvokeNextActionFilterAsync&gt;g__Awaited|10_0(ControllerActionInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
       at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Rethrow(ActionExecutedContextSealed context)
       at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)
       at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.&lt;InvokeInnerFilterAsync&gt;g__Awaited|13_0(ControllerActionInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
       at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.&lt;InvokeNextExceptionFilterAsync&gt;g__Awaited|25_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
    

    I've followed abp documentation to activate this option : import NuGet package and add depends on. Then, I use it like that :

    Configuration.Caching.UseRedis(options =>
                    {
                        options.ConnectionString = _appConfiguration["Abp:RedisCache:ConnectionString"];
                        options.DatabaseId = _appConfiguration.GetValue&lt;int&gt;("Abp:RedisCache:DatabaseId");
                    }, usePerRequestRedisCache: true);
    

    if I deactivate option usePerRequestRedisCache, the app works again.

    Could you please check it ?

  • User Avatar
    0
    ismcagdas created
    Support Team

    Hi,

    Could you upgrade to 6.6.1 and use it as explained here https://aspnetboilerplate.com/Pages/Documents/PerRequestRedisCache ?

    Thanks

  • User Avatar
    0
    Ricavir created

    Hi @ismcagdas,

    I just tried with 6.6.1 and it is not working. If I deactivate usePerRequestRedisCache option like below, everything works again :

    Configuration.Caching.UseRedis(options =>
                    {
                        options.ConnectionString = _appConfiguration["Abp:RedisCache:ConnectionString"];
                        options.DatabaseId = _appConfiguration.GetValue<int>("Abp:RedisCache:DatabaseId");
                    }, usePerRequestRedisCache : false);
    

    If I connect to host, I have following error :

    ERROR 2021-12-02 16:18:33,653 [28   ] Mvc.ExceptionHandling.AbpExceptionFilter - Unable to cast object of type 'Abp.Authorization.Users.UserPermissionCacheItem' to type 'Abp.Authorization.Roles.RolePermissionCacheItem'.
    System.InvalidCastException: Unable to cast object of type 'Abp.Authorization.Users.UserPermissionCacheItem' to type 'Abp.Authorization.Roles.RolePermissionCacheItem'.
       at Abp.Runtime.Caching.TypedCacheWrapper`2.GetAsync(TKey key, Func`2 factory)
       at Abp.Authorization.Roles.AbpRoleManager`2.GetRolePermissionCacheItemAsync(Int32 roleId)
       at Abp.Authorization.Roles.AbpRoleManager`2.IsGrantedAsync(Int32 roleId, Permission permission)
       at Abp.Authorization.Users.AbpUserManager`2.IsGrantedAsync(Int64 userId, Permission permission)
       at Abp.Authorization.Users.AbpUserManager`2.IsGrantedAsync(Int64 userId, String permissionName)
       at Abp.Authorization.PermissionChecker`2.IsGrantedAsync(Int64 userId, String permissionName)
       at Abp.Authorization.PermissionChecker`2.IsGrantedAsync(String permissionName)
       at Abp.Web.Configuration.AbpUserConfigurationBuilder.GetUserAuthConfig()
       at Abp.Web.Configuration.AbpUserConfigurationBuilder.GetAll()
       at Abp.AspNetCore.Mvc.Controllers.AbpUserConfigurationController.GetAll()
       at lambda_method1396(Closure , Object )
       at Microsoft.AspNetCore.Mvc.Infrastructure.ActionMethodExecutor.TaskOfActionResultExecutor.Execute(IActionResultTypeMapper mapper, ObjectMethodExecutor executor, Object controller, Object[] arguments)
       at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.<InvokeActionMethodAsync>g__Awaited|12_0(ControllerActionInvoker invoker, ValueTask`1 actionResultValueTask)
       at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.&lt;InvokeNextActionFilterAsync&gt;g__Awaited|10_0(ControllerActionInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
       at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Rethrow(ActionExecutedContextSealed context)
       at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)
       at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.&lt;InvokeInnerFilterAsync&gt;g__Awaited|13_0(ControllerActionInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
       at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.&lt;InvokeNextExceptionFilterAsync&gt;g__Awaited|25_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
    

    If I connect to a tenant, I have following error :

    ERROR 2021-12-02 16:18:54,004 [33   ] Mvc.ExceptionHandling.AbpExceptionFilter - Unable to cast object of type 'Abp.MultiTenancy.TenantCacheItem' to type 'System.Collections.Generic.Dictionary`2[System.String,Abp.Localization.ApplicationLanguage]'.
    System.InvalidCastException: Unable to cast object of type 'Abp.MultiTenancy.TenantCacheItem' to type 'System.Collections.Generic.Dictionary`2[System.String,Abp.Localization.ApplicationLanguage]'.
       at Abp.Runtime.Caching.TypedCacheWrapper`2.Get(TKey key, Func`2 factory)
       at Abp.Runtime.Caching.TypedCacheExtensions.Get[TKey,TValue](ITypedCache`2 cache, TKey key, Func`1 factory)
       at Abp.Localization.ApplicationLanguageManager.GetLanguageDictionaryFromCache(Nullable`1 tenantId)
       at Abp.Localization.ApplicationLanguageManager.GetLanguageDictionary(Nullable`1 tenantId)
       at Abp.Localization.ApplicationLanguageManager.GetLanguages(Nullable`1 tenantId)
       at Abp.Localization.ApplicationLanguageManager.GetActiveLanguages(Nullable`1 tenantId)
       at Abp.Localization.ApplicationLanguageProvider.GetActiveLanguages()
       at Abp.Localization.LanguageManager.GetActiveLanguages()
       at Abp.Web.Configuration.AbpUserConfigurationBuilder.GetUserLocalizationConfig()
       at Abp.Web.Configuration.AbpUserConfigurationBuilder.GetAll()
       at Abp.AspNetCore.Mvc.Controllers.AbpUserConfigurationController.GetAll()
       at lambda_method1396(Closure , Object )
       at Microsoft.AspNetCore.Mvc.Infrastructure.ActionMethodExecutor.TaskOfActionResultExecutor.Execute(IActionResultTypeMapper mapper, ObjectMethodExecutor executor, Object controller, Object[] arguments)
       at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.&lt;InvokeActionMethodAsync&gt;g__Awaited|12_0(ControllerActionInvoker invoker, ValueTask`1 actionResultValueTask)
       at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.<InvokeNextActionFilterAsync>g__Awaited|10_0(ControllerActionInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
       at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Rethrow(ActionExecutedContextSealed context)
       at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)
       at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.<InvokeInnerFilterAsync>g__Awaited|13_0(ControllerActionInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
       at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeNextExceptionFilterAsync>g__Awaited|25_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
    
  • User Avatar
    0
    ismcagdas created
    Support Team

    Hi @ricavir

    I will test this again and inform you.