Packages

An Elixir library for real-time state sync and delta tracking, with ETS, Redis, and periodic cleanup support.

Current section

Files

Jump to
chord CHANGELOG.md
Raw

CHANGELOG.md

# Changelog
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/).
## [0.3.1] - 2026-03-08
### Fixed
- **Redis `delete_deltas_by_time` deleting all deltas**: ZSET score is `version` but cleanup queried by timestamp, causing `ZREMRANGEBYSCORE` to remove everything. Now correctly fetches members, filters by `inserted_at` from JSON payload, and removes only stale entries.
- **`merge_deltas` crash on nested added-then-removed sequences**: `original_old_value/1` raised `FunctionClauseError` when encountering nested delta maps without an `:action` key. Added catch-all clause.
- **Double-wrapped error tuples in `process_update`**: `{:error, reason}` from backend was wrapped into `{:error, {:error, reason}}`. Now correctly passes through.
- **ETS table initialization race condition**: Two processes could race on `ensure_table_exists`, with the second crashing on `:ets.new` for an already-existing named table. Replaced TOCTOU `if :ets.info == :undefined` check with `try/rescue ArgumentError`.
- **Formatter key path corruption at 3+ nesting levels**: `format_change` produced nested lists like `[[:a, :b], :c]` instead of flat `[:a, :b, :c]` for deeply nested deltas. Fixed path construction to use `List.wrap(key) ++ List.wrap(nested_key)`.
- **`sync_context` crash when `delta_threshold` is `nil`**: `determine_sync_action` performed arithmetic on `nil`, raising `ArithmeticError`. Now guards against nil threshold.
- **Cleanup server timer not cancelled on interval update**: `update_interval` updated state but left the old `Process.send_after` timer pending. Now stores `timer_ref` in state and cancels it before rescheduling.
- **Redis `scan_keys` silently swallowing errors**: SCAN failures returned partial results with no indication. Now logs `Logger.warning` on failure.
- **Telemetry documentation inaccuracies**: Removed false claim that `:result` is included in stop event metadata. Corrected "All events" to "Context events" for `:context_id` metadata (cleanup events pass empty metadata).
- **Duplicate "Context export" bullet** in `Chord` moduledoc.
- **Missing `@impl true`** on catch-all `handle_info` in `Chord.Cleanup.Server`.
- **`ManagerTest` using `async: true`** while mutating global `Application` env. Changed to `async: false`.
- **`DeltaTest` missing `async: true`**: Pure function tests now run concurrently.
## [0.3.0] - 2026-03-07
### New Features
- **Telemetry instrumentation**: All public `Chord.Context.Manager` and `Chord.Cleanup` operations now emit `:telemetry` events, enabling monitoring and metrics integration.
- Context events: `[:chord, :context, :set|:get|:update|:delete|:sync, :start|:stop|:exception]`
- Cleanup events: `[:chord, :cleanup, :run, :start|:stop|:exception]`
- **CI/CD pipeline**: Added GitHub Actions workflow with matrix testing (Elixir 1.17/1.18, OTP 27), format checking, warnings-as-errors compilation, and Dialyzer static analysis.
- **Dialyzer support**: Added `dialyxir` for static type analysis with all issues resolved.
### Improvements
- **ETS backend rewrite**: Replaced composite key with `:ordered_set` table by simple key with `:set` table and atomic `ets.insert` upsert. Context lookups use O(1) `ets.lookup` instead of `match_object`. Added `:persistent_term` caching for table initialization. Benchmarks show 17-83% performance improvement across scenarios.
- **Optional dependencies**: `redix` and `jason` are now optional — only needed when using the Redis backend.
- **Redis SCAN**: Replaced `KEYS` command with cursor-based `SCAN` for production safety with large keyspaces.
- **Lazy Logger calls**: All Logger calls now use lazy evaluation to avoid unnecessary string allocations.
- **Cleanup crash resilience**: Cleanup server now wraps `periodic_cleanup` in try/rescue so the GenServer survives and reschedules even if a cleanup run raises.
- **Cleanup deduplication**: `cleanup_deltas_by_time` now deduplicates by `context_id` before calling the backend, avoiding redundant delete calls when multiple stale deltas exist for the same context.
- **Cleanup error handling**: All backend listing calls in cleanup (`list_contexts`, `list_deltas`, `list_contexts_with_delta_counts`) now gracefully handle `{:error, _}` responses instead of crashing.
### Fixed
- **Delta merge `old_value` preservation**: `merge_deltas/1` now correctly preserves the original `old_value` when merging chained modifications (e.g., added -> modified).
- **Delta merge falsiness bug**: `merge_deltas/1` no longer incorrectly discards `false` as an `old_value` due to `||` operator treating it as falsy.
- **Delta merge removed-then-added**: `merge_deltas/1` now correctly handles re-addition after removal (e.g., removed -> added produces `:modified` instead of silently dropping the re-add).
- **Empty delta list merge**: `merge_deltas([])` now returns `%{}` instead of crashing.
- **Nil value semantics**: Delta calculation now distinguishes between absent keys and keys with `nil` values using `is_map_key/2`.
- **Sync crash on empty deltas**: `sync_deltas_or_fallback` now pattern matches `{:ok, [_ | _]}` to prevent issues with empty delta lists.
- **ETS threshold inversion**: `delete_deltas_exceeding_threshold` now correctly keeps the newest versions instead of the oldest.
- **Context initialization error handling**: `get_or_initialize_context` now handles all error types, not just `:not_found`.
- **Delta formatter callback spec**: Fixed `Chord.Delta.Formatter.Behaviour` to accept both `map()` and `list(map())` return types.
- **Flaky async tests**: Cleanup tests now use `async: false` to prevent global `Application` env interference between test modules.
- **Mox verification**: All mock-based tests now use `Mox.verify_on_exit!` to ensure expectations are actually verified.
## [0.2.0] - 2025-01-27
### New Features
- Added time_unit configuration to allow users to define the time unit (:second or :millisecond) for generating timestamps.
### Improvements
- Delta calculation now handles nested structures more effectively. Example:
- Previous output:
```elixir
%{a: %{value: %{b: %{c: %{d: "d"}}}, action: :added}}
```
- New output:
```elixir
%{a: %{b: %{c: %{d: %{value: "d", action: :added}}}}}
```
This ensures a more logical representation of changes.
- The default delta formatter was redesigned to produce an output format that is easier to process, serialize, and consume. It now includes:
- Simplified structures.
- Improved usability for downstream consumers.
- Enhanced documentation across modules to improve usability and clarity.
- Other changes:
- Various minor adjustments and refinements for consistency and maintainability.
## [0.1.4] - 2025-01-22
### Added
- Enhanced `README.md` with detailed examples for using the Chord library.
- Unit tests for `calculate_delta` to handle `nil` values and ensure proper behavior when handling nested maps.
- Project logo
### Changed
- Updated `delta_ttl` configuration to use seconds for consistency across the library.
- Improved `calculate_delta` function to:
- Handle `nil` values appropriately.
- Mark changes as `:modified` when the old value is `nil`.
### Fixed
- Bug in Redis backend:
- `fetch_delta_counts/1` now correctly splits keys using `String.split/3` with `parts: 3` to handle keys with more than two colons.
## [0.1.3] - 2025-01-06
### Fixed
- Updated README.md to include missing details.
## [0.1.2] - 2025-01-06
### Fixed
- Fixed delta merging logic to correctly handle nested maps and ensure all changes are applied.
## [0.1.1] - 2025-01-05
### Fixed
- Corrected Redis configuration example in documentation.
- Fixed an issue in `Chord.Delta.calculate_delta/2` to correctly handle nested maps and avoid returning empty maps for unchanged nested structures.
## [0.1.0] - 2024-12-31
### Added
- Initial release of the Chord library.
- Real-time state synchronization with full and delta-based updates.
- Support for ETS and Redis backends.
- Developer-friendly APIs for setting, updating, and syncing contexts.
- Periodic cleanup of stale contexts and deltas.
- Support for context export and restore.
- Benchmark results showcasing performance for ETS and Redis under stateless and stateful architectures.