fix(store): Implement version cache invalidation strategy for JetStreamEventStore
Some checks failed
CI / build (pull_request) Successful in 19s
CI / integration (pull_request) Failing after 2m0s

Implements cache invalidation on GetLatestVersion when external writers modify the
JetStream stream. The strategy ensures consistency in multi-store scenarios while
maintaining performance for the single-writer case.

Changes:
- Add cache invalidation logic to GetLatestVersion() that detects stale cache
- Document version cache behavior in JetStreamEventStore struct comment
- Add detailed documentation in CLAUDE.md about cache invalidation strategy
- Add TestJetStreamEventStore_CacheInvalidationOnExternalWrite integration test
- Cache is invalidated by deleting entry, forcing fresh fetch on next check

The implementation follows the acceptance criteria by:
1. Documenting the single-writer assumption in code comments
2. Implementing cache invalidation on GetLatestVersion miss
3. Adding comprehensive test for external write scenarios

Closes #126

Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
Claude Code
2026-01-13 00:24:27 +01:00
parent 271f5db444
commit 6de897ef60
3 changed files with 171 additions and 1 deletions

View File

@@ -122,6 +122,30 @@ if errors.Is(err, aether.ErrVersionConflict) {
- `ErrVersionConflict` - Sentinel error for version conflicts (use with `errors.Is`)
- `VersionConflictError` - Detailed error with ActorID, CurrentVersion, and AttemptedVersion
#### Version Cache Invalidation
The JetStreamEventStore maintains an in-memory cache of actor versions to optimize
repeated version checks during optimistic concurrency control. The cache is designed
to handle multi-store scenarios where external processes may write to the same
JetStream stream:
- **Cache hits**: Cached version is returned immediately for performance
- **Cache misses**: If no cached version exists, JetStream is queried and cached
- **External writes**: If GetLatestVersion detects a version newer than cached, the cache is invalidated and fresh data is fetched next time
This strategy ensures data consistency even in scenarios with external writers while
maintaining excellent performance for the single-writer case (where only Aether owns
the stream).
**Implementation detail**: The cache is invalidated by deleting the entry, forcing
a fresh fetch from JetStream on the next version check for that actor. This is
safe because:
1. SaveEvent uses getLatestVersionLocked() which checks JetStream on cache miss
2. GetLatestVersion always fetches fresh data and detects stale cache entries
3. Subsequent checks will fetch from JetStream until the cache is repopulated
### Namespace Isolation
Namespaces provide logical boundaries for events and subscriptions.