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>
- Remove unused services block that caused CI failure
(Gitea runner doesn't support --name/-p in options field)
- Update build tag to modern //go:build syntax (Go 1.17+)
The workflow already manually installs and starts NATS with JetStream,
making the services block redundant.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit adds comprehensive integration tests for JetStreamEventStore
that validate production event store behavior against a real NATS server.
Tests include:
- Stream creation and configuration
- SaveEvent persistence to JetStream
- GetEvents retrieval in correct order
- GetLatestVersion functionality
- Snapshot save/load operations
- Namespace isolation between stores
- Concurrent writes and version conflict handling
- Persistence across connection disconnects
- Multiple store instance coordination
Also updates CI workflow to run integration tests with a NATS server
enabled with JetStream.
Closes#10
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add support for optional namespace prefixes on JetStreamEventStore streams
to enable complete namespace isolation at the storage level:
- Add Namespace field to JetStreamConfig
- Add NewJetStreamEventStoreWithNamespace convenience constructor
- Prefix stream names with sanitized namespace when configured
- Add GetNamespace and GetStreamName accessor methods
- Add unit tests for namespace functionality
- Document namespace-scoped stores in CLAUDE.md
The namespace prefix is sanitized (spaces, dots, wildcards converted to
underscores) and prepended to the stream name, ensuring events from one
namespace cannot be read from another namespace's store while maintaining
full backward compatibility for non-namespaced stores.
Closes#19
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add ReplayError and ReplayResult types to capture information about
malformed events encountered during replay. This allows callers to
inspect and handle corrupted data rather than having it silently skipped.
Key changes:
- Add ReplayError type with sequence number, raw data, and underlying error
- Add ReplayResult type containing both successfully parsed events and errors
- Add EventStoreWithErrors interface for stores that can report replay errors
- Implement GetEventsWithErrors on JetStreamEventStore
- Update GetEvents to maintain backward compatibility (still skips malformed)
- Add comprehensive unit tests for the new types
This addresses the issue of silent data loss during event-sourced replay
by giving callers visibility into data quality issues.
Closes#39
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add config structs with sensible defaults for tunable parameters:
- JetStreamConfig for stream retention (1 year) and replica count (1)
- HashRingConfig for virtual nodes per physical node (150)
- ShardConfig for shard count (1024) and replication factor (1)
Each component gets a new WithConfig constructor that accepts custom
configuration, while the original constructors continue to work with
defaults. Zero values in configs fall back to defaults for backward
compatibility.
Closes#38
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add Metadata field (map[string]string) to Event struct with omitempty
- Add helper methods for common metadata: SetCorrelationID/GetCorrelationID,
SetCausationID/GetCausationID, SetUserID/GetUserID, SetTraceID/GetTraceID,
SetSpanID/GetSpanID
- Add WithMetadataFrom helper for copying metadata between events
- Add metadata key constants for standard fields
- Add comprehensive unit tests for metadata serialization and helpers
- Add store tests verifying metadata persistence
Closes#7
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add ErrVersionConflict error type and VersionConflictError for detailed
conflict information
- Implement version validation in InMemoryEventStore.SaveEvent that rejects
events with version <= current latest version
- Implement version validation in JetStreamEventStore.SaveEvent with version
caching for performance
- Add comprehensive tests for version conflict detection including concurrent
writes to same actor
- Document versioning semantics in EventStore interface and CLAUDE.md
This ensures events have monotonically increasing versions per actor and
provides clear error messages for version conflicts, enabling optimistic
concurrency control patterns.
Closes#6
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Test SaveEvent persists events correctly (single, multiple, multi-actor)
- Test GetEvents retrieves events in insertion order
- Test GetEvents with fromVersion filtering
- Test GetLatestVersion returns correct version
- Test behavior with non-existent actor IDs (returns empty/zero)
- Test concurrent access safety with race detector
- Add mutex protection to InMemoryEventStore for thread safety
Closes#3
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>