Skip to content

Commit 65c7842

Browse files
committed
docs: add CHANGELOG and update README for v2.0.0 release
Introduce CHANGELOG.md capturing the full v2.0.0 release history: breaking changes, performance wins (sharded eviction, iter.Seq2 migration, xxhash consolidation), new features, and removed items. Update README.md to document v2.0.0 additions: - Sharded eviction section with WithEvictionShardCount guidance - Type-safe access (Typed[V]) section with usage example - WithDistHTTPLimits and WithDistHTTPAuth options in the reference table - Transport hardening section covering TLS, bearer-token auth, body limits, lifecycle context, and LastServeError - Roadmap table: mark TLS/auth as done since v2.0.0
1 parent fa12a60 commit 65c7842

2 files changed

Lines changed: 229 additions & 3 deletions

File tree

CHANGELOG.md

Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
1+
# Changelog
2+
3+
All notable changes to HyperCache are recorded here. The format follows
4+
[Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and the project
5+
adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6+
7+
## [Unreleased]
8+
9+
## [2.0.0] — 2026-05-04
10+
11+
A modernization release. The headline themes:
12+
13+
- Eviction is now sharded by default for concurrency-friendly throughput.
14+
- The distributed-memory backend (`DistMemory`) gained body limits, TLS,
15+
bearer-token auth, lifecycle-context cancellation, and surfaced
16+
listener errors.
17+
- A typed wrapper (`Typed[T, V]`) is available for compile-time
18+
type-safe access without the caller-side type assertions of the
19+
untyped API.
20+
- The legacy `pkg/cache` v1 store and the `longbridgeapp/assert` test
21+
dependency are gone.
22+
23+
The full course-correction plan (Phase 0 baseline → Phase 6 file split,
24+
plus Phase 5a–5e DistMemory hardening) is in commit history. The two
25+
RFCs that informed the design decisions live under [docs/rfcs/](docs/rfcs/).
26+
27+
### Breaking changes
28+
29+
- **`pkg/cache` v1 removed.** All callers must use `pkg/cache/v2`.
30+
- **`longbridgeapp/assert` test dependency removed.** Tests now use
31+
`stretchr/testify/require`. Internal test code only — no impact on
32+
library consumers, but downstream contributors authoring tests
33+
against this codebase must use `require`.
34+
- **`sentinel.ErrMgmtHTTPShutdownTimeout` removed.**
35+
`ManagementHTTPServer.Shutdown` now calls `app.ShutdownWithContext`
36+
and returns the underlying ctx error directly. Callers comparing
37+
against the removed sentinel must switch to `errors.Is(err,
38+
context.DeadlineExceeded)` or equivalent.
39+
- **Sharded eviction is default-on (32 shards).** Items no longer
40+
evict in strict global LRU/LFU order — the algorithm operates
41+
independently within each shard. Total capacity is honored within
42+
±32 (one slot of slack per shard). Use `WithEvictionShardCount(1)`
43+
to restore strict-global ordering at the cost of single-mutex
44+
contention.
45+
- **`hypercache.go` decomposed into 6 files** (`hypercache.go`,
46+
`hypercache_io.go`, `hypercache_eviction.go`,
47+
`hypercache_expiration.go`, `hypercache_dist.go`,
48+
`hypercache_construct.go`). No public API change; third-party
49+
patches against line numbers in the prior single-file layout will
50+
not apply.
51+
- **`ManagementHTTPServer` constructor order fix.**
52+
`WithMgmtReadTimeout` and `WithMgmtWriteTimeout` previously mutated
53+
struct fields *after* `fiber.New` had locked in the defaults — the
54+
options were silent no-ops. Construction order is now correct, so
55+
any code relying on the silent no-op (e.g., setting absurd values
56+
knowing they would be ignored) will see those values take effect.
57+
58+
### Performance
59+
60+
Measurements on Apple M4 Pro, `go test -bench`, `count=5`, benchstat.
61+
Full release snapshot captured in [bench-v2.0.0.txt](bench-v2.0.0.txt).
62+
63+
- **Per-shard atomic `Count`.** `BenchmarkConcurrentMap_Count`:
64+
53 → ~10 ns/op. `_CountParallel`: 1181 → ~13 ns/op. Eliminates the
65+
lock-storm that previously serialized on a single mutex during
66+
eviction-loop count checks.
67+
- **Sharded eviction algorithm** (`pkg/eviction/sharded.go`).
68+
Replaces the global eviction-algorithm mutex with 32 per-shard
69+
mutexes routed by the same hash `ConcurrentMap` uses, so a key's
70+
data shard and eviction shard align (cache-locality on Set).
71+
- **`iter.Seq2` migration** replacing channel-based `IterBuffered`.
72+
`BenchmarkConcurrentMap_All` (renamed from `_IterBuffered`):
73+
757µs → 26.5µs/op (-96.51%). Bytes/op: 1.73 MiB → 0 B/op.
74+
Allocs/op: 230 → 0. Eliminated 32 goroutines + 32 channels per
75+
iteration.
76+
- **xxhash consolidation** (`pkg/cache/v2/hash.go`). Replaced inlined
77+
FNV-1a with `xxhash.Sum64String` folded to 32 bits.
78+
`BenchmarkConcurrentMap_GetShard`: 10.07 → 3.46 ns/op (-65.63%).
79+
- **Sharded item-aware eviction was tried and rejected** per
80+
[RFC 0001](docs/rfcs/0001-backend-owned-eviction.md). The
81+
hypothesis (duplicate-map overhead is the bottleneck) was
82+
falsified — sharded contention dominates. Code removed; lessons
83+
preserved in the RFC for future contributors.
84+
85+
### Features
86+
87+
- **`hypercache.Typed[T, V]` wrapper** for compile-time type-safe
88+
cache access. Wraps an existing `HyperCache[T]`; multiple `Typed`
89+
views can share one underlying cache over disjoint keyspaces.
90+
Includes `Set`, `Get`, `GetTyped` (explicit `ErrTypeMismatch`),
91+
`GetWithInfo`, `GetOrSet`, `GetMultiple`, `Remove`, `Clear`. See
92+
[hypercache_typed.go](hypercache_typed.go) and
93+
[RFC 0002 Phase 1](docs/rfcs/0002-generic-item-typing.md). Phase 2
94+
(deep `Item[V]` generics) is v3 territory, conditional on adoption
95+
signal.
96+
- **`WithDistHTTPLimits(DistHTTPLimits)` option** for the dist
97+
transport: server `BodyLimit` / `ReadTimeout` / `WriteTimeout` /
98+
`IdleTimeout` / `Concurrency`, plus client `ResponseLimit` /
99+
`ClientTimeout`. Defaults: 16 MiB request/response body cap, 5 s
100+
read/write/client timeout, 60 s idle, fiber's 256 KiB concurrency
101+
cap. Partial overrides honored — zero fields inherit defaults.
102+
- **`WithDistHTTPAuth(DistHTTPAuth)` option** for bearer-token auth on
103+
`/internal/*` and `/health` (`Token` for the common case;
104+
`ServerVerify`/`ClientSign` hooks for JWT, mTLS-derived identity,
105+
HMAC, etc.). Constant-time token compare on the server side. The
106+
auto-created HTTP client signs every outgoing request with the
107+
same token. Mismatched-token peers are rejected with HTTP 401
108+
(`sentinel.ErrUnauthorized`).
109+
- **TLS support** via `DistHTTPLimits.TLSConfig`. The server wraps
110+
its listener with `tls.NewListener`; the auto-created HTTP client
111+
attaches the same `*tls.Config` to its `Transport.TLSClientConfig`
112+
with ALPN forced to `http/1.1` (fiber/fasthttp doesn't speak h2).
113+
Same `*tls.Config` configures both sides — operators applying it
114+
consistently across the cluster get encrypted intra-cluster
115+
traffic out of the box. Plaintext peers handshake-fail.
116+
- **Dist server lifecycle context**`DistMemory.LifecycleContext()`
117+
exposes a context derived from the constructor's that is canceled
118+
on `Stop()`. Replaces the prior pattern where handlers captured
119+
the constructor's `context.Background()` and never observed
120+
cancellation. In-flight handlers and replica forwards see `Done()`
121+
the moment `Stop` is called.
122+
- **`LastServeError()` accessor** on both `distHTTPServer` and
123+
`ManagementHTTPServer`. Replaces the prior `_ = serveErr` pattern
124+
that silently swallowed listener-loop crashes — operators can now
125+
surface the failure to logs/alerts.
126+
- **`Stop()` goroutine-leak fix.** Both `distHTTPServer.stop` and
127+
`ManagementHTTPServer.Shutdown` now call
128+
`app.ShutdownWithContext(ctx)` directly instead of wrapping
129+
`app.Shutdown()` in a goroutine and racing it against ctx done
130+
(which leaked the goroutine when ctx fired first).
131+
- **New sentinels:** `sentinel.ErrTypeMismatch`,
132+
`sentinel.ErrUnauthorized`.
133+
134+
### Internal
135+
136+
Worth surfacing for contributors:
137+
138+
- **v2 module layout** is the file split listed under "Breaking
139+
changes" above — readability win, no API change.
140+
- **Test helpers** introduced under `tests/`:
141+
`tests/dist_cluster_helper.go::SetupInProcessCluster[RF]`,
142+
`tests/merkle_node_helper.go`,
143+
`pkg/backend/dist_memory_test_helpers.go::EnableHTTPForTest`
144+
(build tag `test`).
145+
- **Lint discipline:** 35 `nolint` directives total across the repo,
146+
each with a one-line justification. golangci-lint v2.12.1 runs
147+
clean with `--build-tags test`.
148+
149+
### Removed
150+
151+
- `pkg/cache` v1 (see "Breaking changes").
152+
- `longbridgeapp/assert` test dependency (see "Breaking changes").
153+
- `sentinel.ErrMgmtHTTPShutdownTimeout` (see "Breaking changes").
154+
- Experimental `WithItemAwareEviction` option / `IAlgorithmItemAware`
155+
interface / `LRUItemAware` / `ShardedItemAware` types — landed
156+
briefly during the RFC 0001 spike, then torn out per the RFC's
157+
own discipline when the perf gate failed. The
158+
[RFC document](docs/rfcs/0001-backend-owned-eviction.md) preserves
159+
the measurement and the lessons.
160+
161+
[Unreleased]: https://github.com/hyp3rd/hypercache/compare/v2.0.0...HEAD
162+
[2.0.0]: https://github.com/hyp3rd/hypercache/releases/tag/v2.0.0

README.md

Lines changed: 67 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,12 @@ Available algorithm names you can pass to `WithEvictionAlgorithm`:
137137

138138
Note: ARC is experimental and isn’t included in the default registry. If you choose to use it, register it manually or enable it explicitly in your build.
139139

140+
#### Sharded eviction (default since v2.0.0)
141+
142+
The configured algorithm is wrapped by a 32-shard router (`pkg/eviction/sharded.go`) that uses the same key hash as `ConcurrentMap` — so a key's data shard and eviction shard line up. This eliminates the global mutex contention single-instance algorithms (LRU/LFU/Clock/CAWOLFU) suffer from. Total capacity is honored within ±32 (one slot of slack per shard), and items evict per-shard rather than in strict global LRU/LFU order.
143+
144+
Use `WithEvictionShardCount(1)` to disable sharding when you need strict-global ordering at the cost of single-mutex contention. Pass any other positive power of two to tune (e.g. `WithEvictionShardCount(64)`).
145+
140146
## API
141147

142148
`NewInMemoryWithDefaults(ctx, capacity)` is the quickest way to start:
@@ -177,6 +183,7 @@ if err != nil {
177183
| `WithExpirationTriggerBuffer` | Buffer size for coalesced expiration trigger channel. |
178184
| `WithExpirationTriggerDebounce` | Drop rapid-fire triggers within a window. |
179185
| `WithEvictionAlgorithm` | Select eviction algorithm (lru, lfu, clock, cawolfu, arc*). |
186+
| `WithEvictionShardCount` | Number of eviction-algorithm shards (default 32; 1 disables sharding). |
180187
| `WithMaxEvictionCount` | Cap number of items evicted per cycle. |
181188
| `WithMaxCacheSize` | Max cumulative serialized item size (bytes). |
182189
| `WithStatsCollector` | Choose stats collector implementation. |
@@ -191,9 +198,33 @@ if err != nil {
191198
| `WithDistSeeds` | (DistMemory) Static seed addresses to pre-populate membership. |
192199
| `WithDistTombstoneTTL` | (DistMemory) Retain delete tombstones for this duration before compaction (<=0 = infinite). |
193200
| `WithDistTombstoneSweep` | (DistMemory) Interval to run tombstone compaction (<=0 disables). |
201+
| `WithDistHTTPLimits` | (DistMemory) Body / response / timeout / concurrency caps for the dist HTTP server + auto-client. |
202+
| `WithDistHTTPAuth` | (DistMemory) Bearer-token auth (`Token`) plus optional `ServerVerify` / `ClientSign` hooks. |
194203

195204
*ARC is experimental (not registered by default).
196205

206+
### Type-safe access (`Typed[V]`)
207+
208+
The untyped `HyperCache.Get` returns `(any, bool)`, so callers must
209+
type-assert at every call site. `hypercache.NewTyped[T, V]` wraps an
210+
existing `*HyperCache[T]` to provide a compile-time-typed surface
211+
without changing the underlying storage:
212+
213+
```go
214+
hc, _ := hypercache.NewInMemoryWithDefaults(ctx, 10_000)
215+
sessions := hypercache.NewTyped[backend.InMemory, *Session](hc)
216+
217+
_ = sessions.Set(ctx, "u:42", &Session{UserID: "u-42"}, time.Hour)
218+
s, ok := sessions.Get(ctx, "u:42") // s is *Session — no type assert
219+
```
220+
221+
Multiple `Typed[V1]`, `Typed[V2]` views can share one underlying
222+
cache over disjoint keyspaces. Wrong-type reads return `(zero, false)`
223+
by default (fail-soft); use `GetTyped` for an explicit
224+
`sentinel.ErrTypeMismatch`. See
225+
[docs/rfcs/0002-generic-item-typing.md](docs/rfcs/0002-generic-item-typing.md)
226+
for the design and the v3 deep-generics roadmap.
227+
197228
### Redis / Redis Cluster notes
198229

199230
When using Redis or Redis Cluster, item size accounting uses the configured serializer (e.g. msgpack) to align in-memory and remote representations. Provide the serializer via backend options (`WithSerializer` / `WithClusterSerializer`).
@@ -222,16 +253,49 @@ Current capabilities (implemented):
222253
- Lightweight gossip snapshot exchange (in-process only).
223254
- Rebalancing (primary change & lost ownership migrations) with batching and concurrency throttling metrics.
224255
- Latency histograms for Get/Set/Remove.
256+
- HTTP transport hardening: bounded request/response bodies, idle-connection timeout, concurrency cap, bearer-token auth, TLS / mTLS via `*tls.Config`, lifecycle-context cancellation on `Stop`, and surfaced listener errors. See "Transport hardening" below.
225257

226258
Limitations / not yet implemented:
227259

228-
- Replica-only ownership diff migrations.
229260
- Full gossip-based dynamic membership & indirect probing.
230261
- Advanced versioning (HLC / vector clocks).
231262
- Tracing spans for distributed operations.
232-
- Security (TLS/mTLS, auth) & compression.
263+
- Compression on the wire.
233264
- Persistence / durability (out of scope presently).
234265

266+
#### Transport hardening (since v2.0.0)
267+
268+
The dist HTTP server and the auto-created HTTP client share a single configuration surface — apply the same option to every node in the cluster.
269+
270+
```go
271+
// 1) Limits: body caps, timeouts, concurrency, optional TLS.
272+
limits := backend.DistHTTPLimits{
273+
BodyLimit: 16 * 1024 * 1024, // server inbound cap
274+
ResponseLimit: 16 * 1024 * 1024, // client inbound cap
275+
IdleTimeout: 60 * time.Second,
276+
TLSConfig: tlsConfig, // non-nil enables HTTPS on both sides
277+
}
278+
279+
// 2) Auth: a shared bearer token covers most clusters; ServerVerify /
280+
// ClientSign hooks are escape hatches for JWT, mTLS-derived
281+
// identity, HMAC, etc.
282+
auth := backend.DistHTTPAuth{Token: "shared-cluster-secret"}
283+
284+
bi, _ := backend.NewDistMemory(ctx,
285+
backend.WithDistNode("nodeA", "127.0.0.1:7001"),
286+
backend.WithDistReplication(3),
287+
backend.WithDistHTTPLimits(limits),
288+
backend.WithDistHTTPAuth(auth),
289+
)
290+
```
291+
292+
Operational helpers:
293+
294+
- `DistMemory.LifecycleContext()` — context derived from the constructor's; canceled on `Stop()`. In-flight handlers and replica forwards observe `Done()`.
295+
- `LastServeError()` on both `distHTTPServer` and `ManagementHTTPServer` — replaces the prior silent-swallow of listener-loop crashes.
296+
297+
Defaults if `WithDistHTTPLimits` is not supplied: 16 MiB body cap, 5 s read/write/client timeouts, 60 s idle, fiber's 256 KiB concurrency cap. Auth is disabled by default.
298+
235299
#### Rebalancing & Ownership Migration (Experimental Phase 3)
236300

237301
The DistMemory backend includes an experimental periodic rebalancer that:
@@ -283,7 +347,7 @@ Test helpers `AddPeer` and `RemovePeer` simulate join / leave events that trigge
283347
| Advanced versioning (HLC/vector) | Planned |
284348
| Client SDK (direct routing) | Planned |
285349
| Tracing spans | Planned |
286-
| Security (TLS/auth) | Planned |
350+
| Security (TLS/auth) | Done (since v2.0.0; see "Transport hardening") |
287351
| Compression | Planned |
288352
| Persistence | Out of scope (current phase) |
289353
| Chaos / fault injection | Planned |

0 commit comments

Comments
 (0)