Skip to content

UpdateTimestampsCache non cached entities concurrency issue #2735

Closed
@gokhanabatay

Description

@gokhanabatay

Hi @hazzik @maca88,
As I mentioned earlier current UpdateTimestampsCache has still concurrency issue. #2129

In real life scenario some of entities uses second level cache but most of entities does not uses second level cache but still try to update UpdateTimestampsCache. At least it should be configurable in entity level or session factory level, default may be true for backward compability issue.

Non cached entities could be related(foreign key) with cached entity may be better solution StandardQueryCache property spaces and cached entity property spaces only triggers UpdateTimestampsCache?

Current UpdateTimestampsCache uses AsyncReaderWriterLock if one writer grants lock other writers of different entities or readers wait for it. If this is true you could realize the concurrency issue that we are facing under high concurrency (400-500tps).

If still not acceptable please consider a way to implement Custom UpdateTimestampsCache via UpdateTimestampsFactory like cache.provider_class attribute in configuration. Could be named cache.update_timestamps_class.

Currently we are trying to replace current UpdateTimestampsCache via reflection but its not cleanest solution.

var cacheablePersisters = ISessionFactory.GetAllClassMetadata()
                .Where(x => x.Value.GetType() == typeof(SingleTableEntityPersister))
                .Select(x => x.Value)
                .Cast<SingleTableEntityPersister>()
                .Where(x => x.HasCache)
                .ToList();

var sessionFactoryImpl = (SessionFactoryImpl)ISessionFactory;
var standardQueryCache = (StandardQueryCache)sessionFactoryImpl.QueryCache;


var updateTimestamps = (CacheBase)typeof(UpdateTimestampsCache).GetField("_updateTimestamps", BindingFlags.Instance | BindingFlags.NonPublic).GetValue(sessionFactoryImpl.UpdateTimestampsCache);

var updateTimestampsCache = new DistributedUpdateTimestampsCache(updateTimestamps, cacheablePersisters.SelectMany(x => x.PropertySpaces).ToHashSet());

typeof(SessionFactoryImpl).GetField("updateTimestampsCache", BindingFlags.Instance | BindingFlags.NonPublic).SetValue(sessionFactoryImpl, updateTimestampsCache);

if (standardQueryCache != null)
{
    typeof(StandardQueryCache).GetField("_updateTimestampsCache", BindingFlags.Instance | BindingFlags.NonPublic).SetValue(standardQueryCache, updateTimestampsCache);
}
public class DistributedUpdateTimestampsCache : UpdateTimestampsCache
{
    protected readonly ISet<string> CacheableSpaces;

    public DistributedUpdateTimestampsCache(CacheBase updateTimestamps, ISet<string> cacheableSpaces) : base(updateTimestamps)
    {
        CacheableSpaces = cacheableSpaces;
    }

    public override void Invalidate(IReadOnlyCollection<string> spaces)
    {
        spaces = spaces.Where(x => CacheableSpaces.Contains(x)).ToList();

        if (spaces.Any())
        {
            base.Invalidate(spaces);
        }
    }

    public override void PreInvalidate(IReadOnlyCollection<string> spaces)
    {
        spaces = spaces.Where(x => CacheableSpaces.Contains(x)).ToList();

        if (spaces.Any())
        {
            base.PreInvalidate(spaces);
        }
    }

    public override Task InvalidateAsync(IReadOnlyCollection<string> spaces, CancellationToken cancellationToken)
    {
        if (cancellationToken.IsCancellationRequested)
        {
            return Task.FromCanceled<object>(cancellationToken);
        }

        spaces = spaces.Where(x => CacheableSpaces.Contains(x)).ToList();

        if (!spaces.Any())
        {
            return Task.CompletedTask;
        }

        return base.InvalidateAsync(spaces, cancellationToken);
    }

    public override Task PreInvalidateAsync(IReadOnlyCollection<string> spaces, CancellationToken cancellationToken)
    {
        if (cancellationToken.IsCancellationRequested)
        {
            return Task.FromCanceled<object>(cancellationToken);
        }

        spaces = spaces.Where(x => CacheableSpaces.Contains(x)).ToList();

        if (!spaces.Any())
        {
            return Task.CompletedTask;
        }

        return base.PreInvalidateAsync(spaces, cancellationToken);
    }
}

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions