Activities of "alexander.nikonov"

I thought I have dealt with #1, but now all of the sudden it does not work again!

The framework will register all IDistributedCache as Singleton. You dont need to register it again.

Thank you, I've removed excessive registrations.

Memory cache cannot cross project/application process

This is an important note. But I want to clarify. Let's say I have IDistributedCache<CacheItem> and CacheManager, which is also registered as a singleton. CacheManager reads and updates the CacheItem. Both classes - CacheItem and CacheManager reside in Application tier. So when I use CacheManager DI from Application tier - all cache operations work predictable. However, when I invoke CacheManager DI to update the cache from HttpApi.Host tier - I have the feeling that it updates a different cache! So - are you implying that it's a normal behavior and these are in fact two different caches - on two different solution tiers despite the fact that HttpApi.Host layer has project reference to Application?

Sorry - I am lack of time for building the scenario in a test project, considering this described issue is not the only weird thing, there are more coming (this all is related):

  1. I have managed to overcome the described problem: instead of retrieving cockpitCacheManager instance using _serviceProvider.GetRequiredService<CockpitCacheManager>() inside MyRabbitMqReceiver I retrieved it using _serviceProvider.GetRequiredService<IHost>().Services.GetRequiredService<CockpitCacheManager>(); - this way the cache item was not null anymore; to me it looked like using a common IServiceProvider for both singletons;

  2. however updating the cache still causes me the troubles - I don't know why, just thinking aloud: probably it also has something to do with DI...

What is a proper way to update the cache? Am I missing something important here or doing it in a wrong way? Trying to keep the code simple and schematic as much as possible (as always, I am not allowed to share the full code):

    public class CockpitCacheManager
    {
        ...
        public async Task&lt;bool&gt; UpdateAndNotifyClientsAsync(CockpitCacheUpdateDto input)
        {
            using (await _cockpitCacheUpdateSemaphore.LockAsync())
            {
                (var cockpitCacheItem, var isCacheHit) = await GetItemAsync(isForceRefresh); //reading the cache using GetOrAddAsync under the hood

                if (cockpitCacheItem == null)
                {
                    return false;
                }

                using (var scope = _host.Services.CreateScope()) //should the scope look like this?
                {
                    var caseRepository = scope.ServiceProvider.GetRequiredService&lt;ICaseRepository&gt;();
                    ...
                    //updating cache item:
                    if (value1 != null) //input parameter 1
                    {
                        cockpitCacheItem.Field1 = value1;
                    }
                    ...
                    if (valueN != null) //input parameter N
                    {
                        cockpitCacheItem.FieldN = valueN;
                    }
                    ...
                    if (cacheChanged) //old and new cache hash is calculated
                    {
                        await _cockpitCache.SetAsync(CockpitCacheItem.Key, cockpitCacheItem);
                    }
                }
            }
        }
    }        

ABP Framework version: v7.0.1 UI Type: Angular Database System: EF Core Auth Server Separated

I observe strange behavior of ABP memory cache (DistributedCache). But I suspect it has something to do with the relevant services DI registrations.

On Application level, I register two DI instances as a singleton: a cache and a cache manager which reads or update this cache (and yes - I DO have this on Domain level - DependsOn[(...typeof(AbpCachingModule)...)]):

    public class MyApplicationModule : AbpModule
    {
        public override void ConfigureServices(ServiceConfigurationContext context)
        {
            ...
            context.Services.AddSingleton&lt;IDistributedCache&lt;CockpitCacheItem&gt;, DistributedCache&lt;CockpitCacheItem&gt;>();
            context.Services.AddSingleton&lt;CockpitCacheManager&gt;((IServiceProvider provider) => new CockpitCacheManager(provider));
            ...
        }
    }

So I have a single cache instance of this type per solution. It is identified by a single name - so there is only ONE cache ITEM per solution.

However, when I call the method cockpitCacheManager.UpdateAsync - which retrieves and updates the cache item - from HttpApi.Host project (class MyRabbitMqReceiver below), FOR THE FIRST TIME it ALWAYS reports that the cache item is not present in the cache (is null) - so it takes the data from DB. And beginning from the next invocation it's already OK (the item is in cache):

    public class MyHttpApiHostModule : AbpModule
    {
        public override void ConfigureServices(ServiceConfigurationContext context)
        {
            ...
            context.Services.AddSingleton((IServiceProvider provider) => new MyRabbitMqReceiver(provider, "myQueueName"));
            ...
        }
    }
 
    public class MyRabbitMqReceiver: RabbitMqReceiverBase
    {
        ...
        private async Task HandleCacheItemAsync(...)
        {
            var cockpitCacheManager = _serviceProvider.GetService&lt;CockpitCacheManager&gt;();
            await cockpitCacheManager.UpdateAsync(cacheFieldValue1, cacheFieldValue2); //IT DOES NOT SEE THE CACHE ITEM WHEN INVOKED FOR THE FIRST TIME!
            ...
        }
    }        

I won't bore you with cockpitCacheManager.UpdateAsync implementation, because the part related to retrieving a cache item is standard (using your GetOrAddAsync method). The item is 100% in the cache, I fill it from DB prior to the requesting.

Interesting fact is that if I request this cache item from Application layer - it's always retrieved as expected.

Not sure. Probably it's related. You might close this one. But if that ticket is resolved, however this problem does not go away - I will reopen this ticket.

The log has been already attached (Google Drive link above).

Hi. The project is a customized version of ABP project. I don't think it would be possible to do what you offer. Neither I can share the source code - by sending it or demonstrating the screen. Could you please at least make some guesses what could go wrong looking at the attached logs and my comments? Thanks.

Not resolved. Reopening.

Not resolved. Reopening.

UPDATE: please have a look at what's going on during logout at Identity Server, probably would ring some bell to you: https://drive.google.com/open?id=12SCQSi6Je4G9cCTwPoxwf4CzFujMz0Rn&usp=drive_fs

To me it looks like concurrent calls to Task<IdentityUser> FindAsync (which is eventually cancelled as seen from the attached log) from:

    using AbxEps.CentralTools.AbxUsers;
    using AbxEps.CentralTools.Extensions;
    using AbxEps.CentralTools.Jobs;
    using AbxEps.CentralTools.Sessions;
    using AbxEps.CentralTools.Tenants;
    using IdentityServer4.Models;
    using Microsoft.AspNetCore.Http;
    using Microsoft.AspNetCore.Identity;
    using System;
    using System.Collections.Generic;
    using System.Security.Claims;
    using System.Security.Principal;
    using System.Threading.Tasks;
    using Volo.Abp.Identity;
    using Volo.Abp.IdentityServer.AspNetIdentity;
    using Volo.Abp.MultiTenancy;
    using Volo.Abp.PermissionManagement;
    using IdentityUser = Volo.Abp.Identity.IdentityUser;
    
    namespace AbxEps.CentralTools.IdentityServer.Profile
    {
        public class AbxProfileService : AbpProfileService
        {
            private readonly ITenantRepository           _abxTenantRepository;
            private readonly IAbxUserRepository          _abxUserRepository;
            private readonly ISessionRepository          _abxSessionRepository;
            private readonly IJobRepository              _abxJobRepository;
            private readonly IHttpContextAccessor        _httpContextAccessor;
    
            public AbxProfileService
            (
                IdentityUserManager                       userManager,
                IAbxUserRepository                        abxUserRepository,
                IUserClaimsPrincipalFactory&lt;IdentityUser&gt; claimsFactory,
                ICurrentTenant                            currentTenant,
                ITenantRepository                         abxTenantRepository,
                IHttpContextAccessor                      httpContextAccessor,
                ISessionRepository                        abxSessionRepository,
                IJobRepository                            abxJobRepository
            )
                :base(userManager, claimsFactory, currentTenant)
            {
                _abxTenantRepository        = abxTenantRepository;
                _abxUserRepository          = abxUserRepository;
                _httpContextAccessor        = httpContextAccessor;
                _abxSessionRepository       = abxSessionRepository;
                _abxJobRepository           = abxJobRepository;
            }
    
            public override async Task GetProfileDataAsync(ProfileDataRequestContext context)
            {
                using (CurrentTenant.Change(context.Subject.FindTenantId())) //is invoked a couple of times and probably is invoking Task&lt;IdentityUser&gt; FindAsync too below
                {
                    await base.GetProfileDataAsync(context);
                    ...
                }
            }
    
            public override async Task IsActiveAsync(IsActiveContext context)
            {
                using (CurrentTenant.Change(context.Subject.FindTenantId())) //is invoked a couple of times and invoking Task&lt;IdentityUser&gt; FindAsync below
                {
                    var abpUser = await UserManager.GetUserAsync(context.Subject);
                    var abxUser = abpUser == null ? null : await _abxUserRepository.GetAsync(abpUser.Id);
    
                    await base.IsActiveAsync(context);
    
                    ...
                }
            }        
        }
    }
    

and the middleware:

    private async Task OnSessionEndRequestAsync(HttpContext httpContext, IdentityUserManager userManager)
    {
        try
        {
            var user = await userManager.GetUserAsync(httpContext.User); // Cancellation exception!
            ...
        }
        catch(Exception ex)
        {
            _logger.LogError(ex, "Session End handling error");
        }
    }

during logout...

I think all in all you are right about that. But there's another problem. So I put the debug point in intercept call - I'm getting there ONCE or TWO TIMES (those are request to Identity Server) after I already pressed "Logout" button: So I release the debug point and receive this bunch of API requests with error 401... I have created another ticket (https://support.abp.io/QA/Questions/5781/Logout-does-not-actually-logs-out-in-Angular-app) - that I actually cannot logout from the page other than Home page. The guy there answers he cannot do anything, because the problem is not reproduced on his end. So I don't know whether these two problems are related and what to do.

Showing 11 to 20 of 267 entries
Made with ❤️ on ABP v8.2.0-preview Updated on March 25, 2024, 15:11