Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

The Tags Property in OutputCache attribute is not working when using custom policies. #58785

Open
1 task done
skillmaker-dev opened this issue Nov 4, 2024 · 3 comments
Open
1 task done
Labels
area-middleware Includes: URL rewrite, redirect, response cache/compression, session, and other general middlesware feature-output-caching

Comments

@skillmaker-dev
Copy link

Is there an existing issue for this?

  • I have searched the existing issues

Describe the bug

Hey, I'm trying to implement OutputCache with MultiTenancy Support, So I had to create a custom policy for that, I also used tags so that I can evict cache by tags + tenant id, this is how I'm doing it:

Custom OutputCache policy, MultitenantCachePolicy:

public class MultitenantCachePolicy : IOutputCachePolicy
{
    public ValueTask CacheRequestAsync(OutputCacheContext context, CancellationToken cancellation)
    {
        var attemptOutputCaching = AttemptOutputCaching(context);
        context.EnableOutputCaching = true;
        context.AllowCacheLookup = attemptOutputCaching;
        context.AllowCacheStorage = attemptOutputCaching;
        context.AllowLocking = true;
        context.CacheVaryByRules.QueryKeys = "*";

        var tenantId = context.HttpContext.Items["tenantId"] as string ?? "";

        HashSet<string> newTags = [];

        foreach (var tag in context.Tags)
        {
            newTags.Add($"tenant:{tenantId}:{tag}");
        }

        newTags.Add($"tenant:{tenantId}");
        context.Tags.UnionWith(newTags);

        return ValueTask.CompletedTask;
    }

    public ValueTask ServeFromCacheAsync(OutputCacheContext context, CancellationToken cancellation)
    {
        var response = context.HttpContext.Response;

        if (!StringValues.IsNullOrEmpty(response.Headers.SetCookie))
        {
            context.AllowCacheStorage = false;
            return ValueTask.CompletedTask;
        }

        if (response.StatusCode != StatusCodes.Status200OK)
        {
            context.AllowCacheStorage = false;
            return ValueTask.CompletedTask;
        }

        return ValueTask.CompletedTask;
    }

    public ValueTask ServeResponseAsync(OutputCacheContext context, CancellationToken cancellation)
        => ValueTask.CompletedTask;

    private static bool AttemptOutputCaching(OutputCacheContext context)
    {
        var request = context.HttpContext.Request;

        if (!HttpMethods.IsGet(request.Method) && !HttpMethods.IsHead(request.Method))
        {
            return false;
        }

        return true;
    }
}

And this is how I register it:

services.AddOutputCache(options =>
{
    options.AddPolicy(CacheConfig.CachePolicies.MultiTenantCache, o =>
    {
        o.Expire(TimeSpan.FromMinutes(10))
            .VaryByValue(context => CacheExtension.VaryByTenantId(context))
            .AddPolicy<MultitenantCachePolicy>();
    });
}

And this is how I use it in controller endpoints:

[OutputCache(PolicyName = CacheConfig.CachePolicies.MultiTenantCache, Tags = [CacheConfig.CacheTags.DummyTag])]

The problem is that the DummyTagis not getting used at all, when i set a breakpoint in CacheRequestAsyncthe context Tags list is empty, however, if i set the Tag using this way:

options.AddPolicy(CacheConfig.CachePolicies.MultiTenantCache, o =>
    {
        o.Expire(TimeSpan.FromMinutes(10))
            .VaryByValue(context => CacheExtension.VaryByTenantId(context))
            .AddPolicy<MultitenantCachePolicy>()
            .Tag(CacheConfig.CacheTags.DummyTag);
    });

It works this way, what am I doing wrong here?

Expected Behavior

I should be able to register one custom policy, however, when I use that policy in the OutputCache attribute, I should be able to use the Tags property so that each controller endpoint has its Tag while sharing the same policy.

Steps To Reproduce

No response

Exceptions (if any)

No response

.NET Version

8.0.403

Anything else?

No response

@dotnet-issue-labeler dotnet-issue-labeler bot added the needs-area-label Used by the dotnet-issue-labeler to label those issues which couldn't be triaged automatically label Nov 4, 2024
@BrennanConroy
Copy link
Member

Have you tried looking at Tags in ServeFromCacheAsync? I believe attribute policies run after policies from options.

@martincostello martincostello added feature-output-caching area-middleware Includes: URL rewrite, redirect, response cache/compression, session, and other general middlesware and removed needs-area-label Used by the dotnet-issue-labeler to label those issues which couldn't be triaged automatically labels Nov 5, 2024
@skillmaker-dev
Copy link
Author

@BrennanConroy The method doesn't get hit, But i checked ServeResponseAsync and it contains the tag

@BrennanConroy
Copy link
Member

Sure, whichever method is called will have the tag. This seems like expected behavior then.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area-middleware Includes: URL rewrite, redirect, response cache/compression, session, and other general middlesware feature-output-caching
Projects
None yet
Development

No branches or pull requests

3 participants