Releases: bay0/kvs
v0.0.2
Added
Core Features
-
Exists(key string) bool- Check key existence without error handling- O(1) operation with no error returns
- TTL-aware (returns false for expired keys)
- Thread-safe with passive cleanup
-
Len() int- Get total key count in O(1) time- Uses atomic counter for lock-free reads
- Maintained automatically across all operations
- Always accurate, never stale
TTL Support (Time-To-Live)
-
SetWithTTL(key string, val Value, ttl time.Duration) error- Redis-like key expiration- Hybrid cleanup strategy (active + passive)
- Background goroutine with 50ms precision (±50ms)
- Zero TTL treated as no expiration
- Negative TTL returns
ErrInvalidTTL - Absolute timestamps persisted to snapshots
-
Close() error- Graceful shutdown- Stops background TTL cleanup goroutine
- Idempotent (safe to call multiple times)
- 5-second timeout for cleanup termination
- Post-close operations return
ErrStoreClosed
Batch Operations
-
BatchGet(keys []string) (*BatchGetResult, error)- Retrieve multiple keys efficiently- Per-shard locking (not per-key)
- Complexity: O(N + M) where N=keys, M=shards
- Partial success semantics
- Returns both successful values and per-key errors
-
BatchSet(items map[string]Value) (*BatchSetResult, error)- Store multiple key-value pairs- Optimized per-shard locking
- Complexity: O(N + M) where N=items, M=shards
- Returns success count and per-key errors
- Removes any existing TTL (use SetWithTTL for TTL)
-
BatchDelete(keys []string) (*BatchDeleteResult, error)- Remove multiple keys- Per-shard locking optimization
- Complexity: O(N + M) where N=keys, M=shards
- Returns deletion count and per-key errors
- Handles duplicate keys gracefully
Custom Hash Functions
NewKeyValueStoreWithHash(numShards int, hashFunc HashFunc) (*KeyValueStore, error)- Pluggable hash function support
- Default: FNV-1a hashing
- Hash function requirements:
- Must be deterministic
- Must return [0, numShards)
- Must be thread-safe
- Validation: Returns
ErrNilHashFuncif nil
Snapshot Persistence
-
SaveSnapshot(filepath string) error- Save store state to disk- Point-in-time consistent snapshot
- Gob encoding with CRC32 integrity checking
- Versioned binary format (magic: "KVS1", version: 1)
- TTL metadata preserved (absolute timestamps)
- Thread-safe: RLock per shard (concurrent with reads)
- Does not modify store contents
-
LoadSnapshot(filepath string) error- Restore store from disk- Replace semantics (clears existing data atomically)
- Expired keys automatically skipped during load
- CRC32 checksum validation
- Version compatibility checking
- All-or-nothing: corruption leaves store unchanged
Data Structures
New Result Types
BatchGetResult- ContainsValuesmap andErrorsmapBatchSetResult- ContainsSuccessfulcount andErrorsmapBatchDeleteResult- ContainsDeletedcount andErrorsmap
Internal Enhancements
- Added
ttls map[string]time.Timeto shard structure for TTL tracking - Added atomic
keyCount int64to KeyValueStore for O(1) Len() - Added
cleanupCtx context.Contextfor goroutine lifecycle management - Added
cleanupStop context.CancelFuncfor graceful shutdown - Added
closed int32atomic flag for store state tracking - Added
cleanupDone chan struct{}for synchronization
Fixed
-
Snapshot CRC calculation - Fixed incorrect use of
file.WriteTo()which doesn't exist on*os.File- Changed to
io.Copy(crc, file)for proper checksum calculation - Added proper error handling with wrapped errors
- Changed to
-
Test error handling - Fixed 22 unchecked error returns in test suite
- All
Set,Delete,BatchSet,BatchDeletecalls now checked - Added proper
t.Fatalf()for setup failures - Added proper
t.Errorf()for operation failures
- All
-
Code quality - Removed unused
shardKeystype in batch.go- Eliminated linter warnings
- Improved code maintainability
Performance Improvements
- O(1) Len() operation - Atomic counter instead of iterating all shards
- Batch operations - Per-shard locking reduces lock contention by N/M factor
- Passive TTL cleanup - Only cleanup on access, reducing background overhead
- Active TTL cleanup - Background goroutine runs every 50ms (configurable precision)
Error Handling
New Error Codes
ErrInvalidShardIndex- Shard index out of boundsErrCorruptedSnapshot- Snapshot file corrupted or invalid magic bytesErrUnsupportedVersion- Snapshot version not supportedErrInvalidTTL- Negative TTL value providedErrStoreClosed- Operation attempted on closed storeErrNilHashFunc- Nil hash function provided to NewKeyValueStoreWithHash
Breaking Changes
API Changes
-
NewKeyValueStore(numShards int) (*KeyValueStore, error)- Now returns error- Previously returned only
*KeyValueStore - Now validates
numShards > 0and returnsErrInvalidNumShardsif invalid - Migration: Add error handling:
store, err := kvs.NewKeyValueStore(10)
- Previously returned only
-
Keys() []string- Now returns([]string, error)- Previously returned only
[]string - Added error return for consistency (currently always returns nil error)
- Migration: Add error handling:
keys, err := store.Keys()
- Previously returned only
Behavioral Changes
-
Regular
Set()now removes TTL - CallingSet()on a key with TTL removes the TTL- Previously: No TTL support existed
- Now: Use
SetWithTTL()to maintain or update TTL - Migration: If you want TTL, use
SetWithTTL()instead ofSet()
-
Background goroutine lifecycle - Store now starts background cleanup goroutine
- Requires calling
Close()to prevent goroutine leaks - Migration: Always use
defer store.Close()after creating store.
- Requires calling
-
Add Close() calls:
// New requirement in v2.0 store, _ := kvs.NewKeyValueStore(10) defer store.Close() // Must call to stop background goroutines
-
Use new features:
// Check existence without error handling if store.Exists("key") { // Key exists } // Get count efficiently count := store.Len() // Use TTL for temporary data store.SetWithTTL("session", data, 5*time.Minute) // Batch operations for better performance result, _ := store.BatchGet([]string{"key1", "key2", "key3"}) // Persist to disk store.SaveSnapshot("/path/to/backup.kvs")
First release
Basic functions.
Full Changelog: https://github.com/bay0/kvs/commits/v0.0.1