diff --git a/CHANGELOG.md b/CHANGELOG.md index b14a453..eee5d46 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -101,6 +101,29 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) ### Changed +- **BREAKING:** the per-call `expiration` is no longer nullable. Every write on `ICache`, + `ICache`, `IHashCache`, `IHashCache`, `ISetCache`, `ISetCache` and their extension + surfaces takes `TimeSpan` / `DateTimeOffset` instead of `TimeSpan?` / `DateTimeOffset?`. The + nullable was a redundant third state: each of these members already has a sibling overload with no + `expiration` parameter, and passing `null` meant exactly the same thing as not passing it — fall + back to `CachePolicy.DistributedExpiration`, then the provider's `DefaultExpiration`. It also made + `cache.SetAsync(key, value, null)` ambiguous (`CS0121`) between the `TimeSpan?` and + `DateTimeOffset?` overloads, which is why the forwarders had to spell out `(CachePolicy?)null`. + Callers passing a real value are unaffected; a caller forwarding its own `TimeSpan?` now branches + on it and calls the overload without an expiration for the null case. Nullability stays where it + means *inherit* — `CachePolicy.LocalExpiration` / `DistributedExpiration`, the providers' + `DefaultExpiration`, `HashCacheEntryOptions.ExpireTime` / `TimeToLive` — and on reads, where + `TimeToLiveAsync` / `ExpireTimeAsync` still return `null` for a key with no TTL. +- **BREAKING:** a per-call `expiration` that cannot be honored is now rejected instead of silently + absorbed. A `TimeSpan` that is not strictly positive, or a `DateTimeOffset` at or before the + cache's current time, raises `ArgumentOutOfRangeException` (`ParamName` `"expiration"`) and nothing + is written. Previously such a value was quietly treated as "no expiration" and, on `TryAddAsync`, + answered `false` — the same answer as "somebody else holds the key". With the nullable gone there + is no third state left to carry that meaning, so the argument is refused at the boundary. The new + public `CacheExpiration` helper (`ThrowIfNotPositive`, `ThrowIfNotFuture`, `ToDuration`) holds the + guard for out-of-tree implementations. `TimeSpan.MaxValue` and `DateTimeOffset.MaxValue` remain + valid — they are how the providers spell "no TTL". `NullCache`, `NullHashCache` and `NullSetCache` + read no argument at all and so enforce nothing, keeping "caching is off, carry on". - **BREAKING:** `ICache.Compat.cs`, `IHashCache.Compat.cs` and `ISetCache.Compat.cs` are gone. The pre-`CachePolicy` convenience overloads they carried as default interface methods — `GetAsync(key, token)`, `SetAsync(key, value, expiration, token)`, diff --git a/docs/recipes/conditional-add.md b/docs/recipes/conditional-add.md index ef56df8..dbe6047 100644 --- a/docs/recipes/conditional-add.md +++ b/docs/recipes/conditional-add.md @@ -107,9 +107,11 @@ guarantee the method makes. change either side of the call anyway. `IDistributedLock.TryAcquireAsync` does not either; it documents backend-unavailable and already-held as the same `null`. If the two readings need different handling, you need a primitive with a richer result than a `bool`. -- **A non-positive TTL claims nothing.** An expiration that is not in the future returns `false` on - every tier rather than a win, because the entry would be evicted on arrival and the next caller - would be told it won too. +- **A non-positive TTL is a bad argument, not a loss.** `expiration` is non-nullable, so a duration + that is not strictly positive — or a deadline already past — raises `ArgumentOutOfRangeException` + and nothing is written. Returning `false` would be indistinguishable from "somebody else holds the + key", which is exactly the confusion the ambiguity above asks you to design around. To inherit the + policy's TTL, call the overload that has no `expiration` parameter. - **Caching switched off means nobody wins.** `NullCache.TryAddAsync` returns `false` — it cannot complete the write — and it is what `ICacheFactory.CreateCache` falls back to when the requested provider is missing or has `Enabled=false`. That is fail-closed rather than silently at-least-once, diff --git a/docs/reference/interfaces.md b/docs/reference/interfaces.md index 7723a5b..ed74e26 100644 --- a/docs/reference/interfaces.md +++ b/docs/reference/interfaces.md @@ -27,15 +27,15 @@ public interface ICache ValueTask GetOrAddAsync(CacheKey cacheKey, Func> generator, CancellationToken token = default); - ValueTask GetOrAddAsync(CacheKey cacheKey, Func> generator, TimeSpan? expiration, CancellationToken token = default); + ValueTask GetOrAddAsync(CacheKey cacheKey, Func> generator, TimeSpan expiration, CancellationToken token = default); - ValueTask GetOrAddAsync(CacheKey cacheKey, Func> generator, DateTimeOffset? expiration, CancellationToken token = default); + ValueTask GetOrAddAsync(CacheKey cacheKey, Func> generator, DateTimeOffset expiration, CancellationToken token = default); ValueTask[]> GetOrAddAsync(KeyValuePair[] entries, Func[]>> generator, CancellationToken token = default) where TState : notnull; - ValueTask[]> GetOrAddAsync(KeyValuePair[] entries, Func[]>> generator, TimeSpan? expiration, CancellationToken token = default) where TState : notnull; + ValueTask[]> GetOrAddAsync(KeyValuePair[] entries, Func[]>> generator, TimeSpan expiration, CancellationToken token = default) where TState : notnull; - ValueTask[]> GetOrAddAsync(KeyValuePair[] entries, Func[]>> generator, DateTimeOffset? expiration, CancellationToken token = default) where TState : notnull; + ValueTask[]> GetOrAddAsync(KeyValuePair[] entries, Func[]>> generator, DateTimeOffset expiration, CancellationToken token = default) where TState : notnull; ValueTask RemoveAsync(CacheKey cacheKey, CancellationToken token = default); @@ -43,27 +43,27 @@ public interface ICache ValueTask SetAsync(CacheKey cacheKey, T? value, CancellationToken token = default); - ValueTask SetAsync(CacheKey cacheKey, T? value, TimeSpan? expiration, CancellationToken token = default); + ValueTask SetAsync(CacheKey cacheKey, T? value, TimeSpan expiration, CancellationToken token = default); - ValueTask SetAsync(CacheKey cacheKey, T? value, DateTimeOffset? expiration, CancellationToken token = default); + ValueTask SetAsync(CacheKey cacheKey, T? value, DateTimeOffset expiration, CancellationToken token = default); ValueTask SetAsync(KeyValuePair[] keyValues, CancellationToken token = default); - ValueTask SetAsync(KeyValuePair[] keyValues, TimeSpan? expiration = null, CancellationToken token = default); + ValueTask SetAsync(KeyValuePair[] keyValues, TimeSpan expiration, CancellationToken token = default); - ValueTask SetAsync(KeyValuePair[] keyValues, DateTimeOffset? expiration = null, CancellationToken token = default); + ValueTask SetAsync(KeyValuePair[] keyValues, DateTimeOffset expiration, CancellationToken token = default); ValueTask TryAddAsync(CacheKey cacheKey, T? value, CancellationToken token = default); - ValueTask TryAddAsync(CacheKey cacheKey, T? value, TimeSpan? expiration, CancellationToken token = default); + ValueTask TryAddAsync(CacheKey cacheKey, T? value, TimeSpan expiration, CancellationToken token = default); - ValueTask TryAddAsync(CacheKey cacheKey, T? value, DateTimeOffset? expiration, CancellationToken token = default); + ValueTask TryAddAsync(CacheKey cacheKey, T? value, DateTimeOffset expiration, CancellationToken token = default); ValueTask RefreshAsync(CacheKey cacheKey, CancellationToken token = default); - ValueTask RefreshAsync(CacheKey cacheKey, TimeSpan? expiration, CancellationToken token = default); + ValueTask RefreshAsync(CacheKey cacheKey, TimeSpan expiration, CancellationToken token = default); - ValueTask RefreshAsync(CacheKey cacheKey, DateTimeOffset? expiration, CancellationToken token = default); + ValueTask RefreshAsync(CacheKey cacheKey, DateTimeOffset expiration, CancellationToken token = default); ValueTask ContainsAsync(CacheKey cacheKey, CancellationToken token = default); @@ -116,19 +116,19 @@ public interface ICache : IDisposable ValueTask GetOrAddAsync(CacheKey cacheKey, Func> generator, CachePolicy? policy, CancellationToken token = default); - ValueTask GetOrAddAsync(CacheKey cacheKey, Func> generator, TimeSpan? expiration, CachePolicy? policy, CancellationToken token = default); + ValueTask GetOrAddAsync(CacheKey cacheKey, Func> generator, TimeSpan expiration, CachePolicy? policy, CancellationToken token = default); - ValueTask GetOrAddAsync(CacheKey cacheKey, Func> generator, DateTimeOffset? expiration, CachePolicy? policy, CancellationToken token = default); + ValueTask GetOrAddAsync(CacheKey cacheKey, Func> generator, DateTimeOffset expiration, CachePolicy? policy, CancellationToken token = default); ValueTask[]> GetOrAddAsync(KeyValuePair[] entries, Func[]>> generator, CachePolicy? policy, CancellationToken token = default) where TState : notnull => BatchGetOrAdd.RunAsync(this, entries, generator, (pairs, t) => SetAsync(pairs, policy, t), policy, token); - ValueTask[]> GetOrAddAsync(KeyValuePair[] entries, Func[]>> generator, TimeSpan? expiration, CachePolicy? policy, CancellationToken token = default) + ValueTask[]> GetOrAddAsync(KeyValuePair[] entries, Func[]>> generator, TimeSpan expiration, CachePolicy? policy, CancellationToken token = default) where TState : notnull => BatchGetOrAdd.RunAsync(this, entries, generator, (pairs, t) => SetAsync(pairs, expiration, policy, t), policy, token); - ValueTask[]> GetOrAddAsync(KeyValuePair[] entries, Func[]>> generator, DateTimeOffset? expiration, CachePolicy? policy, CancellationToken token = default) + ValueTask[]> GetOrAddAsync(KeyValuePair[] entries, Func[]>> generator, DateTimeOffset expiration, CachePolicy? policy, CancellationToken token = default) where TState : notnull => BatchGetOrAdd.RunAsync(this, entries, generator, (pairs, t) => SetAsync(pairs, expiration, policy, t), policy, token); @@ -138,27 +138,27 @@ public interface ICache : IDisposable ValueTask SetAsync(CacheKey cacheKey, T? value, CachePolicy? policy, CancellationToken token = default); - ValueTask SetAsync(CacheKey cacheKey, T? value, TimeSpan? expiration, CachePolicy? policy, CancellationToken token = default); + ValueTask SetAsync(CacheKey cacheKey, T? value, TimeSpan expiration, CachePolicy? policy, CancellationToken token = default); - ValueTask SetAsync(CacheKey cacheKey, T? value, DateTimeOffset? expiration, CachePolicy? policy, CancellationToken token = default); + ValueTask SetAsync(CacheKey cacheKey, T? value, DateTimeOffset expiration, CachePolicy? policy, CancellationToken token = default); ValueTask SetAsync(KeyValuePair[] keyValues, CachePolicy? policy, CancellationToken token = default); - ValueTask SetAsync(KeyValuePair[] keyValues, TimeSpan? expiration, CachePolicy? policy, CancellationToken token = default); + ValueTask SetAsync(KeyValuePair[] keyValues, TimeSpan expiration, CachePolicy? policy, CancellationToken token = default); - ValueTask SetAsync(KeyValuePair[] keyValues, DateTimeOffset? expiration, CachePolicy? policy, CancellationToken token = default); + ValueTask SetAsync(KeyValuePair[] keyValues, DateTimeOffset expiration, CachePolicy? policy, CancellationToken token = default); ValueTask TryAddAsync(CacheKey cacheKey, T? value, CachePolicy? policy, CancellationToken token = default); - ValueTask TryAddAsync(CacheKey cacheKey, T? value, TimeSpan? expiration, CachePolicy? policy, CancellationToken token = default); + ValueTask TryAddAsync(CacheKey cacheKey, T? value, TimeSpan expiration, CachePolicy? policy, CancellationToken token = default); - ValueTask TryAddAsync(CacheKey cacheKey, T? value, DateTimeOffset? expiration, CachePolicy? policy, CancellationToken token = default); + ValueTask TryAddAsync(CacheKey cacheKey, T? value, DateTimeOffset expiration, CachePolicy? policy, CancellationToken token = default); ValueTask RefreshAsync(CacheKey cacheKey, CachePolicy? policy, CancellationToken token = default); - ValueTask RefreshAsync(CacheKey cacheKey, TimeSpan? expiration, CachePolicy? policy, CancellationToken token = default); + ValueTask RefreshAsync(CacheKey cacheKey, TimeSpan expiration, CachePolicy? policy, CancellationToken token = default); - ValueTask RefreshAsync(CacheKey cacheKey, DateTimeOffset? expiration, CachePolicy? policy, CancellationToken token = default); + ValueTask RefreshAsync(CacheKey cacheKey, DateTimeOffset expiration, CachePolicy? policy, CancellationToken token = default); ValueTask ContainsAsync(CacheKey cacheKey, CancellationToken token = default); @@ -170,6 +170,31 @@ public interface ICache : IDisposable `ICache` is the dynamic-key, dynamic-type cache surface. Unlike `ICache`, the value type is specified as a generic type argument on each method call rather than fixed at cache-creation time, and a `CachePolicy` can be supplied per call rather than resolved by `typeof(T).FullName`. It also exposes `GetCacheEntryAsync` for callers that need cache-entry metadata (hit/miss status, expiration) in addition to the value. `ICache` implements `IDisposable`, but instances returned by `ICacheFactory.CreateCache(...)` are provider-owned (typically singletons resolved through a `Lazy<>`); their lifetime is managed by the provider and the DI container, so callers should not dispose them per use. +### Expiration + +`expiration` is **non-nullable** everywhere it appears on a write — `TimeSpan` or `DateTimeOffset`, never `TimeSpan?`/`DateTimeOffset?`. A caller with nothing to say about lifetime calls the overload that has no `expiration` parameter; a caller that passes one means it. + +```csharp +// I want this lifetime. +await cache.SetAsync(key, order, TimeSpan.FromMinutes(5), policy, token); + +// I have no opinion: resolve it from the policy, then the provider default. +await cache.SetAsync(key, order, policy, token); +``` + +That leaves one resolution chain with no redundant state in it: + +| What the caller does | Lifetime used | +| --- | --- | +| passes `expiration` | exactly that value, no jitter | +| omits `expiration` | `CachePolicy.DistributedExpiration`, jittered by `CachePolicy.JitterMaxDuration` | +| omits it, policy has no TTL | the provider's `DefaultExpiration`, jittered | +| omits it, nothing configured | unbounded — `TimeSpan.MaxValue` / `DateTimeOffset.MaxValue`, which the providers store as "no TTL" | + +Because the argument can no longer be `null`, there is nothing left for a meaningless value to mean, so it is rejected rather than absorbed: a duration that is not strictly positive, or a deadline at or before the cache's current time, raises `ArgumentOutOfRangeException` with `ParamName` `"expiration"` and nothing is written. `TimeSpan.MaxValue` and `DateTimeOffset.MaxValue` stay valid — they are how the providers spell "no TTL". `CacheExpiration` holds the guard if you need it in your own implementation. The no-op caches (`NullCache`, `NullHashCache`, `NullSetCache`) read no argument at all and so enforce nothing; they keep degrading to "caching is off, carry on". + +Nullability stays where it means *inherit*: `CachePolicy.LocalExpiration` / `DistributedExpiration`, the providers' `DefaultExpiration`, and the lifetime fields on `HashCacheEntryOptions`. Reads stay nullable too — `TimeToLiveAsync` and `ExpireTimeAsync` return `null` for a key with no TTL. + Every policy-bearing member takes `policy` as a **required** parameter — there is no `= null` default on the interface. Call sites that do not want a per-call policy use the `CacheExtensions` overloads instead, which omit `policy` (and, where the interface pairs the two, `expiration`) and forward with `policy: null`: ```csharp @@ -190,7 +215,7 @@ The multi-key `GetOrAddAsync` overloads shown above pair each key wit - **`false` is deliberately ambiguous.** It means "you did not create this key" — either it already existed, or the write could not be completed (backing store disconnected, write threw, or the value was a `null`/`default` that the cache has no way to represent). This is fail-closed by design: a caller treating `true` as "I own this key" is never wrongly told it won. The ambiguity is not recoverable: a serialization or command failure also returns `false`, with [`IConnectionState.IsConnected`](#iconnectionstate) still reporting healthy, and [`IDistributedLock.TryAcquireAsync`](#idistributedlock) conflates backend-unavailable with already-held in the same way. Design the `false` branch so that not proceeding is safe; if the two readings must be handled differently, the caller needs a primitive with a richer result than a `bool`. - **It never deletes.** Where `SetAsync` removes the key when handed a `null` and `CacheNullValues` is off, `TryAddAsync` returns `false` and leaves the key untouched. With `CacheNullValues` on, a `null` claims the key via the cached-null sentinel. - **It is a cache primitive, not a lock.** The entry expires on its own TTL, there is no ownership token, and any later `SetAsync`/`RemoveAsync` on the key ignores the claim. For mutual exclusion with a fencing token and explicit release, use [`IDistributedLock`](#idistributedlock). -- **An expiration that is not in the future claims nothing** and reports `false` on every tier. The entry would be evicted on arrival, so answering `true` would hand the same key to every later caller as well. +- **An expiration that is not in the future is rejected**, not answered. `expiration` is non-nullable, so a non-positive duration or a deadline already past is a bad argument and raises `ArgumentOutOfRangeException` — reporting `false` would be indistinguishable from "somebody else holds the key". See [Expiration](#expiration). Tier behavior follows from the same rule — L1 can never arbitrate while an L2 exists, because a key absent locally may well be present in the shared store: @@ -250,25 +275,25 @@ public interface IHashCache ValueTask> GetOrAddAsync(CacheKey cacheKey, Func>> generator, CancellationToken token = default); - ValueTask> GetOrAddAsync(CacheKey cacheKey, Func>> generator, TimeSpan? expiration, CancellationToken token = default); + ValueTask> GetOrAddAsync(CacheKey cacheKey, Func>> generator, TimeSpan expiration, CancellationToken token = default); - ValueTask> GetOrAddAsync(CacheKey cacheKey, Func>> generator, DateTimeOffset? expiration, CancellationToken token = default); + ValueTask> GetOrAddAsync(CacheKey cacheKey, Func>> generator, DateTimeOffset expiration, CancellationToken token = default); ValueTask>> GetCacheEntryAsync(CacheKey cacheKey, CancellationToken token = default); ValueTask SetAsync(CacheKey cacheKey, IDictionary values, CancellationToken token = default); - ValueTask SetAsync(CacheKey cacheKey, IDictionary values, TimeSpan? expiration, CancellationToken token = default); + ValueTask SetAsync(CacheKey cacheKey, IDictionary values, TimeSpan expiration, CancellationToken token = default); - ValueTask SetAsync(CacheKey cacheKey, IDictionary values, DateTimeOffset? expiration, CancellationToken token = default); + ValueTask SetAsync(CacheKey cacheKey, IDictionary values, DateTimeOffset expiration, CancellationToken token = default); ValueTask SetAsync(CacheKey cacheKey, IDictionary values, HashCacheEntryOptions options, CancellationToken token = default); ValueTask RefreshAsync(CacheKey cacheKey, CancellationToken token = default); - ValueTask RefreshAsync(CacheKey cacheKey, TimeSpan? expiration, CancellationToken token = default); + ValueTask RefreshAsync(CacheKey cacheKey, TimeSpan expiration, CancellationToken token = default); - ValueTask RefreshAsync(CacheKey cacheKey, DateTimeOffset? expiration, CancellationToken token = default); + ValueTask RefreshAsync(CacheKey cacheKey, DateTimeOffset expiration, CancellationToken token = default); ValueTask RefreshAsync(CacheKey cacheKey, HashCacheEntryOptions options, CancellationToken token = default); @@ -325,25 +350,25 @@ public interface IHashCache : IDisposable ValueTask> GetOrAddAsync(CacheKey cacheKey, Func>> generator, CachePolicy? policy, CancellationToken token = default); - ValueTask> GetOrAddAsync(CacheKey cacheKey, Func>> generator, TimeSpan? expiration, CachePolicy? policy, CancellationToken token = default); + ValueTask> GetOrAddAsync(CacheKey cacheKey, Func>> generator, TimeSpan expiration, CachePolicy? policy, CancellationToken token = default); - ValueTask> GetOrAddAsync(CacheKey cacheKey, Func>> generator, DateTimeOffset? expiration, CachePolicy? policy, CancellationToken token = default); + ValueTask> GetOrAddAsync(CacheKey cacheKey, Func>> generator, DateTimeOffset expiration, CachePolicy? policy, CancellationToken token = default); - ValueTask> GetOrAddAsync(CacheKey cacheKey, Func>> generator, DateTimeOffset? expiration, HashCacheSetOption? setOption, CachePolicy? policy, CancellationToken token = default); + ValueTask> GetOrAddAsync(CacheKey cacheKey, Func>> generator, DateTimeOffset expiration, HashCacheSetOption? setOption, CachePolicy? policy, CancellationToken token = default); ValueTask SetAsync(CacheKey cacheKey, IDictionary values, CachePolicy? policy, CancellationToken token = default); - ValueTask SetAsync(CacheKey cacheKey, IDictionary values, TimeSpan? expiration, CachePolicy? policy, CancellationToken token = default); + ValueTask SetAsync(CacheKey cacheKey, IDictionary values, TimeSpan expiration, CachePolicy? policy, CancellationToken token = default); - ValueTask SetAsync(CacheKey cacheKey, IDictionary values, DateTimeOffset? expiration, CachePolicy? policy, CancellationToken token = default); + ValueTask SetAsync(CacheKey cacheKey, IDictionary values, DateTimeOffset expiration, CachePolicy? policy, CancellationToken token = default); ValueTask SetAsync(CacheKey cacheKey, IDictionary values, HashCacheEntryOptions options, CachePolicy? policy, CancellationToken token = default); ValueTask RefreshAsync(CacheKey cacheKey, CachePolicy? policy, CancellationToken token = default); - ValueTask RefreshAsync(CacheKey cacheKey, TimeSpan? expiration, CachePolicy? policy, CancellationToken token = default); + ValueTask RefreshAsync(CacheKey cacheKey, TimeSpan expiration, CachePolicy? policy, CancellationToken token = default); - ValueTask RefreshAsync(CacheKey cacheKey, DateTimeOffset? expiration, CachePolicy? policy, CancellationToken token = default); + ValueTask RefreshAsync(CacheKey cacheKey, DateTimeOffset expiration, CachePolicy? policy, CancellationToken token = default); ValueTask RefreshAsync(CacheKey cacheKey, HashCacheEntryOptions options, CachePolicy? policy, CancellationToken token = default); diff --git a/src/UiPath.Caching.Abstractions/CacheExpiration.cs b/src/UiPath.Caching.Abstractions/CacheExpiration.cs new file mode 100644 index 0000000..7a5b905 --- /dev/null +++ b/src/UiPath.Caching.Abstractions/CacheExpiration.cs @@ -0,0 +1,62 @@ +using System.Runtime.CompilerServices; + +namespace UiPath.Caching; + +/// +/// Argument validation for the per-call expiration on the write surface. +/// +/// +/// The write members take a non-nullable / , so +/// there is no null left to absorb a nonsensical value: a caller with nothing to say about +/// lifetime calls the overload that has no expiration parameter and gets +/// , then the cache default. What remains is a value +/// the caller meant, and a duration that is not positive — or a deadline that has already passed — +/// cannot be honored, so it is rejected rather than silently swallowed. +/// +/// This does not police the resolved default: a policy or provider default that leaves entries +/// unbounded still yields / , +/// which the providers read as "no TTL". Those two sentinels stay valid inputs here. +/// +/// +/// Enforcement sits in the implementations that honor the lifetime. and its +/// siblings read no argument at all — not the key, not the type, not the expiration — so they keep +/// degrading to "caching is off, carry on" rather than throwing on a value they never look at. +/// +/// +public static class CacheExpiration +{ + /// Returns , or throws if it is not a positive duration. + /// is zero or negative. + public static TimeSpan ThrowIfNotPositive(TimeSpan expiration, [CallerArgumentExpression(nameof(expiration))] string? paramName = null) + { + if (expiration <= TimeSpan.Zero) + { + throw new ArgumentOutOfRangeException( + paramName, + expiration, + "The cache expiration must be a positive duration. To inherit the policy's DistributedExpiration or the cache default, call the overload without an expiration argument."); + } + + return expiration; + } + + /// Returns , or throws if it is not later than . + /// is at or before . + public static DateTimeOffset ThrowIfNotFuture(DateTimeOffset expiration, DateTimeOffset now, [CallerArgumentExpression(nameof(expiration))] string? paramName = null) + { + if (expiration <= now) + { + throw new ArgumentOutOfRangeException( + paramName, + expiration, + "The cache expiration must be later than the cache's current time. To inherit the policy's DistributedExpiration or the cache default, call the overload without an expiration argument."); + } + + return expiration; + } + + /// Validates a caller deadline against and returns it as a duration from . + /// is at or before . + public static TimeSpan ToDuration(DateTimeOffset expiration, DateTimeOffset now, [CallerArgumentExpression(nameof(expiration))] string? paramName = null) => + ThrowIfNotFuture(expiration, now, paramName) - now; +} diff --git a/src/UiPath.Caching.Abstractions/CacheExtensions.cs b/src/UiPath.Caching.Abstractions/CacheExtensions.cs index e2195b3..c8330c1 100644 --- a/src/UiPath.Caching.Abstractions/CacheExtensions.cs +++ b/src/UiPath.Caching.Abstractions/CacheExtensions.cs @@ -24,48 +24,48 @@ public static class CacheExtensions public static ValueTask GetOrAddAsync(this ICache cache, CacheKey cacheKey, Func> generator, CancellationToken token = default) => cache.GetOrAddAsync(cacheKey, generator, (CachePolicy?)null, token); - public static ValueTask GetOrAddAsync(this ICache cache, CacheKey cacheKey, Func> generator, TimeSpan? expiration, CancellationToken token = default) + public static ValueTask GetOrAddAsync(this ICache cache, CacheKey cacheKey, Func> generator, TimeSpan expiration, CancellationToken token = default) => cache.GetOrAddAsync(cacheKey, generator, expiration, null, token); - public static ValueTask GetOrAddAsync(this ICache cache, CacheKey cacheKey, Func> generator, DateTimeOffset? expiration, CancellationToken token = default) + public static ValueTask GetOrAddAsync(this ICache cache, CacheKey cacheKey, Func> generator, DateTimeOffset expiration, CancellationToken token = default) => cache.GetOrAddAsync(cacheKey, generator, expiration, null, token); public static ValueTask SetAsync(this ICache cache, CacheKey cacheKey, T? value, CancellationToken token = default) => cache.SetAsync(cacheKey, value, (CachePolicy?)null, token); - public static ValueTask SetAsync(this ICache cache, CacheKey cacheKey, T? value, TimeSpan? expiration, CancellationToken token = default) + public static ValueTask SetAsync(this ICache cache, CacheKey cacheKey, T? value, TimeSpan expiration, CancellationToken token = default) => cache.SetAsync(cacheKey, value, expiration, null, token); - public static ValueTask SetAsync(this ICache cache, CacheKey cacheKey, T? value, DateTimeOffset? expiration, CancellationToken token = default) + public static ValueTask SetAsync(this ICache cache, CacheKey cacheKey, T? value, DateTimeOffset expiration, CancellationToken token = default) => cache.SetAsync(cacheKey, value, expiration, null, token); public static ValueTask SetAsync(this ICache cache, KeyValuePair[] keyValues, CancellationToken token = default) => cache.SetAsync(keyValues, (CachePolicy?)null, token); - public static ValueTask SetAsync(this ICache cache, KeyValuePair[] keyValues, TimeSpan? expiration, CancellationToken token = default) + public static ValueTask SetAsync(this ICache cache, KeyValuePair[] keyValues, TimeSpan expiration, CancellationToken token = default) => cache.SetAsync(keyValues, expiration, null, token); - public static ValueTask SetAsync(this ICache cache, KeyValuePair[] keyValues, DateTimeOffset? expiration, CancellationToken token = default) + public static ValueTask SetAsync(this ICache cache, KeyValuePair[] keyValues, DateTimeOffset expiration, CancellationToken token = default) => cache.SetAsync(keyValues, expiration, null, token); /// public static ValueTask TryAddAsync(this ICache cache, CacheKey cacheKey, T? value, CancellationToken token = default) => cache.TryAddAsync(cacheKey, value, (CachePolicy?)null, token); - /// - public static ValueTask TryAddAsync(this ICache cache, CacheKey cacheKey, T? value, TimeSpan? expiration, CancellationToken token = default) + /// + public static ValueTask TryAddAsync(this ICache cache, CacheKey cacheKey, T? value, TimeSpan expiration, CancellationToken token = default) => cache.TryAddAsync(cacheKey, value, expiration, null, token); - /// - public static ValueTask TryAddAsync(this ICache cache, CacheKey cacheKey, T? value, DateTimeOffset? expiration, CancellationToken token = default) + /// + public static ValueTask TryAddAsync(this ICache cache, CacheKey cacheKey, T? value, DateTimeOffset expiration, CancellationToken token = default) => cache.TryAddAsync(cacheKey, value, expiration, null, token); public static ValueTask RefreshAsync(this ICache cache, CacheKey cacheKey, CancellationToken token = default) => cache.RefreshAsync(cacheKey, (CachePolicy?)null, token); - public static ValueTask RefreshAsync(this ICache cache, CacheKey cacheKey, TimeSpan? expiration, CancellationToken token = default) + public static ValueTask RefreshAsync(this ICache cache, CacheKey cacheKey, TimeSpan expiration, CancellationToken token = default) => cache.RefreshAsync(cacheKey, expiration, null, token); - public static ValueTask RefreshAsync(this ICache cache, CacheKey cacheKey, DateTimeOffset? expiration, CancellationToken token = default) + public static ValueTask RefreshAsync(this ICache cache, CacheKey cacheKey, DateTimeOffset expiration, CancellationToken token = default) => cache.RefreshAsync(cacheKey, expiration, null, token); } diff --git a/src/UiPath.Caching.Abstractions/CacheOfT.cs b/src/UiPath.Caching.Abstractions/CacheOfT.cs index bbb79b1..879c6b3 100644 --- a/src/UiPath.Caching.Abstractions/CacheOfT.cs +++ b/src/UiPath.Caching.Abstractions/CacheOfT.cs @@ -46,21 +46,21 @@ public ValueTask ContainsAsync(CacheKey cacheKey, CancellationToken token public ValueTask GetOrAddAsync(CacheKey cacheKey, Func> generator, CancellationToken token = default) => _cache.GetOrAddAsync(GetCacheKey(cacheKey), generator, policy: Policy, token: token); - public ValueTask GetOrAddAsync(CacheKey cacheKey, Func> generator, TimeSpan? expiration, CancellationToken token = default) => + public ValueTask GetOrAddAsync(CacheKey cacheKey, Func> generator, TimeSpan expiration, CancellationToken token = default) => _cache.GetOrAddAsync(GetCacheKey(cacheKey), generator, expiration, Policy, token); - public ValueTask GetOrAddAsync(CacheKey cacheKey, Func> generator, DateTimeOffset? expiration, CancellationToken token = default) => + public ValueTask GetOrAddAsync(CacheKey cacheKey, Func> generator, DateTimeOffset expiration, CancellationToken token = default) => _cache.GetOrAddAsync(GetCacheKey(cacheKey), generator, expiration, Policy, token); public ValueTask[]> GetOrAddAsync(KeyValuePair[] entries, Func[]>> generator, CancellationToken token = default) where TState : notnull => _cache.GetOrAddAsync(MapKeys(entries), generator, policy: Policy, token: token); - public ValueTask[]> GetOrAddAsync(KeyValuePair[] entries, Func[]>> generator, TimeSpan? expiration, CancellationToken token = default) + public ValueTask[]> GetOrAddAsync(KeyValuePair[] entries, Func[]>> generator, TimeSpan expiration, CancellationToken token = default) where TState : notnull => _cache.GetOrAddAsync(MapKeys(entries), generator, expiration, Policy, token); - public ValueTask[]> GetOrAddAsync(KeyValuePair[] entries, Func[]>> generator, DateTimeOffset? expiration, CancellationToken token = default) + public ValueTask[]> GetOrAddAsync(KeyValuePair[] entries, Func[]>> generator, DateTimeOffset expiration, CancellationToken token = default) where TState : notnull => _cache.GetOrAddAsync(MapKeys(entries), generator, expiration, Policy, token); @@ -74,10 +74,10 @@ private KeyValuePair[] MapKeys(KeyValuePair RefreshAsync(CacheKey cacheKey, CancellationToken token = default) => _cache.RefreshAsync(GetCacheKey(cacheKey), policy: Policy, token: token); - public ValueTask RefreshAsync(CacheKey cacheKey, TimeSpan? expiration, CancellationToken token = default) => + public ValueTask RefreshAsync(CacheKey cacheKey, TimeSpan expiration, CancellationToken token = default) => _cache.RefreshAsync(GetCacheKey(cacheKey), expiration, Policy, token); - public ValueTask RefreshAsync(CacheKey cacheKey, DateTimeOffset? expiration, CancellationToken token = default) => + public ValueTask RefreshAsync(CacheKey cacheKey, DateTimeOffset expiration, CancellationToken token = default) => _cache.RefreshAsync(GetCacheKey(cacheKey), expiration, Policy, token); public ValueTask RemoveAsync(CacheKey cacheKey, CancellationToken token = default) => @@ -89,29 +89,29 @@ public ValueTask RemoveAsync(CacheKey[] cacheKeys, CancellationToken token public ValueTask SetAsync(CacheKey cacheKey, T? value, CancellationToken token = default) => _cache.SetAsync(GetCacheKey(cacheKey), value, policy: Policy, token: token); - public ValueTask SetAsync(CacheKey cacheKey, T? value, TimeSpan? expiration, CancellationToken token = default) => + public ValueTask SetAsync(CacheKey cacheKey, T? value, TimeSpan expiration, CancellationToken token = default) => _cache.SetAsync(GetCacheKey(cacheKey), value, expiration, Policy, token); - public ValueTask SetAsync(CacheKey cacheKey, T? value, DateTimeOffset? expiration, CancellationToken token = default) => + public ValueTask SetAsync(CacheKey cacheKey, T? value, DateTimeOffset expiration, CancellationToken token = default) => _cache.SetAsync(GetCacheKey(cacheKey), value, expiration, Policy, token); public ValueTask TryAddAsync(CacheKey cacheKey, T? value, CancellationToken token = default) => _cache.TryAddAsync(GetCacheKey(cacheKey), value, policy: Policy, token: token); - public ValueTask TryAddAsync(CacheKey cacheKey, T? value, TimeSpan? expiration, CancellationToken token = default) => + public ValueTask TryAddAsync(CacheKey cacheKey, T? value, TimeSpan expiration, CancellationToken token = default) => _cache.TryAddAsync(GetCacheKey(cacheKey), value, expiration, Policy, token); - public ValueTask TryAddAsync(CacheKey cacheKey, T? value, DateTimeOffset? expiration, CancellationToken token = default) => + public ValueTask TryAddAsync(CacheKey cacheKey, T? value, DateTimeOffset expiration, CancellationToken token = default) => _cache.TryAddAsync(GetCacheKey(cacheKey), value, expiration, Policy, token); public ValueTask SetAsync(KeyValuePair[] keyValues, CancellationToken token = default) => _cache.SetAsync(GetKeyValuePairs(keyValues), policy: Policy, token: token); - public ValueTask SetAsync(KeyValuePair[] keyValues, TimeSpan? expiration = null, CancellationToken token = default) => + public ValueTask SetAsync(KeyValuePair[] keyValues, TimeSpan expiration, CancellationToken token = default) => _cache.SetAsync(GetKeyValuePairs(keyValues), expiration, Policy, token); - public ValueTask SetAsync(KeyValuePair[] keyValues, DateTimeOffset? expiration = null, CancellationToken token = default) => + public ValueTask SetAsync(KeyValuePair[] keyValues, DateTimeOffset expiration, CancellationToken token = default) => _cache.SetAsync(GetKeyValuePairs(keyValues), expiration, Policy, token); public ValueTask TimeToLiveAsync(CacheKey cacheKey, CancellationToken token = default) => diff --git a/src/UiPath.Caching.Abstractions/CacheSyncExtensions.cs b/src/UiPath.Caching.Abstractions/CacheSyncExtensions.cs index b4d1ba7..c11dc82 100644 --- a/src/UiPath.Caching.Abstractions/CacheSyncExtensions.cs +++ b/src/UiPath.Caching.Abstractions/CacheSyncExtensions.cs @@ -19,10 +19,10 @@ public static class CacheSyncExtensions public static T? GetOrAdd(this ICache cache, CacheKey cacheKey, Func generator, CancellationToken token = default) => cache.GetOrAddAsync(cacheKey, _ => Task.FromResult(generator()), token).AsTask().GetAwaiter().GetResult(); - public static T? GetOrAdd(this ICache cache, CacheKey cacheKey, Func generator, TimeSpan? expiration, CancellationToken token = default) + public static T? GetOrAdd(this ICache cache, CacheKey cacheKey, Func generator, TimeSpan expiration, CancellationToken token = default) => cache.GetOrAddAsync(cacheKey, _ => Task.FromResult(generator()), expiration, token).AsTask().GetAwaiter().GetResult(); - public static T? GetOrAdd(this ICache cache, CacheKey cacheKey, Func generator, DateTimeOffset? expiration, CancellationToken token = default) + public static T? GetOrAdd(this ICache cache, CacheKey cacheKey, Func generator, DateTimeOffset expiration, CancellationToken token = default) => cache.GetOrAddAsync(cacheKey, _ => Task.FromResult(generator()), expiration, token).AsTask().GetAwaiter().GetResult(); public static KeyValuePair[] GetOrAdd(this ICache cache, CacheKey[] cacheKeys, Func[]> generator, CancellationToken token = default) @@ -32,7 +32,7 @@ public static class CacheSyncExtensions token) .AsTask().GetAwaiter().GetResult(); - public static KeyValuePair[] GetOrAdd(this ICache cache, CacheKey[] cacheKeys, Func[]> generator, TimeSpan? expiration, CancellationToken token = default) + public static KeyValuePair[] GetOrAdd(this ICache cache, CacheKey[] cacheKeys, Func[]> generator, TimeSpan expiration, CancellationToken token = default) => cache.GetOrAddAsync( Array.ConvertAll(cacheKeys, k => new KeyValuePair(k, k)), (keys, _) => Task.FromResult(generator(keys)), @@ -40,7 +40,7 @@ public static class CacheSyncExtensions token) .AsTask().GetAwaiter().GetResult(); - public static KeyValuePair[] GetOrAdd(this ICache cache, CacheKey[] cacheKeys, Func[]> generator, DateTimeOffset? expiration, CancellationToken token = default) + public static KeyValuePair[] GetOrAdd(this ICache cache, CacheKey[] cacheKeys, Func[]> generator, DateTimeOffset expiration, CancellationToken token = default) => cache.GetOrAddAsync( Array.ConvertAll(cacheKeys, k => new KeyValuePair(k, k)), (keys, _) => Task.FromResult(generator(keys)), @@ -57,40 +57,40 @@ public static bool Remove(this ICache cache, CacheKey[] cacheKeys, Cancell public static bool Set(this ICache cache, CacheKey cacheKey, T? value, CancellationToken token = default) => cache.SetAsync(cacheKey, value, token).AsTask().GetAwaiter().GetResult(); - public static bool Set(this ICache cache, CacheKey cacheKey, T? value, TimeSpan? expiration, CancellationToken token = default) + public static bool Set(this ICache cache, CacheKey cacheKey, T? value, TimeSpan expiration, CancellationToken token = default) => cache.SetAsync(cacheKey, value, expiration, token).AsTask().GetAwaiter().GetResult(); - public static bool Set(this ICache cache, CacheKey cacheKey, T? value, DateTimeOffset? expiration, CancellationToken token = default) + public static bool Set(this ICache cache, CacheKey cacheKey, T? value, DateTimeOffset expiration, CancellationToken token = default) => cache.SetAsync(cacheKey, value, expiration, token).AsTask().GetAwaiter().GetResult(); public static bool Set(this ICache cache, KeyValuePair[] keyValues, CancellationToken token = default) => cache.SetAsync(keyValues, token).AsTask().GetAwaiter().GetResult(); - public static bool Set(this ICache cache, KeyValuePair[] keyValues, TimeSpan? expiration = null, CancellationToken token = default) + public static bool Set(this ICache cache, KeyValuePair[] keyValues, TimeSpan expiration, CancellationToken token = default) => cache.SetAsync(keyValues, expiration, token).AsTask().GetAwaiter().GetResult(); - public static bool Set(this ICache cache, KeyValuePair[] keyValues, DateTimeOffset? expiration = null, CancellationToken token = default) + public static bool Set(this ICache cache, KeyValuePair[] keyValues, DateTimeOffset expiration, CancellationToken token = default) => cache.SetAsync(keyValues, expiration, token).AsTask().GetAwaiter().GetResult(); /// public static bool TryAdd(this ICache cache, CacheKey cacheKey, T? value, CancellationToken token = default) => cache.TryAddAsync(cacheKey, value, token).AsTask().GetAwaiter().GetResult(); - /// - public static bool TryAdd(this ICache cache, CacheKey cacheKey, T? value, TimeSpan? expiration, CancellationToken token = default) + /// + public static bool TryAdd(this ICache cache, CacheKey cacheKey, T? value, TimeSpan expiration, CancellationToken token = default) => cache.TryAddAsync(cacheKey, value, expiration, token).AsTask().GetAwaiter().GetResult(); - /// - public static bool TryAdd(this ICache cache, CacheKey cacheKey, T? value, DateTimeOffset? expiration, CancellationToken token = default) + /// + public static bool TryAdd(this ICache cache, CacheKey cacheKey, T? value, DateTimeOffset expiration, CancellationToken token = default) => cache.TryAddAsync(cacheKey, value, expiration, token).AsTask().GetAwaiter().GetResult(); public static bool Refresh(this ICache cache, CacheKey cacheKey, CancellationToken token = default) => cache.RefreshAsync(cacheKey, token).AsTask().GetAwaiter().GetResult(); - public static bool Refresh(this ICache cache, CacheKey cacheKey, TimeSpan? expiration, CancellationToken token = default) + public static bool Refresh(this ICache cache, CacheKey cacheKey, TimeSpan expiration, CancellationToken token = default) => cache.RefreshAsync(cacheKey, expiration, token).AsTask().GetAwaiter().GetResult(); - public static bool Refresh(this ICache cache, CacheKey cacheKey, DateTimeOffset? expiration, CancellationToken token = default) + public static bool Refresh(this ICache cache, CacheKey cacheKey, DateTimeOffset expiration, CancellationToken token = default) => cache.RefreshAsync(cacheKey, expiration, token).AsTask().GetAwaiter().GetResult(); public static bool Contains(this ICache cache, CacheKey cacheKey, CancellationToken token = default) diff --git a/src/UiPath.Caching.Abstractions/HashCacheExtensions.cs b/src/UiPath.Caching.Abstractions/HashCacheExtensions.cs index cdd4c07..f910096 100644 --- a/src/UiPath.Caching.Abstractions/HashCacheExtensions.cs +++ b/src/UiPath.Caching.Abstractions/HashCacheExtensions.cs @@ -24,22 +24,22 @@ public static class HashCacheExtensions public static ValueTask> GetOrAddAsync(this IHashCache cache, CacheKey cacheKey, Func>> generator, CancellationToken token = default) => cache.GetOrAddAsync(cacheKey, generator, (CachePolicy?)null, token); - public static ValueTask> GetOrAddAsync(this IHashCache cache, CacheKey cacheKey, Func>> generator, TimeSpan? expiration, CancellationToken token = default) + public static ValueTask> GetOrAddAsync(this IHashCache cache, CacheKey cacheKey, Func>> generator, TimeSpan expiration, CancellationToken token = default) => cache.GetOrAddAsync(cacheKey, generator, expiration, null, token); - public static ValueTask> GetOrAddAsync(this IHashCache cache, CacheKey cacheKey, Func>> generator, DateTimeOffset? expiration, CancellationToken token = default) + public static ValueTask> GetOrAddAsync(this IHashCache cache, CacheKey cacheKey, Func>> generator, DateTimeOffset expiration, CancellationToken token = default) => cache.GetOrAddAsync(cacheKey, generator, expiration, (CachePolicy?)null, token); - public static ValueTask> GetOrAddAsync(this IHashCache cache, CacheKey cacheKey, Func>> generator, DateTimeOffset? expiration, HashCacheSetOption? setOption, CancellationToken token = default) + public static ValueTask> GetOrAddAsync(this IHashCache cache, CacheKey cacheKey, Func>> generator, DateTimeOffset expiration, HashCacheSetOption? setOption, CancellationToken token = default) => cache.GetOrAddAsync(cacheKey, generator, expiration, setOption, null, token); public static ValueTask SetAsync(this IHashCache cache, CacheKey cacheKey, IDictionary values, CancellationToken token = default) => cache.SetAsync(cacheKey, values, (CachePolicy?)null, token); - public static ValueTask SetAsync(this IHashCache cache, CacheKey cacheKey, IDictionary values, TimeSpan? expiration, CancellationToken token = default) + public static ValueTask SetAsync(this IHashCache cache, CacheKey cacheKey, IDictionary values, TimeSpan expiration, CancellationToken token = default) => cache.SetAsync(cacheKey, values, expiration, null, token); - public static ValueTask SetAsync(this IHashCache cache, CacheKey cacheKey, IDictionary values, DateTimeOffset? expiration, CancellationToken token = default) + public static ValueTask SetAsync(this IHashCache cache, CacheKey cacheKey, IDictionary values, DateTimeOffset expiration, CancellationToken token = default) => cache.SetAsync(cacheKey, values, expiration, null, token); public static ValueTask SetAsync(this IHashCache cache, CacheKey cacheKey, IDictionary values, HashCacheEntryOptions options, CancellationToken token = default) @@ -48,10 +48,10 @@ public static ValueTask SetAsync(this IHashCache cache, CacheKey cacheK public static ValueTask RefreshAsync(this IHashCache cache, CacheKey cacheKey, CancellationToken token = default) => cache.RefreshAsync(cacheKey, (CachePolicy?)null, token); - public static ValueTask RefreshAsync(this IHashCache cache, CacheKey cacheKey, TimeSpan? expiration, CancellationToken token = default) + public static ValueTask RefreshAsync(this IHashCache cache, CacheKey cacheKey, TimeSpan expiration, CancellationToken token = default) => cache.RefreshAsync(cacheKey, expiration, null, token); - public static ValueTask RefreshAsync(this IHashCache cache, CacheKey cacheKey, DateTimeOffset? expiration, CancellationToken token = default) + public static ValueTask RefreshAsync(this IHashCache cache, CacheKey cacheKey, DateTimeOffset expiration, CancellationToken token = default) => cache.RefreshAsync(cacheKey, expiration, null, token); public static ValueTask RefreshAsync(this IHashCache cache, CacheKey cacheKey, HashCacheEntryOptions options, CancellationToken token = default) diff --git a/src/UiPath.Caching.Abstractions/HashCacheOfT.cs b/src/UiPath.Caching.Abstractions/HashCacheOfT.cs index cf24ac5..a0259d9 100644 --- a/src/UiPath.Caching.Abstractions/HashCacheOfT.cs +++ b/src/UiPath.Caching.Abstractions/HashCacheOfT.cs @@ -46,10 +46,10 @@ public HashCache( public ValueTask> GetOrAddAsync(CacheKey cacheKey, Func>> generator, CancellationToken token = default) => _cache.GetOrAddAsync(GetCacheKey(cacheKey), generator, policy: Policy, token: token); - public ValueTask> GetOrAddAsync(CacheKey cacheKey, Func>> generator, TimeSpan? expiration, CancellationToken token = default) => + public ValueTask> GetOrAddAsync(CacheKey cacheKey, Func>> generator, TimeSpan expiration, CancellationToken token = default) => _cache.GetOrAddAsync(GetCacheKey(cacheKey), generator, expiration, Policy, token); - public ValueTask> GetOrAddAsync(CacheKey cacheKey, Func>> generator, DateTimeOffset? expiration, CancellationToken token = default) => + public ValueTask> GetOrAddAsync(CacheKey cacheKey, Func>> generator, DateTimeOffset expiration, CancellationToken token = default) => _cache.GetOrAddAsync(GetCacheKey(cacheKey), generator, expiration, Policy, token); public ValueTask>> GetCacheEntryAsync(CacheKey cacheKey, CancellationToken token = default) => @@ -58,10 +58,10 @@ public HashCache( public ValueTask SetAsync(CacheKey cacheKey, IDictionary values, CancellationToken token = default) => _cache.SetAsync(GetCacheKey(cacheKey), values, policy: Policy, token: token); - public ValueTask SetAsync(CacheKey cacheKey, IDictionary values, TimeSpan? expiration, CancellationToken token = default) => + public ValueTask SetAsync(CacheKey cacheKey, IDictionary values, TimeSpan expiration, CancellationToken token = default) => _cache.SetAsync(GetCacheKey(cacheKey), values, expiration, Policy, token); - public ValueTask SetAsync(CacheKey cacheKey, IDictionary values, DateTimeOffset? expiration, CancellationToken token = default) => + public ValueTask SetAsync(CacheKey cacheKey, IDictionary values, DateTimeOffset expiration, CancellationToken token = default) => _cache.SetAsync(GetCacheKey(cacheKey), values, expiration, Policy, token); public ValueTask SetAsync(CacheKey cacheKey, IDictionary values, HashCacheEntryOptions options, CancellationToken token = default) => @@ -70,10 +70,10 @@ public ValueTask SetAsync(CacheKey cacheKey, IDictionary value public ValueTask RefreshAsync(CacheKey cacheKey, CancellationToken token = default) => _cache.RefreshAsync(GetCacheKey(cacheKey), policy: Policy, token: token); - public ValueTask RefreshAsync(CacheKey cacheKey, TimeSpan? expiration, CancellationToken token = default) => + public ValueTask RefreshAsync(CacheKey cacheKey, TimeSpan expiration, CancellationToken token = default) => _cache.RefreshAsync(GetCacheKey(cacheKey), expiration, Policy, token); - public ValueTask RefreshAsync(CacheKey cacheKey, DateTimeOffset? expiration, CancellationToken token = default) => + public ValueTask RefreshAsync(CacheKey cacheKey, DateTimeOffset expiration, CancellationToken token = default) => _cache.RefreshAsync(GetCacheKey(cacheKey), expiration, Policy, token); public ValueTask RefreshAsync(CacheKey cacheKey, HashCacheEntryOptions options, CancellationToken token = default) => diff --git a/src/UiPath.Caching.Abstractions/HashCacheSyncExtensions.cs b/src/UiPath.Caching.Abstractions/HashCacheSyncExtensions.cs index 5c1f051..4923d4c 100644 --- a/src/UiPath.Caching.Abstractions/HashCacheSyncExtensions.cs +++ b/src/UiPath.Caching.Abstractions/HashCacheSyncExtensions.cs @@ -22,10 +22,10 @@ public static class HashCacheSyncExtensions public static IDictionary GetOrAdd(this IHashCache cache, CacheKey cacheKey, Func> generator, CancellationToken token = default) => cache.GetOrAddAsync(cacheKey, _ => Task.FromResult(generator()), token).AsTask().GetAwaiter().GetResult(); - public static IDictionary GetOrAdd(this IHashCache cache, CacheKey cacheKey, Func> generator, TimeSpan? expiration, CancellationToken token = default) + public static IDictionary GetOrAdd(this IHashCache cache, CacheKey cacheKey, Func> generator, TimeSpan expiration, CancellationToken token = default) => cache.GetOrAddAsync(cacheKey, _ => Task.FromResult(generator()), expiration, token).AsTask().GetAwaiter().GetResult(); - public static IDictionary GetOrAdd(this IHashCache cache, CacheKey cacheKey, Func> generator, DateTimeOffset? expiration, CancellationToken token = default) + public static IDictionary GetOrAdd(this IHashCache cache, CacheKey cacheKey, Func> generator, DateTimeOffset expiration, CancellationToken token = default) => cache.GetOrAddAsync(cacheKey, _ => Task.FromResult(generator()), expiration, token).AsTask().GetAwaiter().GetResult(); public static ICacheEntry> GetCacheEntry(this IHashCache cache, CacheKey cacheKey, CancellationToken token = default) @@ -34,10 +34,10 @@ public static class HashCacheSyncExtensions public static bool Set(this IHashCache cache, CacheKey cacheKey, IDictionary values, CancellationToken token = default) => cache.SetAsync(cacheKey, values, token).AsTask().GetAwaiter().GetResult(); - public static bool Set(this IHashCache cache, CacheKey cacheKey, IDictionary values, TimeSpan? expiration, CancellationToken token = default) + public static bool Set(this IHashCache cache, CacheKey cacheKey, IDictionary values, TimeSpan expiration, CancellationToken token = default) => cache.SetAsync(cacheKey, values, expiration, token).AsTask().GetAwaiter().GetResult(); - public static bool Set(this IHashCache cache, CacheKey cacheKey, IDictionary values, DateTimeOffset? expiration, CancellationToken token = default) + public static bool Set(this IHashCache cache, CacheKey cacheKey, IDictionary values, DateTimeOffset expiration, CancellationToken token = default) => cache.SetAsync(cacheKey, values, expiration, token).AsTask().GetAwaiter().GetResult(); public static bool Set(this IHashCache cache, CacheKey cacheKey, IDictionary values, HashCacheEntryOptions options, CancellationToken token = default) @@ -46,10 +46,10 @@ public static bool Set(this IHashCache cache, CacheKey cacheKey, IDictiona public static bool Refresh(this IHashCache cache, CacheKey cacheKey, CancellationToken token = default) => cache.RefreshAsync(cacheKey, token).AsTask().GetAwaiter().GetResult(); - public static bool Refresh(this IHashCache cache, CacheKey cacheKey, TimeSpan? expiration, CancellationToken token = default) + public static bool Refresh(this IHashCache cache, CacheKey cacheKey, TimeSpan expiration, CancellationToken token = default) => cache.RefreshAsync(cacheKey, expiration, token).AsTask().GetAwaiter().GetResult(); - public static bool Refresh(this IHashCache cache, CacheKey cacheKey, DateTimeOffset? expiration, CancellationToken token = default) + public static bool Refresh(this IHashCache cache, CacheKey cacheKey, DateTimeOffset expiration, CancellationToken token = default) => cache.RefreshAsync(cacheKey, expiration, token).AsTask().GetAwaiter().GetResult(); public static bool Refresh(this IHashCache cache, CacheKey cacheKey, HashCacheEntryOptions options, CancellationToken token = default) diff --git a/src/UiPath.Caching.Abstractions/ICache.cs b/src/UiPath.Caching.Abstractions/ICache.cs index ff71b90..49f46a4 100644 --- a/src/UiPath.Caching.Abstractions/ICache.cs +++ b/src/UiPath.Caching.Abstractions/ICache.cs @@ -1,5 +1,14 @@ namespace UiPath.Caching; +/// +/// Expiration. The expiration parameter is not nullable. A caller with nothing to +/// say about lifetime calls the overload that has no expiration parameter and gets +/// , then the cache default; a caller that passes one +/// means it, so a duration that is not positive — or a deadline that has already passed — is +/// rejected with rather than silently ignored. See +/// . The no-op implementations in this package read no argument at all +/// and so enforce nothing. +/// public interface ICache : IDisposable { string Name { get; } @@ -14,19 +23,19 @@ public interface ICache : IDisposable ValueTask GetOrAddAsync(CacheKey cacheKey, Func> generator, CachePolicy? policy, CancellationToken token = default); - ValueTask GetOrAddAsync(CacheKey cacheKey, Func> generator, TimeSpan? expiration, CachePolicy? policy, CancellationToken token = default); + ValueTask GetOrAddAsync(CacheKey cacheKey, Func> generator, TimeSpan expiration, CachePolicy? policy, CancellationToken token = default); - ValueTask GetOrAddAsync(CacheKey cacheKey, Func> generator, DateTimeOffset? expiration, CachePolicy? policy, CancellationToken token = default); + ValueTask GetOrAddAsync(CacheKey cacheKey, Func> generator, DateTimeOffset expiration, CachePolicy? policy, CancellationToken token = default); ValueTask[]> GetOrAddAsync(KeyValuePair[] entries, Func[]>> generator, CachePolicy? policy, CancellationToken token = default) where TState : notnull => BatchGetOrAdd.RunAsync(this, entries, generator, (pairs, t) => SetAsync(pairs, policy, t), policy, token); - ValueTask[]> GetOrAddAsync(KeyValuePair[] entries, Func[]>> generator, TimeSpan? expiration, CachePolicy? policy, CancellationToken token = default) + ValueTask[]> GetOrAddAsync(KeyValuePair[] entries, Func[]>> generator, TimeSpan expiration, CachePolicy? policy, CancellationToken token = default) where TState : notnull => BatchGetOrAdd.RunAsync(this, entries, generator, (pairs, t) => SetAsync(pairs, expiration, policy, t), policy, token); - ValueTask[]> GetOrAddAsync(KeyValuePair[] entries, Func[]>> generator, DateTimeOffset? expiration, CachePolicy? policy, CancellationToken token = default) + ValueTask[]> GetOrAddAsync(KeyValuePair[] entries, Func[]>> generator, DateTimeOffset expiration, CachePolicy? policy, CancellationToken token = default) where TState : notnull => BatchGetOrAdd.RunAsync(this, entries, generator, (pairs, t) => SetAsync(pairs, expiration, policy, t), policy, token); @@ -36,15 +45,15 @@ public interface ICache : IDisposable ValueTask SetAsync(CacheKey cacheKey, T? value, CachePolicy? policy, CancellationToken token = default); - ValueTask SetAsync(CacheKey cacheKey, T? value, TimeSpan? expiration, CachePolicy? policy, CancellationToken token = default); + ValueTask SetAsync(CacheKey cacheKey, T? value, TimeSpan expiration, CachePolicy? policy, CancellationToken token = default); - ValueTask SetAsync(CacheKey cacheKey, T? value, DateTimeOffset? expiration, CachePolicy? policy, CancellationToken token = default); + ValueTask SetAsync(CacheKey cacheKey, T? value, DateTimeOffset expiration, CachePolicy? policy, CancellationToken token = default); ValueTask SetAsync(KeyValuePair[] keyValues, CachePolicy? policy, CancellationToken token = default); - ValueTask SetAsync(KeyValuePair[] keyValues, TimeSpan? expiration, CachePolicy? policy, CancellationToken token = default); + ValueTask SetAsync(KeyValuePair[] keyValues, TimeSpan expiration, CachePolicy? policy, CancellationToken token = default); - ValueTask SetAsync(KeyValuePair[] keyValues, DateTimeOffset? expiration, CachePolicy? policy, CancellationToken token = default); + ValueTask SetAsync(KeyValuePair[] keyValues, DateTimeOffset expiration, CachePolicy? policy, CancellationToken token = default); /// /// Conditional add: writes only if is @@ -62,21 +71,23 @@ ValueTask TryAddAsync(CacheKey cacheKey, T? value, CachePolicy? policy, /// /// - /// Lifetime of the entry if it is created, applied by the same atomic command. Falls back to - /// CachePolicy.DistributedExpiration then the cache default. Not in the future: no-op. + /// Lifetime of the entry if it is created, applied by the same atomic command. Must be a + /// positive duration; to inherit CachePolicy.DistributedExpiration and then the cache + /// default, call the overload without this parameter. /// - ValueTask TryAddAsync(CacheKey cacheKey, T? value, TimeSpan? expiration, CachePolicy? policy, CancellationToken token = default) + /// is not positive. + ValueTask TryAddAsync(CacheKey cacheKey, T? value, TimeSpan expiration, CachePolicy? policy, CancellationToken token = default) => throw ConditionalAdd.NotSupported(Name, GetType().FullName ?? nameof(ICache)); - /// - ValueTask TryAddAsync(CacheKey cacheKey, T? value, DateTimeOffset? expiration, CachePolicy? policy, CancellationToken token = default) + /// + ValueTask TryAddAsync(CacheKey cacheKey, T? value, DateTimeOffset expiration, CachePolicy? policy, CancellationToken token = default) => throw ConditionalAdd.NotSupported(Name, GetType().FullName ?? nameof(ICache)); ValueTask RefreshAsync(CacheKey cacheKey, CachePolicy? policy, CancellationToken token = default); - ValueTask RefreshAsync(CacheKey cacheKey, TimeSpan? expiration, CachePolicy? policy, CancellationToken token = default); + ValueTask RefreshAsync(CacheKey cacheKey, TimeSpan expiration, CachePolicy? policy, CancellationToken token = default); - ValueTask RefreshAsync(CacheKey cacheKey, DateTimeOffset? expiration, CachePolicy? policy, CancellationToken token = default); + ValueTask RefreshAsync(CacheKey cacheKey, DateTimeOffset expiration, CachePolicy? policy, CancellationToken token = default); ValueTask ContainsAsync(CacheKey cacheKey, CancellationToken token = default); diff --git a/src/UiPath.Caching.Abstractions/ICacheOfT.cs b/src/UiPath.Caching.Abstractions/ICacheOfT.cs index 0eb0e69..fe884e8 100644 --- a/src/UiPath.Caching.Abstractions/ICacheOfT.cs +++ b/src/UiPath.Caching.Abstractions/ICacheOfT.cs @@ -9,15 +9,15 @@ public interface ICache ValueTask GetOrAddAsync(CacheKey cacheKey, Func> generator, CancellationToken token = default); - ValueTask GetOrAddAsync(CacheKey cacheKey, Func> generator, TimeSpan? expiration, CancellationToken token = default); + ValueTask GetOrAddAsync(CacheKey cacheKey, Func> generator, TimeSpan expiration, CancellationToken token = default); - ValueTask GetOrAddAsync(CacheKey cacheKey, Func> generator, DateTimeOffset? expiration, CancellationToken token = default); + ValueTask GetOrAddAsync(CacheKey cacheKey, Func> generator, DateTimeOffset expiration, CancellationToken token = default); ValueTask[]> GetOrAddAsync(KeyValuePair[] entries, Func[]>> generator, CancellationToken token = default) where TState : notnull; - ValueTask[]> GetOrAddAsync(KeyValuePair[] entries, Func[]>> generator, TimeSpan? expiration, CancellationToken token = default) where TState : notnull; + ValueTask[]> GetOrAddAsync(KeyValuePair[] entries, Func[]>> generator, TimeSpan expiration, CancellationToken token = default) where TState : notnull; - ValueTask[]> GetOrAddAsync(KeyValuePair[] entries, Func[]>> generator, DateTimeOffset? expiration, CancellationToken token = default) where TState : notnull; + ValueTask[]> GetOrAddAsync(KeyValuePair[] entries, Func[]>> generator, DateTimeOffset expiration, CancellationToken token = default) where TState : notnull; ValueTask RemoveAsync(CacheKey cacheKey, CancellationToken token = default); @@ -25,19 +25,19 @@ public interface ICache ValueTask SetAsync(CacheKey cacheKey, T? value, CancellationToken token = default); - ValueTask SetAsync(CacheKey cacheKey, T? value, TimeSpan? expiration, CancellationToken token = default); + ValueTask SetAsync(CacheKey cacheKey, T? value, TimeSpan expiration, CancellationToken token = default); - ValueTask SetAsync(CacheKey cacheKey, T? value, DateTimeOffset? expiration, CancellationToken token = default); + ValueTask SetAsync(CacheKey cacheKey, T? value, DateTimeOffset expiration, CancellationToken token = default); ValueTask SetAsync(KeyValuePair[] keyValues, CancellationToken token = default); - ValueTask SetAsync(KeyValuePair[] keyValues, TimeSpan? expiration = null, CancellationToken token = default); + ValueTask SetAsync(KeyValuePair[] keyValues, TimeSpan expiration, CancellationToken token = default); - ValueTask SetAsync(KeyValuePair[] keyValues, DateTimeOffset? expiration = null, CancellationToken token = default); + ValueTask SetAsync(KeyValuePair[] keyValues, DateTimeOffset expiration, CancellationToken token = default); /// /// Typed façade over - /// ; + /// ; /// see that member for the contract. /// ValueTask TryAddAsync(CacheKey cacheKey, T? value, CancellationToken token = default) @@ -46,18 +46,18 @@ ValueTask TryAddAsync(CacheKey cacheKey, T? value, CancellationToken token /// /// Lifetime of the entry if it is created. - ValueTask TryAddAsync(CacheKey cacheKey, T? value, TimeSpan? expiration, CancellationToken token = default) + ValueTask TryAddAsync(CacheKey cacheKey, T? value, TimeSpan expiration, CancellationToken token = default) => throw ConditionalAdd.NotSupported(Name, GetType().FullName ?? nameof(ICache)); - /// - ValueTask TryAddAsync(CacheKey cacheKey, T? value, DateTimeOffset? expiration, CancellationToken token = default) + /// + ValueTask TryAddAsync(CacheKey cacheKey, T? value, DateTimeOffset expiration, CancellationToken token = default) => throw ConditionalAdd.NotSupported(Name, GetType().FullName ?? nameof(ICache)); ValueTask RefreshAsync(CacheKey cacheKey, CancellationToken token = default); - ValueTask RefreshAsync(CacheKey cacheKey, TimeSpan? expiration, CancellationToken token = default); + ValueTask RefreshAsync(CacheKey cacheKey, TimeSpan expiration, CancellationToken token = default); - ValueTask RefreshAsync(CacheKey cacheKey, DateTimeOffset? expiration, CancellationToken token = default); + ValueTask RefreshAsync(CacheKey cacheKey, DateTimeOffset expiration, CancellationToken token = default); ValueTask ContainsAsync(CacheKey cacheKey, CancellationToken token = default); diff --git a/src/UiPath.Caching.Abstractions/IHashCache.cs b/src/UiPath.Caching.Abstractions/IHashCache.cs index 3819040..b10d3b8 100644 --- a/src/UiPath.Caching.Abstractions/IHashCache.cs +++ b/src/UiPath.Caching.Abstractions/IHashCache.cs @@ -1,5 +1,14 @@ namespace UiPath.Caching; +/// +/// Expiration. The expiration parameter is not nullable. A caller with nothing to +/// say about lifetime calls the overload that has no expiration parameter and gets +/// , then the cache default; a caller that passes one +/// means it, so a duration that is not positive — or a deadline that has already passed — is +/// rejected with rather than silently ignored. See +/// . The no-op implementations in this package read no argument at all +/// and so enforce nothing. +/// public interface IHashCache : IDisposable { string Name { get; } @@ -14,25 +23,25 @@ public interface IHashCache : IDisposable ValueTask> GetOrAddAsync(CacheKey cacheKey, Func>> generator, CachePolicy? policy, CancellationToken token = default); - ValueTask> GetOrAddAsync(CacheKey cacheKey, Func>> generator, TimeSpan? expiration, CachePolicy? policy, CancellationToken token = default); + ValueTask> GetOrAddAsync(CacheKey cacheKey, Func>> generator, TimeSpan expiration, CachePolicy? policy, CancellationToken token = default); - ValueTask> GetOrAddAsync(CacheKey cacheKey, Func>> generator, DateTimeOffset? expiration, CachePolicy? policy, CancellationToken token = default); + ValueTask> GetOrAddAsync(CacheKey cacheKey, Func>> generator, DateTimeOffset expiration, CachePolicy? policy, CancellationToken token = default); - ValueTask> GetOrAddAsync(CacheKey cacheKey, Func>> generator, DateTimeOffset? expiration, HashCacheSetOption? setOption, CachePolicy? policy, CancellationToken token = default); + ValueTask> GetOrAddAsync(CacheKey cacheKey, Func>> generator, DateTimeOffset expiration, HashCacheSetOption? setOption, CachePolicy? policy, CancellationToken token = default); ValueTask SetAsync(CacheKey cacheKey, IDictionary values, CachePolicy? policy, CancellationToken token = default); - ValueTask SetAsync(CacheKey cacheKey, IDictionary values, TimeSpan? expiration, CachePolicy? policy, CancellationToken token = default); + ValueTask SetAsync(CacheKey cacheKey, IDictionary values, TimeSpan expiration, CachePolicy? policy, CancellationToken token = default); - ValueTask SetAsync(CacheKey cacheKey, IDictionary values, DateTimeOffset? expiration, CachePolicy? policy, CancellationToken token = default); + ValueTask SetAsync(CacheKey cacheKey, IDictionary values, DateTimeOffset expiration, CachePolicy? policy, CancellationToken token = default); ValueTask SetAsync(CacheKey cacheKey, IDictionary values, HashCacheEntryOptions options, CachePolicy? policy, CancellationToken token = default); ValueTask RefreshAsync(CacheKey cacheKey, CachePolicy? policy, CancellationToken token = default); - ValueTask RefreshAsync(CacheKey cacheKey, TimeSpan? expiration, CachePolicy? policy, CancellationToken token = default); + ValueTask RefreshAsync(CacheKey cacheKey, TimeSpan expiration, CachePolicy? policy, CancellationToken token = default); - ValueTask RefreshAsync(CacheKey cacheKey, DateTimeOffset? expiration, CachePolicy? policy, CancellationToken token = default); + ValueTask RefreshAsync(CacheKey cacheKey, DateTimeOffset expiration, CachePolicy? policy, CancellationToken token = default); ValueTask RefreshAsync(CacheKey cacheKey, HashCacheEntryOptions options, CachePolicy? policy, CancellationToken token = default); diff --git a/src/UiPath.Caching.Abstractions/IHashCacheOfT.cs b/src/UiPath.Caching.Abstractions/IHashCacheOfT.cs index 9560f79..5684549 100644 --- a/src/UiPath.Caching.Abstractions/IHashCacheOfT.cs +++ b/src/UiPath.Caching.Abstractions/IHashCacheOfT.cs @@ -12,25 +12,25 @@ public interface IHashCache ValueTask> GetOrAddAsync(CacheKey cacheKey, Func>> generator, CancellationToken token = default); - ValueTask> GetOrAddAsync(CacheKey cacheKey, Func>> generator, TimeSpan? expiration, CancellationToken token = default); + ValueTask> GetOrAddAsync(CacheKey cacheKey, Func>> generator, TimeSpan expiration, CancellationToken token = default); - ValueTask> GetOrAddAsync(CacheKey cacheKey, Func>> generator, DateTimeOffset? expiration, CancellationToken token = default); + ValueTask> GetOrAddAsync(CacheKey cacheKey, Func>> generator, DateTimeOffset expiration, CancellationToken token = default); ValueTask>> GetCacheEntryAsync(CacheKey cacheKey, CancellationToken token = default); ValueTask SetAsync(CacheKey cacheKey, IDictionary values, CancellationToken token = default); - ValueTask SetAsync(CacheKey cacheKey, IDictionary values, TimeSpan? expiration, CancellationToken token = default); + ValueTask SetAsync(CacheKey cacheKey, IDictionary values, TimeSpan expiration, CancellationToken token = default); - ValueTask SetAsync(CacheKey cacheKey, IDictionary values, DateTimeOffset? expiration, CancellationToken token = default); + ValueTask SetAsync(CacheKey cacheKey, IDictionary values, DateTimeOffset expiration, CancellationToken token = default); ValueTask SetAsync(CacheKey cacheKey, IDictionary values, HashCacheEntryOptions options, CancellationToken token = default); ValueTask RefreshAsync(CacheKey cacheKey, CancellationToken token = default); - ValueTask RefreshAsync(CacheKey cacheKey, TimeSpan? expiration, CancellationToken token = default); + ValueTask RefreshAsync(CacheKey cacheKey, TimeSpan expiration, CancellationToken token = default); - ValueTask RefreshAsync(CacheKey cacheKey, DateTimeOffset? expiration, CancellationToken token = default); + ValueTask RefreshAsync(CacheKey cacheKey, DateTimeOffset expiration, CancellationToken token = default); ValueTask RefreshAsync(CacheKey cacheKey, HashCacheEntryOptions options, CancellationToken token = default); diff --git a/src/UiPath.Caching.Abstractions/NullCache.cs b/src/UiPath.Caching.Abstractions/NullCache.cs index f518028..ff5c408 100644 --- a/src/UiPath.Caching.Abstractions/NullCache.cs +++ b/src/UiPath.Caching.Abstractions/NullCache.cs @@ -48,17 +48,17 @@ public ValueTask ContainsAsync(CacheKey cacheKey, CancellationToken tok public ValueTask GetOrAddAsync(CacheKey cacheKey, Func> generator, CachePolicy? policy, CancellationToken token = default) => ReturnGeneratorAsync(generator, token); - public ValueTask GetOrAddAsync(CacheKey cacheKey, Func> generator, TimeSpan? expiration, CachePolicy? policy, CancellationToken token = default) => + public ValueTask GetOrAddAsync(CacheKey cacheKey, Func> generator, TimeSpan expiration, CachePolicy? policy, CancellationToken token = default) => ReturnGeneratorAsync(generator, token); - public ValueTask GetOrAddAsync(CacheKey cacheKey, Func> generator, DateTimeOffset? expiration, CachePolicy? policy, CancellationToken token = default) => + public ValueTask GetOrAddAsync(CacheKey cacheKey, Func> generator, DateTimeOffset expiration, CachePolicy? policy, CancellationToken token = default) => ReturnGeneratorAsync(generator, token); public ValueTask RefreshAsync(CacheKey cacheKey, CachePolicy? policy, CancellationToken token = default) => ReturnTrueAsync(); - public ValueTask RefreshAsync(CacheKey cacheKey, TimeSpan? expiration, CachePolicy? policy, CancellationToken token = default) => ReturnTrueAsync(); + public ValueTask RefreshAsync(CacheKey cacheKey, TimeSpan expiration, CachePolicy? policy, CancellationToken token = default) => ReturnTrueAsync(); - public ValueTask RefreshAsync(CacheKey cacheKey, DateTimeOffset? expiration, CachePolicy? policy, CancellationToken token = default) => ReturnTrueAsync(); + public ValueTask RefreshAsync(CacheKey cacheKey, DateTimeOffset expiration, CachePolicy? policy, CancellationToken token = default) => ReturnTrueAsync(); public ValueTask RemoveAsync(CacheKey cacheKey, CancellationToken token = default) => ReturnTrueAsync(); @@ -66,15 +66,15 @@ public ValueTask ContainsAsync(CacheKey cacheKey, CancellationToken tok public ValueTask SetAsync(CacheKey cacheKey, T? value, CachePolicy? policy, CancellationToken token = default) => ReturnTrueAsync(); - public ValueTask SetAsync(CacheKey cacheKey, T? value, TimeSpan? expiration, CachePolicy? policy, CancellationToken token = default) => ReturnTrueAsync(); + public ValueTask SetAsync(CacheKey cacheKey, T? value, TimeSpan expiration, CachePolicy? policy, CancellationToken token = default) => ReturnTrueAsync(); - public ValueTask SetAsync(CacheKey cacheKey, T? value, DateTimeOffset? expiration, CachePolicy? policy, CancellationToken token = default) => ReturnTrueAsync(); + public ValueTask SetAsync(CacheKey cacheKey, T? value, DateTimeOffset expiration, CachePolicy? policy, CancellationToken token = default) => ReturnTrueAsync(); public ValueTask SetAsync(KeyValuePair[] keyValues, CachePolicy? policy, CancellationToken token = default) => ReturnTrueAsync(); - public ValueTask SetAsync(KeyValuePair[] keyValues, TimeSpan? expiration, CachePolicy? policy, CancellationToken token = default) => ReturnTrueAsync(); + public ValueTask SetAsync(KeyValuePair[] keyValues, TimeSpan expiration, CachePolicy? policy, CancellationToken token = default) => ReturnTrueAsync(); - public ValueTask SetAsync(KeyValuePair[] keyValues, DateTimeOffset? expiration, CachePolicy? policy, CancellationToken token = default) => ReturnTrueAsync(); + public ValueTask SetAsync(KeyValuePair[] keyValues, DateTimeOffset expiration, CachePolicy? policy, CancellationToken token = default) => ReturnTrueAsync(); /// /// Always false: retaining nothing, this store cannot arbitrate a conditional add, and @@ -85,10 +85,10 @@ public ValueTask ContainsAsync(CacheKey cacheKey, CancellationToken tok public ValueTask TryAddAsync(CacheKey cacheKey, T? value, CachePolicy? policy, CancellationToken token = default) => ReturnFalseAsync(); /// - public ValueTask TryAddAsync(CacheKey cacheKey, T? value, TimeSpan? expiration, CachePolicy? policy, CancellationToken token = default) => ReturnFalseAsync(); + public ValueTask TryAddAsync(CacheKey cacheKey, T? value, TimeSpan expiration, CachePolicy? policy, CancellationToken token = default) => ReturnFalseAsync(); /// - public ValueTask TryAddAsync(CacheKey cacheKey, T? value, DateTimeOffset? expiration, CachePolicy? policy, CancellationToken token = default) => ReturnFalseAsync(); + public ValueTask TryAddAsync(CacheKey cacheKey, T? value, DateTimeOffset expiration, CachePolicy? policy, CancellationToken token = default) => ReturnFalseAsync(); public ValueTask TimeToLiveAsync(CacheKey cacheKey, CancellationToken token = default) diff --git a/src/UiPath.Caching.Abstractions/NullHashCache.cs b/src/UiPath.Caching.Abstractions/NullHashCache.cs index e15a9e0..7f3585c 100644 --- a/src/UiPath.Caching.Abstractions/NullHashCache.cs +++ b/src/UiPath.Caching.Abstractions/NullHashCache.cs @@ -54,20 +54,20 @@ public ValueTask ContainsAsync(CacheKey cacheKey, CancellationToken tok public ValueTask> GetOrAddAsync(CacheKey cacheKey, Func>> generator, CachePolicy? policy, CancellationToken token = default) => ReturnGeneratorAsync(generator, token); - public ValueTask> GetOrAddAsync(CacheKey cacheKey, Func>> generator, TimeSpan? expiration, CachePolicy? policy, CancellationToken token = default) => + public ValueTask> GetOrAddAsync(CacheKey cacheKey, Func>> generator, TimeSpan expiration, CachePolicy? policy, CancellationToken token = default) => ReturnGeneratorAsync(generator, token); - public ValueTask> GetOrAddAsync(CacheKey cacheKey, Func>> generator, DateTimeOffset? expiration, CachePolicy? policy, CancellationToken token = default) => + public ValueTask> GetOrAddAsync(CacheKey cacheKey, Func>> generator, DateTimeOffset expiration, CachePolicy? policy, CancellationToken token = default) => ReturnGeneratorAsync(generator, token); - public ValueTask> GetOrAddAsync(CacheKey cacheKey, Func>> generator, DateTimeOffset? expiration, HashCacheSetOption? setOption, CachePolicy? policy, CancellationToken token = default) => + public ValueTask> GetOrAddAsync(CacheKey cacheKey, Func>> generator, DateTimeOffset expiration, HashCacheSetOption? setOption, CachePolicy? policy, CancellationToken token = default) => ReturnGeneratorAsync(generator, token); public ValueTask RefreshAsync(CacheKey cacheKey, CachePolicy? policy, CancellationToken token = default) => ReturnTrueAsync(); - public ValueTask RefreshAsync(CacheKey cacheKey, TimeSpan? expiration, CachePolicy? policy, CancellationToken token = default) => ReturnTrueAsync(); + public ValueTask RefreshAsync(CacheKey cacheKey, TimeSpan expiration, CachePolicy? policy, CancellationToken token = default) => ReturnTrueAsync(); - public ValueTask RefreshAsync(CacheKey cacheKey, DateTimeOffset? expiration, CachePolicy? policy, CancellationToken token = default) => ReturnTrueAsync(); + public ValueTask RefreshAsync(CacheKey cacheKey, DateTimeOffset expiration, CachePolicy? policy, CancellationToken token = default) => ReturnTrueAsync(); public ValueTask RefreshAsync(CacheKey cacheKey, HashCacheEntryOptions options, CachePolicy? policy, CancellationToken token = default) => ReturnTrueAsync(); @@ -75,9 +75,9 @@ public ValueTask ContainsAsync(CacheKey cacheKey, CancellationToken tok public ValueTask SetAsync(CacheKey cacheKey, IDictionary values, CachePolicy? policy, CancellationToken token = default) => ReturnTrueAsync(); - public ValueTask SetAsync(CacheKey cacheKey, IDictionary values, TimeSpan? expiration, CachePolicy? policy, CancellationToken token = default) => ReturnTrueAsync(); + public ValueTask SetAsync(CacheKey cacheKey, IDictionary values, TimeSpan expiration, CachePolicy? policy, CancellationToken token = default) => ReturnTrueAsync(); - public ValueTask SetAsync(CacheKey cacheKey, IDictionary values, DateTimeOffset? expiration, CachePolicy? policy, CancellationToken token = default) => ReturnTrueAsync(); + public ValueTask SetAsync(CacheKey cacheKey, IDictionary values, DateTimeOffset expiration, CachePolicy? policy, CancellationToken token = default) => ReturnTrueAsync(); public ValueTask SetAsync(CacheKey cacheKey, IDictionary values, HashCacheEntryOptions options, CachePolicy? policy, CancellationToken token = default) => ReturnTrueAsync(); diff --git a/src/UiPath.Caching.Abstractions/PublicAPI.Shipped.txt b/src/UiPath.Caching.Abstractions/PublicAPI.Shipped.txt index 5d46a07..3ac8107 100644 --- a/src/UiPath.Caching.Abstractions/PublicAPI.Shipped.txt +++ b/src/UiPath.Caching.Abstractions/PublicAPI.Shipped.txt @@ -91,25 +91,15 @@ UiPath.Caching.Cache.ContainsAsync(UiPath.Caching.CacheKey cacheKey, System.T UiPath.Caching.Cache.ExpireTimeAsync(UiPath.Caching.CacheKey cacheKey, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask UiPath.Caching.Cache.GetAsync(UiPath.Caching.CacheKey cacheKey, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask UiPath.Caching.Cache.GetAsync(UiPath.Caching.CacheKey[]! cacheKeys, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask[]!> -UiPath.Caching.Cache.GetOrAddAsync(UiPath.Caching.CacheKey cacheKey, System.Func!>! generator, System.DateTimeOffset? expiration, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask UiPath.Caching.Cache.GetOrAddAsync(UiPath.Caching.CacheKey cacheKey, System.Func!>! generator, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask -UiPath.Caching.Cache.GetOrAddAsync(UiPath.Caching.CacheKey cacheKey, System.Func!>! generator, System.TimeSpan? expiration, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask -UiPath.Caching.Cache.GetOrAddAsync(System.Collections.Generic.KeyValuePair[]! entries, System.Func[]!>!>! generator, System.DateTimeOffset? expiration, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask[]!> UiPath.Caching.Cache.GetOrAddAsync(System.Collections.Generic.KeyValuePair[]! entries, System.Func[]!>!>! generator, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask[]!> -UiPath.Caching.Cache.GetOrAddAsync(System.Collections.Generic.KeyValuePair[]! entries, System.Func[]!>!>! generator, System.TimeSpan? expiration, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask[]!> UiPath.Caching.Cache.Name.get -> string! UiPath.Caching.Cache.Policy.get -> UiPath.Caching.CachePolicy? -UiPath.Caching.Cache.RefreshAsync(UiPath.Caching.CacheKey cacheKey, System.DateTimeOffset? expiration, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask UiPath.Caching.Cache.RefreshAsync(UiPath.Caching.CacheKey cacheKey, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask -UiPath.Caching.Cache.RefreshAsync(UiPath.Caching.CacheKey cacheKey, System.TimeSpan? expiration, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask UiPath.Caching.Cache.RemoveAsync(UiPath.Caching.CacheKey cacheKey, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask UiPath.Caching.Cache.RemoveAsync(UiPath.Caching.CacheKey[]! cacheKeys, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask -UiPath.Caching.Cache.SetAsync(System.Collections.Generic.KeyValuePair[]! keyValues, System.DateTimeOffset? expiration = null, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask UiPath.Caching.Cache.SetAsync(System.Collections.Generic.KeyValuePair[]! keyValues, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask -UiPath.Caching.Cache.SetAsync(System.Collections.Generic.KeyValuePair[]! keyValues, System.TimeSpan? expiration = null, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask -UiPath.Caching.Cache.SetAsync(UiPath.Caching.CacheKey cacheKey, T? value, System.DateTimeOffset? expiration, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask UiPath.Caching.Cache.SetAsync(UiPath.Caching.CacheKey cacheKey, T? value, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask -UiPath.Caching.Cache.SetAsync(UiPath.Caching.CacheKey cacheKey, T? value, System.TimeSpan? expiration, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask UiPath.Caching.Cache.TimeToLiveAsync(UiPath.Caching.CacheKey cacheKey, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask UiPath.Caching.CacheFactoryExtensions UiPath.Caching.CacheKey @@ -197,21 +187,15 @@ UiPath.Caching.HashCache.GetAsync(UiPath.Caching.CacheKey cacheKey, string![] UiPath.Caching.HashCache.GetCacheEntryAsync(UiPath.Caching.CacheKey cacheKey, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask!>!> UiPath.Caching.HashCache.GetItemAsync(UiPath.Caching.CacheKey cacheKey, string! field, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask UiPath.Caching.HashCache.GetMetadataAsync(UiPath.Caching.CacheKey cacheKey, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask?> -UiPath.Caching.HashCache.GetOrAddAsync(UiPath.Caching.CacheKey cacheKey, System.Func!>!>! generator, System.DateTimeOffset? expiration, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask!> UiPath.Caching.HashCache.GetOrAddAsync(UiPath.Caching.CacheKey cacheKey, System.Func!>!>! generator, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask!> -UiPath.Caching.HashCache.GetOrAddAsync(UiPath.Caching.CacheKey cacheKey, System.Func!>!>! generator, System.TimeSpan? expiration, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask!> UiPath.Caching.HashCache.HashCache(UiPath.Caching.ICacheFactory! cacheFactory, UiPath.Caching.ICacheKeyStrategy? cacheKeyStrategy = null, UiPath.Caching.ICachePolicyFactory? policyFactory = null, string? policyName = null) -> void UiPath.Caching.HashCache.HashCache(UiPath.Caching.IHashCache! cache, UiPath.Caching.ICacheKeyStrategy? cacheKeyStrategy = null, UiPath.Caching.CachePolicy? policy = null) -> void UiPath.Caching.HashCache.Name.get -> string! UiPath.Caching.HashCache.Policy.get -> UiPath.Caching.CachePolicy? -UiPath.Caching.HashCache.RefreshAsync(UiPath.Caching.CacheKey cacheKey, System.DateTimeOffset? expiration, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask UiPath.Caching.HashCache.RefreshAsync(UiPath.Caching.CacheKey cacheKey, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask -UiPath.Caching.HashCache.RefreshAsync(UiPath.Caching.CacheKey cacheKey, System.TimeSpan? expiration, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask UiPath.Caching.HashCache.RefreshAsync(UiPath.Caching.CacheKey cacheKey, UiPath.Caching.HashCacheEntryOptions options, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask UiPath.Caching.HashCache.RemoveAsync(UiPath.Caching.CacheKey cacheKey, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask -UiPath.Caching.HashCache.SetAsync(UiPath.Caching.CacheKey cacheKey, System.Collections.Generic.IDictionary! values, System.DateTimeOffset? expiration, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask UiPath.Caching.HashCache.SetAsync(UiPath.Caching.CacheKey cacheKey, System.Collections.Generic.IDictionary! values, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask -UiPath.Caching.HashCache.SetAsync(UiPath.Caching.CacheKey cacheKey, System.Collections.Generic.IDictionary! values, System.TimeSpan? expiration, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask UiPath.Caching.HashCache.SetAsync(UiPath.Caching.CacheKey cacheKey, System.Collections.Generic.IDictionary! values, UiPath.Caching.HashCacheEntryOptions options, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask UiPath.Caching.HashCache.SetMetadataAsync(UiPath.Caching.CacheKey cacheKey, System.Collections.Generic.IDictionary! metadata, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask UiPath.Caching.HashCache.TimeToLiveAsync(UiPath.Caching.CacheKey cacheKey, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask @@ -243,24 +227,14 @@ UiPath.Caching.ICache.ContainsAsync(UiPath.Caching.CacheKey cacheKey, System. UiPath.Caching.ICache.ExpireTimeAsync(UiPath.Caching.CacheKey cacheKey, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask UiPath.Caching.ICache.GetAsync(UiPath.Caching.CacheKey cacheKey, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask UiPath.Caching.ICache.GetAsync(UiPath.Caching.CacheKey[]! cacheKeys, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask[]!> -UiPath.Caching.ICache.GetOrAddAsync(UiPath.Caching.CacheKey cacheKey, System.Func!>! generator, System.DateTimeOffset? expiration, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask UiPath.Caching.ICache.GetOrAddAsync(UiPath.Caching.CacheKey cacheKey, System.Func!>! generator, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask -UiPath.Caching.ICache.GetOrAddAsync(UiPath.Caching.CacheKey cacheKey, System.Func!>! generator, System.TimeSpan? expiration, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask -UiPath.Caching.ICache.GetOrAddAsync(System.Collections.Generic.KeyValuePair[]! entries, System.Func[]!>!>! generator, System.DateTimeOffset? expiration, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask[]!> UiPath.Caching.ICache.GetOrAddAsync(System.Collections.Generic.KeyValuePair[]! entries, System.Func[]!>!>! generator, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask[]!> -UiPath.Caching.ICache.GetOrAddAsync(System.Collections.Generic.KeyValuePair[]! entries, System.Func[]!>!>! generator, System.TimeSpan? expiration, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask[]!> UiPath.Caching.ICache.Name.get -> string! -UiPath.Caching.ICache.RefreshAsync(UiPath.Caching.CacheKey cacheKey, System.DateTimeOffset? expiration, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask UiPath.Caching.ICache.RefreshAsync(UiPath.Caching.CacheKey cacheKey, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask -UiPath.Caching.ICache.RefreshAsync(UiPath.Caching.CacheKey cacheKey, System.TimeSpan? expiration, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask UiPath.Caching.ICache.RemoveAsync(UiPath.Caching.CacheKey cacheKey, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask UiPath.Caching.ICache.RemoveAsync(UiPath.Caching.CacheKey[]! cacheKeys, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask -UiPath.Caching.ICache.SetAsync(System.Collections.Generic.KeyValuePair[]! keyValues, System.DateTimeOffset? expiration = null, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask UiPath.Caching.ICache.SetAsync(System.Collections.Generic.KeyValuePair[]! keyValues, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask -UiPath.Caching.ICache.SetAsync(System.Collections.Generic.KeyValuePair[]! keyValues, System.TimeSpan? expiration = null, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask -UiPath.Caching.ICache.SetAsync(UiPath.Caching.CacheKey cacheKey, T? value, System.DateTimeOffset? expiration, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask UiPath.Caching.ICache.SetAsync(UiPath.Caching.CacheKey cacheKey, T? value, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask -UiPath.Caching.ICache.SetAsync(UiPath.Caching.CacheKey cacheKey, T? value, System.TimeSpan? expiration, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask UiPath.Caching.ICache.TimeToLiveAsync(UiPath.Caching.CacheKey cacheKey, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask UiPath.Caching.ICacheChangeToken UiPath.Caching.ICacheChangeToken.Expiration.get -> System.DateTimeOffset? @@ -315,18 +289,12 @@ UiPath.Caching.IHashCache.GetAsync(UiPath.Caching.CacheKey cacheKey, string![ UiPath.Caching.IHashCache.GetCacheEntryAsync(UiPath.Caching.CacheKey cacheKey, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask!>!> UiPath.Caching.IHashCache.GetItemAsync(UiPath.Caching.CacheKey cacheKey, string! field, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask UiPath.Caching.IHashCache.GetMetadataAsync(UiPath.Caching.CacheKey cacheKey, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask?> -UiPath.Caching.IHashCache.GetOrAddAsync(UiPath.Caching.CacheKey cacheKey, System.Func!>!>! generator, System.DateTimeOffset? expiration, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask!> UiPath.Caching.IHashCache.GetOrAddAsync(UiPath.Caching.CacheKey cacheKey, System.Func!>!>! generator, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask!> -UiPath.Caching.IHashCache.GetOrAddAsync(UiPath.Caching.CacheKey cacheKey, System.Func!>!>! generator, System.TimeSpan? expiration, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask!> UiPath.Caching.IHashCache.Name.get -> string! -UiPath.Caching.IHashCache.RefreshAsync(UiPath.Caching.CacheKey cacheKey, System.DateTimeOffset? expiration, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask UiPath.Caching.IHashCache.RefreshAsync(UiPath.Caching.CacheKey cacheKey, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask -UiPath.Caching.IHashCache.RefreshAsync(UiPath.Caching.CacheKey cacheKey, System.TimeSpan? expiration, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask UiPath.Caching.IHashCache.RefreshAsync(UiPath.Caching.CacheKey cacheKey, UiPath.Caching.HashCacheEntryOptions options, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask UiPath.Caching.IHashCache.RemoveAsync(UiPath.Caching.CacheKey cacheKey, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask -UiPath.Caching.IHashCache.SetAsync(UiPath.Caching.CacheKey cacheKey, System.Collections.Generic.IDictionary! values, System.DateTimeOffset? expiration, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask UiPath.Caching.IHashCache.SetAsync(UiPath.Caching.CacheKey cacheKey, System.Collections.Generic.IDictionary! values, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask -UiPath.Caching.IHashCache.SetAsync(UiPath.Caching.CacheKey cacheKey, System.Collections.Generic.IDictionary! values, System.TimeSpan? expiration, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask UiPath.Caching.IHashCache.SetAsync(UiPath.Caching.CacheKey cacheKey, System.Collections.Generic.IDictionary! values, UiPath.Caching.HashCacheEntryOptions options, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask UiPath.Caching.IHashCache.SetMetadataAsync(UiPath.Caching.CacheKey cacheKey, System.Collections.Generic.IDictionary! metadata, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask UiPath.Caching.IHashCache.TimeToLiveAsync(UiPath.Caching.CacheKey cacheKey, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask diff --git a/src/UiPath.Caching.Abstractions/PublicAPI.Unshipped.txt b/src/UiPath.Caching.Abstractions/PublicAPI.Unshipped.txt index 3651025..cd96e0c 100644 --- a/src/UiPath.Caching.Abstractions/PublicAPI.Unshipped.txt +++ b/src/UiPath.Caching.Abstractions/PublicAPI.Unshipped.txt @@ -1,7 +1,18 @@ #nullable enable -UiPath.Caching.Cache.TryAddAsync(UiPath.Caching.CacheKey cacheKey, T? value, System.DateTimeOffset? expiration, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask +UiPath.Caching.Cache.GetOrAddAsync(UiPath.Caching.CacheKey cacheKey, System.Func!>! generator, System.DateTimeOffset expiration, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask +UiPath.Caching.Cache.GetOrAddAsync(UiPath.Caching.CacheKey cacheKey, System.Func!>! generator, System.TimeSpan expiration, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask +UiPath.Caching.Cache.GetOrAddAsync(System.Collections.Generic.KeyValuePair[]! entries, System.Func[]!>!>! generator, System.DateTimeOffset expiration, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask[]!> +UiPath.Caching.Cache.GetOrAddAsync(System.Collections.Generic.KeyValuePair[]! entries, System.Func[]!>!>! generator, System.TimeSpan expiration, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask[]!> +UiPath.Caching.Cache.RefreshAsync(UiPath.Caching.CacheKey cacheKey, System.DateTimeOffset expiration, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask +UiPath.Caching.Cache.RefreshAsync(UiPath.Caching.CacheKey cacheKey, System.TimeSpan expiration, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask +UiPath.Caching.Cache.SetAsync(System.Collections.Generic.KeyValuePair[]! keyValues, System.DateTimeOffset expiration, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask +UiPath.Caching.Cache.SetAsync(System.Collections.Generic.KeyValuePair[]! keyValues, System.TimeSpan expiration, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask +UiPath.Caching.Cache.SetAsync(UiPath.Caching.CacheKey cacheKey, T? value, System.DateTimeOffset expiration, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask +UiPath.Caching.Cache.SetAsync(UiPath.Caching.CacheKey cacheKey, T? value, System.TimeSpan expiration, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask +UiPath.Caching.Cache.TryAddAsync(UiPath.Caching.CacheKey cacheKey, T? value, System.DateTimeOffset expiration, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask UiPath.Caching.Cache.TryAddAsync(UiPath.Caching.CacheKey cacheKey, T? value, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask -UiPath.Caching.Cache.TryAddAsync(UiPath.Caching.CacheKey cacheKey, T? value, System.TimeSpan? expiration, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask +UiPath.Caching.Cache.TryAddAsync(UiPath.Caching.CacheKey cacheKey, T? value, System.TimeSpan expiration, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask +UiPath.Caching.CacheExpiration UiPath.Caching.CacheExtensions UiPath.Caching.CacheKey.CacheKey(string? name, UiPath.Caching.CacheKeyCasing casing) -> void UiPath.Caching.CacheKey.Casing.get -> UiPath.Caching.CacheKeyCasing @@ -14,82 +25,104 @@ UiPath.Caching.CacheKeyComparer.CacheKeyComparer() -> void UiPath.Caching.CacheOptions.KeyCasing.get -> UiPath.Caching.CacheKeyCasing UiPath.Caching.CacheOptions.KeyCasing.set -> void UiPath.Caching.CacheSyncExtensions +UiPath.Caching.HashCache.GetOrAddAsync(UiPath.Caching.CacheKey cacheKey, System.Func!>!>! generator, System.DateTimeOffset expiration, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask!> +UiPath.Caching.HashCache.GetOrAddAsync(UiPath.Caching.CacheKey cacheKey, System.Func!>!>! generator, System.TimeSpan expiration, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask!> +UiPath.Caching.HashCache.RefreshAsync(UiPath.Caching.CacheKey cacheKey, System.DateTimeOffset expiration, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask +UiPath.Caching.HashCache.RefreshAsync(UiPath.Caching.CacheKey cacheKey, System.TimeSpan expiration, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask +UiPath.Caching.HashCache.SetAsync(UiPath.Caching.CacheKey cacheKey, System.Collections.Generic.IDictionary! values, System.DateTimeOffset expiration, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask +UiPath.Caching.HashCache.SetAsync(UiPath.Caching.CacheKey cacheKey, System.Collections.Generic.IDictionary! values, System.TimeSpan expiration, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask UiPath.Caching.HashCacheExtensions UiPath.Caching.HashCacheSyncExtensions UiPath.Caching.ICache.GetAsync(UiPath.Caching.CacheKey cacheKey, UiPath.Caching.CachePolicy? policy, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask UiPath.Caching.ICache.GetAsync(UiPath.Caching.CacheKey[]! cacheKeys, UiPath.Caching.CachePolicy? policy, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask[]!> UiPath.Caching.ICache.GetCacheEntriesAsync(UiPath.Caching.CacheKey[]! cacheKeys, UiPath.Caching.CachePolicy? policy, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask!>[]!> UiPath.Caching.ICache.GetCacheEntryAsync(UiPath.Caching.CacheKey cacheKey, UiPath.Caching.CachePolicy? policy, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask!> -UiPath.Caching.ICache.GetOrAddAsync(System.Collections.Generic.KeyValuePair[]! entries, System.Func[]!>!>! generator, System.DateTimeOffset? expiration, UiPath.Caching.CachePolicy? policy, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask[]!> -UiPath.Caching.ICache.GetOrAddAsync(System.Collections.Generic.KeyValuePair[]! entries, System.Func[]!>!>! generator, System.TimeSpan? expiration, UiPath.Caching.CachePolicy? policy, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask[]!> +UiPath.Caching.ICache.GetOrAddAsync(System.Collections.Generic.KeyValuePair[]! entries, System.Func[]!>!>! generator, System.DateTimeOffset expiration, UiPath.Caching.CachePolicy? policy, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask[]!> +UiPath.Caching.ICache.GetOrAddAsync(System.Collections.Generic.KeyValuePair[]! entries, System.Func[]!>!>! generator, System.TimeSpan expiration, UiPath.Caching.CachePolicy? policy, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask[]!> UiPath.Caching.ICache.GetOrAddAsync(System.Collections.Generic.KeyValuePair[]! entries, System.Func[]!>!>! generator, UiPath.Caching.CachePolicy? policy, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask[]!> -UiPath.Caching.ICache.GetOrAddAsync(UiPath.Caching.CacheKey cacheKey, System.Func!>! generator, System.DateTimeOffset? expiration, UiPath.Caching.CachePolicy? policy, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask -UiPath.Caching.ICache.GetOrAddAsync(UiPath.Caching.CacheKey cacheKey, System.Func!>! generator, System.TimeSpan? expiration, UiPath.Caching.CachePolicy? policy, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask +UiPath.Caching.ICache.GetOrAddAsync(UiPath.Caching.CacheKey cacheKey, System.Func!>! generator, System.DateTimeOffset expiration, UiPath.Caching.CachePolicy? policy, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask +UiPath.Caching.ICache.GetOrAddAsync(UiPath.Caching.CacheKey cacheKey, System.Func!>! generator, System.TimeSpan expiration, UiPath.Caching.CachePolicy? policy, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask UiPath.Caching.ICache.GetOrAddAsync(UiPath.Caching.CacheKey cacheKey, System.Func!>! generator, UiPath.Caching.CachePolicy? policy, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask -UiPath.Caching.ICache.RefreshAsync(UiPath.Caching.CacheKey cacheKey, System.DateTimeOffset? expiration, UiPath.Caching.CachePolicy? policy, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask -UiPath.Caching.ICache.RefreshAsync(UiPath.Caching.CacheKey cacheKey, System.TimeSpan? expiration, UiPath.Caching.CachePolicy? policy, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask +UiPath.Caching.ICache.RefreshAsync(UiPath.Caching.CacheKey cacheKey, System.DateTimeOffset expiration, UiPath.Caching.CachePolicy? policy, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask +UiPath.Caching.ICache.RefreshAsync(UiPath.Caching.CacheKey cacheKey, System.TimeSpan expiration, UiPath.Caching.CachePolicy? policy, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask UiPath.Caching.ICache.RefreshAsync(UiPath.Caching.CacheKey cacheKey, UiPath.Caching.CachePolicy? policy, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask -UiPath.Caching.ICache.SetAsync(System.Collections.Generic.KeyValuePair[]! keyValues, System.DateTimeOffset? expiration, UiPath.Caching.CachePolicy? policy, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask -UiPath.Caching.ICache.SetAsync(System.Collections.Generic.KeyValuePair[]! keyValues, System.TimeSpan? expiration, UiPath.Caching.CachePolicy? policy, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask +UiPath.Caching.ICache.SetAsync(System.Collections.Generic.KeyValuePair[]! keyValues, System.DateTimeOffset expiration, UiPath.Caching.CachePolicy? policy, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask +UiPath.Caching.ICache.SetAsync(System.Collections.Generic.KeyValuePair[]! keyValues, System.TimeSpan expiration, UiPath.Caching.CachePolicy? policy, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask UiPath.Caching.ICache.SetAsync(System.Collections.Generic.KeyValuePair[]! keyValues, UiPath.Caching.CachePolicy? policy, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask -UiPath.Caching.ICache.SetAsync(UiPath.Caching.CacheKey cacheKey, T? value, System.DateTimeOffset? expiration, UiPath.Caching.CachePolicy? policy, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask -UiPath.Caching.ICache.SetAsync(UiPath.Caching.CacheKey cacheKey, T? value, System.TimeSpan? expiration, UiPath.Caching.CachePolicy? policy, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask +UiPath.Caching.ICache.SetAsync(UiPath.Caching.CacheKey cacheKey, T? value, System.DateTimeOffset expiration, UiPath.Caching.CachePolicy? policy, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask +UiPath.Caching.ICache.SetAsync(UiPath.Caching.CacheKey cacheKey, T? value, System.TimeSpan expiration, UiPath.Caching.CachePolicy? policy, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask UiPath.Caching.ICache.SetAsync(UiPath.Caching.CacheKey cacheKey, T? value, UiPath.Caching.CachePolicy? policy, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask -UiPath.Caching.ICache.TryAddAsync(UiPath.Caching.CacheKey cacheKey, T? value, System.DateTimeOffset? expiration, UiPath.Caching.CachePolicy? policy, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask -UiPath.Caching.ICache.TryAddAsync(UiPath.Caching.CacheKey cacheKey, T? value, System.TimeSpan? expiration, UiPath.Caching.CachePolicy? policy, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask +UiPath.Caching.ICache.TryAddAsync(UiPath.Caching.CacheKey cacheKey, T? value, System.DateTimeOffset expiration, UiPath.Caching.CachePolicy? policy, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask +UiPath.Caching.ICache.TryAddAsync(UiPath.Caching.CacheKey cacheKey, T? value, System.TimeSpan expiration, UiPath.Caching.CachePolicy? policy, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask UiPath.Caching.ICache.TryAddAsync(UiPath.Caching.CacheKey cacheKey, T? value, UiPath.Caching.CachePolicy? policy, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask -UiPath.Caching.ICache.TryAddAsync(UiPath.Caching.CacheKey cacheKey, T? value, System.DateTimeOffset? expiration, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask +UiPath.Caching.ICache.GetOrAddAsync(UiPath.Caching.CacheKey cacheKey, System.Func!>! generator, System.DateTimeOffset expiration, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask +UiPath.Caching.ICache.GetOrAddAsync(UiPath.Caching.CacheKey cacheKey, System.Func!>! generator, System.TimeSpan expiration, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask +UiPath.Caching.ICache.GetOrAddAsync(System.Collections.Generic.KeyValuePair[]! entries, System.Func[]!>!>! generator, System.DateTimeOffset expiration, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask[]!> +UiPath.Caching.ICache.GetOrAddAsync(System.Collections.Generic.KeyValuePair[]! entries, System.Func[]!>!>! generator, System.TimeSpan expiration, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask[]!> +UiPath.Caching.ICache.RefreshAsync(UiPath.Caching.CacheKey cacheKey, System.DateTimeOffset expiration, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask +UiPath.Caching.ICache.RefreshAsync(UiPath.Caching.CacheKey cacheKey, System.TimeSpan expiration, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask +UiPath.Caching.ICache.SetAsync(System.Collections.Generic.KeyValuePair[]! keyValues, System.DateTimeOffset expiration, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask +UiPath.Caching.ICache.SetAsync(System.Collections.Generic.KeyValuePair[]! keyValues, System.TimeSpan expiration, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask +UiPath.Caching.ICache.SetAsync(UiPath.Caching.CacheKey cacheKey, T? value, System.DateTimeOffset expiration, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask +UiPath.Caching.ICache.SetAsync(UiPath.Caching.CacheKey cacheKey, T? value, System.TimeSpan expiration, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask +UiPath.Caching.ICache.TryAddAsync(UiPath.Caching.CacheKey cacheKey, T? value, System.DateTimeOffset expiration, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask UiPath.Caching.ICache.TryAddAsync(UiPath.Caching.CacheKey cacheKey, T? value, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask -UiPath.Caching.ICache.TryAddAsync(UiPath.Caching.CacheKey cacheKey, T? value, System.TimeSpan? expiration, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask +UiPath.Caching.ICache.TryAddAsync(UiPath.Caching.CacheKey cacheKey, T? value, System.TimeSpan expiration, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask UiPath.Caching.IHashCache.GetAsync(UiPath.Caching.CacheKey cacheKey, UiPath.Caching.CachePolicy? policy, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask!> UiPath.Caching.IHashCache.GetAsync(UiPath.Caching.CacheKey cacheKey, string![]! fields, UiPath.Caching.CachePolicy? policy, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask!> UiPath.Caching.IHashCache.GetCacheEntryAsync(UiPath.Caching.CacheKey cacheKey, UiPath.Caching.CachePolicy? policy, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask!>!> UiPath.Caching.IHashCache.GetItemAsync(UiPath.Caching.CacheKey cacheKey, string! field, UiPath.Caching.CachePolicy? policy, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask -UiPath.Caching.IHashCache.GetOrAddAsync(UiPath.Caching.CacheKey cacheKey, System.Func!>!>! generator, System.DateTimeOffset? expiration, UiPath.Caching.CachePolicy? policy, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask!> -UiPath.Caching.IHashCache.GetOrAddAsync(UiPath.Caching.CacheKey cacheKey, System.Func!>!>! generator, System.DateTimeOffset? expiration, UiPath.Caching.HashCacheSetOption? setOption, UiPath.Caching.CachePolicy? policy, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask!> -UiPath.Caching.IHashCache.GetOrAddAsync(UiPath.Caching.CacheKey cacheKey, System.Func!>!>! generator, System.TimeSpan? expiration, UiPath.Caching.CachePolicy? policy, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask!> +UiPath.Caching.IHashCache.GetOrAddAsync(UiPath.Caching.CacheKey cacheKey, System.Func!>!>! generator, System.DateTimeOffset expiration, UiPath.Caching.CachePolicy? policy, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask!> +UiPath.Caching.IHashCache.GetOrAddAsync(UiPath.Caching.CacheKey cacheKey, System.Func!>!>! generator, System.DateTimeOffset expiration, UiPath.Caching.HashCacheSetOption? setOption, UiPath.Caching.CachePolicy? policy, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask!> +UiPath.Caching.IHashCache.GetOrAddAsync(UiPath.Caching.CacheKey cacheKey, System.Func!>!>! generator, System.TimeSpan expiration, UiPath.Caching.CachePolicy? policy, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask!> UiPath.Caching.IHashCache.GetOrAddAsync(UiPath.Caching.CacheKey cacheKey, System.Func!>!>! generator, UiPath.Caching.CachePolicy? policy, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask!> -UiPath.Caching.IHashCache.RefreshAsync(UiPath.Caching.CacheKey cacheKey, System.DateTimeOffset? expiration, UiPath.Caching.CachePolicy? policy, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask -UiPath.Caching.IHashCache.RefreshAsync(UiPath.Caching.CacheKey cacheKey, System.TimeSpan? expiration, UiPath.Caching.CachePolicy? policy, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask +UiPath.Caching.IHashCache.RefreshAsync(UiPath.Caching.CacheKey cacheKey, System.DateTimeOffset expiration, UiPath.Caching.CachePolicy? policy, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask +UiPath.Caching.IHashCache.RefreshAsync(UiPath.Caching.CacheKey cacheKey, System.TimeSpan expiration, UiPath.Caching.CachePolicy? policy, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask UiPath.Caching.IHashCache.RefreshAsync(UiPath.Caching.CacheKey cacheKey, UiPath.Caching.CachePolicy? policy, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask UiPath.Caching.IHashCache.RefreshAsync(UiPath.Caching.CacheKey cacheKey, UiPath.Caching.HashCacheEntryOptions options, UiPath.Caching.CachePolicy? policy, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask -UiPath.Caching.IHashCache.SetAsync(UiPath.Caching.CacheKey cacheKey, System.Collections.Generic.IDictionary! values, System.DateTimeOffset? expiration, UiPath.Caching.CachePolicy? policy, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask -UiPath.Caching.IHashCache.SetAsync(UiPath.Caching.CacheKey cacheKey, System.Collections.Generic.IDictionary! values, System.TimeSpan? expiration, UiPath.Caching.CachePolicy? policy, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask +UiPath.Caching.IHashCache.SetAsync(UiPath.Caching.CacheKey cacheKey, System.Collections.Generic.IDictionary! values, System.DateTimeOffset expiration, UiPath.Caching.CachePolicy? policy, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask +UiPath.Caching.IHashCache.SetAsync(UiPath.Caching.CacheKey cacheKey, System.Collections.Generic.IDictionary! values, System.TimeSpan expiration, UiPath.Caching.CachePolicy? policy, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask UiPath.Caching.IHashCache.SetAsync(UiPath.Caching.CacheKey cacheKey, System.Collections.Generic.IDictionary! values, UiPath.Caching.CachePolicy? policy, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask UiPath.Caching.IHashCache.SetAsync(UiPath.Caching.CacheKey cacheKey, System.Collections.Generic.IDictionary! values, UiPath.Caching.HashCacheEntryOptions options, UiPath.Caching.CachePolicy? policy, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask +UiPath.Caching.IHashCache.GetOrAddAsync(UiPath.Caching.CacheKey cacheKey, System.Func!>!>! generator, System.DateTimeOffset expiration, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask!> +UiPath.Caching.IHashCache.GetOrAddAsync(UiPath.Caching.CacheKey cacheKey, System.Func!>!>! generator, System.TimeSpan expiration, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask!> +UiPath.Caching.IHashCache.RefreshAsync(UiPath.Caching.CacheKey cacheKey, System.DateTimeOffset expiration, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask +UiPath.Caching.IHashCache.RefreshAsync(UiPath.Caching.CacheKey cacheKey, System.TimeSpan expiration, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask +UiPath.Caching.IHashCache.SetAsync(UiPath.Caching.CacheKey cacheKey, System.Collections.Generic.IDictionary! values, System.DateTimeOffset expiration, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask +UiPath.Caching.IHashCache.SetAsync(UiPath.Caching.CacheKey cacheKey, System.Collections.Generic.IDictionary! values, System.TimeSpan expiration, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask UiPath.Caching.NullCache.GetAsync(UiPath.Caching.CacheKey cacheKey, UiPath.Caching.CachePolicy? policy, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask UiPath.Caching.NullCache.GetAsync(UiPath.Caching.CacheKey[]! cacheKeys, UiPath.Caching.CachePolicy? policy, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask[]!> UiPath.Caching.NullCache.GetCacheEntriesAsync(UiPath.Caching.CacheKey[]! cacheKeys, UiPath.Caching.CachePolicy? policy, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask!>[]!> UiPath.Caching.NullCache.GetCacheEntryAsync(UiPath.Caching.CacheKey cacheKey, UiPath.Caching.CachePolicy? policy, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask!> -UiPath.Caching.NullCache.GetOrAddAsync(UiPath.Caching.CacheKey cacheKey, System.Func!>! generator, System.DateTimeOffset? expiration, UiPath.Caching.CachePolicy? policy, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask -UiPath.Caching.NullCache.GetOrAddAsync(UiPath.Caching.CacheKey cacheKey, System.Func!>! generator, System.TimeSpan? expiration, UiPath.Caching.CachePolicy? policy, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask +UiPath.Caching.NullCache.GetOrAddAsync(UiPath.Caching.CacheKey cacheKey, System.Func!>! generator, System.DateTimeOffset expiration, UiPath.Caching.CachePolicy? policy, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask +UiPath.Caching.NullCache.GetOrAddAsync(UiPath.Caching.CacheKey cacheKey, System.Func!>! generator, System.TimeSpan expiration, UiPath.Caching.CachePolicy? policy, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask UiPath.Caching.NullCache.GetOrAddAsync(UiPath.Caching.CacheKey cacheKey, System.Func!>! generator, UiPath.Caching.CachePolicy? policy, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask -UiPath.Caching.NullCache.RefreshAsync(UiPath.Caching.CacheKey cacheKey, System.DateTimeOffset? expiration, UiPath.Caching.CachePolicy? policy, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask -UiPath.Caching.NullCache.RefreshAsync(UiPath.Caching.CacheKey cacheKey, System.TimeSpan? expiration, UiPath.Caching.CachePolicy? policy, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask +UiPath.Caching.NullCache.RefreshAsync(UiPath.Caching.CacheKey cacheKey, System.DateTimeOffset expiration, UiPath.Caching.CachePolicy? policy, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask +UiPath.Caching.NullCache.RefreshAsync(UiPath.Caching.CacheKey cacheKey, System.TimeSpan expiration, UiPath.Caching.CachePolicy? policy, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask UiPath.Caching.NullCache.RefreshAsync(UiPath.Caching.CacheKey cacheKey, UiPath.Caching.CachePolicy? policy, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask -UiPath.Caching.NullCache.SetAsync(System.Collections.Generic.KeyValuePair[]! keyValues, System.DateTimeOffset? expiration, UiPath.Caching.CachePolicy? policy, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask -UiPath.Caching.NullCache.SetAsync(System.Collections.Generic.KeyValuePair[]! keyValues, System.TimeSpan? expiration, UiPath.Caching.CachePolicy? policy, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask +UiPath.Caching.NullCache.SetAsync(System.Collections.Generic.KeyValuePair[]! keyValues, System.DateTimeOffset expiration, UiPath.Caching.CachePolicy? policy, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask +UiPath.Caching.NullCache.SetAsync(System.Collections.Generic.KeyValuePair[]! keyValues, System.TimeSpan expiration, UiPath.Caching.CachePolicy? policy, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask UiPath.Caching.NullCache.SetAsync(System.Collections.Generic.KeyValuePair[]! keyValues, UiPath.Caching.CachePolicy? policy, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask -UiPath.Caching.NullCache.SetAsync(UiPath.Caching.CacheKey cacheKey, T? value, System.DateTimeOffset? expiration, UiPath.Caching.CachePolicy? policy, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask -UiPath.Caching.NullCache.SetAsync(UiPath.Caching.CacheKey cacheKey, T? value, System.TimeSpan? expiration, UiPath.Caching.CachePolicy? policy, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask +UiPath.Caching.NullCache.SetAsync(UiPath.Caching.CacheKey cacheKey, T? value, System.DateTimeOffset expiration, UiPath.Caching.CachePolicy? policy, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask +UiPath.Caching.NullCache.SetAsync(UiPath.Caching.CacheKey cacheKey, T? value, System.TimeSpan expiration, UiPath.Caching.CachePolicy? policy, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask UiPath.Caching.NullCache.SetAsync(UiPath.Caching.CacheKey cacheKey, T? value, UiPath.Caching.CachePolicy? policy, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask -UiPath.Caching.NullCache.TryAddAsync(UiPath.Caching.CacheKey cacheKey, T? value, System.DateTimeOffset? expiration, UiPath.Caching.CachePolicy? policy, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask -UiPath.Caching.NullCache.TryAddAsync(UiPath.Caching.CacheKey cacheKey, T? value, System.TimeSpan? expiration, UiPath.Caching.CachePolicy? policy, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask +UiPath.Caching.NullCache.TryAddAsync(UiPath.Caching.CacheKey cacheKey, T? value, System.DateTimeOffset expiration, UiPath.Caching.CachePolicy? policy, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask +UiPath.Caching.NullCache.TryAddAsync(UiPath.Caching.CacheKey cacheKey, T? value, System.TimeSpan expiration, UiPath.Caching.CachePolicy? policy, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask UiPath.Caching.NullCache.TryAddAsync(UiPath.Caching.CacheKey cacheKey, T? value, UiPath.Caching.CachePolicy? policy, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask UiPath.Caching.NullHashCache.GetAsync(UiPath.Caching.CacheKey cacheKey, UiPath.Caching.CachePolicy? policy, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask!> UiPath.Caching.NullHashCache.GetAsync(UiPath.Caching.CacheKey cacheKey, string![]! fields, UiPath.Caching.CachePolicy? policy, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask!> UiPath.Caching.NullHashCache.GetCacheEntryAsync(UiPath.Caching.CacheKey cacheKey, UiPath.Caching.CachePolicy? policy, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask!>!> UiPath.Caching.NullHashCache.GetItemAsync(UiPath.Caching.CacheKey cacheKey, string! field, UiPath.Caching.CachePolicy? policy, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask -UiPath.Caching.NullHashCache.GetOrAddAsync(UiPath.Caching.CacheKey cacheKey, System.Func!>!>! generator, System.DateTimeOffset? expiration, UiPath.Caching.CachePolicy? policy, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask!> -UiPath.Caching.NullHashCache.GetOrAddAsync(UiPath.Caching.CacheKey cacheKey, System.Func!>!>! generator, System.DateTimeOffset? expiration, UiPath.Caching.HashCacheSetOption? setOption, UiPath.Caching.CachePolicy? policy, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask!> -UiPath.Caching.NullHashCache.GetOrAddAsync(UiPath.Caching.CacheKey cacheKey, System.Func!>!>! generator, System.TimeSpan? expiration, UiPath.Caching.CachePolicy? policy, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask!> +UiPath.Caching.NullHashCache.GetOrAddAsync(UiPath.Caching.CacheKey cacheKey, System.Func!>!>! generator, System.DateTimeOffset expiration, UiPath.Caching.CachePolicy? policy, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask!> +UiPath.Caching.NullHashCache.GetOrAddAsync(UiPath.Caching.CacheKey cacheKey, System.Func!>!>! generator, System.DateTimeOffset expiration, UiPath.Caching.HashCacheSetOption? setOption, UiPath.Caching.CachePolicy? policy, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask!> +UiPath.Caching.NullHashCache.GetOrAddAsync(UiPath.Caching.CacheKey cacheKey, System.Func!>!>! generator, System.TimeSpan expiration, UiPath.Caching.CachePolicy? policy, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask!> UiPath.Caching.NullHashCache.GetOrAddAsync(UiPath.Caching.CacheKey cacheKey, System.Func!>!>! generator, UiPath.Caching.CachePolicy? policy, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask!> -UiPath.Caching.NullHashCache.RefreshAsync(UiPath.Caching.CacheKey cacheKey, System.DateTimeOffset? expiration, UiPath.Caching.CachePolicy? policy, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask -UiPath.Caching.NullHashCache.RefreshAsync(UiPath.Caching.CacheKey cacheKey, System.TimeSpan? expiration, UiPath.Caching.CachePolicy? policy, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask +UiPath.Caching.NullHashCache.RefreshAsync(UiPath.Caching.CacheKey cacheKey, System.DateTimeOffset expiration, UiPath.Caching.CachePolicy? policy, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask +UiPath.Caching.NullHashCache.RefreshAsync(UiPath.Caching.CacheKey cacheKey, System.TimeSpan expiration, UiPath.Caching.CachePolicy? policy, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask UiPath.Caching.NullHashCache.RefreshAsync(UiPath.Caching.CacheKey cacheKey, UiPath.Caching.CachePolicy? policy, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask UiPath.Caching.NullHashCache.RefreshAsync(UiPath.Caching.CacheKey cacheKey, UiPath.Caching.HashCacheEntryOptions options, UiPath.Caching.CachePolicy? policy, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask -UiPath.Caching.NullHashCache.SetAsync(UiPath.Caching.CacheKey cacheKey, System.Collections.Generic.IDictionary! values, System.DateTimeOffset? expiration, UiPath.Caching.CachePolicy? policy, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask -UiPath.Caching.NullHashCache.SetAsync(UiPath.Caching.CacheKey cacheKey, System.Collections.Generic.IDictionary! values, System.TimeSpan? expiration, UiPath.Caching.CachePolicy? policy, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask +UiPath.Caching.NullHashCache.SetAsync(UiPath.Caching.CacheKey cacheKey, System.Collections.Generic.IDictionary! values, System.DateTimeOffset expiration, UiPath.Caching.CachePolicy? policy, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask +UiPath.Caching.NullHashCache.SetAsync(UiPath.Caching.CacheKey cacheKey, System.Collections.Generic.IDictionary! values, System.TimeSpan expiration, UiPath.Caching.CachePolicy? policy, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask UiPath.Caching.NullHashCache.SetAsync(UiPath.Caching.CacheKey cacheKey, System.Collections.Generic.IDictionary! values, UiPath.Caching.CachePolicy? policy, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask UiPath.Caching.NullHashCache.SetAsync(UiPath.Caching.CacheKey cacheKey, System.Collections.Generic.IDictionary! values, UiPath.Caching.HashCacheEntryOptions options, UiPath.Caching.CachePolicy? policy, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask UiPath.Caching.SystemJsonByteSerializerProxy @@ -98,25 +131,28 @@ UiPath.Caching.SystemJsonByteSerializerProxy.Serialize(object? value) -> byte[]? UiPath.Caching.SystemJsonByteSerializerProxy.SystemJsonByteSerializerProxy(System.Text.Json.JsonSerializerOptions? options = null) -> void UiPath.Caching.SystemJsonByteSerializerProxy.TryDeserialize(object? value, out T? result) -> bool UiPath.Caching.SystemJsonByteSerializerProxy.TryDeserialize(string? value, out T? result) -> bool +static UiPath.Caching.CacheExpiration.ThrowIfNotFuture(System.DateTimeOffset expiration, System.DateTimeOffset now, string? paramName = null) -> System.DateTimeOffset +static UiPath.Caching.CacheExpiration.ThrowIfNotPositive(System.TimeSpan expiration, string? paramName = null) -> System.TimeSpan +static UiPath.Caching.CacheExpiration.ToDuration(System.DateTimeOffset expiration, System.DateTimeOffset now, string? paramName = null) -> System.TimeSpan static UiPath.Caching.CacheExtensions.GetAsync(this UiPath.Caching.ICache! cache, UiPath.Caching.CacheKey cacheKey, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask static UiPath.Caching.CacheExtensions.GetAsync(this UiPath.Caching.ICache! cache, UiPath.Caching.CacheKey[]! cacheKeys, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask[]!> static UiPath.Caching.CacheExtensions.GetCacheEntriesAsync(this UiPath.Caching.ICache! cache, UiPath.Caching.CacheKey[]! cacheKeys, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask!>[]!> static UiPath.Caching.CacheExtensions.GetCacheEntryAsync(this UiPath.Caching.ICache! cache, UiPath.Caching.CacheKey cacheKey, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask!> -static UiPath.Caching.CacheExtensions.GetOrAddAsync(this UiPath.Caching.ICache! cache, UiPath.Caching.CacheKey cacheKey, System.Func!>! generator, System.DateTimeOffset? expiration, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask +static UiPath.Caching.CacheExtensions.GetOrAddAsync(this UiPath.Caching.ICache! cache, UiPath.Caching.CacheKey cacheKey, System.Func!>! generator, System.DateTimeOffset expiration, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask static UiPath.Caching.CacheExtensions.GetOrAddAsync(this UiPath.Caching.ICache! cache, UiPath.Caching.CacheKey cacheKey, System.Func!>! generator, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask -static UiPath.Caching.CacheExtensions.GetOrAddAsync(this UiPath.Caching.ICache! cache, UiPath.Caching.CacheKey cacheKey, System.Func!>! generator, System.TimeSpan? expiration, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask -static UiPath.Caching.CacheExtensions.RefreshAsync(this UiPath.Caching.ICache! cache, UiPath.Caching.CacheKey cacheKey, System.DateTimeOffset? expiration, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask +static UiPath.Caching.CacheExtensions.GetOrAddAsync(this UiPath.Caching.ICache! cache, UiPath.Caching.CacheKey cacheKey, System.Func!>! generator, System.TimeSpan expiration, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask +static UiPath.Caching.CacheExtensions.RefreshAsync(this UiPath.Caching.ICache! cache, UiPath.Caching.CacheKey cacheKey, System.DateTimeOffset expiration, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask static UiPath.Caching.CacheExtensions.RefreshAsync(this UiPath.Caching.ICache! cache, UiPath.Caching.CacheKey cacheKey, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask -static UiPath.Caching.CacheExtensions.RefreshAsync(this UiPath.Caching.ICache! cache, UiPath.Caching.CacheKey cacheKey, System.TimeSpan? expiration, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask -static UiPath.Caching.CacheExtensions.SetAsync(this UiPath.Caching.ICache! cache, System.Collections.Generic.KeyValuePair[]! keyValues, System.DateTimeOffset? expiration, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask +static UiPath.Caching.CacheExtensions.RefreshAsync(this UiPath.Caching.ICache! cache, UiPath.Caching.CacheKey cacheKey, System.TimeSpan expiration, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask +static UiPath.Caching.CacheExtensions.SetAsync(this UiPath.Caching.ICache! cache, System.Collections.Generic.KeyValuePair[]! keyValues, System.DateTimeOffset expiration, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask static UiPath.Caching.CacheExtensions.SetAsync(this UiPath.Caching.ICache! cache, System.Collections.Generic.KeyValuePair[]! keyValues, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask -static UiPath.Caching.CacheExtensions.SetAsync(this UiPath.Caching.ICache! cache, System.Collections.Generic.KeyValuePair[]! keyValues, System.TimeSpan? expiration, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask -static UiPath.Caching.CacheExtensions.SetAsync(this UiPath.Caching.ICache! cache, UiPath.Caching.CacheKey cacheKey, T? value, System.DateTimeOffset? expiration, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask +static UiPath.Caching.CacheExtensions.SetAsync(this UiPath.Caching.ICache! cache, System.Collections.Generic.KeyValuePair[]! keyValues, System.TimeSpan expiration, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask +static UiPath.Caching.CacheExtensions.SetAsync(this UiPath.Caching.ICache! cache, UiPath.Caching.CacheKey cacheKey, T? value, System.DateTimeOffset expiration, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask static UiPath.Caching.CacheExtensions.SetAsync(this UiPath.Caching.ICache! cache, UiPath.Caching.CacheKey cacheKey, T? value, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask -static UiPath.Caching.CacheExtensions.SetAsync(this UiPath.Caching.ICache! cache, UiPath.Caching.CacheKey cacheKey, T? value, System.TimeSpan? expiration, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask -static UiPath.Caching.CacheExtensions.TryAddAsync(this UiPath.Caching.ICache! cache, UiPath.Caching.CacheKey cacheKey, T? value, System.DateTimeOffset? expiration, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask +static UiPath.Caching.CacheExtensions.SetAsync(this UiPath.Caching.ICache! cache, UiPath.Caching.CacheKey cacheKey, T? value, System.TimeSpan expiration, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask +static UiPath.Caching.CacheExtensions.TryAddAsync(this UiPath.Caching.ICache! cache, UiPath.Caching.CacheKey cacheKey, T? value, System.DateTimeOffset expiration, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask static UiPath.Caching.CacheExtensions.TryAddAsync(this UiPath.Caching.ICache! cache, UiPath.Caching.CacheKey cacheKey, T? value, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask -static UiPath.Caching.CacheExtensions.TryAddAsync(this UiPath.Caching.ICache! cache, UiPath.Caching.CacheKey cacheKey, T? value, System.TimeSpan? expiration, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask +static UiPath.Caching.CacheExtensions.TryAddAsync(this UiPath.Caching.ICache! cache, UiPath.Caching.CacheKey cacheKey, T? value, System.TimeSpan expiration, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask static UiPath.Caching.CacheKey.DefaultCasing.get -> UiPath.Caching.CacheKeyCasing static UiPath.Caching.CacheKey.DefaultCasing.set -> void static UiPath.Caching.CacheKeyComparer.Insensitive.get -> UiPath.Caching.CacheKeyComparer! @@ -125,42 +161,42 @@ static UiPath.Caching.CacheSyncExtensions.Contains(this UiPath.Caching.ICache static UiPath.Caching.CacheSyncExtensions.ExpireTime(this UiPath.Caching.ICache! cache, UiPath.Caching.CacheKey cacheKey, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.DateTimeOffset? static UiPath.Caching.CacheSyncExtensions.Get(this UiPath.Caching.ICache! cache, UiPath.Caching.CacheKey cacheKey, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> T? static UiPath.Caching.CacheSyncExtensions.Get(this UiPath.Caching.ICache! cache, UiPath.Caching.CacheKey[]! cacheKeys, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Collections.Generic.KeyValuePair[]! -static UiPath.Caching.CacheSyncExtensions.GetOrAdd(this UiPath.Caching.ICache! cache, UiPath.Caching.CacheKey cacheKey, System.Func! generator, System.DateTimeOffset? expiration, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> T? +static UiPath.Caching.CacheSyncExtensions.GetOrAdd(this UiPath.Caching.ICache! cache, UiPath.Caching.CacheKey cacheKey, System.Func! generator, System.DateTimeOffset expiration, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> T? static UiPath.Caching.CacheSyncExtensions.GetOrAdd(this UiPath.Caching.ICache! cache, UiPath.Caching.CacheKey cacheKey, System.Func! generator, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> T? -static UiPath.Caching.CacheSyncExtensions.GetOrAdd(this UiPath.Caching.ICache! cache, UiPath.Caching.CacheKey cacheKey, System.Func! generator, System.TimeSpan? expiration, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> T? -static UiPath.Caching.CacheSyncExtensions.GetOrAdd(this UiPath.Caching.ICache! cache, UiPath.Caching.CacheKey[]! cacheKeys, System.Func[]!>! generator, System.DateTimeOffset? expiration, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Collections.Generic.KeyValuePair[]! +static UiPath.Caching.CacheSyncExtensions.GetOrAdd(this UiPath.Caching.ICache! cache, UiPath.Caching.CacheKey cacheKey, System.Func! generator, System.TimeSpan expiration, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> T? +static UiPath.Caching.CacheSyncExtensions.GetOrAdd(this UiPath.Caching.ICache! cache, UiPath.Caching.CacheKey[]! cacheKeys, System.Func[]!>! generator, System.DateTimeOffset expiration, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Collections.Generic.KeyValuePair[]! static UiPath.Caching.CacheSyncExtensions.GetOrAdd(this UiPath.Caching.ICache! cache, UiPath.Caching.CacheKey[]! cacheKeys, System.Func[]!>! generator, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Collections.Generic.KeyValuePair[]! -static UiPath.Caching.CacheSyncExtensions.GetOrAdd(this UiPath.Caching.ICache! cache, UiPath.Caching.CacheKey[]! cacheKeys, System.Func[]!>! generator, System.TimeSpan? expiration, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Collections.Generic.KeyValuePair[]! -static UiPath.Caching.CacheSyncExtensions.Refresh(this UiPath.Caching.ICache! cache, UiPath.Caching.CacheKey cacheKey, System.DateTimeOffset? expiration, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> bool +static UiPath.Caching.CacheSyncExtensions.GetOrAdd(this UiPath.Caching.ICache! cache, UiPath.Caching.CacheKey[]! cacheKeys, System.Func[]!>! generator, System.TimeSpan expiration, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Collections.Generic.KeyValuePair[]! +static UiPath.Caching.CacheSyncExtensions.Refresh(this UiPath.Caching.ICache! cache, UiPath.Caching.CacheKey cacheKey, System.DateTimeOffset expiration, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> bool static UiPath.Caching.CacheSyncExtensions.Refresh(this UiPath.Caching.ICache! cache, UiPath.Caching.CacheKey cacheKey, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> bool -static UiPath.Caching.CacheSyncExtensions.Refresh(this UiPath.Caching.ICache! cache, UiPath.Caching.CacheKey cacheKey, System.TimeSpan? expiration, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> bool +static UiPath.Caching.CacheSyncExtensions.Refresh(this UiPath.Caching.ICache! cache, UiPath.Caching.CacheKey cacheKey, System.TimeSpan expiration, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> bool static UiPath.Caching.CacheSyncExtensions.Remove(this UiPath.Caching.ICache! cache, UiPath.Caching.CacheKey cacheKey, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> bool static UiPath.Caching.CacheSyncExtensions.Remove(this UiPath.Caching.ICache! cache, UiPath.Caching.CacheKey[]! cacheKeys, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> bool -static UiPath.Caching.CacheSyncExtensions.Set(this UiPath.Caching.ICache! cache, System.Collections.Generic.KeyValuePair[]! keyValues, System.DateTimeOffset? expiration = null, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> bool +static UiPath.Caching.CacheSyncExtensions.Set(this UiPath.Caching.ICache! cache, System.Collections.Generic.KeyValuePair[]! keyValues, System.DateTimeOffset expiration, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> bool static UiPath.Caching.CacheSyncExtensions.Set(this UiPath.Caching.ICache! cache, System.Collections.Generic.KeyValuePair[]! keyValues, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> bool -static UiPath.Caching.CacheSyncExtensions.Set(this UiPath.Caching.ICache! cache, System.Collections.Generic.KeyValuePair[]! keyValues, System.TimeSpan? expiration = null, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> bool -static UiPath.Caching.CacheSyncExtensions.Set(this UiPath.Caching.ICache! cache, UiPath.Caching.CacheKey cacheKey, T? value, System.DateTimeOffset? expiration, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> bool +static UiPath.Caching.CacheSyncExtensions.Set(this UiPath.Caching.ICache! cache, System.Collections.Generic.KeyValuePair[]! keyValues, System.TimeSpan expiration, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> bool +static UiPath.Caching.CacheSyncExtensions.Set(this UiPath.Caching.ICache! cache, UiPath.Caching.CacheKey cacheKey, T? value, System.DateTimeOffset expiration, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> bool static UiPath.Caching.CacheSyncExtensions.Set(this UiPath.Caching.ICache! cache, UiPath.Caching.CacheKey cacheKey, T? value, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> bool -static UiPath.Caching.CacheSyncExtensions.Set(this UiPath.Caching.ICache! cache, UiPath.Caching.CacheKey cacheKey, T? value, System.TimeSpan? expiration, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> bool +static UiPath.Caching.CacheSyncExtensions.Set(this UiPath.Caching.ICache! cache, UiPath.Caching.CacheKey cacheKey, T? value, System.TimeSpan expiration, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> bool static UiPath.Caching.CacheSyncExtensions.TimeToLive(this UiPath.Caching.ICache! cache, UiPath.Caching.CacheKey cacheKey, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.TimeSpan? -static UiPath.Caching.CacheSyncExtensions.TryAdd(this UiPath.Caching.ICache! cache, UiPath.Caching.CacheKey cacheKey, T? value, System.DateTimeOffset? expiration, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> bool +static UiPath.Caching.CacheSyncExtensions.TryAdd(this UiPath.Caching.ICache! cache, UiPath.Caching.CacheKey cacheKey, T? value, System.DateTimeOffset expiration, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> bool static UiPath.Caching.CacheSyncExtensions.TryAdd(this UiPath.Caching.ICache! cache, UiPath.Caching.CacheKey cacheKey, T? value, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> bool -static UiPath.Caching.CacheSyncExtensions.TryAdd(this UiPath.Caching.ICache! cache, UiPath.Caching.CacheKey cacheKey, T? value, System.TimeSpan? expiration, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> bool +static UiPath.Caching.CacheSyncExtensions.TryAdd(this UiPath.Caching.ICache! cache, UiPath.Caching.CacheKey cacheKey, T? value, System.TimeSpan expiration, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> bool static UiPath.Caching.HashCacheExtensions.GetAsync(this UiPath.Caching.IHashCache! cache, UiPath.Caching.CacheKey cacheKey, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask!> static UiPath.Caching.HashCacheExtensions.GetAsync(this UiPath.Caching.IHashCache! cache, UiPath.Caching.CacheKey cacheKey, string![]! fields, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask!> static UiPath.Caching.HashCacheExtensions.GetCacheEntryAsync(this UiPath.Caching.IHashCache! cache, UiPath.Caching.CacheKey cacheKey, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask!>!> static UiPath.Caching.HashCacheExtensions.GetItemAsync(this UiPath.Caching.IHashCache! cache, UiPath.Caching.CacheKey cacheKey, string! field, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask -static UiPath.Caching.HashCacheExtensions.GetOrAddAsync(this UiPath.Caching.IHashCache! cache, UiPath.Caching.CacheKey cacheKey, System.Func!>!>! generator, System.DateTimeOffset? expiration, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask!> -static UiPath.Caching.HashCacheExtensions.GetOrAddAsync(this UiPath.Caching.IHashCache! cache, UiPath.Caching.CacheKey cacheKey, System.Func!>!>! generator, System.DateTimeOffset? expiration, UiPath.Caching.HashCacheSetOption? setOption, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask!> +static UiPath.Caching.HashCacheExtensions.GetOrAddAsync(this UiPath.Caching.IHashCache! cache, UiPath.Caching.CacheKey cacheKey, System.Func!>!>! generator, System.DateTimeOffset expiration, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask!> +static UiPath.Caching.HashCacheExtensions.GetOrAddAsync(this UiPath.Caching.IHashCache! cache, UiPath.Caching.CacheKey cacheKey, System.Func!>!>! generator, System.DateTimeOffset expiration, UiPath.Caching.HashCacheSetOption? setOption, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask!> static UiPath.Caching.HashCacheExtensions.GetOrAddAsync(this UiPath.Caching.IHashCache! cache, UiPath.Caching.CacheKey cacheKey, System.Func!>!>! generator, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask!> -static UiPath.Caching.HashCacheExtensions.GetOrAddAsync(this UiPath.Caching.IHashCache! cache, UiPath.Caching.CacheKey cacheKey, System.Func!>!>! generator, System.TimeSpan? expiration, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask!> -static UiPath.Caching.HashCacheExtensions.RefreshAsync(this UiPath.Caching.IHashCache! cache, UiPath.Caching.CacheKey cacheKey, System.DateTimeOffset? expiration, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask +static UiPath.Caching.HashCacheExtensions.GetOrAddAsync(this UiPath.Caching.IHashCache! cache, UiPath.Caching.CacheKey cacheKey, System.Func!>!>! generator, System.TimeSpan expiration, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask!> +static UiPath.Caching.HashCacheExtensions.RefreshAsync(this UiPath.Caching.IHashCache! cache, UiPath.Caching.CacheKey cacheKey, System.DateTimeOffset expiration, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask static UiPath.Caching.HashCacheExtensions.RefreshAsync(this UiPath.Caching.IHashCache! cache, UiPath.Caching.CacheKey cacheKey, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask -static UiPath.Caching.HashCacheExtensions.RefreshAsync(this UiPath.Caching.IHashCache! cache, UiPath.Caching.CacheKey cacheKey, System.TimeSpan? expiration, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask +static UiPath.Caching.HashCacheExtensions.RefreshAsync(this UiPath.Caching.IHashCache! cache, UiPath.Caching.CacheKey cacheKey, System.TimeSpan expiration, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask static UiPath.Caching.HashCacheExtensions.RefreshAsync(this UiPath.Caching.IHashCache! cache, UiPath.Caching.CacheKey cacheKey, UiPath.Caching.HashCacheEntryOptions options, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask -static UiPath.Caching.HashCacheExtensions.SetAsync(this UiPath.Caching.IHashCache! cache, UiPath.Caching.CacheKey cacheKey, System.Collections.Generic.IDictionary! values, System.DateTimeOffset? expiration, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask +static UiPath.Caching.HashCacheExtensions.SetAsync(this UiPath.Caching.IHashCache! cache, UiPath.Caching.CacheKey cacheKey, System.Collections.Generic.IDictionary! values, System.DateTimeOffset expiration, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask static UiPath.Caching.HashCacheExtensions.SetAsync(this UiPath.Caching.IHashCache! cache, UiPath.Caching.CacheKey cacheKey, System.Collections.Generic.IDictionary! values, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask -static UiPath.Caching.HashCacheExtensions.SetAsync(this UiPath.Caching.IHashCache! cache, UiPath.Caching.CacheKey cacheKey, System.Collections.Generic.IDictionary! values, System.TimeSpan? expiration, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask +static UiPath.Caching.HashCacheExtensions.SetAsync(this UiPath.Caching.IHashCache! cache, UiPath.Caching.CacheKey cacheKey, System.Collections.Generic.IDictionary! values, System.TimeSpan expiration, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask static UiPath.Caching.HashCacheExtensions.SetAsync(this UiPath.Caching.IHashCache! cache, UiPath.Caching.CacheKey cacheKey, System.Collections.Generic.IDictionary! values, UiPath.Caching.HashCacheEntryOptions options, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask static UiPath.Caching.HashCacheSyncExtensions.Contains(this UiPath.Caching.IHashCache! cache, UiPath.Caching.CacheKey cacheKey, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> bool static UiPath.Caching.HashCacheSyncExtensions.ExpireTime(this UiPath.Caching.IHashCache! cache, UiPath.Caching.CacheKey cacheKey, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.DateTimeOffset? @@ -169,17 +205,17 @@ static UiPath.Caching.HashCacheSyncExtensions.Get(this UiPath.Caching.IHashCa static UiPath.Caching.HashCacheSyncExtensions.GetCacheEntry(this UiPath.Caching.IHashCache! cache, UiPath.Caching.CacheKey cacheKey, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> UiPath.Caching.ICacheEntry!>! static UiPath.Caching.HashCacheSyncExtensions.GetItem(this UiPath.Caching.IHashCache! cache, UiPath.Caching.CacheKey cacheKey, string! field, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> T? static UiPath.Caching.HashCacheSyncExtensions.GetMetadata(this UiPath.Caching.IHashCache! cache, UiPath.Caching.CacheKey cacheKey, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Collections.Generic.IDictionary? -static UiPath.Caching.HashCacheSyncExtensions.GetOrAdd(this UiPath.Caching.IHashCache! cache, UiPath.Caching.CacheKey cacheKey, System.Func!>! generator, System.DateTimeOffset? expiration, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Collections.Generic.IDictionary! +static UiPath.Caching.HashCacheSyncExtensions.GetOrAdd(this UiPath.Caching.IHashCache! cache, UiPath.Caching.CacheKey cacheKey, System.Func!>! generator, System.DateTimeOffset expiration, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Collections.Generic.IDictionary! static UiPath.Caching.HashCacheSyncExtensions.GetOrAdd(this UiPath.Caching.IHashCache! cache, UiPath.Caching.CacheKey cacheKey, System.Func!>! generator, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Collections.Generic.IDictionary! -static UiPath.Caching.HashCacheSyncExtensions.GetOrAdd(this UiPath.Caching.IHashCache! cache, UiPath.Caching.CacheKey cacheKey, System.Func!>! generator, System.TimeSpan? expiration, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Collections.Generic.IDictionary! -static UiPath.Caching.HashCacheSyncExtensions.Refresh(this UiPath.Caching.IHashCache! cache, UiPath.Caching.CacheKey cacheKey, System.DateTimeOffset? expiration, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> bool +static UiPath.Caching.HashCacheSyncExtensions.GetOrAdd(this UiPath.Caching.IHashCache! cache, UiPath.Caching.CacheKey cacheKey, System.Func!>! generator, System.TimeSpan expiration, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Collections.Generic.IDictionary! +static UiPath.Caching.HashCacheSyncExtensions.Refresh(this UiPath.Caching.IHashCache! cache, UiPath.Caching.CacheKey cacheKey, System.DateTimeOffset expiration, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> bool static UiPath.Caching.HashCacheSyncExtensions.Refresh(this UiPath.Caching.IHashCache! cache, UiPath.Caching.CacheKey cacheKey, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> bool -static UiPath.Caching.HashCacheSyncExtensions.Refresh(this UiPath.Caching.IHashCache! cache, UiPath.Caching.CacheKey cacheKey, System.TimeSpan? expiration, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> bool +static UiPath.Caching.HashCacheSyncExtensions.Refresh(this UiPath.Caching.IHashCache! cache, UiPath.Caching.CacheKey cacheKey, System.TimeSpan expiration, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> bool static UiPath.Caching.HashCacheSyncExtensions.Refresh(this UiPath.Caching.IHashCache! cache, UiPath.Caching.CacheKey cacheKey, UiPath.Caching.HashCacheEntryOptions options, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> bool static UiPath.Caching.HashCacheSyncExtensions.Remove(this UiPath.Caching.IHashCache! cache, UiPath.Caching.CacheKey cacheKey, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> bool -static UiPath.Caching.HashCacheSyncExtensions.Set(this UiPath.Caching.IHashCache! cache, UiPath.Caching.CacheKey cacheKey, System.Collections.Generic.IDictionary! values, System.DateTimeOffset? expiration, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> bool +static UiPath.Caching.HashCacheSyncExtensions.Set(this UiPath.Caching.IHashCache! cache, UiPath.Caching.CacheKey cacheKey, System.Collections.Generic.IDictionary! values, System.DateTimeOffset expiration, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> bool static UiPath.Caching.HashCacheSyncExtensions.Set(this UiPath.Caching.IHashCache! cache, UiPath.Caching.CacheKey cacheKey, System.Collections.Generic.IDictionary! values, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> bool -static UiPath.Caching.HashCacheSyncExtensions.Set(this UiPath.Caching.IHashCache! cache, UiPath.Caching.CacheKey cacheKey, System.Collections.Generic.IDictionary! values, System.TimeSpan? expiration, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> bool +static UiPath.Caching.HashCacheSyncExtensions.Set(this UiPath.Caching.IHashCache! cache, UiPath.Caching.CacheKey cacheKey, System.Collections.Generic.IDictionary! values, System.TimeSpan expiration, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> bool static UiPath.Caching.HashCacheSyncExtensions.Set(this UiPath.Caching.IHashCache! cache, UiPath.Caching.CacheKey cacheKey, System.Collections.Generic.IDictionary! values, UiPath.Caching.HashCacheEntryOptions options, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> bool static UiPath.Caching.HashCacheSyncExtensions.SetMetadata(this UiPath.Caching.IHashCache! cache, UiPath.Caching.CacheKey cacheKey, System.Collections.Generic.IDictionary! metadata, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> bool static UiPath.Caching.HashCacheSyncExtensions.TimeToLive(this UiPath.Caching.IHashCache! cache, UiPath.Caching.CacheKey cacheKey, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.TimeSpan? diff --git a/src/UiPath.Caching.Queue/ISetCache.cs b/src/UiPath.Caching.Queue/ISetCache.cs index af7c45f..62e4c05 100644 --- a/src/UiPath.Caching.Queue/ISetCache.cs +++ b/src/UiPath.Caching.Queue/ISetCache.cs @@ -6,6 +6,15 @@ namespace UiPath.Caching; /// insertion order and PopAsync removes a random member (Redis SPOP). Use the dedicated list /// caches when order matters. /// +/// +/// Expiration. The expiration parameter is not nullable. A caller with nothing to +/// say about lifetime calls the overload that has no expiration parameter and gets +/// , then the cache default; a caller that passes one +/// means it, so a duration that is not positive — or a deadline that has already passed — is +/// rejected with rather than silently ignored. See +/// . The no-op implementations in this package read no argument at all +/// and so enforce nothing. +/// public interface ISetCache : IDisposable { string Name { get; } @@ -21,10 +30,10 @@ public interface ISetCache : IDisposable ValueTask AddAsync(CacheKey cacheKey, IEnumerable items, CachePolicy? policy, CancellationToken token = default); /// - ValueTask AddAsync(CacheKey cacheKey, IEnumerable items, TimeSpan? expiration, CachePolicy? policy, CancellationToken token = default); + ValueTask AddAsync(CacheKey cacheKey, IEnumerable items, TimeSpan expiration, CachePolicy? policy, CancellationToken token = default); /// - ValueTask AddAsync(CacheKey cacheKey, IEnumerable items, DateTimeOffset? expiration, CachePolicy? policy, CancellationToken token = default); + ValueTask AddAsync(CacheKey cacheKey, IEnumerable items, DateTimeOffset expiration, CachePolicy? policy, CancellationToken token = default); /// /// Removes and returns a random member of the set (Redis SPOP). The set is unordered, so this is diff --git a/src/UiPath.Caching.Queue/ISetCacheOfT.cs b/src/UiPath.Caching.Queue/ISetCacheOfT.cs index 875783b..a58a0bf 100644 --- a/src/UiPath.Caching.Queue/ISetCacheOfT.cs +++ b/src/UiPath.Caching.Queue/ISetCacheOfT.cs @@ -21,10 +21,10 @@ public interface ISetCache ValueTask AddAsync(CacheKey cacheKey, IEnumerable items, CancellationToken token = default); /// - ValueTask AddAsync(CacheKey cacheKey, IEnumerable items, TimeSpan? expiration, CancellationToken token = default); + ValueTask AddAsync(CacheKey cacheKey, IEnumerable items, TimeSpan expiration, CancellationToken token = default); /// - ValueTask AddAsync(CacheKey cacheKey, IEnumerable items, DateTimeOffset? expiration, CancellationToken token = default); + ValueTask AddAsync(CacheKey cacheKey, IEnumerable items, DateTimeOffset expiration, CancellationToken token = default); /// /// Removes and returns a random member of the set (Redis SPOP). The set is unordered, so this is diff --git a/src/UiPath.Caching.Queue/MultilayerSetCache.cs b/src/UiPath.Caching.Queue/MultilayerSetCache.cs index 1265fc4..55767ef 100644 --- a/src/UiPath.Caching.Queue/MultilayerSetCache.cs +++ b/src/UiPath.Caching.Queue/MultilayerSetCache.cs @@ -58,12 +58,15 @@ public async ValueTask AddAsync(CacheKey cacheKey, T item, CachePolicy? } public ValueTask AddAsync(CacheKey cacheKey, IEnumerable items, CachePolicy? policy, CancellationToken token = default) => - AddAsync(cacheKey, items, expiration: (DateTimeOffset?)null, policy, token); + AddCoreAsync(cacheKey, items, expiration: null, policy, token); - public ValueTask AddAsync(CacheKey cacheKey, IEnumerable items, TimeSpan? expiration, CachePolicy? policy, CancellationToken token = default) => - AddAsync(cacheKey, items, FromTtl(expiration), policy, token); + public ValueTask AddAsync(CacheKey cacheKey, IEnumerable items, TimeSpan expiration, CachePolicy? policy, CancellationToken token = default) => + AddCoreAsync(cacheKey, items, DateTimeOffset.UtcNow.Add(CacheExpiration.ThrowIfNotPositive(expiration)), policy, token); - public async ValueTask AddAsync(CacheKey cacheKey, IEnumerable items, DateTimeOffset? expiration, CachePolicy? policy, CancellationToken token = default) + public ValueTask AddAsync(CacheKey cacheKey, IEnumerable items, DateTimeOffset expiration, CachePolicy? policy, CancellationToken token = default) => + AddCoreAsync(cacheKey, items, CacheExpiration.ThrowIfNotFuture(expiration, DateTimeOffset.UtcNow), policy, token); + + private async ValueTask AddCoreAsync(CacheKey cacheKey, IEnumerable items, DateTimeOffset? expiration, CachePolicy? policy, CancellationToken token) { NotCacheableException.ThrowIfNotCacheable(); return await InternalAddAsync(cacheKey, Materialize(items), expiration, policy, token).ConfigureAwait(false); @@ -244,7 +247,11 @@ private async ValueTask InternalAddAsync(CacheKey cacheKey, IEnumerable { return await _memorySetCache.AddAsync(key, items, LocalWriteExpiration(expiration, policy), token).ConfigureAwait(false); } - var added = await _inner.AddAsync(cacheKey, items, expiration, policy, token).ConfigureAwait(false); + // null here is "no caller expiration": the inner cache resolves it from the policy, which is + // the overload that carries no expiration argument. + var added = expiration is { } deadline + ? await _inner.AddAsync(cacheKey, items, deadline, policy, token).ConfigureAwait(false) + : await _inner.AddAsync(cacheKey, items, policy, token).ConfigureAwait(false); await _memorySetCache.AddAsync(key, items, CancellationToken.None).ConfigureAwait(false); return added; } diff --git a/src/UiPath.Caching.Queue/NullSetCache.cs b/src/UiPath.Caching.Queue/NullSetCache.cs index c0a8728..d7efc0d 100644 --- a/src/UiPath.Caching.Queue/NullSetCache.cs +++ b/src/UiPath.Caching.Queue/NullSetCache.cs @@ -11,9 +11,9 @@ public sealed class NullSetCache : ISetCache public ValueTask AddAsync(CacheKey cacheKey, IEnumerable items, CachePolicy? policy, CancellationToken token = default) => ReturnZeroAsync(); - public ValueTask AddAsync(CacheKey cacheKey, IEnumerable items, TimeSpan? expiration, CachePolicy? policy, CancellationToken token = default) => ReturnZeroAsync(); + public ValueTask AddAsync(CacheKey cacheKey, IEnumerable items, TimeSpan expiration, CachePolicy? policy, CancellationToken token = default) => ReturnZeroAsync(); - public ValueTask AddAsync(CacheKey cacheKey, IEnumerable items, DateTimeOffset? expiration, CachePolicy? policy, CancellationToken token = default) => ReturnZeroAsync(); + public ValueTask AddAsync(CacheKey cacheKey, IEnumerable items, DateTimeOffset expiration, CachePolicy? policy, CancellationToken token = default) => ReturnZeroAsync(); public ValueTask PopAsync(CacheKey cacheKey, CachePolicy? policy, CancellationToken token = default) { diff --git a/src/UiPath.Caching.Queue/PublicAPI.Shipped.txt b/src/UiPath.Caching.Queue/PublicAPI.Shipped.txt index 309ca31..9bf2c0c 100644 --- a/src/UiPath.Caching.Queue/PublicAPI.Shipped.txt +++ b/src/UiPath.Caching.Queue/PublicAPI.Shipped.txt @@ -16,9 +16,7 @@ UiPath.Caching.ISetCache.RemoveAsync(UiPath.Caching.CacheKey cacheKey, System UiPath.Caching.ISetCache.RemoveItemAsync(UiPath.Caching.CacheKey cacheKey, T item, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask UiPath.Caching.ISetCache.RemoveItemsAsync(UiPath.Caching.CacheKey cacheKey, System.Collections.Generic.IEnumerable! items, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask UiPath.Caching.ISetCache -UiPath.Caching.ISetCache.AddAsync(UiPath.Caching.CacheKey cacheKey, System.Collections.Generic.IEnumerable! items, System.DateTimeOffset? expiration, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask UiPath.Caching.ISetCache.AddAsync(UiPath.Caching.CacheKey cacheKey, System.Collections.Generic.IEnumerable! items, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask -UiPath.Caching.ISetCache.AddAsync(UiPath.Caching.CacheKey cacheKey, System.Collections.Generic.IEnumerable! items, System.TimeSpan? expiration, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask UiPath.Caching.ISetCache.AddAsync(UiPath.Caching.CacheKey cacheKey, T item, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask UiPath.Caching.ISetCache.ContainsAsync(UiPath.Caching.CacheKey cacheKey, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask UiPath.Caching.ISetCache.ContainsItemAsync(UiPath.Caching.CacheKey cacheKey, T item, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask @@ -129,9 +127,7 @@ UiPath.Caching.RedisQueueCacheProvider.Enabled.get -> bool UiPath.Caching.RedisQueueCacheProvider.Name.get -> string! UiPath.Caching.RedisQueueCacheProvider.RedisQueueCacheProvider(Microsoft.Extensions.Options.IOptions! redisCacheOptions, Microsoft.Extensions.Options.IOptions! cacheOptions, Microsoft.Extensions.Options.IOptions! setCacheOptions, UiPath.Caching.Redis.IRedisConnector! redis, UiPath.Caching.ISerializerProxy! serializerProxy, UiPath.Caching.Policies.IResiliencePipelineProvider! resiliencePipelineProvider, UiPath.Caching.Telemetry.ICachingTelemetryProvider! cachingTelemetryProvider, Microsoft.Extensions.Logging.ILoggerFactory! loggerFactory, UiPath.Caching.ICachePolicyFactory! policyFactory) -> void UiPath.Caching.SetCache -UiPath.Caching.SetCache.AddAsync(UiPath.Caching.CacheKey cacheKey, System.Collections.Generic.IEnumerable! items, System.DateTimeOffset? expiration, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask UiPath.Caching.SetCache.AddAsync(UiPath.Caching.CacheKey cacheKey, System.Collections.Generic.IEnumerable! items, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask -UiPath.Caching.SetCache.AddAsync(UiPath.Caching.CacheKey cacheKey, System.Collections.Generic.IEnumerable! items, System.TimeSpan? expiration, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask UiPath.Caching.SetCache.AddAsync(UiPath.Caching.CacheKey cacheKey, T item, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask UiPath.Caching.SetCache.ContainsAsync(UiPath.Caching.CacheKey cacheKey, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask UiPath.Caching.SetCache.ContainsItemAsync(UiPath.Caching.CacheKey cacheKey, T item, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask diff --git a/src/UiPath.Caching.Queue/PublicAPI.Unshipped.txt b/src/UiPath.Caching.Queue/PublicAPI.Unshipped.txt index 6b61b88..bc15121 100644 --- a/src/UiPath.Caching.Queue/PublicAPI.Unshipped.txt +++ b/src/UiPath.Caching.Queue/PublicAPI.Unshipped.txt @@ -1,37 +1,41 @@ #nullable enable -UiPath.Caching.ISetCache.AddAsync(UiPath.Caching.CacheKey cacheKey, System.Collections.Generic.IEnumerable! items, System.DateTimeOffset? expiration, UiPath.Caching.CachePolicy? policy, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask -UiPath.Caching.ISetCache.AddAsync(UiPath.Caching.CacheKey cacheKey, System.Collections.Generic.IEnumerable! items, System.TimeSpan? expiration, UiPath.Caching.CachePolicy? policy, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask +UiPath.Caching.ISetCache.AddAsync(UiPath.Caching.CacheKey cacheKey, System.Collections.Generic.IEnumerable! items, System.DateTimeOffset expiration, UiPath.Caching.CachePolicy? policy, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask +UiPath.Caching.ISetCache.AddAsync(UiPath.Caching.CacheKey cacheKey, System.Collections.Generic.IEnumerable! items, System.TimeSpan expiration, UiPath.Caching.CachePolicy? policy, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask UiPath.Caching.ISetCache.AddAsync(UiPath.Caching.CacheKey cacheKey, System.Collections.Generic.IEnumerable! items, UiPath.Caching.CachePolicy? policy, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask UiPath.Caching.ISetCache.AddAsync(UiPath.Caching.CacheKey cacheKey, T item, UiPath.Caching.CachePolicy? policy, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask UiPath.Caching.ISetCache.MembersAsync(UiPath.Caching.CacheKey cacheKey, UiPath.Caching.CachePolicy? policy, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask!> UiPath.Caching.ISetCache.PopAsync(UiPath.Caching.CacheKey cacheKey, UiPath.Caching.CachePolicy? policy, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask UiPath.Caching.ISetCache.PopAsync(UiPath.Caching.CacheKey cacheKey, long count, UiPath.Caching.CachePolicy? policy, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask!> -UiPath.Caching.NullSetCache.AddAsync(UiPath.Caching.CacheKey cacheKey, System.Collections.Generic.IEnumerable! items, System.DateTimeOffset? expiration, UiPath.Caching.CachePolicy? policy, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask -UiPath.Caching.NullSetCache.AddAsync(UiPath.Caching.CacheKey cacheKey, System.Collections.Generic.IEnumerable! items, System.TimeSpan? expiration, UiPath.Caching.CachePolicy? policy, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask +UiPath.Caching.ISetCache.AddAsync(UiPath.Caching.CacheKey cacheKey, System.Collections.Generic.IEnumerable! items, System.DateTimeOffset expiration, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask +UiPath.Caching.ISetCache.AddAsync(UiPath.Caching.CacheKey cacheKey, System.Collections.Generic.IEnumerable! items, System.TimeSpan expiration, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask +UiPath.Caching.NullSetCache.AddAsync(UiPath.Caching.CacheKey cacheKey, System.Collections.Generic.IEnumerable! items, System.DateTimeOffset expiration, UiPath.Caching.CachePolicy? policy, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask +UiPath.Caching.NullSetCache.AddAsync(UiPath.Caching.CacheKey cacheKey, System.Collections.Generic.IEnumerable! items, System.TimeSpan expiration, UiPath.Caching.CachePolicy? policy, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask UiPath.Caching.NullSetCache.AddAsync(UiPath.Caching.CacheKey cacheKey, System.Collections.Generic.IEnumerable! items, UiPath.Caching.CachePolicy? policy, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask UiPath.Caching.NullSetCache.AddAsync(UiPath.Caching.CacheKey cacheKey, T item, UiPath.Caching.CachePolicy? policy, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask UiPath.Caching.NullSetCache.MembersAsync(UiPath.Caching.CacheKey cacheKey, UiPath.Caching.CachePolicy? policy, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask!> UiPath.Caching.NullSetCache.PopAsync(UiPath.Caching.CacheKey cacheKey, UiPath.Caching.CachePolicy? policy, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask UiPath.Caching.NullSetCache.PopAsync(UiPath.Caching.CacheKey cacheKey, long count, UiPath.Caching.CachePolicy? policy, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask!> -UiPath.Caching.Redis.RedisSetCache.AddAsync(UiPath.Caching.CacheKey cacheKey, System.Collections.Generic.IEnumerable! items, System.DateTimeOffset? expiration, UiPath.Caching.CachePolicy? policy, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask -UiPath.Caching.Redis.RedisSetCache.AddAsync(UiPath.Caching.CacheKey cacheKey, System.Collections.Generic.IEnumerable! items, System.TimeSpan? expiration, UiPath.Caching.CachePolicy? policy, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask +UiPath.Caching.Redis.RedisSetCache.AddAsync(UiPath.Caching.CacheKey cacheKey, System.Collections.Generic.IEnumerable! items, System.DateTimeOffset expiration, UiPath.Caching.CachePolicy? policy, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask +UiPath.Caching.Redis.RedisSetCache.AddAsync(UiPath.Caching.CacheKey cacheKey, System.Collections.Generic.IEnumerable! items, System.TimeSpan expiration, UiPath.Caching.CachePolicy? policy, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask UiPath.Caching.Redis.RedisSetCache.AddAsync(UiPath.Caching.CacheKey cacheKey, System.Collections.Generic.IEnumerable! items, UiPath.Caching.CachePolicy? policy, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask UiPath.Caching.Redis.RedisSetCache.AddAsync(UiPath.Caching.CacheKey cacheKey, T item, UiPath.Caching.CachePolicy? policy, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask UiPath.Caching.Redis.RedisSetCache.MembersAsync(UiPath.Caching.CacheKey cacheKey, UiPath.Caching.CachePolicy? policy, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask!> UiPath.Caching.Redis.RedisSetCache.PopAsync(UiPath.Caching.CacheKey cacheKey, UiPath.Caching.CachePolicy? policy, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask UiPath.Caching.Redis.RedisSetCache.PopAsync(UiPath.Caching.CacheKey cacheKey, long count, UiPath.Caching.CachePolicy? policy, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask!> +UiPath.Caching.SetCache.AddAsync(UiPath.Caching.CacheKey cacheKey, System.Collections.Generic.IEnumerable! items, System.DateTimeOffset expiration, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask +UiPath.Caching.SetCache.AddAsync(UiPath.Caching.CacheKey cacheKey, System.Collections.Generic.IEnumerable! items, System.TimeSpan expiration, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask UiPath.Caching.SetCacheExtensions UiPath.Caching.SetCacheSyncExtensions -static UiPath.Caching.SetCacheExtensions.AddAsync(this UiPath.Caching.ISetCache! cache, UiPath.Caching.CacheKey cacheKey, System.Collections.Generic.IEnumerable! items, System.DateTimeOffset? expiration, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask +static UiPath.Caching.SetCacheExtensions.AddAsync(this UiPath.Caching.ISetCache! cache, UiPath.Caching.CacheKey cacheKey, System.Collections.Generic.IEnumerable! items, System.DateTimeOffset expiration, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask static UiPath.Caching.SetCacheExtensions.AddAsync(this UiPath.Caching.ISetCache! cache, UiPath.Caching.CacheKey cacheKey, System.Collections.Generic.IEnumerable! items, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask -static UiPath.Caching.SetCacheExtensions.AddAsync(this UiPath.Caching.ISetCache! cache, UiPath.Caching.CacheKey cacheKey, System.Collections.Generic.IEnumerable! items, System.TimeSpan? expiration, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask +static UiPath.Caching.SetCacheExtensions.AddAsync(this UiPath.Caching.ISetCache! cache, UiPath.Caching.CacheKey cacheKey, System.Collections.Generic.IEnumerable! items, System.TimeSpan expiration, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask static UiPath.Caching.SetCacheExtensions.AddAsync(this UiPath.Caching.ISetCache! cache, UiPath.Caching.CacheKey cacheKey, T item, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask static UiPath.Caching.SetCacheExtensions.MembersAsync(this UiPath.Caching.ISetCache! cache, UiPath.Caching.CacheKey cacheKey, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask!> static UiPath.Caching.SetCacheExtensions.PopAsync(this UiPath.Caching.ISetCache! cache, UiPath.Caching.CacheKey cacheKey, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask static UiPath.Caching.SetCacheExtensions.PopAsync(this UiPath.Caching.ISetCache! cache, UiPath.Caching.CacheKey cacheKey, long count, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask!> -static UiPath.Caching.SetCacheSyncExtensions.Add(this UiPath.Caching.ISetCache! cache, UiPath.Caching.CacheKey cacheKey, System.Collections.Generic.IEnumerable! items, System.DateTimeOffset? expiration, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> long +static UiPath.Caching.SetCacheSyncExtensions.Add(this UiPath.Caching.ISetCache! cache, UiPath.Caching.CacheKey cacheKey, System.Collections.Generic.IEnumerable! items, System.DateTimeOffset expiration, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> long static UiPath.Caching.SetCacheSyncExtensions.Add(this UiPath.Caching.ISetCache! cache, UiPath.Caching.CacheKey cacheKey, System.Collections.Generic.IEnumerable! items, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> long -static UiPath.Caching.SetCacheSyncExtensions.Add(this UiPath.Caching.ISetCache! cache, UiPath.Caching.CacheKey cacheKey, System.Collections.Generic.IEnumerable! items, System.TimeSpan? expiration, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> long +static UiPath.Caching.SetCacheSyncExtensions.Add(this UiPath.Caching.ISetCache! cache, UiPath.Caching.CacheKey cacheKey, System.Collections.Generic.IEnumerable! items, System.TimeSpan expiration, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> long static UiPath.Caching.SetCacheSyncExtensions.Add(this UiPath.Caching.ISetCache! cache, UiPath.Caching.CacheKey cacheKey, T item, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> bool static UiPath.Caching.SetCacheSyncExtensions.Contains(this UiPath.Caching.ISetCache! cache, UiPath.Caching.CacheKey cacheKey, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> bool static UiPath.Caching.SetCacheSyncExtensions.ContainsItem(this UiPath.Caching.ISetCache! cache, UiPath.Caching.CacheKey cacheKey, T item, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> bool diff --git a/src/UiPath.Caching.Queue/RedisSetCache.cs b/src/UiPath.Caching.Queue/RedisSetCache.cs index 8804097..74f1d36 100644 --- a/src/UiPath.Caching.Queue/RedisSetCache.cs +++ b/src/UiPath.Caching.Queue/RedisSetCache.cs @@ -41,27 +41,25 @@ public async ValueTask AddAsync(CacheKey cacheKey, T item, CachePolicy? { NotCacheableException.ThrowIfNotCacheable(); var value = _serializer.Serialize(item); - var added = await AddManyInnerAsync(cacheKey, [value], ResolveExpiration((DateTimeOffset?)null, policy), token).ConfigureAwait(false); + var added = await AddManyInnerAsync(cacheKey, [value], PolicyDeadline(policy), token).ConfigureAwait(false); return added > 0; } public ValueTask AddAsync(CacheKey cacheKey, IEnumerable items, CachePolicy? policy, CancellationToken token = default) => - AddAsync(cacheKey, items, expiration: (TimeSpan?)null, policy, token); + AddCoreAsync(cacheKey, items, PolicyDeadline(policy), token); - public ValueTask AddAsync(CacheKey cacheKey, IEnumerable items, TimeSpan? expiration, CachePolicy? policy, CancellationToken token = default) - { - NotCacheableException.ThrowIfNotCacheable(); - ArgumentNullException.ThrowIfNull(items); - var values = items.Select(i => _serializer.Serialize(i)).ToArray(); - return AddManyInnerAsync(cacheKey, values, Clock.ToDateTimeOffset(ResolveExpiration(expiration, policy)), token); - } + public ValueTask AddAsync(CacheKey cacheKey, IEnumerable items, TimeSpan expiration, CachePolicy? policy, CancellationToken token = default) => + AddCoreAsync(cacheKey, items, Clock.UtcNow.Add(CallerDuration(expiration)), token); + + public ValueTask AddAsync(CacheKey cacheKey, IEnumerable items, DateTimeOffset expiration, CachePolicy? policy, CancellationToken token = default) => + AddCoreAsync(cacheKey, items, CallerDeadline(expiration), token); - public ValueTask AddAsync(CacheKey cacheKey, IEnumerable items, DateTimeOffset? expiration, CachePolicy? policy, CancellationToken token = default) + private ValueTask AddCoreAsync(CacheKey cacheKey, IEnumerable items, DateTimeOffset expiration, CancellationToken token) { NotCacheableException.ThrowIfNotCacheable(); ArgumentNullException.ThrowIfNull(items); var values = items.Select(i => _serializer.Serialize(i)).ToArray(); - return AddManyInnerAsync(cacheKey, values, ResolveExpiration(expiration, policy), token); + return AddManyInnerAsync(cacheKey, values, expiration, token); } private async ValueTask AddManyInnerAsync(CacheKey cacheKey, RedisValue[] values, DateTimeOffset expiration, CancellationToken token) diff --git a/src/UiPath.Caching.Queue/SetCacheExtensions.cs b/src/UiPath.Caching.Queue/SetCacheExtensions.cs index 69cf8cb..a16b281 100644 --- a/src/UiPath.Caching.Queue/SetCacheExtensions.cs +++ b/src/UiPath.Caching.Queue/SetCacheExtensions.cs @@ -18,11 +18,11 @@ public static ValueTask AddAsync(this ISetCache cache, CacheKey cacheKe => cache.AddAsync(cacheKey, items, (CachePolicy?)null, token); /// - public static ValueTask AddAsync(this ISetCache cache, CacheKey cacheKey, IEnumerable items, TimeSpan? expiration, CancellationToken token = default) + public static ValueTask AddAsync(this ISetCache cache, CacheKey cacheKey, IEnumerable items, TimeSpan expiration, CancellationToken token = default) => cache.AddAsync(cacheKey, items, expiration, null, token); /// - public static ValueTask AddAsync(this ISetCache cache, CacheKey cacheKey, IEnumerable items, DateTimeOffset? expiration, CancellationToken token = default) + public static ValueTask AddAsync(this ISetCache cache, CacheKey cacheKey, IEnumerable items, DateTimeOffset expiration, CancellationToken token = default) => cache.AddAsync(cacheKey, items, expiration, null, token); /// diff --git a/src/UiPath.Caching.Queue/SetCacheOfT.cs b/src/UiPath.Caching.Queue/SetCacheOfT.cs index 2d7faf4..2ec49bf 100644 --- a/src/UiPath.Caching.Queue/SetCacheOfT.cs +++ b/src/UiPath.Caching.Queue/SetCacheOfT.cs @@ -24,10 +24,10 @@ public ValueTask AddAsync(CacheKey cacheKey, T item, CancellationToken tok public ValueTask AddAsync(CacheKey cacheKey, IEnumerable items, CancellationToken token = default) => _cache.AddAsync(GetCacheKey(cacheKey), items, policy: Policy, token: token); - public ValueTask AddAsync(CacheKey cacheKey, IEnumerable items, TimeSpan? expiration, CancellationToken token = default) => + public ValueTask AddAsync(CacheKey cacheKey, IEnumerable items, TimeSpan expiration, CancellationToken token = default) => _cache.AddAsync(GetCacheKey(cacheKey), items, expiration, Policy, token); - public ValueTask AddAsync(CacheKey cacheKey, IEnumerable items, DateTimeOffset? expiration, CancellationToken token = default) => + public ValueTask AddAsync(CacheKey cacheKey, IEnumerable items, DateTimeOffset expiration, CancellationToken token = default) => _cache.AddAsync(GetCacheKey(cacheKey), items, expiration, Policy, token); public ValueTask PopAsync(CacheKey cacheKey, CancellationToken token = default) => diff --git a/src/UiPath.Caching.Queue/SetCacheSyncExtensions.cs b/src/UiPath.Caching.Queue/SetCacheSyncExtensions.cs index 33b27b0..c1281c7 100644 --- a/src/UiPath.Caching.Queue/SetCacheSyncExtensions.cs +++ b/src/UiPath.Caching.Queue/SetCacheSyncExtensions.cs @@ -19,11 +19,11 @@ public static long Add(this ISetCache cache, CacheKey cacheKey, IEnumerabl => cache.AddAsync(cacheKey, items, token).AsTask().GetAwaiter().GetResult(); /// - public static long Add(this ISetCache cache, CacheKey cacheKey, IEnumerable items, TimeSpan? expiration, CancellationToken token = default) + public static long Add(this ISetCache cache, CacheKey cacheKey, IEnumerable items, TimeSpan expiration, CancellationToken token = default) => cache.AddAsync(cacheKey, items, expiration, token).AsTask().GetAwaiter().GetResult(); /// - public static long Add(this ISetCache cache, CacheKey cacheKey, IEnumerable items, DateTimeOffset? expiration, CancellationToken token = default) + public static long Add(this ISetCache cache, CacheKey cacheKey, IEnumerable items, DateTimeOffset expiration, CancellationToken token = default) => cache.AddAsync(cacheKey, items, expiration, token).AsTask().GetAwaiter().GetResult(); /// diff --git a/src/UiPath.Caching/Distributed/UiPathDistributedCache.cs b/src/UiPath.Caching/Distributed/UiPathDistributedCache.cs index 204098e..df9e76c 100644 --- a/src/UiPath.Caching/Distributed/UiPathDistributedCache.cs +++ b/src/UiPath.Caching/Distributed/UiPathDistributedCache.cs @@ -116,10 +116,19 @@ private ValueTask StoreAsync( IDictionary fields, DateTimeOffset now, TimeSpan? ttl, - CancellationToken token) => - ttl is null && _allowUnboundedEntries - ? _cache.SetAsync(cacheKey, fields, (DateTimeOffset?)DateTimeOffset.MaxValue, _policy, token) - : _cache.SetAsync(cacheKey, fields, ttl ?? Clamp(now, _defaultEntryExpiration), _policy, token); + CancellationToken token) + { + if (ttl is null && _allowUnboundedEntries) + { + return _cache.SetAsync(cacheKey, fields, DateTimeOffset.MaxValue, _policy, token); + } + + // Caller TTL, else this adapter's configured default. With neither, the provider's own + // default applies — which is what the overload carrying no expiration asks for. + return (ttl ?? Clamp(now, _defaultEntryExpiration)) is { } duration + ? _cache.SetAsync(cacheKey, fields, duration, _policy, token) + : _cache.SetAsync(cacheKey, fields, _policy, token); + } /// Composes the storage key by running the configured strategy over the validated caller key. private CacheKey Encode(string key) @@ -216,7 +225,7 @@ private async ValueTask SlideAsync( return; } - _ = await _cache.RefreshAsync(cacheKey, (DateTimeOffset?)target, _policy, token).ConfigureAwait(false); + _ = await _cache.RefreshAsync(cacheKey, target, _policy, token).ConfigureAwait(false); } /// Expiration metadata as written, decoded once. Null means the sentinel: that deadline was not set. diff --git a/src/UiPath.Caching/MultilayerCache.cs b/src/UiPath.Caching/MultilayerCache.cs index 0032988..3d0746a 100644 --- a/src/UiPath.Caching/MultilayerCache.cs +++ b/src/UiPath.Caching/MultilayerCache.cs @@ -85,30 +85,18 @@ public MultilayerCache( return GetOrAddInternalAsync(cacheKey, generator, writeExpiration, duration, policy, token); } - public ValueTask GetOrAddAsync(CacheKey cacheKey, Func> generator, TimeSpan? expiration, CachePolicy? policy, CancellationToken token = default) + public ValueTask GetOrAddAsync(CacheKey cacheKey, Func> generator, TimeSpan expiration, CachePolicy? policy, CancellationToken token = default) { ArgumentNullException.ThrowIfNull(generator); - policy ??= _defaultPolicy; - var duration = expiration ?? policy.DistributedExpiration; - return GetOrAddInternalAsync(cacheKey, generator, _clock.ToDateTimeOffset(ResolveWriteDuration(policy, expiration)), duration, policy, token); + var (writeExpiration, duration) = CallerWrite(expiration); + return GetOrAddInternalAsync(cacheKey, generator, writeExpiration, duration, policy ?? _defaultPolicy, token); } - public ValueTask GetOrAddAsync(CacheKey cacheKey, Func> generator, DateTimeOffset? expiration, CachePolicy? policy, CancellationToken token = default) + public ValueTask GetOrAddAsync(CacheKey cacheKey, Func> generator, DateTimeOffset expiration, CachePolicy? policy, CancellationToken token = default) { ArgumentNullException.ThrowIfNull(generator); - policy ??= _defaultPolicy; - TimeSpan? duration; - if (expiration.HasValue) - { - duration = expiration.Value - _clock.UtcNow; - if (duration is { } d && d <= TimeSpan.Zero) { duration = null; } - } - else - { - duration = policy.DistributedExpiration; - expiration = _clock.ToDateTimeOffset(ResolveWriteDuration(policy)); - } - return GetOrAddInternalAsync(cacheKey, generator, expiration, duration, policy, token); + var (writeExpiration, duration) = CallerWrite(expiration); + return GetOrAddInternalAsync(cacheKey, generator, writeExpiration, duration, policy ?? _defaultPolicy, token); } public ValueTask[]> GetOrAddAsync(KeyValuePair[] entries, Func[]>> generator, CachePolicy? policy, CancellationToken token = default) @@ -122,34 +110,22 @@ public MultilayerCache( return GetOrAddBatchInternalAsync(entries, generator, writeExpiration, duration, policy, token); } - public ValueTask[]> GetOrAddAsync(KeyValuePair[] entries, Func[]>> generator, TimeSpan? expiration, CachePolicy? policy, CancellationToken token = default) + public ValueTask[]> GetOrAddAsync(KeyValuePair[] entries, Func[]>> generator, TimeSpan expiration, CachePolicy? policy, CancellationToken token = default) where TState : notnull { ArgumentNullException.ThrowIfNull(entries); ArgumentNullException.ThrowIfNull(generator); - policy ??= _defaultPolicy; - var duration = expiration ?? policy.DistributedExpiration; - return GetOrAddBatchInternalAsync(entries, generator, _clock.ToDateTimeOffset(ResolveWriteDuration(policy, expiration)), duration, policy, token); + var (writeExpiration, duration) = CallerWrite(expiration); + return GetOrAddBatchInternalAsync(entries, generator, writeExpiration, duration, policy ?? _defaultPolicy, token); } - public ValueTask[]> GetOrAddAsync(KeyValuePair[] entries, Func[]>> generator, DateTimeOffset? expiration, CachePolicy? policy, CancellationToken token = default) + public ValueTask[]> GetOrAddAsync(KeyValuePair[] entries, Func[]>> generator, DateTimeOffset expiration, CachePolicy? policy, CancellationToken token = default) where TState : notnull { ArgumentNullException.ThrowIfNull(entries); ArgumentNullException.ThrowIfNull(generator); - policy ??= _defaultPolicy; - TimeSpan? duration; - if (expiration.HasValue) - { - duration = expiration.Value - _clock.UtcNow; - if (duration is { } d && d <= TimeSpan.Zero) { duration = null; } - } - else - { - duration = policy.DistributedExpiration; - expiration = _clock.ToDateTimeOffset(ResolveWriteDuration(policy)); - } - return GetOrAddBatchInternalAsync(entries, generator, expiration, duration, policy, token); + var (writeExpiration, duration) = CallerWrite(expiration); + return GetOrAddBatchInternalAsync(entries, generator, writeExpiration, duration, policy ?? _defaultPolicy, token); } private async ValueTask GetOrAddInternalAsync(CacheKey cacheKey, Func> generator, DateTimeOffset? expiration, TimeSpan? effectiveDuration, CachePolicy policy, CancellationToken token) @@ -634,20 +610,18 @@ private List> SelectEntriesToStore( public ValueTask SetAsync(CacheKey cacheKey, T? value, CachePolicy? policy, CancellationToken token = default) { policy ??= _defaultPolicy; - return SetAsync(cacheKey, value, ResolveWriteDuration(policy), policy, token); + return SetCoreAsync(cacheKey, value, PolicyDeadline(policy), policy, token); } - public ValueTask SetAsync(CacheKey cacheKey, T? value, TimeSpan? expiration, CachePolicy? policy, CancellationToken token = default) - { - policy ??= _defaultPolicy; - return SetAsync(cacheKey, value, _clock.ToDateTimeOffset(ResolveWriteDuration(policy, expiration)), policy, token); - } + public ValueTask SetAsync(CacheKey cacheKey, T? value, TimeSpan expiration, CachePolicy? policy, CancellationToken token = default) => + SetCoreAsync(cacheKey, value, CallerDeadline(expiration), policy ?? _defaultPolicy, token); + + public ValueTask SetAsync(CacheKey cacheKey, T? value, DateTimeOffset expiration, CachePolicy? policy, CancellationToken token = default) => + SetCoreAsync(cacheKey, value, CallerDeadline(expiration), policy ?? _defaultPolicy, token); - public async ValueTask SetAsync(CacheKey cacheKey, T? value, DateTimeOffset? expiration, CachePolicy? policy, CancellationToken token = default) + private async ValueTask SetCoreAsync(CacheKey cacheKey, T? value, DateTimeOffset expiration, CachePolicy policy, CancellationToken token) { NotCacheableException.ThrowIfNotCacheable(); - policy ??= _defaultPolicy; - expiration ??= _clock.ToDateTimeOffset(ResolveWriteDuration(policy)); var cacheEntryOptions = _entryBuilder.BuildEntryOptions(cacheKey, expiration, token); if (value is null && !_multiLayerCacheOptions.CacheNullValues) { @@ -669,18 +643,6 @@ public async ValueTask SetAsync(CacheKey cacheKey, T? value, DateTimeOf } - public ValueTask TryAddAsync(CacheKey cacheKey, T? value, CachePolicy? policy, CancellationToken token = default) - { - policy ??= _defaultPolicy; - return TryAddAsync(cacheKey, value, ResolveWriteDuration(policy), policy, token); - } - - public ValueTask TryAddAsync(CacheKey cacheKey, T? value, TimeSpan? expiration, CachePolicy? policy, CancellationToken token = default) - { - policy ??= _defaultPolicy; - return TryAddAsync(cacheKey, value, _clock.ToDateTimeOffset(ResolveWriteDuration(policy, expiration)), policy, token); - } - /// /// The L2 arbitrates and the L1 is populated only after a win — the reverse of SetAsync. /// L1 cannot arbitrate: a key absent locally may exist in the shared store, so a local probe @@ -688,11 +650,23 @@ public ValueTask TryAddAsync(CacheKey cacheKey, T? value, TimeSpan? exp /// checks its connection first) rather than being gated on GetInnerCacheDisconnected, /// whose state also covers the broadcast transport — a dead topic must not stop a healthy Redis. /// - public async ValueTask TryAddAsync(CacheKey cacheKey, T? value, DateTimeOffset? expiration, CachePolicy? policy, CancellationToken token = default) + public ValueTask TryAddAsync(CacheKey cacheKey, T? value, CachePolicy? policy, CancellationToken token = default) { - NotCacheableException.ThrowIfNotCacheable(); policy ??= _defaultPolicy; - expiration ??= _clock.ToDateTimeOffset(ResolveWriteDuration(policy)); + return TryAddCoreAsync(cacheKey, value, PolicyDeadline(policy), policy, token); + } + + /// + public ValueTask TryAddAsync(CacheKey cacheKey, T? value, TimeSpan expiration, CachePolicy? policy, CancellationToken token = default) => + TryAddCoreAsync(cacheKey, value, CallerDeadline(expiration), policy ?? _defaultPolicy, token); + + /// + public ValueTask TryAddAsync(CacheKey cacheKey, T? value, DateTimeOffset expiration, CachePolicy? policy, CancellationToken token = default) => + TryAddCoreAsync(cacheKey, value, CallerDeadline(expiration), policy ?? _defaultPolicy, token); + + private async ValueTask TryAddCoreAsync(CacheKey cacheKey, T? value, DateTimeOffset expiration, CachePolicy policy, CancellationToken token) + { + NotCacheableException.ThrowIfNotCacheable(); var options = _entryBuilder.BuildEntryOptions(cacheKey, expiration, token); if (value is null && !_multiLayerCacheOptions.CacheNullValues) @@ -822,20 +796,18 @@ private async ValueTask LocalTryAddAsync(CacheEntryOptions options, T? public ValueTask SetAsync(KeyValuePair[] keyValues, CachePolicy? policy, CancellationToken token = default) { policy ??= _defaultPolicy; - return SetAsync(keyValues, ResolveWriteDuration(policy), policy, token); + return SetCoreAsync(keyValues, PolicyDeadline(policy), policy, token); } - public ValueTask SetAsync(KeyValuePair[] keyValues, TimeSpan? expiration, CachePolicy? policy, CancellationToken token = default) - { - policy ??= _defaultPolicy; - return SetAsync(keyValues, _clock.ToDateTimeOffset(ResolveWriteDuration(policy, expiration)), policy, token); - } + public ValueTask SetAsync(KeyValuePair[] keyValues, TimeSpan expiration, CachePolicy? policy, CancellationToken token = default) => + SetCoreAsync(keyValues, CallerDeadline(expiration), policy ?? _defaultPolicy, token); + + public ValueTask SetAsync(KeyValuePair[] keyValues, DateTimeOffset expiration, CachePolicy? policy, CancellationToken token = default) => + SetCoreAsync(keyValues, CallerDeadline(expiration), policy ?? _defaultPolicy, token); - public async ValueTask SetAsync(KeyValuePair[] keyValues, DateTimeOffset? expiration, CachePolicy? policy, CancellationToken token = default) + private async ValueTask SetCoreAsync(KeyValuePair[] keyValues, DateTimeOffset expiration, CachePolicy policy, CancellationToken token) { NotCacheableException.ThrowIfNotCacheable(); - policy ??= _defaultPolicy; - expiration ??= _clock.ToDateTimeOffset(ResolveWriteDuration(policy)); var removeEntries = new List(); var setEntries = new List>(); foreach (var keyValue in keyValues) @@ -904,20 +876,18 @@ public ValueTask RemoveAsync(CacheKey[] cacheKey, CancellationToken tok public ValueTask RefreshAsync(CacheKey cacheKey, CachePolicy? policy, CancellationToken token = default) { policy ??= _defaultPolicy; - return RefreshAsync(cacheKey, ResolveWriteDuration(policy), policy, token); + return RefreshCoreAsync(cacheKey, PolicyDeadline(policy), policy, token); } - public ValueTask RefreshAsync(CacheKey cacheKey, TimeSpan? expiration, CachePolicy? policy, CancellationToken token = default) - { - policy ??= _defaultPolicy; - return RefreshAsync(cacheKey, _clock.ToDateTimeOffset(ResolveWriteDuration(policy, expiration)), policy, token); - } + public ValueTask RefreshAsync(CacheKey cacheKey, TimeSpan expiration, CachePolicy? policy, CancellationToken token = default) => + RefreshCoreAsync(cacheKey, CallerDeadline(expiration), policy ?? _defaultPolicy, token); - public async ValueTask RefreshAsync(CacheKey cacheKey, DateTimeOffset? expiration, CachePolicy? policy, CancellationToken token = default) + public ValueTask RefreshAsync(CacheKey cacheKey, DateTimeOffset expiration, CachePolicy? policy, CancellationToken token = default) => + RefreshCoreAsync(cacheKey, CallerDeadline(expiration), policy ?? _defaultPolicy, token); + + private async ValueTask RefreshCoreAsync(CacheKey cacheKey, DateTimeOffset expiration, CachePolicy policy, CancellationToken token) { NotCacheableException.ThrowIfNotCacheable(); - policy ??= _defaultPolicy; - expiration ??= _clock.ToDateTimeOffset(ResolveWriteDuration(policy)); var cacheEntryOptions = _entryBuilder.BuildEntryOptions(cacheKey, expiration, token); LogClearingCached(cacheEntryOptions.CacheKey); _memoryCache.Remove(cacheEntryOptions.CacheKey); @@ -1241,8 +1211,12 @@ private async ValueTask InternalSetAsync(CacheEntryValue[] cacheEntr return MemSet(policy.LocalExpirationDisconnected ?? _multiLayerCacheOptions.LocalMaxExpirationDisconnected); } - DateTimeOffset? batchExpiration = cacheEntries.Length > 0 ? cacheEntries[0].CacheEntry.Expiration : null; - var set = await _innerCache.SetAsync(cacheKeyValuePairs, batchExpiration, policy, token).ConfigureAwait(false); + // One deadline for the batch, taken from the first entry — every entry in a batch is + // built from the same resolved expiration. With no entries there is nothing to date, + // so the write inherits the policy. + var set = cacheEntries.Length > 0 + ? await _innerCache.SetAsync(cacheKeyValuePairs, cacheEntries[0].CacheEntry.Expiration, policy, token).ConfigureAwait(false) + : await _innerCache.SetAsync(cacheKeyValuePairs, policy, token).ConfigureAwait(false); return set && MemSet(policy.LocalExpiration ?? _multiLayerCacheOptions.LocalMaxExpiration); } catch (Exception ex) diff --git a/src/UiPath.Caching/MultilayerCacheBase.cs b/src/UiPath.Caching/MultilayerCacheBase.cs index 742ec38..c8b125b 100644 --- a/src/UiPath.Caching/MultilayerCacheBase.cs +++ b/src/UiPath.Caching/MultilayerCacheBase.cs @@ -1,3 +1,4 @@ +using System.Runtime.CompilerServices; using UiPath.Caching.Config; using UiPath.Caching.Locking; using UiPath.Caching.Telemetry; @@ -192,6 +193,36 @@ private static TimeSpan NonNegativeOrFallback(TimeSpan? value, TimeSpan fallback return ApplyJitter(resolved, policy.JitterMaxDuration, _clock.UtcNow); } + /// + /// Validates a caller-supplied duration and pairs it with the deadline it implies. The write path + /// needs both: the deadline for the entry options, the duration for the L1 cap and the rehydrate + /// trigger. Jitter is deliberately not applied — a caller-supplied lifetime is honored exactly. + /// + private protected (DateTimeOffset Expiration, TimeSpan Duration) CallerWrite(TimeSpan expiration, [CallerArgumentExpression(nameof(expiration))] string? paramName = null) + { + var duration = CacheExpiration.ThrowIfNotPositive(expiration, paramName); + return (_clock.UtcNow.Add(duration), duration); + } + + /// + private protected (DateTimeOffset Expiration, TimeSpan Duration) CallerWrite(DateTimeOffset expiration, [CallerArgumentExpression(nameof(expiration))] string? paramName = null) + { + var now = _clock.UtcNow; + return (CacheExpiration.ThrowIfNotFuture(expiration, now, paramName), expiration - now); + } + + /// Write deadline for a call that carried no expiration: the policy's L2 TTL jittered, then the cache default. + private protected DateTimeOffset PolicyDeadline(CachePolicy policy) => + _clock.ToDateTimeOffset(ResolveWriteDuration(policy)); + + /// Write deadline for a caller-supplied duration, validated. + private protected DateTimeOffset CallerDeadline(TimeSpan expiration, [CallerArgumentExpression(nameof(expiration))] string? paramName = null) => + CallerWrite(expiration, paramName).Expiration; + + /// Write deadline for a caller-supplied deadline, validated. + private protected DateTimeOffset CallerDeadline(DateTimeOffset expiration, [CallerArgumentExpression(nameof(expiration))] string? paramName = null) => + CacheExpiration.ThrowIfNotFuture(expiration, _clock.UtcNow, paramName); + /// /// Acquires only the local lock for , for the callers to which it is a /// correctness requirement rather than a de-duplication optimization — the conditional add on a diff --git a/src/UiPath.Caching/MultilayerHashCache.cs b/src/UiPath.Caching/MultilayerHashCache.cs index 634837d..ec9f225 100644 --- a/src/UiPath.Caching/MultilayerHashCache.cs +++ b/src/UiPath.Caching/MultilayerHashCache.cs @@ -79,30 +79,18 @@ public MultilayerHashCache( return GetOrAddInternalAsync(cacheKey, generator, writeExpiration, duration, HashCacheSetOption.KeyReplace, policy, token); } - public ValueTask> GetOrAddAsync(CacheKey cacheKey, Func>> generator, TimeSpan? expiration, CachePolicy? policy, CancellationToken token = default) + public ValueTask> GetOrAddAsync(CacheKey cacheKey, Func>> generator, TimeSpan expiration, CachePolicy? policy, CancellationToken token = default) { ArgumentNullException.ThrowIfNull(generator); - policy ??= _defaultPolicy; - var duration = expiration ?? policy.DistributedExpiration; - return GetOrAddInternalAsync(cacheKey, generator, _clock.ToDateTimeOffset(ResolveWriteDuration(policy, expiration)), duration, HashCacheSetOption.KeyReplace, policy, token); + var (writeExpiration, duration) = CallerWrite(expiration); + return GetOrAddInternalAsync(cacheKey, generator, writeExpiration, duration, HashCacheSetOption.KeyReplace, policy ?? _defaultPolicy, token); } - public ValueTask> GetOrAddAsync(CacheKey cacheKey, Func>> generator, DateTimeOffset? expiration, CachePolicy? policy, CancellationToken token = default) + public ValueTask> GetOrAddAsync(CacheKey cacheKey, Func>> generator, DateTimeOffset expiration, CachePolicy? policy, CancellationToken token = default) { ArgumentNullException.ThrowIfNull(generator); - policy ??= _defaultPolicy; - TimeSpan? duration; - if (expiration.HasValue) - { - duration = expiration.Value - _clock.UtcNow; - if (duration is { } d && d <= TimeSpan.Zero) { duration = null; } - } - else - { - duration = policy.DistributedExpiration; - expiration = _clock.ToDateTimeOffset(ResolveWriteDuration(policy)); - } - return GetOrAddInternalAsync(cacheKey, generator, expiration, duration, HashCacheSetOption.KeyReplace, policy, token); + var (writeExpiration, duration) = CallerWrite(expiration); + return GetOrAddInternalAsync(cacheKey, generator, writeExpiration, duration, HashCacheSetOption.KeyReplace, policy ?? _defaultPolicy, token); } /// @@ -112,22 +100,11 @@ public MultilayerHashCache( /// _metadata_-as-empty-marker is present; we collapse that to for the /// caller, who can always iterate the result without a null check. /// - public ValueTask> GetOrAddAsync(CacheKey cacheKey, Func>> generator, DateTimeOffset? expiration, HashCacheSetOption? setOption, CachePolicy? policy, CancellationToken token = default) + public ValueTask> GetOrAddAsync(CacheKey cacheKey, Func>> generator, DateTimeOffset expiration, HashCacheSetOption? setOption, CachePolicy? policy, CancellationToken token = default) { ArgumentNullException.ThrowIfNull(generator); - policy ??= _defaultPolicy; - TimeSpan? duration; - if (expiration.HasValue) - { - duration = expiration.Value - _clock.UtcNow; - if (duration is { } d && d <= TimeSpan.Zero) { duration = null; } - } - else - { - duration = policy.DistributedExpiration; - expiration = _clock.ToDateTimeOffset(ResolveWriteDuration(policy)); - } - return GetOrAddInternalAsync(cacheKey, generator, expiration, duration, setOption ?? HashCacheSetOption.KeyReplace, policy, token); + var (writeExpiration, duration) = CallerWrite(expiration); + return GetOrAddInternalAsync(cacheKey, generator, writeExpiration, duration, setOption ?? HashCacheSetOption.KeyReplace, policy ?? _defaultPolicy, token); } private async ValueTask> GetOrAddInternalAsync(CacheKey cacheKey, Func>> generator, DateTimeOffset? expiration, TimeSpan? effectiveDuration, HashCacheSetOption setOption, CachePolicy policy, CancellationToken token) @@ -210,20 +187,18 @@ private void TryHashRehydrate(CacheKey originalCacheKey, DateTimeOffset entry public ValueTask SetAsync(CacheKey cacheKey, IDictionary values, CachePolicy? policy, CancellationToken token = default) { policy ??= _defaultPolicy; - return SetAsync(cacheKey, values, ResolveWriteDuration(policy), policy, token); + return SetCoreAsync(cacheKey, values, PolicyDeadline(policy), policy, token); } - public ValueTask SetAsync(CacheKey cacheKey, IDictionary values, TimeSpan? expiration, CachePolicy? policy, CancellationToken token = default) - { - policy ??= _defaultPolicy; - return SetAsync(cacheKey, values, _clock.ToDateTimeOffset(ResolveWriteDuration(policy, expiration)), policy, token); - } + public ValueTask SetAsync(CacheKey cacheKey, IDictionary values, TimeSpan expiration, CachePolicy? policy, CancellationToken token = default) => + SetCoreAsync(cacheKey, values, CallerDeadline(expiration), policy ?? _defaultPolicy, token); + + public ValueTask SetAsync(CacheKey cacheKey, IDictionary values, DateTimeOffset expiration, CachePolicy? policy, CancellationToken token = default) => + SetCoreAsync(cacheKey, values, CallerDeadline(expiration), policy ?? _defaultPolicy, token); - public async ValueTask SetAsync(CacheKey cacheKey, IDictionary values, DateTimeOffset? expiration, CachePolicy? policy, CancellationToken token = default) + private async ValueTask SetCoreAsync(CacheKey cacheKey, IDictionary values, DateTimeOffset expiration, CachePolicy policy, CancellationToken token) { NotCacheableException.ThrowIfNotCacheable(); - policy ??= _defaultPolicy; - expiration ??= _clock.ToDateTimeOffset(ResolveWriteDuration(policy)); var options = _entryBuilder.BuildEntryOptions(cacheKey, expiration, token: token); if (IsNullOrEmpty(values) && !_multiLayerCacheOptions.CacheNullValues) { @@ -290,20 +265,14 @@ public ValueTask RemoveAsync(CacheKey cacheKey, CancellationToken token return RemoveAsync(_entryBuilder.BuildEntryOptions(cacheKey, default, token: token)); } - public ValueTask RefreshAsync(CacheKey cacheKey, CachePolicy? policy, CancellationToken token = default) - { - policy ??= _defaultPolicy; - return RefreshAsync(cacheKey, ResolveWriteDuration(policy), policy, token); - } + public ValueTask RefreshAsync(CacheKey cacheKey, CachePolicy? policy, CancellationToken token = default) => + RefreshAsync(cacheKey, new HashCacheEntryOptions(), policy, token); - public ValueTask RefreshAsync(CacheKey cacheKey, TimeSpan? expiration, CachePolicy? policy, CancellationToken token = default) - { - policy ??= _defaultPolicy; - return RefreshAsync(cacheKey, _clock.ToDateTimeOffset(ResolveWriteDuration(policy, expiration)), policy, token); - } + public ValueTask RefreshAsync(CacheKey cacheKey, TimeSpan expiration, CachePolicy? policy, CancellationToken token = default) => + RefreshAsync(cacheKey, new HashCacheEntryOptions(CallerDeadline(expiration)), policy, token); - public ValueTask RefreshAsync(CacheKey cacheKey, DateTimeOffset? expiration, CachePolicy? policy, CancellationToken token = default) => - RefreshAsync(cacheKey, new HashCacheEntryOptions(expiration), policy, token); + public ValueTask RefreshAsync(CacheKey cacheKey, DateTimeOffset expiration, CachePolicy? policy, CancellationToken token = default) => + RefreshAsync(cacheKey, new HashCacheEntryOptions(CallerDeadline(expiration)), policy, token); public async ValueTask RefreshAsync(CacheKey cacheKey, HashCacheEntryOptions options, CachePolicy? policy, CancellationToken token = default) { diff --git a/src/UiPath.Caching/PublicAPI.Shipped.txt b/src/UiPath.Caching/PublicAPI.Shipped.txt index 3d6b0c8..58e5a90 100644 --- a/src/UiPath.Caching/PublicAPI.Shipped.txt +++ b/src/UiPath.Caching/PublicAPI.Shipped.txt @@ -358,6 +358,8 @@ UiPath.Caching.InMemoryCacheProvider.Enabled.get -> bool UiPath.Caching.InMemoryCacheProvider.InMemoryCacheProvider(Microsoft.Extensions.Options.IOptions! optionsAccessor, Microsoft.Extensions.Options.IOptions! cacheOptionsAccessor, UiPath.Caching.IMemoryCacheFactory! memoryCacheFactory, UiPath.Caching.Broadcast.ICacheEventFactory! cacheEventFactory, UiPath.Caching.Broadcast.IChangeTokenFactory! changeTokenFactory, UiPath.Caching.Broadcast.ITopicFactory! topicFactory, UiPath.Caching.Telemetry.ICachingTelemetryProvider! cachingTelemetryProvider, Microsoft.Extensions.Logging.ILoggerFactory! loggerFactory, UiPath.Caching.Locking.ILocalLock! localLock, UiPath.Caching.ICachePolicyFactory! policyFactory) -> void UiPath.Caching.InMemoryCacheProvider.Name.get -> string! UiPath.Caching.InMemoryRedisCacheOptions +UiPath.Caching.InMemoryRedisCacheOptions.BroadcastEnable.get -> bool +UiPath.Caching.InMemoryRedisCacheOptions.BroadcastEnable.set -> void UiPath.Caching.InMemoryRedisCacheOptions.CacheKeyStrategy.get -> UiPath.Caching.ICacheKeyStrategy? UiPath.Caching.InMemoryRedisCacheOptions.CacheKeyStrategy.set -> void UiPath.Caching.InMemoryRedisCacheOptions.CacheNullValues.get -> bool @@ -535,8 +537,6 @@ UiPath.Caching.Redis.RedisCacheBase.OnConnectionFailed -> System.EventHandler? UiPath.Caching.Redis.RedisCacheBase.OnConnectionRestored -> System.EventHandler? UiPath.Caching.Redis.RedisCacheBase.OnReconnected -> System.EventHandler? UiPath.Caching.Redis.RedisCacheBase.RedisCacheBase(UiPath.Caching.Redis.IRedisConnector! redis, UiPath.Caching.Telemetry.ICachingTelemetryProvider! telemetryProvider, UiPath.Caching.Redis.RedisCacheOptions! redisCacheOptions, UiPath.Caching.CacheOptions! cacheOptions, UiPath.Caching.ICachePolicyFactory! policyFactory) -> void -UiPath.Caching.Redis.RedisCacheBase.ResolveExpiration(System.DateTimeOffset? expiration, UiPath.Caching.CachePolicy? policy) -> System.DateTimeOffset -UiPath.Caching.Redis.RedisCacheBase.ResolveExpiration(System.TimeSpan? expiration, UiPath.Caching.CachePolicy? policy) -> System.TimeSpan? UiPath.Caching.Redis.RedisCacheBase.Telemetry.get -> UiPath.Caching.Telemetry.ICachingTelemetryProvider! UiPath.Caching.Redis.RedisCacheBase.TrackRead(UiPath.Caching.Telemetry.ITelemetryOperation! operation, bool hit, StackExchange.Redis.RedisKey key) -> void UiPath.Caching.Redis.RedisCacheOptions @@ -759,6 +759,7 @@ static UiPath.Caching.Config.ServiceCollectionExtensions.AddCaching(this Microso static UiPath.Caching.Config.ServiceCollectionExtensions.AddCaching(this Microsoft.Extensions.DependencyInjection.IServiceCollection! services, System.Action! configure) -> Microsoft.Extensions.DependencyInjection.IServiceCollection! static UiPath.Caching.Config.ServiceCollectionExtensions.AddCaching(this Microsoft.Extensions.DependencyInjection.IServiceCollection! services, System.Action! configure, System.Action! configureOptions) -> Microsoft.Extensions.DependencyInjection.IServiceCollection! static UiPath.Caching.Config.ServiceCollectionExtensions.TryAddMemoryCacheFactory(this Microsoft.Extensions.DependencyInjection.IServiceCollection! services) -> Microsoft.Extensions.DependencyInjection.IServiceCollection! +static UiPath.Caching.Config.ServiceCollectionExtensions.TryConfigure(this Microsoft.Extensions.DependencyInjection.IServiceCollection! services, System.Action! configureOptions) -> Microsoft.Extensions.DependencyInjection.IServiceCollection! static UiPath.Caching.MemoryCacheExtensions.Monitor(this Microsoft.Extensions.Caching.Memory.IMemoryCache! cache, UiPath.Caching.ICacheOptions! cacheOptions, UiPath.Caching.Telemetry.ICachingTelemetryProvider! telemetryProvider, string! name) -> System.IDisposable! static UiPath.Caching.Metrics.GetReadTopicMetricName(string! topicName) -> string! static UiPath.Caching.Metrics.GetWriteTopicMetricName(string! topicName) -> string! @@ -819,6 +820,3 @@ virtual UiPath.Caching.Redis.RedisCacheBase.Dispose(bool disposing) -> void ~UiPath.Caching.Config.NullConfigurationSection.this[string key].set -> void ~static readonly UiPath.Caching.Config.NullConfiguration.Instance -> Microsoft.Extensions.Configuration.IConfiguration ~static readonly UiPath.Caching.Config.NullConfigurationSection.Instance -> Microsoft.Extensions.Configuration.IConfigurationSection -static UiPath.Caching.Config.ServiceCollectionExtensions.TryConfigure(this Microsoft.Extensions.DependencyInjection.IServiceCollection! services, System.Action! configureOptions) -> Microsoft.Extensions.DependencyInjection.IServiceCollection! -UiPath.Caching.InMemoryRedisCacheOptions.BroadcastEnable.get -> bool -UiPath.Caching.InMemoryRedisCacheOptions.BroadcastEnable.set -> void diff --git a/src/UiPath.Caching/PublicAPI.Unshipped.txt b/src/UiPath.Caching/PublicAPI.Unshipped.txt index 00628a2..a6fb3b2 100644 --- a/src/UiPath.Caching/PublicAPI.Unshipped.txt +++ b/src/UiPath.Caching/PublicAPI.Unshipped.txt @@ -14,7 +14,13 @@ UiPath.Caching.Distributed.UiPathDistributedCacheOptions.RedisKeyDifferentiator. UiPath.Caching.Distributed.UiPathDistributedCacheOptions.RedisKeyStrategyFactory.get -> UiPath.Caching.Redis.IRedisKeyStrategyFactory? UiPath.Caching.Distributed.UiPathDistributedCacheOptions.RedisKeyStrategyFactory.set -> void UiPath.Caching.Distributed.UiPathDistributedCacheOptions.UiPathDistributedCacheOptions() -> void +UiPath.Caching.Redis.RedisCacheBase.CallerDeadline(System.DateTimeOffset expiration, string? paramName = null) -> System.DateTimeOffset +UiPath.Caching.Redis.RedisCacheBase.CallerDuration(System.DateTimeOffset expiration, string? paramName = null) -> System.TimeSpan +UiPath.Caching.Redis.RedisCacheBase.OptionsDeadline(System.DateTimeOffset? expireTime, System.TimeSpan? timeToLive, UiPath.Caching.CachePolicy? policy) -> System.DateTimeOffset +UiPath.Caching.Redis.RedisCacheBase.PolicyDeadline(UiPath.Caching.CachePolicy? policy) -> System.DateTimeOffset +UiPath.Caching.Redis.RedisCacheBase.PolicyDuration(UiPath.Caching.CachePolicy? policy) -> System.TimeSpan UiPath.Caching.Redis.RedisCacheOptions.AwaitRefresh.get -> bool UiPath.Caching.Redis.RedisCacheOptions.AwaitRefresh.set -> void const UiPath.Caching.Config.DistributedCacheCollectionExtensions.DistributedCacheServiceKey = "UiPath.Caching.Distributed" -> string! static UiPath.Caching.Config.DistributedCacheCollectionExtensions.AddDistributedCache(this UiPath.Caching.Config.ICachingBuilder! builder, string! providerName, System.Action? configure = null) -> UiPath.Caching.Config.ICachingBuilder! +static UiPath.Caching.Redis.RedisCacheBase.CallerDuration(System.TimeSpan expiration, string? paramName = null) -> System.TimeSpan diff --git a/src/UiPath.Caching/Redis/RedisCache.cs b/src/UiPath.Caching/Redis/RedisCache.cs index 0cabf6a..ff48b8c 100644 --- a/src/UiPath.Caching/Redis/RedisCache.cs +++ b/src/UiPath.Caching/Redis/RedisCache.cs @@ -72,19 +72,21 @@ public RedisCache( } public ValueTask GetOrAddAsync(CacheKey cacheKey, Func> generator, CachePolicy? policy, CancellationToken token = default) => - GetOrAddAsync(cacheKey, generator, expiration: (TimeSpan?)null, policy, token); + GetOrAddCoreAsync(cacheKey, generator, PolicyDuration(policy), policy, token); - public ValueTask GetOrAddAsync(CacheKey cacheKey, Func> generator, DateTimeOffset? expiration, CachePolicy? policy, CancellationToken token = default) => - GetOrAddAsync(cacheKey, generator, expiration is { } d ? d.Subtract(Clock.UtcNow) : (TimeSpan?)null, policy, token); + public ValueTask GetOrAddAsync(CacheKey cacheKey, Func> generator, DateTimeOffset expiration, CachePolicy? policy, CancellationToken token = default) => + GetOrAddCoreAsync(cacheKey, generator, CallerDuration(expiration), policy, token); - public ValueTask GetOrAddAsync(CacheKey cacheKey, Func> generator, TimeSpan? expiration, CachePolicy? policy, CancellationToken token = default) + public ValueTask GetOrAddAsync(CacheKey cacheKey, Func> generator, TimeSpan expiration, CachePolicy? policy, CancellationToken token = default) => + GetOrAddCoreAsync(cacheKey, generator, CallerDuration(expiration), policy, token); + + private ValueTask GetOrAddCoreAsync(CacheKey cacheKey, Func> generator, TimeSpan duration, CachePolicy? policy, CancellationToken token) { NotCacheableException.ThrowIfNotCacheable(); ArgumentNullException.ThrowIfNull(generator); var redisKey = ToRedisKey(cacheKey, token); - var effectiveExpiration = ResolveExpiration(expiration, policy); var wrappedGenerator = WrapWithFactoryTimeout(generator, (policy ?? DefaultPolicy)?.FactoryTimeout, cacheKey); - return GetOrAddInternalAsync(redisKey, wrappedGenerator, Clock.ToTimeSpan(effectiveExpiration), token); + return GetOrAddInternalAsync(redisKey, wrappedGenerator, duration, token); } /// @@ -97,7 +99,7 @@ public RedisCache( } /// - public ValueTask[]> GetOrAddAsync(KeyValuePair[] entries, Func[]>> generator, TimeSpan? expiration, CachePolicy? policy, CancellationToken token = default) + public ValueTask[]> GetOrAddAsync(KeyValuePair[] entries, Func[]>> generator, TimeSpan expiration, CachePolicy? policy, CancellationToken token = default) where TState : notnull { ArgumentNullException.ThrowIfNull(generator); @@ -106,7 +108,7 @@ public RedisCache( } /// - public ValueTask[]> GetOrAddAsync(KeyValuePair[] entries, Func[]>> generator, DateTimeOffset? expiration, CachePolicy? policy, CancellationToken token = default) + public ValueTask[]> GetOrAddAsync(KeyValuePair[] entries, Func[]>> generator, DateTimeOffset expiration, CachePolicy? policy, CancellationToken token = default) where TState : notnull { ArgumentNullException.ThrowIfNull(generator); @@ -143,16 +145,18 @@ public RedisCache( } public ValueTask RefreshAsync(CacheKey cacheKey, CachePolicy? policy, CancellationToken token = default) => - RefreshAsync(cacheKey, expiration: (TimeSpan?)null, policy, token); + RefreshCoreAsync(cacheKey, PolicyDeadline(policy), token); + + public ValueTask RefreshAsync(CacheKey cacheKey, TimeSpan expiration, CachePolicy? policy, CancellationToken token = default) => + RefreshCoreAsync(cacheKey, Clock.UtcNow.Add(CallerDuration(expiration)), token); - public ValueTask RefreshAsync(CacheKey cacheKey, TimeSpan? expiration, CachePolicy? policy, CancellationToken token = default) => - RefreshAsync(cacheKey, Clock.ToDateTimeOffset(ResolveExpiration(expiration, policy)), policy, token); + public ValueTask RefreshAsync(CacheKey cacheKey, DateTimeOffset expiration, CachePolicy? policy, CancellationToken token = default) => + RefreshCoreAsync(cacheKey, CallerDeadline(expiration), token); - public async ValueTask RefreshAsync(CacheKey cacheKey, DateTimeOffset? expiration, CachePolicy? policy, CancellationToken token = default) + private async ValueTask RefreshCoreAsync(CacheKey cacheKey, DateTimeOffset expiration, CancellationToken token) { NotCacheableException.ThrowIfNotCacheable(); var redisKey = ToRedisKey(cacheKey, token); - expiration = ResolveExpiration(expiration, policy); LogRefreshingKey(redisKey, expiration); var ret = false; @@ -172,7 +176,7 @@ public async ValueTask RefreshAsync(CacheKey cacheKey, DateTimeOffset? ret = await _write.ExecuteAsync(async token => { token.ThrowIfCancellationRequested(); - return await Database.KeyExpireAsync(redisKey, expiration.Value.UtcDateTime, RefreshFlags).ConfigureAwait(false); + return await Database.KeyExpireAsync(redisKey, expiration.UtcDateTime, RefreshFlags).ConfigureAwait(false); }, default, token).ConfigureAwait(false); } operation.Stop(); @@ -202,57 +206,48 @@ public ValueTask RemoveAsync(CacheKey[] cacheKey, CancellationToken tok } public ValueTask SetAsync(CacheKey cacheKey, T? value, CachePolicy? policy, CancellationToken token = default) => - SetAsync(cacheKey, value, expiration: (TimeSpan?)null, policy, token); + SetCoreAsync(cacheKey, value, PolicyDuration(policy), token); - public ValueTask SetAsync(CacheKey cacheKey, T? value, TimeSpan? expiration, CachePolicy? policy, CancellationToken token = default) - { - NotCacheableException.ThrowIfNotCacheable(); - var effective = ResolveExpiration(expiration, policy); - return SetInternalAsync(ToRedisKey(cacheKey, token), value, Clock.ToTimeSpan(effective), token); - } + public ValueTask SetAsync(CacheKey cacheKey, T? value, TimeSpan expiration, CachePolicy? policy, CancellationToken token = default) => + SetCoreAsync(cacheKey, value, CallerDuration(expiration), token); - public ValueTask SetAsync(CacheKey cacheKey, T? value, DateTimeOffset? expiration, CachePolicy? policy, CancellationToken token = default) - { - NotCacheableException.ThrowIfNotCacheable(); - var effective = ResolveExpiration(expiration, policy); - return SetInternalAsync(ToRedisKey(cacheKey, token), value, Clock.ToTimeSpan(effective), token); - } + public ValueTask SetAsync(CacheKey cacheKey, T? value, DateTimeOffset expiration, CachePolicy? policy, CancellationToken token = default) => + SetCoreAsync(cacheKey, value, CallerDuration(expiration), token); - public ValueTask SetAsync(KeyValuePair[] keyValues, CachePolicy? policy, CancellationToken token = default) + private ValueTask SetCoreAsync(CacheKey cacheKey, T? value, TimeSpan duration, CancellationToken token) { NotCacheableException.ThrowIfNotCacheable(); - return SetAsync(keyValues, expiration: (TimeSpan?)null, policy, token); + return SetInternalAsync(ToRedisKey(cacheKey, token), value, duration, token); } - public ValueTask SetAsync(KeyValuePair[] keyValues, TimeSpan? expiration, CachePolicy? policy, CancellationToken token = default) - { - NotCacheableException.ThrowIfNotCacheable(); - var effective = ResolveExpiration(expiration, policy); - return SetInternalAsync(keyValues, Clock.ToTimeSpan(effective), token); - } + public ValueTask SetAsync(KeyValuePair[] keyValues, CachePolicy? policy, CancellationToken token = default) => + SetCoreAsync(keyValues, PolicyDuration(policy), token); + + public ValueTask SetAsync(KeyValuePair[] keyValues, TimeSpan expiration, CachePolicy? policy, CancellationToken token = default) => + SetCoreAsync(keyValues, CallerDuration(expiration), token); + + public ValueTask SetAsync(KeyValuePair[] keyValues, DateTimeOffset expiration, CachePolicy? policy, CancellationToken token = default) => + SetCoreAsync(keyValues, CallerDuration(expiration), token); - public ValueTask SetAsync(KeyValuePair[] keyValues, DateTimeOffset? expiration, CachePolicy? policy, CancellationToken token = default) + private ValueTask SetCoreAsync(KeyValuePair[] keyValues, TimeSpan duration, CancellationToken token) { NotCacheableException.ThrowIfNotCacheable(); - var effective = ResolveExpiration(expiration, policy); - return SetInternalAsync(keyValues, Clock.ToTimeSpan(effective), token); + return SetInternalAsync(keyValues, duration, token); } public ValueTask TryAddAsync(CacheKey cacheKey, T? value, CachePolicy? policy, CancellationToken token = default) => - TryAddAsync(cacheKey, value, expiration: (TimeSpan?)null, policy, token); + TryAddCoreAsync(cacheKey, value, PolicyDuration(policy), token); - public ValueTask TryAddAsync(CacheKey cacheKey, T? value, TimeSpan? expiration, CachePolicy? policy, CancellationToken token = default) - { - NotCacheableException.ThrowIfNotCacheable(); - var effective = ResolveExpiration(expiration, policy); - return TryAddInternalAsync(ToRedisKey(cacheKey, token), value, Clock.ToTimeSpan(effective), token); - } + public ValueTask TryAddAsync(CacheKey cacheKey, T? value, TimeSpan expiration, CachePolicy? policy, CancellationToken token = default) => + TryAddCoreAsync(cacheKey, value, CallerDuration(expiration), token); + + public ValueTask TryAddAsync(CacheKey cacheKey, T? value, DateTimeOffset expiration, CachePolicy? policy, CancellationToken token = default) => + TryAddCoreAsync(cacheKey, value, CallerDuration(expiration), token); - public ValueTask TryAddAsync(CacheKey cacheKey, T? value, DateTimeOffset? expiration, CachePolicy? policy, CancellationToken token = default) + private ValueTask TryAddCoreAsync(CacheKey cacheKey, T? value, TimeSpan duration, CancellationToken token) { NotCacheableException.ThrowIfNotCacheable(); - var effective = ResolveExpiration(expiration, policy); - return TryAddInternalAsync(ToRedisKey(cacheKey, token), value, Clock.ToTimeSpan(effective), token); + return TryAddInternalAsync(ToRedisKey(cacheKey, token), value, duration, token); } public async ValueTask ContainsAsync(CacheKey cacheKey, CancellationToken token = default) diff --git a/src/UiPath.Caching/Redis/RedisCacheBase.cs b/src/UiPath.Caching/Redis/RedisCacheBase.cs index 0154301..0710b57 100644 --- a/src/UiPath.Caching/Redis/RedisCacheBase.cs +++ b/src/UiPath.Caching/Redis/RedisCacheBase.cs @@ -1,3 +1,4 @@ +using System.Runtime.CompilerServices; using UiPath.Caching.Config; using UiPath.Caching.Telemetry; @@ -53,11 +54,42 @@ protected void TrackRead(ITelemetryOperation operation, bool hit, RedisKey key) protected CacheClock Clock { get; } - protected TimeSpan? ResolveExpiration(TimeSpan? expiration, CachePolicy? policy) => - expiration ?? policy?.DistributedExpiration ?? DefaultExpiration; - - protected DateTimeOffset ResolveExpiration(DateTimeOffset? expiration, CachePolicy? policy) => - expiration ?? Clock.ToDateTimeOffset(policy?.DistributedExpiration ?? DefaultExpiration); + /// + /// Write duration for a call that carried no expiration: the policy's L2 TTL, then the + /// cache default, then for "no TTL". + /// + protected TimeSpan PolicyDuration(CachePolicy? policy) => + Clock.ToTimeSpan(policy?.DistributedExpiration ?? DefaultExpiration); + + /// + /// Write deadline for a call that carried no expiration, resolved the same way as + /// and yielding for "no TTL". + /// + protected DateTimeOffset PolicyDeadline(CachePolicy? policy) => + Clock.ToDateTimeOffset(policy?.DistributedExpiration ?? DefaultExpiration); + + /// + /// Write deadline carried by an entry-options object. keeps + /// its lifetime fields nullable — an options object is the one seam where null still + /// means "inherit" — so this resolves ExpireTime, then TimeToLive, then the policy + /// and cache defaults. + /// + protected DateTimeOffset OptionsDeadline(DateTimeOffset? expireTime, TimeSpan? timeToLive, CachePolicy? policy) => + expireTime.HasValue + ? Clock.ToDateTimeOffset(expireTime) + : Clock.ToDateTimeOffset(timeToLive ?? policy?.DistributedExpiration ?? DefaultExpiration); + + /// Validates a caller-supplied duration. + protected static TimeSpan CallerDuration(TimeSpan expiration, [CallerArgumentExpression(nameof(expiration))] string? paramName = null) => + CacheExpiration.ThrowIfNotPositive(expiration, paramName); + + /// Validates a caller-supplied deadline and turns it into a duration from the cache's now. + protected TimeSpan CallerDuration(DateTimeOffset expiration, [CallerArgumentExpression(nameof(expiration))] string? paramName = null) => + CacheExpiration.ToDuration(expiration, Clock.UtcNow, paramName); + + /// Validates a caller-supplied deadline. + protected DateTimeOffset CallerDeadline(DateTimeOffset expiration, [CallerArgumentExpression(nameof(expiration))] string? paramName = null) => + CacheExpiration.ThrowIfNotFuture(expiration, Clock.UtcNow, paramName); public event EventHandler? OnConnectionFailed { diff --git a/src/UiPath.Caching/Redis/RedisHashCache.cs b/src/UiPath.Caching/Redis/RedisHashCache.cs index 86dc1f8..795af39 100644 --- a/src/UiPath.Caching/Redis/RedisHashCache.cs +++ b/src/UiPath.Caching/Redis/RedisHashCache.cs @@ -72,15 +72,18 @@ public RedisHashCache( } public ValueTask> GetOrAddAsync(CacheKey cacheKey, Func>> generator, CachePolicy? policy, CancellationToken token = default) => - GetOrAddAsync(cacheKey, generator, expiration: (TimeSpan?)null, policy, token); + GetOrAddCoreAsync(cacheKey, generator, PolicyDeadline(policy), HashCacheSetOption.KeyReplace, policy, token); - public ValueTask> GetOrAddAsync(CacheKey cacheKey, Func>> generator, TimeSpan? expiration, CachePolicy? policy, CancellationToken token = default) => - GetOrAddAsync(cacheKey, generator, Clock.ToDateTimeOffset(ResolveExpiration(expiration, policy)), HashCacheSetOption.KeyReplace, policy, token); + public ValueTask> GetOrAddAsync(CacheKey cacheKey, Func>> generator, TimeSpan expiration, CachePolicy? policy, CancellationToken token = default) => + GetOrAddCoreAsync(cacheKey, generator, Clock.UtcNow.Add(CallerDuration(expiration)), HashCacheSetOption.KeyReplace, policy, token); - public ValueTask> GetOrAddAsync(CacheKey cacheKey, Func>> generator, DateTimeOffset? expiration, CachePolicy? policy, CancellationToken token = default) => - GetOrAddAsync(cacheKey, generator, expiration, HashCacheSetOption.KeyReplace, policy, token); + public ValueTask> GetOrAddAsync(CacheKey cacheKey, Func>> generator, DateTimeOffset expiration, CachePolicy? policy, CancellationToken token = default) => + GetOrAddCoreAsync(cacheKey, generator, CallerDeadline(expiration), HashCacheSetOption.KeyReplace, policy, token); - public async ValueTask> GetOrAddAsync(CacheKey cacheKey, Func>> generator, DateTimeOffset? expiration, HashCacheSetOption? setOption, CachePolicy? policy, CancellationToken token = default) + public ValueTask> GetOrAddAsync(CacheKey cacheKey, Func>> generator, DateTimeOffset expiration, HashCacheSetOption? setOption, CachePolicy? policy, CancellationToken token = default) => + GetOrAddCoreAsync(cacheKey, generator, CallerDeadline(expiration), setOption ?? HashCacheSetOption.KeyReplace, policy, token); + + private async ValueTask> GetOrAddCoreAsync(CacheKey cacheKey, Func>> generator, DateTimeOffset effectiveExpiration, HashCacheSetOption setOption, CachePolicy? policy, CancellationToken token) { NotCacheableException.ThrowIfNotCacheable(); ArgumentNullException.ThrowIfNull(generator); @@ -91,17 +94,16 @@ public RedisHashCache( } LogCacheMissed(cacheKey); - var effectiveExpiration = ResolveExpiration(expiration, policy); var wrappedGenerator = WrapWithFactoryTimeout(generator, (policy ?? DefaultPolicy)?.FactoryTimeout, cacheKey); var ret = await wrappedGenerator(token).ConfigureAwait(false); if (ret.Count > 0) { - var options = new HashCacheEntryOptions(effectiveExpiration, default, default, setOption ?? HashCacheSetOption.KeyReplace); + var options = new HashCacheEntryOptions(effectiveExpiration, default, default, setOption); await SetAsync(cacheKey, ret, options, policy, token).ConfigureAwait(false); } else if (_cacheNullValues) { - var options = new HashCacheEntryOptions(effectiveExpiration, default, default, setOption ?? HashCacheSetOption.KeyReplace); + var options = new HashCacheEntryOptions(effectiveExpiration, default, default, setOption); await SetEmptyMarkerAsync(cacheKey, options, token).ConfigureAwait(false); } else @@ -225,16 +227,18 @@ public async ValueTask ContainsAsync(CacheKey cacheKey, CancellationTok } public ValueTask RefreshAsync(CacheKey cacheKey, CachePolicy? policy, CancellationToken token = default) => - RefreshAsync(cacheKey, expiration: (TimeSpan?)null, policy, token); + RefreshCoreAsync(cacheKey, PolicyDeadline(policy), token); + + public ValueTask RefreshAsync(CacheKey cacheKey, TimeSpan expiration, CachePolicy? policy, CancellationToken token = default) => + RefreshCoreAsync(cacheKey, Clock.UtcNow.Add(CallerDuration(expiration)), token); - public ValueTask RefreshAsync(CacheKey cacheKey, TimeSpan? expiration, CachePolicy? policy, CancellationToken token = default) => - RefreshAsync(cacheKey, Clock.ToDateTimeOffset(ResolveExpiration(expiration, policy)), policy, token); + public ValueTask RefreshAsync(CacheKey cacheKey, DateTimeOffset expiration, CachePolicy? policy, CancellationToken token = default) => + RefreshCoreAsync(cacheKey, CallerDeadline(expiration), token); - public async ValueTask RefreshAsync(CacheKey cacheKey, DateTimeOffset? expiration, CachePolicy? policy, CancellationToken token = default) + private async ValueTask RefreshCoreAsync(CacheKey cacheKey, DateTimeOffset localExpiration, CancellationToken token) { NotCacheableException.ThrowIfNotCacheable(); var redisKey = ToRedisKey(cacheKey, token); - var localExpiration = ResolveExpiration(expiration, policy); LogRefreshingKey(redisKey, localExpiration); var ret = false; var operation = StartOperation(); @@ -270,10 +274,7 @@ public async ValueTask RefreshAsync(CacheKey cacheKey, HashCacheEntryOp { NotCacheableException.ThrowIfNotCacheable(); var redisKey = ToRedisKey(cacheKey, token); - var resolvedTtl = ResolveExpiration(options.TimeToLive, policy); - var expiration = options.ExpireTime.HasValue - ? Clock.ToDateTimeOffset(options.ExpireTime) - : Clock.ToDateTimeOffset(resolvedTtl); + var expiration = OptionsDeadline(options.ExpireTime, options.TimeToLive, policy); var now = Clock.UtcNow; var ret = false; var operation = StartOperation(); @@ -380,17 +381,19 @@ public async ValueTask RemoveAsync(CacheKey cacheKey, CancellationToken } public ValueTask SetAsync(CacheKey cacheKey, IDictionary values, CachePolicy? policy, CancellationToken token = default) => - SetAsync(cacheKey, values, expiration: (TimeSpan?)null, policy, token); + SetCoreAsync(cacheKey, values, PolicyDeadline(policy), token); - public ValueTask SetAsync(CacheKey cacheKey, IDictionary values, TimeSpan? expiration, CachePolicy? policy, CancellationToken token = default) => - SetAsync(cacheKey, values, Clock.ToDateTimeOffset(ResolveExpiration(expiration, policy)), policy, token); + public ValueTask SetAsync(CacheKey cacheKey, IDictionary values, TimeSpan expiration, CachePolicy? policy, CancellationToken token = default) => + SetCoreAsync(cacheKey, values, Clock.UtcNow.Add(CallerDuration(expiration)), token); - public ValueTask SetAsync(CacheKey cacheKey, IDictionary values, DateTimeOffset? expiration, CachePolicy? policy, CancellationToken token = default) + public ValueTask SetAsync(CacheKey cacheKey, IDictionary values, DateTimeOffset expiration, CachePolicy? policy, CancellationToken token = default) => + SetCoreAsync(cacheKey, values, CallerDeadline(expiration), token); + + private ValueTask SetCoreAsync(CacheKey cacheKey, IDictionary values, DateTimeOffset effective, CancellationToken token) { NotCacheableException.ThrowIfNotCacheable(); ValidateForWrite(values); var redisKey = ToRedisKey(cacheKey, token); - var effective = ResolveExpiration(expiration, policy); var hashEntries = new HashEntry[values.Count]; var i = 0; foreach (var kv in values) @@ -417,10 +420,7 @@ public ValueTask SetAsync(CacheKey cacheKey, IDictionary va entries[i] = new HashEntry(KnownFieldNames.MetadataKey, _serializer.Serialize(options.Metadata)); } - var resolvedTtl = ResolveExpiration(options.TimeToLive, policy); - var expiration = options.ExpireTime.HasValue - ? Clock.ToDateTimeOffset(options.ExpireTime) - : Clock.ToDateTimeOffset(resolvedTtl); + var expiration = OptionsDeadline(options.ExpireTime, options.TimeToLive, policy); var setOption = values.Count == 0 && _cacheNullValues ? HashCacheSetOption.KeyReplace : options.SetOption; return SetInnerAsync(redisKey, entries, setOption, expiration, token); diff --git a/tests/UiPath.Caching.Tests/CacheExpirationTests.cs b/tests/UiPath.Caching.Tests/CacheExpirationTests.cs new file mode 100644 index 0000000..aa2abbf --- /dev/null +++ b/tests/UiPath.Caching.Tests/CacheExpirationTests.cs @@ -0,0 +1,207 @@ +using Microsoft.Extensions.Logging.Abstractions; +using UiPath.Caching.Config; +using UiPath.Caching.Locking; +using UiPath.Caching.Telemetry; + +namespace UiPath.Caching.Tests; + +/// +/// The guard itself. covers it reaching the write surface. +/// +public class CacheExpirationTests +{ + [Theory] + [InlineData(0)] + [InlineData(-1)] + [InlineData(-3600)] + public void ThrowIfNotPositive_rejects_a_non_positive_duration(int seconds) + { + var act = () => CacheExpiration.ThrowIfNotPositive(TimeSpan.FromSeconds(seconds)); + + act.Should().Throw(); + } + + [Fact] + public void ThrowIfNotPositive_returns_the_value_it_accepts() + { + CacheExpiration.ThrowIfNotPositive(TimeSpan.FromTicks(1)).Should().Be(TimeSpan.FromTicks(1)); + CacheExpiration.ThrowIfNotPositive(TimeSpan.MaxValue).Should().Be(TimeSpan.MaxValue); + } + + [Fact] + public void ThrowIfNotPositive_names_the_parameter_it_was_given() + { + var expiration = TimeSpan.Zero; + + var act = () => CacheExpiration.ThrowIfNotPositive(expiration); + + act.Should().Throw().And.ParamName.Should().Be(nameof(expiration)); + } + + [Fact] + public void ThrowIfNotFuture_rejects_now_and_the_past() + { + var now = DateTimeOffset.UtcNow; + + var atNow = () => CacheExpiration.ThrowIfNotFuture(now, now); + var beforeNow = () => CacheExpiration.ThrowIfNotFuture(now.AddTicks(-1), now); + + atNow.Should().Throw(); + beforeNow.Should().Throw(); + } + + /// + /// is how the providers spell "no TTL", so the guard must + /// let it through rather than treating the sentinel as a bad argument. + /// + [Fact] + public void ThrowIfNotFuture_accepts_the_unbounded_sentinel() + { + var now = DateTimeOffset.UtcNow; + + CacheExpiration.ThrowIfNotFuture(DateTimeOffset.MaxValue, now).Should().Be(DateTimeOffset.MaxValue); + CacheExpiration.ThrowIfNotFuture(now.AddTicks(1), now).Should().Be(now.AddTicks(1)); + } + + [Fact] + public void ToDuration_measures_the_deadline_from_now() + { + var now = DateTimeOffset.UtcNow; + + CacheExpiration.ToDuration(now.AddMinutes(5), now).Should().Be(TimeSpan.FromMinutes(5)); + } + + [Fact] + public void ToDuration_rejects_a_deadline_that_has_passed() + { + var now = DateTimeOffset.UtcNow; + + var act = () => CacheExpiration.ToDuration(now.AddMinutes(-5), now); + + act.Should().Throw(); + } +} + +/// +/// The guard on the write surface, exercised through a real in-memory cache so what is covered is +/// the contract rather than one implementation's plumbing. Every write overload that takes an +/// expiration is here, because the point of dropping the nullable is that the rejection is uniform. +/// +public class CacheExpirationGuardTests +{ + private static CancellationToken Ct => TestContext.Current.CancellationToken; + + private static readonly TimeSpan[] NonPositive = [TimeSpan.Zero, TimeSpan.FromMinutes(-5)]; + + private static MultilayerCache CreateSut() + { + var options = new InMemoryCacheOptions(); + var cacheOptions = new CacheOptions { AppShortName = "test" }; + return new MultilayerCache( + KnownCacheProviderNames.InMemory, + NullCache.Instance, + new MemoryCacheFactory(null, NullLoggerFactory.Instance), + NullChangeTokenFactory.Instance, + NullTopicFactory.Instance, + NullCacheEventFactory.Instance, + NullTelemetryProvider.Instance, + options, + options, + cacheOptions, + localLock: new AsyncKeyedLocalLock(Options.Create(cacheOptions)), + distributedLock: NullDistributedLock.Instance, + policyFactory: NullCachePolicyFactory.Instance, + logger: NullLogger.Instance); + } + + private static DateTimeOffset Past => DateTimeOffset.UtcNow.AddMinutes(-5); + + private static async Task Rejects(Func write) + { + (await write.Should().ThrowAsync()).And.ParamName.Should().Be("expiration"); + } + + [Fact] + public async Task SetAsync_rejects_a_non_positive_duration() + { + using var sut = CreateSut(); + + foreach (var duration in NonPositive) + { + await Rejects(async () => await sut.SetAsync("k", "v", duration, policy: null, Ct)); + } + + (await sut.GetAsync("k", policy: null, token: Ct)).Should().BeNull(); + } + + [Fact] + public async Task SetAsync_rejects_a_deadline_that_has_passed() + { + using var sut = CreateSut(); + + await Rejects(async () => await sut.SetAsync("k", "v", Past, policy: null, Ct)); + + (await sut.GetAsync("k", policy: null, token: Ct)).Should().BeNull(); + } + + [Fact] + public async Task Batch_SetAsync_rejects_a_bad_expiration() + { + using var sut = CreateSut(); + KeyValuePair[] pairs = [new("k", "v")]; + + await Rejects(async () => await sut.SetAsync(pairs, TimeSpan.Zero, policy: null, Ct)); + await Rejects(async () => await sut.SetAsync(pairs, Past, policy: null, Ct)); + + (await sut.GetAsync("k", policy: null, token: Ct)).Should().BeNull(); + } + + [Fact] + public async Task RefreshAsync_rejects_a_bad_expiration() + { + using var sut = CreateSut(); + + await Rejects(async () => await sut.RefreshAsync("k", TimeSpan.Zero, policy: null, Ct)); + await Rejects(async () => await sut.RefreshAsync("k", Past, policy: null, Ct)); + } + + /// The generator must not run either — a bad lifetime is caught before any work. + [Fact] + public async Task GetOrAddAsync_rejects_a_bad_expiration_without_calling_the_generator() + { + using var sut = CreateSut(); + var called = false; + Func> generator = _ => + { + called = true; + return Task.FromResult("v"); + }; + + await Rejects(async () => await sut.GetOrAddAsync("k", generator, TimeSpan.Zero, policy: null, Ct)); + await Rejects(async () => await sut.GetOrAddAsync("k", generator, Past, policy: null, Ct)); + + called.Should().BeFalse(); + } + + [Fact] + public async Task Batch_GetOrAddAsync_rejects_a_bad_expiration() + { + using var sut = CreateSut(); + KeyValuePair[] entries = [new("k", "s")]; + + await Rejects(async () => await sut.GetOrAddAsync( + entries, (_, _) => Task.FromResult[]>([new("s", "v")]), TimeSpan.Zero, policy: null, Ct)); + await Rejects(async () => await sut.GetOrAddAsync( + entries, (_, _) => Task.FromResult[]>([new("s", "v")]), Past, policy: null, Ct)); + } + + /// The overload without an expiration is the supported way to ask for the policy default. + [Fact] + public async Task Omitting_the_expiration_still_writes() + { + using var sut = CreateSut(); + + (await sut.SetAsync("k", "v", policy: null, Ct)).Should().BeTrue(); + (await sut.GetAsync("k", policy: null, token: Ct)).Should().Be("v"); + } +} diff --git a/tests/UiPath.Caching.Tests/Distributed/DistributedCacheRedisIntegrationTests.cs b/tests/UiPath.Caching.Tests/Distributed/DistributedCacheRedisIntegrationTests.cs index 7f3280f..7934d90 100644 --- a/tests/UiPath.Caching.Tests/Distributed/DistributedCacheRedisIntegrationTests.cs +++ b/tests/UiPath.Caching.Tests/Distributed/DistributedCacheRedisIntegrationTests.cs @@ -162,14 +162,14 @@ public async Task Refresh_is_applied_and_reported_before_it_returns() await hash.SetAsync(cacheKey, new Dictionary { ["data"] = [1] }, TimeSpan.FromMinutes(5), null, token); - var applied = await hash.RefreshAsync(cacheKey, (DateTimeOffset?)DateTimeOffset.UtcNow.AddMinutes(30), null, token); + var applied = await hash.RefreshAsync(cacheKey, DateTimeOffset.UtcNow.AddMinutes(30), null, token); applied.Should().BeTrue("the reply is observed, so the result is meaningful"); (await database.KeyTimeToLiveAsync(redisKey)).Should() .BeGreaterThan(TimeSpan.FromMinutes(10), "no polling needed once the reply is awaited"); var absent = new CacheKey($"{UiPathDistributedCacheOptions.DefaultKeyPrefix}:{Unique()}", CacheKeyCasing.Sensitive); - (await hash.RefreshAsync(absent, (DateTimeOffset?)DateTimeOffset.UtcNow.AddMinutes(30), null, token)) + (await hash.RefreshAsync(absent, DateTimeOffset.UtcNow.AddMinutes(30), null, token)) .Should().BeFalse("a missing key is now distinguishable from a hit"); await hash.RemoveAsync(cacheKey, token); diff --git a/tests/UiPath.Caching.Tests/Distributed/UiPathDistributedCacheTests.cs b/tests/UiPath.Caching.Tests/Distributed/UiPathDistributedCacheTests.cs index 9bac865..d60df6d 100644 --- a/tests/UiPath.Caching.Tests/Distributed/UiPathDistributedCacheTests.cs +++ b/tests/UiPath.Caching.Tests/Distributed/UiPathDistributedCacheTests.cs @@ -105,7 +105,7 @@ await _inner.Received(1).GetAsync( Arg.Any(), Arg.Any(), Arg.Any()); await _inner.Received(1).SetAsync( Arg.Is(k => k.Name == expected), - Arg.Any>(), Arg.Any(), Arg.Any(), Arg.Any()); + Arg.Any>(), Arg.Any(), Arg.Any()); await _inner.Received(1).RemoveAsync( Arg.Is(k => k.Name == expected), Arg.Any()); } @@ -204,7 +204,7 @@ public async Task Failed_write_logs_the_key() new PrefixCacheKeyStrategy(UiPathDistributedCacheOptions.DefaultKeyPrefix), policy: null, logger, _clock); _inner.SetAsync(Arg.Any(), Arg.Any>(), - Arg.Any(), Arg.Any(), Arg.Any()).Returns(false); + Arg.Any(), Arg.Any(), Arg.Any()).Returns(false); await cache.SetAsync(key, Payload, new DistributedCacheEntryOptions(), TestContext.Current.CancellationToken); @@ -250,7 +250,7 @@ public async Task Get_returns_payload_and_slides_when_sliding_set() (await _cache.GetAsync("k", TestContext.Current.CancellationToken)).Should().Equal(Payload); await _inner.Received(1).RefreshAsync( - Arg.Any(), (DateTimeOffset?)Now.Add(sliding), Arg.Any(), Arg.Any()); + Arg.Any(), Now.Add(sliding), Arg.Any(), Arg.Any()); } [Fact] @@ -272,7 +272,7 @@ public async Task Get_slide_is_capped_by_absolute_expiration() await _cache.GetAsync("k", TestContext.Current.CancellationToken); await _inner.Received(1).RefreshAsync( - Arg.Any(), (DateTimeOffset?)absolute, Arg.Any(), Arg.Any()); + Arg.Any(), absolute, Arg.Any(), Arg.Any()); } [Fact] @@ -282,7 +282,7 @@ public async Task Get_without_sliding_does_not_refresh() (await _cache.GetAsync("k", TestContext.Current.CancellationToken)).Should().Equal(Payload); - await _inner.DidNotReceiveWithAnyArgs().RefreshAsync(default, (DateTimeOffset?)null, null, TestContext.Current.CancellationToken); + await _inner.DidNotReceiveWithAnyArgs().RefreshAsync(default, default(DateTimeOffset), null, TestContext.Current.CancellationToken); } [Fact] @@ -293,7 +293,7 @@ public async Task Absurd_sliding_window_clamps_instead_of_overflowing() (await _cache.GetAsync("k", TestContext.Current.CancellationToken)).Should().Equal(Payload); await _inner.Received(1).RefreshAsync( - Arg.Any(), (DateTimeOffset?)DateTimeOffset.MaxValue, Arg.Any(), Arg.Any()); + Arg.Any(), DateTimeOffset.MaxValue, Arg.Any(), Arg.Any()); } [Fact] @@ -312,7 +312,7 @@ public async Task Expired_absolute_entry_is_a_miss_and_is_not_removed() (await _cache.GetAsync("k", TestContext.Current.CancellationToken)).Should().BeNull(); await _inner.DidNotReceiveWithAnyArgs().RemoveAsync(default, TestContext.Current.CancellationToken); - await _inner.DidNotReceiveWithAnyArgs().RefreshAsync(default, (DateTimeOffset?)null, null, TestContext.Current.CancellationToken); + await _inner.DidNotReceiveWithAnyArgs().RefreshAsync(default, default(DateTimeOffset), null, TestContext.Current.CancellationToken); } @@ -323,7 +323,7 @@ public async Task Set_writes_payload_and_metadata_fields() IDictionary? written = null; TimeSpan? ttl = null; await _inner.SetAsync(Arg.Any(), Arg.Do>(v => written = v), - Arg.Do(t => ttl = t), Arg.Any(), Arg.Any()); + Arg.Do(t => ttl = t), Arg.Any(), Arg.Any()); await _cache.SetAsync("k", Payload, new DistributedCacheEntryOptions { SlidingExpiration = sliding }, TestContext.Current.CancellationToken); @@ -339,7 +339,7 @@ public async Task Set_with_both_uses_min_of_sliding_and_remaining_absolute() { TimeSpan? ttl = null; await _inner.SetAsync(Arg.Any(), Arg.Any>(), - Arg.Do(t => ttl = t), Arg.Any(), Arg.Any()); + Arg.Do(t => ttl = t), Arg.Any(), Arg.Any()); await _cache.SetAsync("k", Payload, new DistributedCacheEntryOptions { @@ -355,7 +355,7 @@ public async Task Set_with_relative_absolute_records_the_deadline() { IDictionary? written = null; await _inner.SetAsync(Arg.Any(), Arg.Do>(v => written = v), - Arg.Any(), Arg.Any(), Arg.Any()); + Arg.Any(), Arg.Any(), Arg.Any()); await _cache.SetAsync("k", Payload, new DistributedCacheEntryOptions { @@ -371,7 +371,7 @@ public async Task Set_with_no_expiration_uses_the_configured_default() { TimeSpan? ttl = null; await _inner.SetAsync(Arg.Any(), Arg.Any>(), - Arg.Do(t => ttl = t), Arg.Any(), Arg.Any()); + Arg.Do(t => ttl = t), Arg.Any(), Arg.Any()); var bounded = Build(new UiPathDistributedCacheOptions { DefaultEntryExpiration = TimeSpan.FromHours(2) }); await bounded.SetAsync("k", Payload, new DistributedCacheEntryOptions(), TestContext.Current.CancellationToken); @@ -382,13 +382,14 @@ await _inner.SetAsync(Arg.Any(), Arg.Any> [Fact] public async Task Set_with_no_expiration_and_no_default_defers_to_the_tier() { - TimeSpan? ttl = TimeSpan.FromDays(999); - await _inner.SetAsync(Arg.Any(), Arg.Any>(), - Arg.Do(t => ttl = t), Arg.Any(), Arg.Any()); - await _cache.SetAsync("k", Payload, new DistributedCacheEntryOptions(), TestContext.Current.CancellationToken); - ttl.Should().BeNull(); + // The write carries no expiration argument at all, which is the only way left to ask the + // tier for its own default. + await _inner.Received(1).SetAsync( + Arg.Any(), Arg.Any>(), Arg.Any(), Arg.Any()); + await _inner.DidNotReceive().SetAsync( + Arg.Any(), Arg.Any>(), Arg.Any(), Arg.Any(), Arg.Any()); } [Fact] @@ -412,7 +413,7 @@ await _inner.Received(1).GetAsync( Arg.Any(), Arg.Is(f => f != null && !f.Contains(DataField)), Arg.Any(), Arg.Any()); await _inner.Received(1).RefreshAsync( - Arg.Any(), (DateTimeOffset?)Now.Add(sliding), Arg.Any(), Arg.Any()); + Arg.Any(), Now.Add(sliding), Arg.Any(), Arg.Any()); } [Fact] @@ -440,7 +441,7 @@ await _inner.SetAsync(Arg.Any(), Arg.Do>( written.Should().NotBeNull(); written!.Should().ContainKey(SlidingExpirationField); written[DataField].Should().Equal(Payload); - await _inner.DidNotReceiveWithAnyArgs().RefreshAsync(default, (DateTimeOffset?)null, null, TestContext.Current.CancellationToken); + await _inner.DidNotReceiveWithAnyArgs().RefreshAsync(default, default(DateTimeOffset), null, TestContext.Current.CancellationToken); } @@ -456,7 +457,7 @@ public async Task Unbounded_entries_persist_instead_of_taking_a_default() { DateTimeOffset? expiration = null; await _inner.SetAsync(Arg.Any(), Arg.Any>(), - Arg.Do(e => expiration = e), Arg.Any(), Arg.Any()); + Arg.Do(e => expiration = e), Arg.Any(), Arg.Any()); var unbounded = Build(new UiPathDistributedCacheOptions { AllowUnboundedEntries = true }); await unbounded.SetAsync("k", Payload, new DistributedCacheEntryOptions(), TestContext.Current.CancellationToken); @@ -469,7 +470,7 @@ public async Task Absurd_sliding_write_clamps_the_ttl() { TimeSpan? ttl = null; await _inner.SetAsync(Arg.Any(), Arg.Any>(), - Arg.Do(t => ttl = t), Arg.Any(), Arg.Any()); + Arg.Do(t => ttl = t), Arg.Any(), Arg.Any()); await _cache.SetAsync("k", Payload, new DistributedCacheEntryOptions { SlidingExpiration = TimeSpan.MaxValue }, TestContext.Current.CancellationToken); @@ -501,7 +502,7 @@ public async Task Absurd_default_entry_expiration_is_clamped() { TimeSpan? ttl = null; await _inner.SetAsync(Arg.Any(), Arg.Any>(), - Arg.Do(t => ttl = t), Arg.Any(), Arg.Any()); + Arg.Do(t => ttl = t), Arg.Any(), Arg.Any()); var cache = Build(new UiPathDistributedCacheOptions { DefaultEntryExpiration = TimeSpan.MaxValue }); await cache.SetAsync("k", Payload, new DistributedCacheEntryOptions(), TestContext.Current.CancellationToken); @@ -629,7 +630,9 @@ public async Task Everything_a_write_produces_decodes_as_a_hit( var token = TestContext.Current.CancellationToken; IDictionary? written = null; await _inner.SetAsync(Arg.Any(), Arg.Do>(v => written = v), - Arg.Any(), Arg.Any(), Arg.Any()); + Arg.Any(), Arg.Any(), Arg.Any()); + await _inner.SetAsync(Arg.Any(), Arg.Do>(v => written = v), + Arg.Any(), Arg.Any()); await _cache.SetAsync("k", Payload, options, token); diff --git a/tests/UiPath.Caching.Tests/Fakes/DictionaryCache.cs b/tests/UiPath.Caching.Tests/Fakes/DictionaryCache.cs index 5325d78..3216fc9 100644 --- a/tests/UiPath.Caching.Tests/Fakes/DictionaryCache.cs +++ b/tests/UiPath.Caching.Tests/Fakes/DictionaryCache.cs @@ -67,25 +67,25 @@ public ValueTask SetAsync(KeyValuePair[] keyValues, Cache public ValueTask GetOrAddAsync(CacheKey cacheKey, Func> generator, CachePolicy? policy, CancellationToken token = default) => throw new NotSupportedException(); - public ValueTask GetOrAddAsync(CacheKey cacheKey, Func> generator, TimeSpan? expiration, CachePolicy? policy, CancellationToken token = default) => + public ValueTask GetOrAddAsync(CacheKey cacheKey, Func> generator, TimeSpan expiration, CachePolicy? policy, CancellationToken token = default) => throw new NotSupportedException(); - public ValueTask GetOrAddAsync(CacheKey cacheKey, Func> generator, DateTimeOffset? expiration, CachePolicy? policy, CancellationToken token = default) => + public ValueTask GetOrAddAsync(CacheKey cacheKey, Func> generator, DateTimeOffset expiration, CachePolicy? policy, CancellationToken token = default) => throw new NotSupportedException(); public ValueTask SetAsync(CacheKey cacheKey, T? value, CachePolicy? policy, CancellationToken token = default) => SetAsync([new KeyValuePair(cacheKey, value)], policy, token); - public ValueTask SetAsync(CacheKey cacheKey, T? value, TimeSpan? expiration, CachePolicy? policy, CancellationToken token = default) => + public ValueTask SetAsync(CacheKey cacheKey, T? value, TimeSpan expiration, CachePolicy? policy, CancellationToken token = default) => SetAsync([new KeyValuePair(cacheKey, value)], policy, token); - public ValueTask SetAsync(CacheKey cacheKey, T? value, DateTimeOffset? expiration, CachePolicy? policy, CancellationToken token = default) => + public ValueTask SetAsync(CacheKey cacheKey, T? value, DateTimeOffset expiration, CachePolicy? policy, CancellationToken token = default) => SetAsync([new KeyValuePair(cacheKey, value)], policy, token); - public ValueTask SetAsync(KeyValuePair[] keyValues, TimeSpan? expiration, CachePolicy? policy, CancellationToken token = default) => + public ValueTask SetAsync(KeyValuePair[] keyValues, TimeSpan expiration, CachePolicy? policy, CancellationToken token = default) => SetAsync(keyValues, policy, token); - public ValueTask SetAsync(KeyValuePair[] keyValues, DateTimeOffset? expiration, CachePolicy? policy, CancellationToken token = default) => + public ValueTask SetAsync(KeyValuePair[] keyValues, DateTimeOffset expiration, CachePolicy? policy, CancellationToken token = default) => SetAsync(keyValues, policy, token); // A real conditional add: the dictionary itself decides, so this fake can stand in for a store @@ -100,10 +100,10 @@ public ValueTask TryAddAsync(CacheKey cacheKey, T? value, CachePolicy? return ValueTask.FromResult(_store.TryAdd(cacheKey, value)); } - public ValueTask TryAddAsync(CacheKey cacheKey, T? value, TimeSpan? expiration, CachePolicy? policy, CancellationToken token = default) => + public ValueTask TryAddAsync(CacheKey cacheKey, T? value, TimeSpan expiration, CachePolicy? policy, CancellationToken token = default) => TryAddAsync(cacheKey, value, policy, token); - public ValueTask TryAddAsync(CacheKey cacheKey, T? value, DateTimeOffset? expiration, CachePolicy? policy, CancellationToken token = default) => + public ValueTask TryAddAsync(CacheKey cacheKey, T? value, DateTimeOffset expiration, CachePolicy? policy, CancellationToken token = default) => TryAddAsync(cacheKey, value, policy, token); public ValueTask RemoveAsync(CacheKey cacheKey, CancellationToken token = default) => @@ -117,9 +117,9 @@ public ValueTask RemoveAsync(CacheKey[] cacheKey, CancellationToken tok public ValueTask RefreshAsync(CacheKey cacheKey, CachePolicy? policy, CancellationToken token = default) => ValueTask.FromResult(true); - public ValueTask RefreshAsync(CacheKey cacheKey, TimeSpan? expiration, CachePolicy? policy, CancellationToken token = default) => ValueTask.FromResult(true); + public ValueTask RefreshAsync(CacheKey cacheKey, TimeSpan expiration, CachePolicy? policy, CancellationToken token = default) => ValueTask.FromResult(true); - public ValueTask RefreshAsync(CacheKey cacheKey, DateTimeOffset? expiration, CachePolicy? policy, CancellationToken token = default) => ValueTask.FromResult(true); + public ValueTask RefreshAsync(CacheKey cacheKey, DateTimeOffset expiration, CachePolicy? policy, CancellationToken token = default) => ValueTask.FromResult(true); public ValueTask ContainsAsync(CacheKey cacheKey, CancellationToken token = default) => ValueTask.FromResult(_store.ContainsKey(cacheKey)); diff --git a/tests/UiPath.Caching.Tests/InMemorySetCacheTests.cs b/tests/UiPath.Caching.Tests/InMemorySetCacheTests.cs index 1467d71..92ffa17 100644 --- a/tests/UiPath.Caching.Tests/InMemorySetCacheTests.cs +++ b/tests/UiPath.Caching.Tests/InMemorySetCacheTests.cs @@ -138,13 +138,13 @@ public async Task Remove_deletes_whole_set() } [Fact] - public async Task Add_with_past_expiration_stores_nothing() + public async Task Add_with_past_expiration_is_rejected() { var sut = CreateSut(); - var added = await sut.AddAsync("k", new[] { "a" }, TimeSpan.FromSeconds(-1), null, Ct); + var act = async () => await sut.AddAsync("k", new[] { "a" }, TimeSpan.FromSeconds(-1), null, Ct); - added.Should().Be(0); + (await act.Should().ThrowAsync()).And.ParamName.Should().Be("expiration"); (await sut.ContainsAsync("k", Ct)).Should().BeFalse(); } diff --git a/tests/UiPath.Caching.Tests/Locking/MultilayerCacheBatchGetOrAddLockTests.cs b/tests/UiPath.Caching.Tests/Locking/MultilayerCacheBatchGetOrAddLockTests.cs index 8898e72..00e68bf 100644 --- a/tests/UiPath.Caching.Tests/Locking/MultilayerCacheBatchGetOrAddLockTests.cs +++ b/tests/UiPath.Caching.Tests/Locking/MultilayerCacheBatchGetOrAddLockTests.cs @@ -237,14 +237,14 @@ public ValueTask InitializeAsync() _innerCache.GetCacheEntryAsync(Arg.Any(), Arg.Any(), Arg.Any()) .Returns>(c => Entry(c.Arg())); - _innerCache.SetAsync(Arg.Any[]>(), Arg.Any(), Arg.Any(), Arg.Any()) + _innerCache.SetAsync(Arg.Any[]>(), Arg.Any(), Arg.Any(), Arg.Any()) .Returns(c => { foreach (var pair in c.Arg[]>()!) { _stored[pair.Key] = pair.Value; } return true; }); - _innerCache.SetAsync(Arg.Any(), Arg.Any(), Arg.Any(), Arg.Any(), Arg.Any()) + _innerCache.SetAsync(Arg.Any(), Arg.Any(), Arg.Any(), Arg.Any(), Arg.Any()) .Returns(c => { _stored[c.Arg()] = c.ArgAt(1); diff --git a/tests/UiPath.Caching.Tests/Locking/MultilayerCacheGetOrAddLockTests.cs b/tests/UiPath.Caching.Tests/Locking/MultilayerCacheGetOrAddLockTests.cs index 8d51ba7..46756af 100644 --- a/tests/UiPath.Caching.Tests/Locking/MultilayerCacheGetOrAddLockTests.cs +++ b/tests/UiPath.Caching.Tests/Locking/MultilayerCacheGetOrAddLockTests.cs @@ -46,7 +46,7 @@ public async Task GetOrAddAsync_serializes_concurrent_generator_invocations_for_ string? storedValue = null; _innerCache.GetCacheEntryAsync((CacheKey)cacheKey, Arg.Any(), Arg.Any()) .Returns(_ => new TestCacheEntry { Value = storedValue }); - _innerCache.SetAsync((CacheKey)cacheKey, Arg.Any(), Arg.Any(), Arg.Any(), Arg.Any()) + _innerCache.SetAsync((CacheKey)cacheKey, Arg.Any(), Arg.Any(), Arg.Any(), Arg.Any()) .Returns(c => { storedValue = c.Arg(); return true; }); var generatorCalls = 0; diff --git a/tests/UiPath.Caching.Tests/MemoryCacheSetterTests.cs b/tests/UiPath.Caching.Tests/MemoryCacheSetterTests.cs index 1d5cadf..123f537 100644 --- a/tests/UiPath.Caching.Tests/MemoryCacheSetterTests.cs +++ b/tests/UiPath.Caching.Tests/MemoryCacheSetterTests.cs @@ -127,7 +127,7 @@ public void RefreshMetadata_swallows_NewEntry_exception_and_emits_failure_event( .Returns(token); var cacheEntity = _fixture.Create(); - cacheEntity.NewEntry(Arg.Any(), Arg.Any?>()) + cacheEntity.NewEntry(Arg.Any(), Arg.Any?>()) .Returns(_ => throw new InvalidOperationException("simulated NewEntry failure")); var x = new InternalHashCacheEntryOptions() diff --git a/tests/UiPath.Caching.Tests/MultilayerCacheBatchGetOrAddTests.cs b/tests/UiPath.Caching.Tests/MultilayerCacheBatchGetOrAddTests.cs index ff236e8..e6ecf29 100644 --- a/tests/UiPath.Caching.Tests/MultilayerCacheBatchGetOrAddTests.cs +++ b/tests/UiPath.Caching.Tests/MultilayerCacheBatchGetOrAddTests.cs @@ -274,7 +274,7 @@ public async Task Expiration_overloads_flow_through_to_the_inner_write() _innerSetCalls.Should().HaveCount(2); await _innerCache.Received(1).SetAsync( Arg.Any[]>(), - Arg.Is(d => d.HasValue && d.Value == expiration), + Arg.Is(d => d == expiration), Arg.Any(), Arg.Any()); } @@ -345,7 +345,7 @@ public ValueTask InitializeAsync() : new TestCacheEntry { Value = null, Expiration = DateTimeOffset.MinValue })) .ToArray()); - _innerCache.SetAsync(Arg.Any[]>(), Arg.Any(), Arg.Any(), Arg.Any()) + _innerCache.SetAsync(Arg.Any[]>(), Arg.Any(), Arg.Any(), Arg.Any()) .Returns(c => { var pairs = c.Arg[]>()!; diff --git a/tests/UiPath.Caching.Tests/MultilayerCacheBatchRehydrateTests.cs b/tests/UiPath.Caching.Tests/MultilayerCacheBatchRehydrateTests.cs index 57101a4..d1631c5 100644 --- a/tests/UiPath.Caching.Tests/MultilayerCacheBatchRehydrateTests.cs +++ b/tests/UiPath.Caching.Tests/MultilayerCacheBatchRehydrateTests.cs @@ -450,7 +450,7 @@ public ValueTask InitializeAsync() : new TestCacheEntry { Value = null, Expiration = DateTimeOffset.MinValue })) .ToArray()); - _innerCache.SetAsync(Arg.Any[]>(), Arg.Any(), Arg.Any(), Arg.Any()) + _innerCache.SetAsync(Arg.Any[]>(), Arg.Any(), Arg.Any(), Arg.Any()) .Returns(c => { var pairs = c.Arg[]>()!; diff --git a/tests/UiPath.Caching.Tests/MultilayerCachePerNamePolicyWiringTests.cs b/tests/UiPath.Caching.Tests/MultilayerCachePerNamePolicyWiringTests.cs index 2b47d47..e621669 100644 --- a/tests/UiPath.Caching.Tests/MultilayerCachePerNamePolicyWiringTests.cs +++ b/tests/UiPath.Caching.Tests/MultilayerCachePerNamePolicyWiringTests.cs @@ -41,7 +41,7 @@ public async Task GetOrAdd_uses_policy_DistributedExpiration_when_caller_omits_e await _innerCache.Received(1).SetAsync( _cacheKey, "v", - Arg.Is(d => d.HasValue && d.Value - DateTimeOffset.UtcNow > policyTtl - TimeSpan.FromSeconds(5) && d.Value - DateTimeOffset.UtcNow < policyTtl + TimeSpan.FromSeconds(5)), + Arg.Is(d => d - DateTimeOffset.UtcNow > policyTtl - TimeSpan.FromSeconds(5) && d - DateTimeOffset.UtcNow < policyTtl + TimeSpan.FromSeconds(5)), Arg.Any(), Arg.Any()); } @@ -62,7 +62,7 @@ public async Task GetOrAdd_caller_expiration_beats_policy_DistributedExpiration( await _innerCache.Received(1).SetAsync( _cacheKey, "v", - Arg.Is(d => d.HasValue && d.Value - DateTimeOffset.UtcNow > callerTtl - TimeSpan.FromSeconds(5) && d.Value - DateTimeOffset.UtcNow < callerTtl + TimeSpan.FromSeconds(5)), + Arg.Is(d => d - DateTimeOffset.UtcNow > callerTtl - TimeSpan.FromSeconds(5) && d - DateTimeOffset.UtcNow < callerTtl + TimeSpan.FromSeconds(5)), Arg.Any(), Arg.Any()); } @@ -87,7 +87,7 @@ public async Task GetOrAdd_picks_up_DefaultCachePolicy_when_caller_passes_null_p await _innerCache.Received(1).SetAsync( _cacheKey, "v", - Arg.Is(d => d.HasValue && d.Value - DateTimeOffset.UtcNow > defaultTtl - TimeSpan.FromSeconds(5) && d.Value - DateTimeOffset.UtcNow < defaultTtl + TimeSpan.FromSeconds(5)), + Arg.Is(d => d - DateTimeOffset.UtcNow > defaultTtl - TimeSpan.FromSeconds(5) && d - DateTimeOffset.UtcNow < defaultTtl + TimeSpan.FromSeconds(5)), Arg.Any(), Arg.Any()); } @@ -111,7 +111,7 @@ public async Task GetOrAdd_falls_back_to_cache_options_DefaultExpiration_when_po await _innerCache.Received(1).SetAsync( _cacheKey, "v", - Arg.Is(d => d.HasValue && d.Value - DateTimeOffset.UtcNow > optionsTtl - TimeSpan.FromSeconds(5) && d.Value - DateTimeOffset.UtcNow < optionsTtl + TimeSpan.FromSeconds(5)), + Arg.Is(d => d - DateTimeOffset.UtcNow > optionsTtl - TimeSpan.FromSeconds(5) && d - DateTimeOffset.UtcNow < optionsTtl + TimeSpan.FromSeconds(5)), Arg.Any(), Arg.Any()); } @@ -125,7 +125,7 @@ public async Task SetAsync_uses_DefaultCachePolicy_DistributedExpiration_when_ca _fixture.Inject(new CacheOptions { DefaultCachePolicy = defaultPolicy, AppShortName = "test" }); _sut = null; - _innerCache.SetAsync(_cacheKey, Arg.Any(), Arg.Any(), Arg.Any(), Arg.Any()) + _innerCache.SetAsync(_cacheKey, Arg.Any(), Arg.Any(), Arg.Any(), Arg.Any()) .Returns(true); _topic.PublishAsync(Arg.Any(), Arg.Any()) .Returns(_ => true); @@ -135,7 +135,7 @@ public async Task SetAsync_uses_DefaultCachePolicy_DistributedExpiration_when_ca await _innerCache.Received(1).SetAsync( _cacheKey, "v", - Arg.Is(d => d.HasValue && d.Value - DateTimeOffset.UtcNow > defaultTtl - TimeSpan.FromSeconds(5) && d.Value - DateTimeOffset.UtcNow < defaultTtl + TimeSpan.FromSeconds(5)), + Arg.Is(d => d - DateTimeOffset.UtcNow > defaultTtl - TimeSpan.FromSeconds(5) && d - DateTimeOffset.UtcNow < defaultTtl + TimeSpan.FromSeconds(5)), Arg.Any(), Arg.Any()); } @@ -144,7 +144,7 @@ await _innerCache.Received(1).SetAsync( public async Task SetAsync_falls_back_to_cache_options_DefaultExpiration_when_policy_omits_TTL() { // Default policy with no DistributedExpiration → SetAsync uses _multiLayerCacheOptions.DefaultExpiration. - _innerCache.SetAsync(_cacheKey, Arg.Any(), Arg.Any(), Arg.Any(), Arg.Any()) + _innerCache.SetAsync(_cacheKey, Arg.Any(), Arg.Any(), Arg.Any(), Arg.Any()) .Returns(true); _topic.PublishAsync(Arg.Any(), Arg.Any()) .Returns(_ => true); @@ -155,7 +155,7 @@ public async Task SetAsync_falls_back_to_cache_options_DefaultExpiration_when_po await _innerCache.Received(1).SetAsync( _cacheKey, "v", - Arg.Is(d => d.HasValue && d.Value - DateTimeOffset.UtcNow > optionsTtl - TimeSpan.FromSeconds(5) && d.Value - DateTimeOffset.UtcNow < optionsTtl + TimeSpan.FromSeconds(5)), + Arg.Is(d => d - DateTimeOffset.UtcNow > optionsTtl - TimeSpan.FromSeconds(5) && d - DateTimeOffset.UtcNow < optionsTtl + TimeSpan.FromSeconds(5)), Arg.Any(), Arg.Any()); } @@ -216,7 +216,7 @@ public async Task RefreshAsync_uses_DefaultCachePolicy_DistributedExpiration_whe _fixture.Inject(new CacheOptions { DefaultCachePolicy = defaultPolicy, AppShortName = "test" }); _sut = null; - _innerCache.RefreshAsync(_cacheKey, Arg.Any(), Arg.Any(), Arg.Any()) + _innerCache.RefreshAsync(_cacheKey, Arg.Any(), Arg.Any(), Arg.Any()) .Returns(true); _topic.PublishAsync(Arg.Any(), Arg.Any()) .Returns(_ => true); @@ -225,9 +225,8 @@ public async Task RefreshAsync_uses_DefaultCachePolicy_DistributedExpiration_whe await _innerCache.Received(1).RefreshAsync( _cacheKey, - Arg.Is(d => d.HasValue - && d.Value - DateTimeOffset.UtcNow > policyTtl - TimeSpan.FromSeconds(5) - && d.Value - DateTimeOffset.UtcNow < policyTtl + TimeSpan.FromSeconds(5)), Arg.Any(), Arg.Any()); + Arg.Is(d => d - DateTimeOffset.UtcNow > policyTtl - TimeSpan.FromSeconds(5) + && d - DateTimeOffset.UtcNow < policyTtl + TimeSpan.FromSeconds(5)), Arg.Any(), Arg.Any()); } [Fact] @@ -240,7 +239,7 @@ public async Task SetAsync_jitters_policy_derived_expiration_within_max() _fixture.Inject(new CacheOptions { DefaultCachePolicy = defaultPolicy, AppShortName = "test" }); _sut = null; - _innerCache.SetAsync(_cacheKey, Arg.Any(), Arg.Any(), Arg.Any(), Arg.Any()) + _innerCache.SetAsync(_cacheKey, Arg.Any(), Arg.Any(), Arg.Any(), Arg.Any()) .Returns(true); _topic.PublishAsync(Arg.Any(), Arg.Any()) .Returns(_ => true); @@ -250,9 +249,8 @@ public async Task SetAsync_jitters_policy_derived_expiration_within_max() await _innerCache.Received(1).SetAsync( _cacheKey, "v", - Arg.Is(d => d.HasValue - && d.Value - DateTimeOffset.UtcNow >= baseTtl - TimeSpan.FromSeconds(5) - && d.Value - DateTimeOffset.UtcNow <= baseTtl + maxJitter + TimeSpan.FromSeconds(5)), + Arg.Is(d => d - DateTimeOffset.UtcNow >= baseTtl - TimeSpan.FromSeconds(5) + && d - DateTimeOffset.UtcNow <= baseTtl + maxJitter + TimeSpan.FromSeconds(5)), Arg.Any(), Arg.Any()); } @@ -267,7 +265,7 @@ public async Task SetAsync_honors_caller_explicit_expiration_without_jitter() var callerTtl = TimeSpan.FromMinutes(2); - _innerCache.SetAsync(_cacheKey, Arg.Any(), Arg.Any(), Arg.Any(), Arg.Any()) + _innerCache.SetAsync(_cacheKey, Arg.Any(), Arg.Any(), Arg.Any(), Arg.Any()) .Returns(true); _topic.PublishAsync(Arg.Any(), Arg.Any()) .Returns(_ => true); @@ -277,9 +275,8 @@ public async Task SetAsync_honors_caller_explicit_expiration_without_jitter() await _innerCache.Received(1).SetAsync( _cacheKey, "v", - Arg.Is(d => d.HasValue - && d.Value - DateTimeOffset.UtcNow > callerTtl - TimeSpan.FromSeconds(5) - && d.Value - DateTimeOffset.UtcNow < callerTtl + TimeSpan.FromSeconds(5)), + Arg.Is(d => d - DateTimeOffset.UtcNow > callerTtl - TimeSpan.FromSeconds(5) + && d - DateTimeOffset.UtcNow < callerTtl + TimeSpan.FromSeconds(5)), Arg.Any(), Arg.Any()); } @@ -304,9 +301,8 @@ public async Task GetOrAdd_jitters_policy_derived_expiration_within_max() await _innerCache.Received(1).SetAsync( _cacheKey, "v", - Arg.Is(d => d.HasValue - && d.Value - DateTimeOffset.UtcNow >= baseTtl - TimeSpan.FromSeconds(5) - && d.Value - DateTimeOffset.UtcNow <= baseTtl + maxJitter + TimeSpan.FromSeconds(5)), + Arg.Is(d => d - DateTimeOffset.UtcNow >= baseTtl - TimeSpan.FromSeconds(5) + && d - DateTimeOffset.UtcNow <= baseTtl + maxJitter + TimeSpan.FromSeconds(5)), Arg.Any(), Arg.Any()); } @@ -320,19 +316,18 @@ public async Task SetAsync_jitters_options_DefaultExpiration_when_policy_Distrib _fixture.Inject(new CacheOptions { DefaultCachePolicy = defaultPolicy, AppShortName = "test" }); _sut = null; - _innerCache.SetAsync(_cacheKey, Arg.Any(), Arg.Any(), Arg.Any(), Arg.Any()) + _innerCache.SetAsync(_cacheKey, Arg.Any(), Arg.Any(), Arg.Any(), Arg.Any()) .Returns(true); _topic.PublishAsync(Arg.Any(), Arg.Any()) .Returns(_ => true); - await Sut.SetAsync(_cacheKey, "v", (TimeSpan?)null, policy: null, TestContext.Current.CancellationToken); + await Sut.SetAsync(_cacheKey, "v", policy: null, TestContext.Current.CancellationToken); await _innerCache.Received(1).SetAsync( _cacheKey, "v", - Arg.Is(d => d.HasValue - && d.Value - DateTimeOffset.UtcNow >= optionsTtl - TimeSpan.FromSeconds(5) - && d.Value - DateTimeOffset.UtcNow <= optionsTtl + maxJitter + TimeSpan.FromSeconds(5)), + Arg.Is(d => d - DateTimeOffset.UtcNow >= optionsTtl - TimeSpan.FromSeconds(5) + && d - DateTimeOffset.UtcNow <= optionsTtl + maxJitter + TimeSpan.FromSeconds(5)), Arg.Any(), Arg.Any()); } @@ -347,19 +342,18 @@ public async Task SetAsync_DateTimeOffset_overload_jitters_when_caller_passes_nu _fixture.Inject(new CacheOptions { DefaultCachePolicy = defaultPolicy, AppShortName = "test" }); _sut = null; - _innerCache.SetAsync(_cacheKey, Arg.Any(), Arg.Any(), Arg.Any(), Arg.Any()) + _innerCache.SetAsync(_cacheKey, Arg.Any(), Arg.Any(), Arg.Any(), Arg.Any()) .Returns(true); _topic.PublishAsync(Arg.Any(), Arg.Any()) .Returns(_ => true); - await Sut.SetAsync(_cacheKey, "v", (DateTimeOffset?)null, policy: null, TestContext.Current.CancellationToken); + await Sut.SetAsync(_cacheKey, "v", policy: null, TestContext.Current.CancellationToken); await _innerCache.Received(1).SetAsync( _cacheKey, "v", - Arg.Is(d => d.HasValue - && d.Value - DateTimeOffset.UtcNow >= baseTtl - TimeSpan.FromSeconds(5) - && d.Value - DateTimeOffset.UtcNow <= baseTtl + maxJitter + TimeSpan.FromSeconds(5)), + Arg.Is(d => d - DateTimeOffset.UtcNow >= baseTtl - TimeSpan.FromSeconds(5) + && d - DateTimeOffset.UtcNow <= baseTtl + maxJitter + TimeSpan.FromSeconds(5)), Arg.Any(), Arg.Any()); } @@ -374,7 +368,7 @@ public async Task SetAsync_bulk_KeyValuePair_overload_jitters_policy_derived_exp _fixture.Inject(new CacheOptions { DefaultCachePolicy = defaultPolicy, AppShortName = "test" }); _sut = null; - _innerCache.SetAsync(Arg.Any[]>(), Arg.Any(), Arg.Any(), Arg.Any()) + _innerCache.SetAsync(Arg.Any[]>(), Arg.Any(), Arg.Any(), Arg.Any()) .Returns(true); _topic.PublishAsync(Arg.Any(), Arg.Any()) .Returns(_ => true); @@ -384,9 +378,8 @@ public async Task SetAsync_bulk_KeyValuePair_overload_jitters_policy_derived_exp await _innerCache.Received(1).SetAsync( Arg.Any[]>(), - Arg.Is(d => d.HasValue - && d.Value - DateTimeOffset.UtcNow >= baseTtl - TimeSpan.FromSeconds(5) - && d.Value - DateTimeOffset.UtcNow <= baseTtl + maxJitter + TimeSpan.FromSeconds(5)), + Arg.Is(d => d - DateTimeOffset.UtcNow >= baseTtl - TimeSpan.FromSeconds(5) + && d - DateTimeOffset.UtcNow <= baseTtl + maxJitter + TimeSpan.FromSeconds(5)), Arg.Any(), Arg.Any()); } @@ -404,7 +397,7 @@ public async Task SetAsync_jitter_actually_varies_across_calls() var ttls = new List(); _innerCache.SetAsync(_cacheKey, Arg.Any(), - Arg.Do(d => { if (d.HasValue) { ttls.Add(d.Value - DateTimeOffset.UtcNow); } }), + Arg.Do(d => ttls.Add(d - DateTimeOffset.UtcNow)), Arg.Any(), Arg.Any()) .Returns(true); _topic.PublishAsync(Arg.Any(), Arg.Any()) @@ -428,7 +421,7 @@ public async Task SetAsync_clamps_jitter_when_base_plus_jitter_would_overflow_Da _fixture.Inject(new CacheOptions { DefaultCachePolicy = defaultPolicy, AppShortName = "test" }); _sut = null; - _innerCache.SetAsync(_cacheKey, Arg.Any(), Arg.Any(), Arg.Any(), Arg.Any()) + _innerCache.SetAsync(_cacheKey, Arg.Any(), Arg.Any(), Arg.Any(), Arg.Any()) .Returns(true); _topic.PublishAsync(Arg.Any(), Arg.Any()) .Returns(_ => true); @@ -441,7 +434,7 @@ await act.Should().NotThrowAsync( await _innerCache.Received(1).SetAsync( _cacheKey, "v", - Arg.Is(d => d.HasValue && d.Value <= DateTimeOffset.MaxValue), + Arg.Is(d => d <= DateTimeOffset.MaxValue), Arg.Any(), Arg.Any()); } diff --git a/tests/UiPath.Caching.Tests/MultilayerCacheRehydrateTests.cs b/tests/UiPath.Caching.Tests/MultilayerCacheRehydrateTests.cs index 6d0d08c..1256417 100644 --- a/tests/UiPath.Caching.Tests/MultilayerCacheRehydrateTests.cs +++ b/tests/UiPath.Caching.Tests/MultilayerCacheRehydrateTests.cs @@ -102,13 +102,13 @@ public async Task Rehydrate_writes_value_back_through_inner_cache_on_success() var acquiredLock = Substitute.For(); _distributedLock.TryAcquireAsync(Arg.Any(), Arg.Any(), Arg.Any()) .Returns(acquiredLock); - _innerCache.SetAsync(_cacheKey, Arg.Any(), Arg.Any(), Arg.Any(), Arg.Any()) + _innerCache.SetAsync(_cacheKey, Arg.Any(), Arg.Any(), Arg.Any(), Arg.Any()) .Returns(true); await Sut.GetOrAddAsync(_cacheKey, generator, RehydratePolicy(), token); await WaitForAsync(() => _innerCache.ReceivedCalls().Any(c => c.GetMethodInfo().Name == nameof(ICache.SetAsync)), TimeSpan.FromSeconds(5), token); - await _innerCache.Received(1).SetAsync(_cacheKey, "rehydrated", Arg.Any(), Arg.Any(), Arg.Any()); + await _innerCache.Received(1).SetAsync(_cacheKey, "rehydrated", Arg.Any(), Arg.Any(), Arg.Any()); await acquiredLock.Received(1).DisposeAsync(); } @@ -135,7 +135,7 @@ public async Task Rehydrate_skipped_when_distributed_lock_unavailable() await WaitForAsync(() => _distributedLock.ReceivedCalls().Any(), TimeSpan.FromSeconds(5), token); await Task.Delay(50, TestContext.Current.CancellationToken); generatorCalls.Should().Be(0); - await _innerCache.DidNotReceive().SetAsync(_cacheKey, Arg.Any(), Arg.Any(), Arg.Any(), Arg.Any()); + await _innerCache.DidNotReceive().SetAsync(_cacheKey, Arg.Any(), Arg.Any(), Arg.Any(), Arg.Any()); } [Fact] @@ -157,7 +157,7 @@ public async Task Rehydrate_does_not_release_lock_on_generator_failure() await WaitForAsync(() => _distributedLock.ReceivedCalls().Any(), TimeSpan.FromSeconds(5), token); await Task.Delay(100, TestContext.Current.CancellationToken); await acquiredLock.DidNotReceive().DisposeAsync(); - await _innerCache.DidNotReceive().SetAsync(_cacheKey, Arg.Any(), Arg.Any(), Arg.Any(), Arg.Any()); + await _innerCache.DidNotReceive().SetAsync(_cacheKey, Arg.Any(), Arg.Any(), Arg.Any(), Arg.Any()); } [Fact] @@ -199,7 +199,7 @@ public async Task Rehydrate_holds_lock_when_inner_write_returns_false() _distributedLock.TryAcquireAsync(Arg.Any(), Arg.Any(), Arg.Any()) .Returns(acquiredLock); // Inner cache returns false from SetAsync — rehydrate must NOT report success. - _innerCache.SetAsync(_cacheKey, Arg.Any(), Arg.Any(), Arg.Any(), Arg.Any()) + _innerCache.SetAsync(_cacheKey, Arg.Any(), Arg.Any(), Arg.Any(), Arg.Any()) .Returns(false); await Sut.GetOrAddAsync(_cacheKey, generator, RehydratePolicy(), token); @@ -254,7 +254,7 @@ public async Task Rehydrate_publishes_broadcast_so_other_nodes_invalidate_their_ var acquiredLock = Substitute.For(); _distributedLock.TryAcquireAsync(Arg.Any(), Arg.Any(), Arg.Any()) .Returns(acquiredLock); - _innerCache.SetAsync(_cacheKey, Arg.Any(), Arg.Any(), Arg.Any(), Arg.Any()) + _innerCache.SetAsync(_cacheKey, Arg.Any(), Arg.Any(), Arg.Any(), Arg.Any()) .Returns(true); await Sut.GetOrAddAsync(_cacheKey, generator, RehydratePolicy(), token); @@ -307,7 +307,7 @@ public async Task Rehydrate_with_factory_returning_null_preserves_original_entry var acquiredLock = Substitute.For(); _distributedLock.TryAcquireAsync(Arg.Any(), Arg.Any(), Arg.Any()) .Returns(acquiredLock); - _innerCache.SetAsync(_cacheKey, Arg.Any(), Arg.Any(), Arg.Any(), Arg.Any()) + _innerCache.SetAsync(_cacheKey, Arg.Any(), Arg.Any(), Arg.Any(), Arg.Any()) .Returns(true); await Sut.GetOrAddAsync(_cacheKey, generator, RehydratePolicy(), token); @@ -316,7 +316,7 @@ public async Task Rehydrate_with_factory_returning_null_preserves_original_entry await _innerCache.Received(1).SetAsync( _cacheKey, null, - Arg.Is(d => d.HasValue && Math.Abs((d.Value - originalDeadline).TotalSeconds) < 1), + Arg.Is(d => Math.Abs((d - originalDeadline).TotalSeconds) < 1), Arg.Any(), Arg.Any()); } diff --git a/tests/UiPath.Caching.Tests/MultilayerCacheTests.cs b/tests/UiPath.Caching.Tests/MultilayerCacheTests.cs index bb2b40d..d42244d 100644 --- a/tests/UiPath.Caching.Tests/MultilayerCacheTests.cs +++ b/tests/UiPath.Caching.Tests/MultilayerCacheTests.cs @@ -421,7 +421,7 @@ public async Task GetOrAdd_data_from_generator_default() var actual = await Sut.GetOrAddAsync(_cacheKey, generator, _fixture.Create(), token: testContextAccessor.Current.CancellationToken); generatorWasCalled.Should().BeTrue(); _memoryCache.Received(0).CreateEntry(_innerCacheKey); - await _innerCache.Received(0).SetAsync(_innerCacheKey, Arg.Any(), Arg.Any(), Arg.Any(), Arg.Any()); + await _innerCache.Received(0).SetAsync(_innerCacheKey, Arg.Any(), Arg.Any(), Arg.Any(), Arg.Any()); actual.Should().BeNull(); } @@ -517,7 +517,7 @@ public async Task Multi_set_keeps_null_entries_in_set_path_when_CacheNullValues_ { _options.CacheNullValues = true; _sut = null; - _innerCache.SetAsync(Arg.Any[]>(), Arg.Any(), Arg.Any(), Arg.Any()) + _innerCache.SetAsync(Arg.Any[]>(), Arg.Any(), Arg.Any(), Arg.Any()) .Returns(true); var pairs = new KeyValuePair[] @@ -530,7 +530,7 @@ public async Task Multi_set_keeps_null_entries_in_set_path_when_CacheNullValues_ await _innerCache.DidNotReceive().RemoveAsync(Arg.Any(), Arg.Any()); await _innerCache.Received(1).SetAsync( Arg.Is[]>(p => p != null && p.Length == 2 && p.Any(kv => kv.Value == null)), - Arg.Any(), Arg.Any(), Arg.Any()); + Arg.Any(), Arg.Any(), Arg.Any()); } [Fact] @@ -538,7 +538,7 @@ public async Task Multi_set_forwards_caller_expiration_to_inner_cache() { _options.CacheNullValues = true; _sut = null; - _innerCache.SetAsync(Arg.Any[]>(), Arg.Any(), Arg.Any(), Arg.Any()) + _innerCache.SetAsync(Arg.Any[]>(), Arg.Any(), Arg.Any(), Arg.Any()) .Returns(true); var ttl = TimeSpan.FromMinutes(7); @@ -551,7 +551,7 @@ public async Task Multi_set_forwards_caller_expiration_to_inner_cache() await _innerCache.Received(1).SetAsync( Arg.Any[]>(), - Arg.Is(exp => exp.HasValue && exp.Value > _clock.UtcNow), Arg.Any(), Arg.Any()); + Arg.Is(exp => exp > _clock.UtcNow), Arg.Any(), Arg.Any()); } [Fact] @@ -595,7 +595,7 @@ public async Task Set_value_inner_cache_throw_exception() var actual = await Sut.SetAsync(_cacheKey, value, _fixture.Create(), token: testContextAccessor.Current.CancellationToken); actual.Should().BeFalse(); - actual = await Sut.SetAsync(_cacheKey, value, _fixture.Create(), token: testContextAccessor.Current.CancellationToken); + actual = await Sut.SetAsync(_cacheKey, value, DateTimeOffset.UtcNow.AddMinutes(5), token: testContextAccessor.Current.CancellationToken); actual.Should().BeFalse(); } @@ -912,7 +912,7 @@ public async Task Refresh_value_default_expiration() [Fact] public async Task Refresh_value_TimeSpan() { - var expiration = _fixture.Create(); + var expiration = TimeSpan.FromMinutes(5); await Sut.RefreshAsync(_cacheKey, expiration, token: testContextAccessor.Current.CancellationToken); _memoryCache.Received(1).Remove(_innerCacheKey); await _innerCache.Received(1).RefreshAsync(_innerCacheKey, Arg.Any(), Arg.Any(), Arg.Any()); @@ -934,7 +934,7 @@ public async Task Refresh_value_DateTimeOffset() [InlineData(true)] public async Task Refresh_inner_cache_exception_timespan(bool eventFired) { - var expiration = _fixture.Create(); + var expiration = TimeSpan.FromMinutes(5); _innerCache.RefreshAsync(_innerCacheKey, Arg.Any(), Arg.Any(), Arg.Any()) .ThrowsAsync(new Exception()); _topic.PublishAsync(Arg.Any(), Arg.Any()) @@ -1029,7 +1029,7 @@ public async Task Read_ExpireTime_For_Key() }; _changeTokenFactory.Create(Arg.Any(), Arg.Any>(), Arg.Any(), Arg.Any()) .Returns(token); - _innerCache.SetAsync(_innerCacheKey, Arg.Any(), Arg.Any(), Arg.Any(), Arg.Any()) + _innerCache.SetAsync(_innerCacheKey, Arg.Any(), Arg.Any(), Arg.Any(), Arg.Any()) .Returns(true); var expiration = _clock.UtcNow.AddYears(1); @@ -1055,7 +1055,7 @@ public async Task Read_ExpireTimeToLive_For_Key() _changeTokenFactory.Create(Arg.Any(), Arg.Any>(), Arg.Any(), Arg.Any()) .Returns(_ => token); - _innerCache.SetAsync(_innerCacheKey, Arg.Any(), Arg.Any(), Arg.Any(), Arg.Any()) + _innerCache.SetAsync(_innerCacheKey, Arg.Any(), Arg.Any(), Arg.Any(), Arg.Any()) .Returns(true); _topic.PublishAsync(Arg.Any(), Arg.Any()) .Returns(_ => true); @@ -1077,7 +1077,7 @@ public async Task When_no_inner_cache_expire_time_use_max() _innerCache.GetCacheEntryAsync(_innerCacheKey, Arg.Any(), Arg.Any()) .Returns(new TestCacheEntry { Value = expected, Expiration = DateTimeOffset.MaxValue }); _options.DefaultExpiration = null; - _ = await Sut.GetOrAddAsync(_cacheKey, generator, expiration: default(DateTimeOffset?), token: testContextAccessor.Current.CancellationToken); + _ = await Sut.GetOrAddAsync(_cacheKey, generator, token: testContextAccessor.Current.CancellationToken); cacheEntry.AbsoluteExpiration.Should().Be(DateTimeOffset.MaxValue); } @@ -1338,7 +1338,7 @@ public async Task Multi_SetAsync_calls_inner_cache_based_on_innerCacheDisconnect _topicProvider.IsConnected.Returns(isConnected); _topic.PublishAsync(Arg.Any(), Arg.Any()) .Returns(_ => true); - _innerCache.SetAsync(Arg.Any[]>(), Arg.Any(), Arg.Any(), Arg.Any()) + _innerCache.SetAsync(Arg.Any[]>(), Arg.Any(), Arg.Any(), Arg.Any()) .Returns(true); var actual = await Sut.SetAsync(new KeyValuePair[] { new(_cacheKey, value), new(_multiKey, value) }, _fixture.Create(), policy: null, token: testContextAccessor.Current.CancellationToken); @@ -1348,12 +1348,12 @@ public async Task Multi_SetAsync_calls_inner_cache_based_on_innerCacheDisconnect if (innerCacheDisconnected) { - await _innerCache.DidNotReceive().SetAsync(Arg.Any[]>(), Arg.Any(), Arg.Any(), Arg.Any()); + await _innerCache.DidNotReceive().SetAsync(Arg.Any[]>(), Arg.Any(), Arg.Any(), Arg.Any()); await _topic.DidNotReceive().PublishAsync(Arg.Any(), Arg.Any()); } else { - await _innerCache.Received(1).SetAsync(Arg.Any[]>(), Arg.Any(), Arg.Any(), Arg.Any()); + await _innerCache.Received(1).SetAsync(Arg.Any[]>(), Arg.Any(), Arg.Any(), Arg.Any()); await _topic.Received(2).PublishAsync(Arg.Any(), Arg.Any()); } } diff --git a/tests/UiPath.Caching.Tests/MultilayerCacheTryAddTests.cs b/tests/UiPath.Caching.Tests/MultilayerCacheTryAddTests.cs index 29b4605..ba17002 100644 --- a/tests/UiPath.Caching.Tests/MultilayerCacheTryAddTests.cs +++ b/tests/UiPath.Caching.Tests/MultilayerCacheTryAddTests.cs @@ -42,14 +42,14 @@ public class MultilayerCacheTryAddTests(ITestContextAccessor testContextAccessor public async Task TryAdd_delegates_the_decision_to_the_inner_cache() { var value = _fixture.Create(); - _innerCache.TryAddAsync(_cacheKey, value, Arg.Any(), Arg.Any(), Arg.Any()) + _innerCache.TryAddAsync(_cacheKey, value, Arg.Any(), Arg.Any(), Arg.Any()) .Returns(true); _topic.PublishAsync(Arg.Any(), Arg.Any()).Returns(_ => true); var added = await Sut.TryAddAsync(_cacheKey, value, policy: null, token: Ct); added.Should().BeTrue(); - await _innerCache.Received(1).TryAddAsync(_cacheKey, value, Arg.Any(), Arg.Any(), Arg.Any()); + await _innerCache.Received(1).TryAddAsync(_cacheKey, value, Arg.Any(), Arg.Any(), Arg.Any()); _memoryCache.Received(1).CreateEntry(_cacheKey); } @@ -64,20 +64,20 @@ public async Task TryAdd_never_probes_the_local_tier_to_decide() x[1] = new TestCacheEntry { Value = _fixture.Create() }; return true; }); - _innerCache.TryAddAsync(_cacheKey, Arg.Any(), Arg.Any(), Arg.Any(), Arg.Any()) + _innerCache.TryAddAsync(_cacheKey, Arg.Any(), Arg.Any(), Arg.Any(), Arg.Any()) .Returns(true); _topic.PublishAsync(Arg.Any(), Arg.Any()).Returns(_ => true); var added = await Sut.TryAddAsync(_cacheKey, _fixture.Create(), policy: null, token: Ct); added.Should().BeTrue("the inner cache said the key was free, and it is the only authority"); - await _innerCache.Received(1).TryAddAsync(_cacheKey, Arg.Any(), Arg.Any(), Arg.Any(), Arg.Any()); + await _innerCache.Received(1).TryAddAsync(_cacheKey, Arg.Any(), Arg.Any(), Arg.Any(), Arg.Any()); } [Fact] public async Task TryAdd_leaves_both_tiers_untouched_when_the_inner_cache_reports_a_loss() { - _innerCache.TryAddAsync(_cacheKey, Arg.Any(), Arg.Any(), Arg.Any(), Arg.Any()) + _innerCache.TryAddAsync(_cacheKey, Arg.Any(), Arg.Any(), Arg.Any(), Arg.Any()) .Returns(false); var added = await Sut.TryAddAsync(_cacheKey, _fixture.Create(), policy: null, token: Ct); @@ -90,7 +90,7 @@ public async Task TryAdd_leaves_both_tiers_untouched_when_the_inner_cache_report [Fact] public async Task TryAdd_broadcasts_after_a_win_so_peers_drop_stale_local_copies() { - _innerCache.TryAddAsync(_cacheKey, Arg.Any(), Arg.Any(), Arg.Any(), Arg.Any()) + _innerCache.TryAddAsync(_cacheKey, Arg.Any(), Arg.Any(), Arg.Any(), Arg.Any()) .Returns(true); _topic.PublishAsync(Arg.Any(), Arg.Any()).Returns(_ => true); @@ -102,7 +102,7 @@ public async Task TryAdd_broadcasts_after_a_win_so_peers_drop_stale_local_copies [Fact] public async Task TryAdd_still_reports_the_win_when_the_broadcast_fails() { - _innerCache.TryAddAsync(_cacheKey, Arg.Any(), Arg.Any(), Arg.Any(), Arg.Any()) + _innerCache.TryAddAsync(_cacheKey, Arg.Any(), Arg.Any(), Arg.Any(), Arg.Any()) .Returns(true); _topic.PublishAsync(Arg.Any(), Arg.Any()) .ThrowsAsync(new InvalidOperationException("broadcast down")); @@ -115,7 +115,7 @@ public async Task TryAdd_still_reports_the_win_when_the_broadcast_fails() [Fact] public async Task TryAdd_fails_closed_when_the_inner_cache_throws() { - _innerCache.TryAddAsync(_cacheKey, Arg.Any(), Arg.Any(), Arg.Any(), Arg.Any()) + _innerCache.TryAddAsync(_cacheKey, Arg.Any(), Arg.Any(), Arg.Any(), Arg.Any()) .ThrowsAsync(new InvalidOperationException("redis down")); var added = await Sut.TryAddAsync(_cacheKey, _fixture.Create(), policy: null, token: Ct); @@ -127,7 +127,7 @@ public async Task TryAdd_fails_closed_when_the_inner_cache_throws() [Fact] public async Task TryAdd_surfaces_an_inner_cache_that_cannot_arbitrate_at_all() { - _innerCache.TryAddAsync(_cacheKey, Arg.Any(), Arg.Any(), Arg.Any(), Arg.Any()) + _innerCache.TryAddAsync(_cacheKey, Arg.Any(), Arg.Any(), Arg.Any(), Arg.Any()) .ThrowsAsync(new NotSupportedException("no NX here")); var act = async () => await Sut.TryAddAsync(_cacheKey, _fixture.Create(), policy: null, token: Ct); @@ -136,23 +136,31 @@ public async Task TryAdd_surfaces_an_inner_cache_that_cannot_arbitrate_at_all() _memoryCache.DidNotReceive().CreateEntry(_cacheKey); } + /// + /// A deadline that has passed used to be a silent no-op returning false. The expiration is no + /// longer nullable, so there is nothing left for such a value to mean and it is rejected at the + /// boundary instead of being confused with "somebody else holds the key". + /// [Fact] - public async Task TryAdd_claims_nothing_for_an_expiration_that_has_already_passed() + public async Task TryAdd_rejects_an_expiration_that_has_already_passed() { - var added = await Sut.TryAddAsync(_cacheKey, _fixture.Create(), DateTimeOffset.UtcNow.AddMinutes(-5), token: Ct); + var act = async () => await Sut.TryAddAsync(_cacheKey, _fixture.Create(), DateTimeOffset.UtcNow.AddMinutes(-5), token: Ct); - added.Should().BeFalse(); - await _innerCache.DidNotReceive().TryAddAsync(_cacheKey, Arg.Any(), Arg.Any(), Arg.Any(), Arg.Any()); + (await act.Should().ThrowAsync()).And.ParamName.Should().Be("expiration"); + await _innerCache.DidNotReceive().TryAddAsync(_cacheKey, Arg.Any(), Arg.Any(), Arg.Any(), Arg.Any()); _memoryCache.DidNotReceive().CreateEntry(_cacheKey); } - [Fact] - public async Task TryAdd_claims_nothing_for_a_zero_expiration() + /// + [Theory] + [InlineData(0)] + [InlineData(-5)] + public async Task TryAdd_rejects_a_non_positive_expiration(int minutes) { - var added = await Sut.TryAddAsync(_cacheKey, _fixture.Create(), TimeSpan.Zero, token: Ct); + var act = async () => await Sut.TryAddAsync(_cacheKey, _fixture.Create(), TimeSpan.FromMinutes(minutes), token: Ct); - added.Should().BeFalse(); - await _innerCache.DidNotReceive().TryAddAsync(_cacheKey, Arg.Any(), Arg.Any(), Arg.Any(), Arg.Any()); + (await act.Should().ThrowAsync()).And.ParamName.Should().Be("expiration"); + await _innerCache.DidNotReceive().TryAddAsync(_cacheKey, Arg.Any(), Arg.Any(), Arg.Any(), Arg.Any()); } [Fact] @@ -160,7 +168,7 @@ public async Task A_broadcast_that_reports_not_published_still_stands_and_is_log { // CacheSetAsync signals an ordinary publish failure with false rather than throwing, so the // catch alone would let it pass unreported. - _innerCache.TryAddAsync(_cacheKey, Arg.Any(), Arg.Any(), Arg.Any(), Arg.Any()) + _innerCache.TryAddAsync(_cacheKey, Arg.Any(), Arg.Any(), Arg.Any(), Arg.Any()) .Returns(true); _topic.PublishAsync(Arg.Any(), Arg.Any()).Returns(_ => false); @@ -180,7 +188,7 @@ public async Task A_failed_broadcast_still_populates_the_local_tier() { // The broadcast and the L1 write are independent best-effort steps after the win; a dead // topic must not cost the winning node its local copy. - _innerCache.TryAddAsync(_cacheKey, Arg.Any(), Arg.Any(), Arg.Any(), Arg.Any()) + _innerCache.TryAddAsync(_cacheKey, Arg.Any(), Arg.Any(), Arg.Any(), Arg.Any()) .Returns(true); _topic.PublishAsync(Arg.Any(), Arg.Any()) .ThrowsAsync(new InvalidOperationException("broadcast down")); @@ -196,7 +204,7 @@ public async Task TryAdd_fails_closed_when_the_inner_cache_cannot_claim_the_key( { _options.UseLocalOnlyWhenDisconnected = true; _options.ConnectionMonitorEnabled = true; - _innerCache.TryAddAsync(_cacheKey, Arg.Any(), Arg.Any(), Arg.Any(), Arg.Any()) + _innerCache.TryAddAsync(_cacheKey, Arg.Any(), Arg.Any(), Arg.Any(), Arg.Any()) .Returns(false); _sut = null; @@ -212,14 +220,14 @@ public async Task A_disconnected_broadcast_transport_does_not_stop_a_healthy_L2_ _options.UseLocalOnlyWhenDisconnected = true; _options.ConnectionMonitorEnabled = true; _topicProvider.IsConnected.Returns(false); - _innerCache.TryAddAsync(_cacheKey, Arg.Any(), Arg.Any(), Arg.Any(), Arg.Any()) + _innerCache.TryAddAsync(_cacheKey, Arg.Any(), Arg.Any(), Arg.Any(), Arg.Any()) .Returns(true); _sut = null; var added = await Sut.TryAddAsync(_cacheKey, _fixture.Create(), policy: null, token: Ct); added.Should().BeTrue("the aggregate connection state covers the topic too, and broadcast is best-effort after a win"); - await _innerCache.Received(1).TryAddAsync(_cacheKey, Arg.Any(), Arg.Any(), Arg.Any(), Arg.Any()); + await _innerCache.Received(1).TryAddAsync(_cacheKey, Arg.Any(), Arg.Any(), Arg.Any(), Arg.Any()); } [Fact] @@ -231,7 +239,7 @@ public async Task TryAdd_surfaces_a_cancellation_raised_by_the_inner_cache() // CA2012: NSubstitute intercepts the call and Returns only uses the ValueTask as its // receiver — it is never awaited, so there is no single-consumption hazard here. #pragma warning disable CA2012 - _innerCache.TryAddAsync(_cacheKey, Arg.Any(), Arg.Any(), Arg.Any(), Arg.Any()) + _innerCache.TryAddAsync(_cacheKey, Arg.Any(), Arg.Any(), Arg.Any(), Arg.Any()) .Returns>(_ => { cts.Cancel(); @@ -243,7 +251,7 @@ public async Task TryAdd_surfaces_a_cancellation_raised_by_the_inner_cache() await act.Should().ThrowAsync( "reporting false would say someone else owns the key, which InMemoryRedis must not claim any more than Redis does"); - await _innerCache.Received(1).TryAddAsync(_cacheKey, Arg.Any(), Arg.Any(), Arg.Any(), Arg.Any()); + await _innerCache.Received(1).TryAddAsync(_cacheKey, Arg.Any(), Arg.Any(), Arg.Any(), Arg.Any()); } [Fact] @@ -258,7 +266,7 @@ public async Task TryAdd_of_a_null_value_never_deletes_and_never_reaches_the_inn // SetAsync removes the key in this case; a conditional add must not. _memoryCache.DidNotReceive().Remove(_cacheKey); await _innerCache.DidNotReceive().RemoveAsync(_cacheKey, Arg.Any()); - await _innerCache.DidNotReceive().TryAddAsync(_cacheKey, Arg.Any(), Arg.Any(), Arg.Any(), Arg.Any()); + await _innerCache.DidNotReceive().TryAddAsync(_cacheKey, Arg.Any(), Arg.Any(), Arg.Any(), Arg.Any()); } [Fact] @@ -266,21 +274,21 @@ public async Task TryAdd_of_a_null_value_reaches_the_inner_cache_when_CacheNullV { _options.CacheNullValues = true; _sut = null; - _innerCache.TryAddAsync(_cacheKey, default, Arg.Any(), Arg.Any(), Arg.Any()) + _innerCache.TryAddAsync(_cacheKey, default, Arg.Any(), Arg.Any(), Arg.Any()) .Returns(true); _topic.PublishAsync(Arg.Any(), Arg.Any()).Returns(_ => true); var added = await Sut.TryAddAsync(_cacheKey, default(string), policy: null, token: Ct); added.Should().BeTrue(); - await _innerCache.Received(1).TryAddAsync(_cacheKey, default, Arg.Any(), Arg.Any(), Arg.Any()); + await _innerCache.Received(1).TryAddAsync(_cacheKey, default, Arg.Any(), Arg.Any(), Arg.Any()); } [Fact] public async Task TryAdd_forwards_the_caller_expiration_to_the_inner_cache() { var ttl = TimeSpan.FromMinutes(7); - _innerCache.TryAddAsync(_cacheKey, Arg.Any(), Arg.Any(), Arg.Any(), Arg.Any()) + _innerCache.TryAddAsync(_cacheKey, Arg.Any(), Arg.Any(), Arg.Any(), Arg.Any()) .Returns(true); _topic.PublishAsync(Arg.Any(), Arg.Any()).Returns(_ => true); @@ -289,7 +297,7 @@ public async Task TryAdd_forwards_the_caller_expiration_to_the_inner_cache() await _innerCache.Received(1).TryAddAsync( _cacheKey, Arg.Any(), - Arg.Is(e => e.HasValue && e.Value > DateTimeOffset.UtcNow), + Arg.Is(e => e > DateTimeOffset.UtcNow), Arg.Any(), Arg.Any()); } @@ -551,13 +559,14 @@ public async Task A_non_positive_local_retention_claims_nothing(int minutes) } [Fact] - public async Task An_expiration_that_has_already_passed_claims_nothing() + public async Task An_expiration_that_has_already_passed_is_rejected() { using var sut = CreateSut(); var past = DateTimeOffset.UtcNow.AddMinutes(-5); - (await sut.TryAddAsync("k", "first", past, token: Ct)).Should().BeFalse(); - (await sut.TryAddAsync("k", "second", past, token: Ct)).Should().BeFalse(); + var act = async () => await sut.TryAddAsync("k", "first", past, token: Ct); + + (await act.Should().ThrowAsync()).And.ParamName.Should().Be("expiration"); (await sut.GetAsync("k", policy: null, token: Ct)).Should().BeNull(); } diff --git a/tests/UiPath.Caching.Tests/MultilayerHashCachePerNameJitterTests.cs b/tests/UiPath.Caching.Tests/MultilayerHashCachePerNameJitterTests.cs index 88471fc..8172a55 100644 --- a/tests/UiPath.Caching.Tests/MultilayerHashCachePerNameJitterTests.cs +++ b/tests/UiPath.Caching.Tests/MultilayerHashCachePerNameJitterTests.cs @@ -99,7 +99,7 @@ public async Task SetAsync_jitters_options_DefaultExpiration_when_policy_Distrib .Returns(_ => true); var values = new Dictionary { ["f"] = "v" }; - await Sut.SetAsync(_cacheKey, values, (TimeSpan?)null, policy: null, token); + await Sut.SetAsync(_cacheKey, values, policy: null, token); await _innerCache.Received(1).SetAsync( _cacheKey, @@ -127,7 +127,7 @@ public async Task RefreshAsync_TimeSpan_overload_jitters_policy_derived_expirati _topic.PublishAsync(Arg.Any(), Arg.Any()) .Returns(_ => true); - await Sut.RefreshAsync(_cacheKey, (TimeSpan?)null, policy: null, token); + await Sut.RefreshAsync(_cacheKey, policy: null, token); await _innerCache.Received(1).RefreshAsync( _cacheKey, diff --git a/tests/UiPath.Caching.Tests/MultilayerHashCacheTests.cs b/tests/UiPath.Caching.Tests/MultilayerHashCacheTests.cs index 9a76ead..50648f3 100644 --- a/tests/UiPath.Caching.Tests/MultilayerHashCacheTests.cs +++ b/tests/UiPath.Caching.Tests/MultilayerHashCacheTests.cs @@ -211,7 +211,7 @@ public async Task GetOrAdd_data_from_inner_cache_HashCacheSetOption(HashCacheSet _innerCache.GetCacheEntryAsync(_innerCacheKey, Arg.Any(), Arg.Any()) .Returns(expectedCacheEntry); - var actual = await Sut.GetOrAddAsync(_cacheKey, generator, expiration: _fixture.Create(), setOption: hashCacheSetOption, token: testContextAccessor.Current.CancellationToken); + var actual = await Sut.GetOrAddAsync(_cacheKey, generator, expiration: DateTimeOffset.UtcNow.AddMinutes(5), setOption: hashCacheSetOption, token: testContextAccessor.Current.CancellationToken); generatorWasCalled.Should().BeFalse(); actual.Should().BeEquivalentTo(expected); } @@ -234,7 +234,7 @@ public async Task GetOrAdd_data_from_inner_cache_datetime() _innerCache.GetCacheEntryAsync(_innerCacheKey, Arg.Any(), Arg.Any()) .Returns(expectedCacheEntry); - var actual = await Sut.GetOrAddAsync(_cacheKey, generator, _fixture.Create(), (CachePolicy?)null, testContextAccessor.Current.CancellationToken); + var actual = await Sut.GetOrAddAsync(_cacheKey, generator, DateTimeOffset.UtcNow.AddMinutes(5), (CachePolicy?)null, testContextAccessor.Current.CancellationToken); generatorWasCalled.Should().BeFalse(); actual.Should().BeEquivalentTo(expected); } @@ -519,7 +519,7 @@ public async Task Set_value_inner_cache_throw_exception() var actual = await Sut.SetAsync(_cacheKey, expected, _fixture.Create(), token: testContextAccessor.Current.CancellationToken); actual.Should().BeFalse(); - actual = await Sut.SetAsync(_cacheKey, expected, _fixture.Create(), token: testContextAccessor.Current.CancellationToken); + actual = await Sut.SetAsync(_cacheKey, expected, DateTimeOffset.UtcNow.AddMinutes(5), token: testContextAccessor.Current.CancellationToken); actual.Should().BeFalse(); } @@ -781,7 +781,7 @@ public async Task Remove_evict_token_non_active() [Fact] public async Task Refresh_value_TimeSpan() { - var expiration = _fixture.Create(); + var expiration = TimeSpan.FromMinutes(5); await Sut.RefreshAsync(_cacheKey, expiration, token: testContextAccessor.Current.CancellationToken); _memoryCache.Received(1).Remove(_innerCacheKey); await _innerCache.Received(1).RefreshAsync(_innerCacheKey, Arg.Any(), Arg.Any(), Arg.Any()); @@ -812,7 +812,7 @@ public async Task Refresh_value_DateTimeOffset() [InlineData(true)] public async Task Refresh_inner_cache_exception_timespan(bool eventFired) { - var expiration = _fixture.Create(); + var expiration = TimeSpan.FromMinutes(5); _innerCache.RefreshAsync(_innerCacheKey, Arg.Any(), Arg.Any(), Arg.Any()) .ThrowsAsync(new Exception()); _topic.PublishAsync(Arg.Any(), Arg.Any()) @@ -1109,7 +1109,7 @@ public async Task When_inner_cache_returns_max_expiration_local_uses_max() .Returns(cacheEntry); _options.DefaultExpiration = null; - _ = await Sut.GetOrAddAsync(_cacheKey, generator, default(DateTimeOffset?), (CachePolicy?)null, testContextAccessor.Current.CancellationToken); + _ = await Sut.GetOrAddAsync(_cacheKey, generator, (CachePolicy?)null, testContextAccessor.Current.CancellationToken); cacheEntry.AbsoluteExpiration.Should().Be(DateTimeOffset.MaxValue); } diff --git a/tests/UiPath.Caching.Tests/MultilayerSetCacheTests.cs b/tests/UiPath.Caching.Tests/MultilayerSetCacheTests.cs index 58f5fd8..7b70eb0 100644 --- a/tests/UiPath.Caching.Tests/MultilayerSetCacheTests.cs +++ b/tests/UiPath.Caching.Tests/MultilayerSetCacheTests.cs @@ -72,7 +72,7 @@ public async Task Add_many_writes_through_into_the_local_snapshot() var (sut, l2) = CreateSut(); SetupMembers(l2, "a"); // The batch overloads normalize into the DateTimeOffset one before hitting L2. - l2.AddAsync(default, default!, default(DateTimeOffset?), default, Ct).ReturnsForAnyArgs(_ => 2L); + l2.AddAsync(default, default!, default(DateTimeOffset), default, Ct).ReturnsForAnyArgs(_ => 2L); await sut.MembersAsync("k", token: Ct); // primes L1 await sut.AddAsync("k", (IEnumerable)new[] { "b", "c" }, (CachePolicy?)null, Ct); diff --git a/tests/UiPath.Caching.Tests/NullCacheConditionalAddTests.cs b/tests/UiPath.Caching.Tests/NullCacheConditionalAddTests.cs index 17af5fe..960e679 100644 --- a/tests/UiPath.Caching.Tests/NullCacheConditionalAddTests.cs +++ b/tests/UiPath.Caching.Tests/NullCacheConditionalAddTests.cs @@ -15,15 +15,14 @@ public async Task TryAdd_reports_not_added_for_every_caller() (await sut.TryAddAsync("k", "second", policy: null, token: Ct)).Should().BeFalse(); } - [Theory] - [InlineData(null)] - [InlineData(5)] - public async Task TryAdd_reports_not_added_whatever_the_expiration(int? minutes) + [Fact] + public async Task TryAdd_reports_not_added_whatever_the_expiration() { - TimeSpan? ttl = minutes is { } m ? TimeSpan.FromMinutes(m) : null; + var ttl = TimeSpan.FromMinutes(5); + (await NullCache.Instance.TryAddAsync("k", "v", token: Ct)).Should().BeFalse(); (await NullCache.Instance.TryAddAsync("k", "v", ttl, token: Ct)).Should().BeFalse(); - (await NullCache.Instance.TryAddAsync("k", "v", ttl.HasValue ? DateTimeOffset.UtcNow.Add(ttl.Value) : null, token: Ct)).Should().BeFalse(); + (await NullCache.Instance.TryAddAsync("k", "v", DateTimeOffset.UtcNow.Add(ttl), token: Ct)).Should().BeFalse(); } [Fact] diff --git a/tests/UiPath.Caching.Tests/Redis/RedisCacheTests.cs b/tests/UiPath.Caching.Tests/Redis/RedisCacheTests.cs index 0c02fe1..03a29b2 100644 --- a/tests/UiPath.Caching.Tests/Redis/RedisCacheTests.cs +++ b/tests/UiPath.Caching.Tests/Redis/RedisCacheTests.cs @@ -676,11 +676,11 @@ public async Task Refresh_no_expiration_no_default(Type expirationType) _cacheOptions.DefaultExpiration = null; if (expirationType == typeof(TimeSpan)) { - await Sut.RefreshAsync(_cacheKey, default(TimeSpan?), token: testContextAccessor.Current.CancellationToken); + await Sut.RefreshAsync(_cacheKey, policy: null, token: testContextAccessor.Current.CancellationToken); } else if (expirationType == typeof(DateTimeOffset)) { - await Sut.RefreshAsync(_cacheKey, default(DateTimeOffset?), token: testContextAccessor.Current.CancellationToken); + await Sut.RefreshAsync(_cacheKey, policy: null, token: testContextAccessor.Current.CancellationToken); } else { @@ -701,7 +701,7 @@ public async Task GetOrAdd_default_key_expiration() actualExpiration = ci.Arg(); return _fixture.Create(); }); - await Sut.GetOrAddAsync(_cacheKey, _ => Task.FromResult(_fixture.Create()), expiration: default(DateTimeOffset?), token: testContextAccessor.Current.CancellationToken); + await Sut.GetOrAddAsync(_cacheKey, _ => Task.FromResult(_fixture.Create()), token: testContextAccessor.Current.CancellationToken); var expectedExpiration = _clock.UtcNow.Add(_cacheOptions.DefaultExpiration!.Value).Subtract(_clock.UtcNow); await _database.Received(1).StringSetAsync(_redisKey, Arg.Any(), Arg.Is(t => expectedExpiration == t), When.Always, CommandFlags.DemandMaster); @@ -834,9 +834,9 @@ public async Task GetOrAdd_key_expiration_no_default_expiration() actualExpiration = ci.Arg(); return _fixture.Create(); }); - DateTimeOffset? expiration = _fixture.Create(); - await Sut.GetOrAddAsync(_cacheKey, _ => Task.FromResult(_fixture.Create()), expiration: expiration, token: testContextAccessor.Current.CancellationToken); - var expectedExpiration = expiration.Value.Subtract(_clock.UtcNow); + var expiration = _clock.UtcNow.AddMinutes(5); + await Sut.GetOrAddAsync(_cacheKey, _ => Task.FromResult(_fixture.Create()), expiration: expiration, policy: null, token: testContextAccessor.Current.CancellationToken); + var expectedExpiration = expiration.Subtract(_clock.UtcNow); await _database.Received(1).StringSetAsync(_redisKey, Arg.Any(), Arg.Is(t => expectedExpiration == t), When.Always, CommandFlags.DemandMaster); } @@ -845,17 +845,9 @@ public async Task GetOrAdd_key_expiration_no_default_expiration() [InlineData(5)] public async Task Refresh_redis_exception(int? expirationMinutes) { - TimeSpan? expiration = null; - TimeSpan expectedExpiration; - if (expirationMinutes.HasValue) - { - expiration = TimeSpan.FromMinutes(expirationMinutes.Value); - expectedExpiration = expiration.Value; - } - else - { - expectedExpiration = _cacheOptions.DefaultExpiration ?? TimeSpan.MinValue; - } + var expectedExpiration = expirationMinutes.HasValue + ? TimeSpan.FromMinutes(expirationMinutes.Value) + : _cacheOptions.DefaultExpiration ?? TimeSpan.MinValue; DateTime? actualExpiration = default; _database.KeyExpireAsync(_redisKey, Arg.Any(), CommandFlags.DemandMaster| CommandFlags.FireAndForget) @@ -864,7 +856,11 @@ public async Task Refresh_redis_exception(int? expirationMinutes) actualExpiration = ci.Arg(); return new RedisException("test"); }); - await Sut.RefreshAsync(_cacheKey, expiration, token: testContextAccessor.Current.CancellationToken); + // No expiration minutes means the caller omits the argument entirely, which is now the + // only way to ask for the policy default. + await (expirationMinutes.HasValue + ? Sut.RefreshAsync(_cacheKey, TimeSpan.FromMinutes(expirationMinutes.Value), policy: null, testContextAccessor.Current.CancellationToken) + : Sut.RefreshAsync(_cacheKey, policy: null, testContextAccessor.Current.CancellationToken)); actualExpiration.GetValueOrDefault().Subtract(_now.UtcDateTime).Should().BeCloseTo(expectedExpiration, TimeSpan.FromSeconds(10)); } @@ -1234,16 +1230,23 @@ public async Task SetAsync_deletes_key_when_CacheNullValues_false_and_value_is_n await _database.DidNotReceive().StringSetAsync(_redisKey, Arg.Any(), Arg.Any(), Arg.Any(), Arg.Any()); } + /// + /// The write used to accept a deadline in the past and turn it into a delete. Expiration is no + /// longer nullable, so a past deadline is a bad argument rather than a shorthand, and it is + /// rejected before the key is touched at all. (SetInternalAsync still guards a + /// non-positive resolved duration — that path is now only reachable from a misconfigured + /// provider default, not from a caller.) + /// [Fact] - public async Task SetAsync_null_with_past_expiration_deletes_even_when_CacheNullValues_true() + public async Task SetAsync_rejects_a_past_expiration_even_when_CacheNullValues_true() { _cacheOptions.CacheNullValues = true; var pastExpiration = _clock.UtcNow.AddMinutes(-5); - var ok = await Sut.SetAsync(_cacheKey, value: null, pastExpiration, token: testContextAccessor.Current.CancellationToken); + var act = async () => await Sut.SetAsync(_cacheKey, value: null, pastExpiration, token: testContextAccessor.Current.CancellationToken); - ok.Should().BeTrue(); - await _database.Received().KeyDeleteAsync(_redisKey, Arg.Any()); + (await act.Should().ThrowAsync()).And.ParamName.Should().Be("expiration"); + await _database.DidNotReceive().KeyDeleteAsync(_redisKey, Arg.Any()); await _database.DidNotReceive().StringSetAsync(_redisKey, Arg.Any(), Arg.Any(), Arg.Any(), Arg.Any()); } @@ -1392,7 +1395,7 @@ private async Task Set_works_as_expected(Type expirationType) } else if (expirationType == typeof(DateTimeOffset)) { - actualResponse = await Sut.SetAsync(_cacheKey, value, _fixture.Create(), token: testContextAccessor.Current.CancellationToken); + actualResponse = await Sut.SetAsync(_cacheKey, value, DateTimeOffset.UtcNow.AddMinutes(5), token: testContextAccessor.Current.CancellationToken); } else { @@ -1420,7 +1423,7 @@ private async Task Multi_set_works_as_expected(Type expirationType) } else if (expirationType == typeof(DateTimeOffset)) { - actualResponse = await Sut.SetAsync(new KeyValuePair[] { new(_cacheKey, value), new(_multiKey, value) }, _fixture.Create(), policy: null, token: testContextAccessor.Current.CancellationToken); + actualResponse = await Sut.SetAsync(new KeyValuePair[] { new(_cacheKey, value), new(_multiKey, value) }, DateTimeOffset.UtcNow.AddMinutes(5), policy: null, token: testContextAccessor.Current.CancellationToken); } else { diff --git a/tests/UiPath.Caching.Tests/Redis/RedisCacheTryAddTests.cs b/tests/UiPath.Caching.Tests/Redis/RedisCacheTryAddTests.cs index 012edeb..3d82bc7 100644 --- a/tests/UiPath.Caching.Tests/Redis/RedisCacheTryAddTests.cs +++ b/tests/UiPath.Caching.Tests/Redis/RedisCacheTryAddTests.cs @@ -241,18 +241,23 @@ public async Task A_redis_failure_that_is_not_a_cancellation_still_reports_not_a added.Should().BeFalse(); } + /// + /// A non-positive lifetime used to return false, which is the same answer as "the key already + /// exists". With expiration non-nullable there is no third state to lean on, so the argument is + /// rejected rather than answered. + /// [Theory] [InlineData(0)] [InlineData(-5)] - public async Task TryAdd_claims_nothing_for_a_non_positive_expiration(int minutes) + public async Task TryAdd_rejects_a_non_positive_expiration(int minutes) { - var added = await Sut.TryAddAsync( + var act = async () => await Sut.TryAddAsync( _cacheKey, _fixture.Create(), TimeSpan.FromMinutes(minutes), token: testContextAccessor.Current.CancellationToken); - added.Should().BeFalse(); + (await act.Should().ThrowAsync()).And.ParamName.Should().Be("expiration"); await _database.DidNotReceive().StringSetAsync(Arg.Any(), Arg.Any(), Arg.Any(), Arg.Any(), Arg.Any()); } diff --git a/tests/UiPath.Caching.Tests/Redis/RedisHashCacheTests.cs b/tests/UiPath.Caching.Tests/Redis/RedisHashCacheTests.cs index f462ad5..55d525f 100644 --- a/tests/UiPath.Caching.Tests/Redis/RedisHashCacheTests.cs +++ b/tests/UiPath.Caching.Tests/Redis/RedisHashCacheTests.cs @@ -300,7 +300,7 @@ public async Task GetOrAdd_generator_not_called() return Task.FromResult(fields.ToDictionary(k => k, k => _fixture.Create()) as IDictionary); }; - var actual = await Sut.GetOrAddAsync(_cacheKey, generator, _fixture.Create(), token: testContextAccessor.Current.CancellationToken); + var actual = await Sut.GetOrAddAsync(_cacheKey, generator, TimeSpan.FromMinutes(5), token: testContextAccessor.Current.CancellationToken); actual.Should().BeEquivalentTo(expected); generatorCalled.Should().BeFalse(); } @@ -348,7 +348,7 @@ public async Task GetOrAdd_generator_called() return Task.FromResult(expected); }; _transaction.ExecuteAsync(Arg.Any()).Returns(true); - var actual = await Sut.GetOrAddAsync(_cacheKey, generator, _fixture.Create(), token: testContextAccessor.Current.CancellationToken); + var actual = await Sut.GetOrAddAsync(_cacheKey, generator, TimeSpan.FromMinutes(5), token: testContextAccessor.Current.CancellationToken); actual.Should().BeEquivalentTo(expected); _database.Received(1).CreateTransaction(); await _transaction.Received(1).HashSetAsync(_redisKey, Arg.Any(), CommandFlags.DemandMaster); @@ -401,7 +401,7 @@ public async Task GetOrAdd_generator_called_empty_result() generatorCalled = true; return Task.FromResult(expected); }; - var actual = await Sut.GetOrAddAsync(_cacheKey, generator, _fixture.Create(), token: testContextAccessor.Current.CancellationToken); + var actual = await Sut.GetOrAddAsync(_cacheKey, generator, TimeSpan.FromMinutes(5), token: testContextAccessor.Current.CancellationToken); actual.Should().BeEquivalentTo(expected); _database.Received(0).CreateTransaction(); await _transaction.Received(0).HashSetAsync(_redisKey, Arg.Any(), CommandFlags.DemandMaster); @@ -423,7 +423,7 @@ public async Task GetOrAdd_returns_empty_dict_without_invoking_generator_when_on return Task.FromResult>(new Dictionary { ["fresh"] = "v" }); }; - var actual = await Sut.GetOrAddAsync(_cacheKey, generator, _fixture.Create(), token: testContextAccessor.Current.CancellationToken); + var actual = await Sut.GetOrAddAsync(_cacheKey, generator, TimeSpan.FromMinutes(5), token: testContextAccessor.Current.CancellationToken); actual.Should().BeEmpty(); generatorCalled.Should().BeFalse(); @@ -497,7 +497,7 @@ public async Task GetOrAdd_empty_result_with_CacheNullValues_writes_metadata_mar _transaction.HashSetAsync(_redisKey, Arg.Do(h => captured = h), Arg.Any()) .Returns(Task.CompletedTask); - var actual = await Sut.GetOrAddAsync(_cacheKey, _ => Task.FromResult(generated), _fixture.Create(), token: testContextAccessor.Current.CancellationToken); + var actual = await Sut.GetOrAddAsync(_cacheKey, _ => Task.FromResult(generated), TimeSpan.FromMinutes(5), token: testContextAccessor.Current.CancellationToken); actual.Should().BeEmpty(); captured.Should().NotBeNull(); @@ -515,7 +515,7 @@ public async Task GetOrAdd_empty_result_without_CacheNullValues_deletes_key() _database.HashGetAllAsync(_redisKey, CommandFlags.PreferReplica) .Returns(_ => Array.Empty()); - var actual = await Sut.GetOrAddAsync(_cacheKey, _ => Task.FromResult(generated), _fixture.Create(), token: testContextAccessor.Current.CancellationToken); + var actual = await Sut.GetOrAddAsync(_cacheKey, _ => Task.FromResult(generated), TimeSpan.FromMinutes(5), token: testContextAccessor.Current.CancellationToken); actual.Should().BeEmpty(); await _database.Received().KeyDeleteAsync(_redisKey, Arg.Any()); @@ -598,7 +598,7 @@ public async Task GetOrAdd_legacy_nonempty_metadata_only_hash_runs_generator_whe return Task.FromResult>(new Dictionary { ["fresh"] = "v" }); }; - await Sut.GetOrAddAsync(_cacheKey, generator, _fixture.Create(), token: testContextAccessor.Current.CancellationToken); + await Sut.GetOrAddAsync(_cacheKey, generator, TimeSpan.FromMinutes(5), token: testContextAccessor.Current.CancellationToken); generatorCalled.Should().BeTrue("only Length==0 _metadata_ is the cached-empty sentinel in the GetOrAdd probe path; legacy non-empty _metadata_-only hashes must remain misses"); } @@ -618,7 +618,7 @@ public async Task GetOrAdd_marker_only_hash_runs_generator_when_CacheNullValues_ return Task.FromResult>(generated); }; - var actual = await Sut.GetOrAddAsync(_cacheKey, generator, _fixture.Create(), token: testContextAccessor.Current.CancellationToken); + var actual = await Sut.GetOrAddAsync(_cacheKey, generator, TimeSpan.FromMinutes(5), token: testContextAccessor.Current.CancellationToken); generatorCalled.Should().BeTrue(); actual.Should().BeEquivalentTo(generated); @@ -682,8 +682,9 @@ public async Task GetOrAdd_empty_result_forces_KeyReplace_even_when_caller_passe await Sut.GetOrAddAsync( _cacheKey, _ => Task.FromResult(generated), - expiration: (DateTimeOffset?)_now.AddMinutes(5), + expiration: _now.AddMinutes(5), setOption: HashCacheSetOption.HashReplace, + policy: null, token: testContextAccessor.Current.CancellationToken); await _transaction.Received(1).KeyDeleteAsync(_redisKey); @@ -852,7 +853,6 @@ public async Task Contains_redis_exception() [InlineData(3)] public async Task Refresh_works_as_expected(int? expirationMinutes) { - TimeSpan? expiration = expirationMinutes.HasValue ? TimeSpan.FromMinutes(expirationMinutes.Value) : null; var fieldsCalled = false; DateTime actualExpiration = default; _database.KeyExpireAsync(_redisKey, Arg.Any(), CommandFlags.DemandMaster | CommandFlags.FireAndForget) @@ -863,7 +863,11 @@ public async Task Refresh_works_as_expected(int? expirationMinutes) return _fixture.Create(); }); - await Sut.RefreshAsync(_cacheKey, expiration, token: testContextAccessor.Current.CancellationToken); + // No expiration minutes means the caller omits the argument entirely, which is now the + // only way to ask for the policy default. + await (expirationMinutes.HasValue + ? Sut.RefreshAsync(_cacheKey, TimeSpan.FromMinutes(expirationMinutes.Value), policy: null, testContextAccessor.Current.CancellationToken) + : Sut.RefreshAsync(_cacheKey, policy: null, testContextAccessor.Current.CancellationToken)); fieldsCalled.Should().BeTrue(); _logger.ReceivedCalls().Should().Contain(c => c.GetMethodInfo().Name == "Log" && (LogLevel)c.GetArguments()[0]! == LogLevel.Trace); var expectedTime = expirationMinutes.HasValue @@ -897,7 +901,7 @@ public async Task Refresh_redis_exception_timespan() _database.KeyExpireAsync(_redisKey, Arg.Any(), CommandFlags.DemandMaster | CommandFlags.FireAndForget) .ThrowsAsync(); - var actual = await Sut.RefreshAsync(_cacheKey, _fixture.Create(), token: testContextAccessor.Current.CancellationToken); + var actual = await Sut.RefreshAsync(_cacheKey, TimeSpan.FromMinutes(5), token: testContextAccessor.Current.CancellationToken); actual.Should().BeFalse(); } @@ -907,7 +911,7 @@ public async Task Refresh_redis_exception_datetime() _database.KeyExpireAsync(_redisKey, Arg.Any(), CommandFlags.DemandMaster | CommandFlags.FireAndForget) .ThrowsAsync(); - var actual = await Sut.RefreshAsync(_cacheKey, _fixture.Create(), token: testContextAccessor.Current.CancellationToken); + var actual = await Sut.RefreshAsync(_cacheKey, _clock.UtcNow.AddMinutes(5), token: testContextAccessor.Current.CancellationToken); actual.Should().BeFalse(); } @@ -1140,7 +1144,7 @@ public async Task Set_empty_values() return expected; }); - var actual = await Sut.SetAsync(_cacheKey, values, _fixture.Create(), token: testContextAccessor.Current.CancellationToken); + var actual = await Sut.SetAsync(_cacheKey, values, TimeSpan.FromMinutes(5), token: testContextAccessor.Current.CancellationToken); actionCalled.Should().BeTrue(); actual.Should().Be(expected); _database.Received(0).CreateTransaction(); @@ -1174,7 +1178,7 @@ public async Task Set_redis_exception() var entries = _fixture.CreateMany().ToArray(); IDictionary values = entries.ToDictionary(k => k.Name.ToString(), k => (string?)k.Value); _transaction.ExecuteAsync(Arg.Any()).ThrowsAsync(); - var actual = await Sut.SetAsync(_cacheKey, values, _fixture.Create(), token: testContextAccessor.Current.CancellationToken); + var actual = await Sut.SetAsync(_cacheKey, values, TimeSpan.FromMinutes(5), token: testContextAccessor.Current.CancellationToken); actual.Should().BeFalse(); _database.Received(1).CreateTransaction(); }