Skip to content

Commit a31860e

Browse files
committed
additional guidance on cache key composition
1 parent 7be7675 commit a31860e

File tree

1 file changed

+34
-0
lines changed

1 file changed

+34
-0
lines changed

aspnetcore/performance/caching/hybrid.md

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,40 @@ The stateless overload of `GetOrCreateAsync` is recommended for most scenarios.
5252

5353
:::code language="csharp" source="~/performance/caching/hybrid/samples/9.x/HCMinimal/Program.cs" id="snippet_getorcreate" highlight="5-12":::
5454

55+
### Cache key guidance
56+
57+
The `key` passed to `GetOrCreateAsync` is usually formed using string concatenation, and must uniquely identify
58+
the data being cached both in terms of the identifiers/values used to load that data, and in terms of other data
59+
cached in your application. For example:
60+
61+
```c#
62+
cache.GetOrCreateAsync($"/orders/{region}/{orderId}", ...);
63+
```
64+
65+
or
66+
67+
```c#
68+
cache.GetOrCreateAsync($"user_prefs_{userId}", ...);
69+
```
70+
71+
**It is the caller's responsibility** to ensure that this key scheme is valid and cannot cause data to become confused, either between separate uses of `HybridCache`, or based on
72+
different inputs used in the cache key. In particular, it is not recommended to use external user input (in particular, but not limited to, raw `string` values from a UI) as part
73+
of cache keys, as this could allow malicious access attempts, or could be used in a service-denial attack by saturating your cache with data with meaningless keys generated from
74+
random strings. In the above (valid) example, the *order* data and *user preference* data are clearly distinct, where `orderid` and `userId` are our internally generated
75+
identifiers, and `region` might be an enum or string from a *predefined list* of known regions. There is no significance placed on tokens such as `/` or `_`; the composed value is
76+
treated as an opaque identifying string - the examples above just illustrate some common approaches. This guidance applies equally to any `string`-based cache API, including
77+
`HybridCache`, `IDistributedCache`, `IMemoryCache`, etc.
78+
79+
The inline interpolated string syntax (`$"..."` above, directly inside the `GetOrCreateAsync` call) is recommended when using `HybridCache`, as this allows for planned future
80+
improvements that bypass the need to allocate a `string` for the key in many scenarios.
81+
82+
Additional key restrictions:
83+
84+
- keys may restricted to valid lengths; for example, the default `HybridCache` implementation (via `AddHybridCache(...)`) restricts keys to 1024 characters by default, (configurable via `HybridCacheOptions.MaximumKeyLength`) with longer keys bypassing the cache mechanisms to prevent saturation.
85+
- keys must be valid unicode sequences; if invalid unicode sequences are passed, the behaviour is undefined.
86+
- when using an out-of-process secondary cache (`IDistributedCache`), the specific backend implementation may impose additional restrictions - as a hypothetical example (no such actual
87+
backend is known), a particular backend might use case-insensitive key logic; the default `HybridCache` (via `AddHybridCache(...)`) will detect this scenario to prevent confusion attacks, however it may still result in conflicting keys becoming overwritten/evicted sooner than expected.
88+
5589
### The alternative `GetOrCreateAsync` overload
5690

5791
The alternative overload might reduce some overhead from [captured variables](/dotnet/csharp/language-reference/operators/lambda-expressions#capture-of-outer-variables-and-variable-scope-in-lambda-expressions) and per-instance callbacks, but at the expense of more complex code. For most scenarios the performance increase doesn't outweigh the code complexity. Here's an example that uses the alternative overload:

0 commit comments

Comments
 (0)