Skip to content

v1.0.0-preview.1

Pre-release
Pre-release

Choose a tag to compare

@matthewdevenny matthewdevenny released this 30 Jul 00:16
· 1 commit to main since this release
v1.0.0-preview.1
137eb25

⚠️ Breaking changes since v0.3.0

Cache entries use a new binary envelope (#48, #54)

The CacheEntry envelope changed from JSON-with-a-base64-payload to compact binary framing —
[version:1][flags:1][absExpTicks:8?][sldExpTicks:8?][raw payload]. There is no read shim for the old format.

  • Entries written by v0.3.0 or earlier are treated as a cache miss (logged at Debug, EventId 102 UndeserializableEntry), never an error. A v0.3.0 node cannot read this release's envelope either, so during a rolling deploy each version only sees entries written in its own format.
  • Nothing is evicted and nothing throws — a node never deletes bytes it cannot read, so rolling deployments are safe and there is no migration step. TTL'd entries are reaped by NATS; TTL-less entries are overwritten by the next Set.
  • Impact: expect a cold cache after upgrading. If that is unacceptable, pre-warm it or move to a new bucket / CacheKeyPrefix.
  • Details: Cache Entry Format and Upgrades.

NATS.Client.KeyValueStore bumped 2.8.2 → 3.0.1 (major) (#56)

Apps that also reference NATS.Net packages directly (for example NATS.Extensions.Microsoft.DependencyInjection) must move to the matching 3.x versions. The bump required no source changes in this library. The minimum server version is unchanged (NATS 2.11+); integration tests now run against 2.14.3.

CacheEntryJsonContext removed (#48)

The public JsonSerializerContext for CacheEntry is gone, and CacheEntry is now a plain POCO — the [JsonPropertyName] attributes went with it. Code referencing CacheEntryJsonContext, or depending on the stored absexp / sldexp / data JSON property names, no longer compiles or no longer applies. The envelope is an internal storage detail.

A missing BucketName now fails fast (#51)

AddNatsDistributedCache — and AddNatsHybridCache, which delegates to it — now registers options with .Validate(...).ValidateOnStart().

  • Under the Generic Host, a null / empty / whitespace BucketName throws OptionsValidationException at host startup instead of surfacing at first cache use.
  • Direct new NatsCache(...) throws ArgumentException ("BucketName must be set") instead of NullReferenceException. Any catch keyed on NullReferenceException needs updating.

TryGetAsync propagates caller cancellation (#52)

NatsCache.TryGetAsync(string, IBufferWriter<byte>, CancellationToken) previously caught everything and returned false silently. It still returns false for read failures — now logged at Warning (EventId 101) rather than silently — but an OperationCanceledException from the caller's cancelled token now propagates instead of masquerading as a cache miss.

Relatedly, Remove failures are now consistently logged at Error (previously inconsistent or not logged at all), and every failure is logged exactly once.

Expirations beyond ~68 years are rejected (#54)

NATS encodes message TTLs as (int)ttl.TotalSeconds, which overflows above int.MaxValue seconds. SlidingExpiration, AbsoluteExpiration, and AbsoluteExpirationRelativeToNow windows exceeding 24855.03:14:07 now throw ArgumentOutOfRangeException from Set/SetAsync, instead of writing an entry with an overflowed TTL header. The read path fails closed on the same ceiling, so nothing that can be written reads back as an undeserializable miss.

The MaxValue sentinels are the deliberate exception: DateTimeOffset.MaxValue (absolute) and TimeSpan.MaxValue (sliding / relative) are now normalized to "never expire" rather than producing an enormous TTL.

Expiration is computed from TimeProvider, in UTC, on an inclusive boundary (#46)

Every clock read moved from DateTimeOffset.Now to TimeProvider.GetUtcNow().

  • If your container registers a TimeProvider, the cache now uses it for all expiration math. See Controlling Expiration Timing.
  • The absolute-expiration read check is now inclusive (>=): an entry whose absolute expiration is exactly "now" reads as expired, matching TTL computation and BCL MemoryCache.
  • The public NatsCache constructor is unchanged (the clock is an internal init-only property), so this is not an ABI break.

Also worth knowing (not breaking)

  • DI registration shape changed (#60). AddNatsDistributedCache now registers the concrete NatsCache as the singleton and forwards both IDistributedCache and the new INatsCacheMaintenance to that one instance, so a purge and a cache read share the same KV store, key prefix, and key encoder. Resolving IDistributedCache behaves as before.
  • Both packages are now marked trimmable (#58) via <IsAotCompatible>true</IsAotCompatible> (zero IL2xxx/IL3xxx warnings on net8.0 and net10.0). The key-validation regex moved from RegexOptions.Compiled to source-generated [GeneratedRegex].
  • New direct dependency: System.Diagnostics.DiagnosticSource 10.0.9 for the telemetry types (#56). It was already in the graph transitively, so this adds no new nodes.
  • Target frameworks are unchanged: net8.0 and net10.0.

What's Changed

Full Changelog: v0.3.0...v1.0.0-preview.1