Skip to content

Conversation

@AndyButland
Copy link
Contributor

@AndyButland AndyButland commented Oct 6, 2025

Prerequisites

  • I have added steps to test this contribution in the description below

Fixes #20352 caused by a regression introduced by #19890.

Description

Although the reported issue related to Redis, this is visible with any L2 cache other than a memory cache where deserialization is required.

The problem comes about because we are using object to verify the existence of something in the cache, rather than the actual object ContentCacheNode we always store, which has configured serialization rules. As such the deserialization is using the default JSON serializer, which fails as we aren't stored cached values as JSON.

Testing

Set up SQL L2 cache, which if running from source, is done by:

  • Add the following to Directory.Packages.props:
<ItemGroup>
  <PackageVersion Include="Microsoft.Extensions.Caching.SqlServer" Version="9.0.9" />
</ItemGroup>
  • Add the following to Umbraco.Web.UI.csproj:
<ItemGroup>
  <PackageReference Include="Microsoft.Extensions.Caching.SqlServer" />
</ItemGroup>
  • Add the following to Program.cs, just before WebApplication app = builder.Build();:
builder.Services.AddDistributedSqlServerCache(options =>
{
    options.ConnectionString = builder.Configuration.GetConnectionString("SqlDistributedCache");
    options.SchemaName = "dbo";
    options.TableName = "TestCache";
});
  • Create a SQL Server database
  • Populate the database with:
dotnet tool install --global dotnet-sql-cache
dotnet sql-cache create "Server=.\SQLEXPRESS;Database=MyDatabase;Integrated Security=true;TrustServerCertificate=true;" dbo TestCache
  • Add the following connection string to appSettings.json:
"SqlDistributedCache": "Server=.\\SQLEXPRESS;Database=UmbracoCms16HybridCache;Integrated Security=true;TrustServerCertificate=true;"

Now verify the issue resolution with:

  • Start up Umbraco using code from main. The first time it'll be fine, as the table is empty and will be populated.
  • Verify via select * from TestCache
  • Stop and start Umbraco again. You should now hit the error:
An error occurred starting the application
System.AggregateException: One or more errors occurred. (One or more errors occurred. ('0x92' is an invalid start of a value. Path: $ | LineNumber: 0 | BytePositionInLine: 0.))
  • Check out the code in this branch and empty the table with truncate table TestCache
  • Repeat starting up Umbraco twice. This time there should be no error on the second restart.

Copilot AI review requested due to automatic review settings October 6, 2025 14:27
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR fixes a hybrid cache deserialization issue where the ExistsAsync method was checking for generic object types instead of the specific ContentCacheNode type, causing failures with L2 caches that require proper serialization (like SQL Server or Redis).

Key Changes

  • Made ExistsAsync extension method generic to use proper type checking
  • Updated all callers to specify ContentCacheNode as the type parameter
  • Added proper test coverage with ContentCacheNode objects instead of strings

Reviewed Changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated no comments.

File Description
src/Umbraco.PublishedCache.HybridCache/Extensions/HybridCacheExtensions.cs Made ExistsAsync method generic to accept type parameter
src/Umbraco.PublishedCache.HybridCache/Services/DocumentCacheService.cs Updated ExistsAsync calls to specify ContentCacheNode type
src/Umbraco.PublishedCache.HybridCache/Services/MediaCacheService.cs Updated ExistsAsync calls to specify ContentCacheNode type
tests/Umbraco.Tests.UnitTests/Umbraco.PublishedCache.HybridCache/Extensions/HybridCacheExtensionsTests.cs Updated tests to use ContentCacheNode objects and proper generic method calls

@AndyButland AndyButland changed the title Hybrid cache Checked for ContentCacheNode instead of object on exists for hybrid cache to ensure correct deserialization (closes #20352) Hybrid cache: Check for ContentCacheNode instead of object on exists for hybrid cache to ensure correct deserialization (closes #20352) Oct 6, 2025
Copy link
Member

@Zeegaan Zeegaan left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good, works great 🚀

@Zeegaan Zeegaan merged commit 184c17e into main Oct 6, 2025
26 checks passed
@Zeegaan Zeegaan deleted the v16/bugfix/fix-deserialization-issue-on-exists-check-with-l2-hybrid-cache branch October 6, 2025 19:20
Zeegaan pushed a commit that referenced this pull request Oct 6, 2025
…s for hybrid cache to ensure correct deserialization (closes #20352) (#20383)

Checked for ContentCacheNode instead of object on exists for hybrid cache to ensure correct deserialization.

(cherry picked from commit 184c17e)
@umbracocommunity
Copy link

This pull request has been mentioned on Umbraco community forum. There might be relevant details there:

https://forum.umbraco.com/t/hybrid-cache-defaultjsonserializer-error/5889/8

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[v16] HybridCache - Cache deserialization failure

4 participants