Skip to content

Releases: bay0/kvs

v0.0.2

12 Oct 21:16

Choose a tag to compare

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 ErrNilHashFunc if 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 - Contains Values map and Errors map
  • BatchSetResult - Contains Successful count and Errors map
  • BatchDeleteResult - Contains Deleted count and Errors map

Internal Enhancements

  • Added ttls map[string]time.Time to shard structure for TTL tracking
  • Added atomic keyCount int64 to KeyValueStore for O(1) Len()
  • Added cleanupCtx context.Context for goroutine lifecycle management
  • Added cleanupStop context.CancelFunc for graceful shutdown
  • Added closed int32 atomic 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
  • Test error handling - Fixed 22 unchecked error returns in test suite

    • All Set, Delete, BatchSet, BatchDelete calls now checked
    • Added proper t.Fatalf() for setup failures
    • Added proper t.Errorf() for operation failures
  • Code quality - Removed unused shardKeys type 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 bounds
  • ErrCorruptedSnapshot - Snapshot file corrupted or invalid magic bytes
  • ErrUnsupportedVersion - Snapshot version not supported
  • ErrInvalidTTL - Negative TTL value provided
  • ErrStoreClosed - Operation attempted on closed store
  • ErrNilHashFunc - 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 > 0 and returns ErrInvalidNumShards if invalid
    • Migration: Add error handling: store, err := kvs.NewKeyValueStore(10)
  • 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()

Behavioral Changes

  • Regular Set() now removes TTL - Calling Set() 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 of Set()
  • 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.
  1. Add Close() calls:

    // New requirement in v2.0
    store, _ := kvs.NewKeyValueStore(10)
    defer store.Close() // Must call to stop background goroutines
  2. 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

18 Feb 22:34

Choose a tag to compare