Splits monolithic cluster.rs into tidal-server/src/cluster/ modules. Adds redeliver-missed relay, bounded HLC drift, lag tracking, and reconcile idempotence. Five new tier-3 test suites (chaos, lifecycle, multiproc, region, routes, runbook) all green. Docs, CHANGELOG, and ROADMAP updated with G4/G5/G6 known gaps.
11 KiB
Changelog
All notable changes to tidalDB will be documented in this file.
[Unreleased]
Added
M9 — Community Sync & Revocation
- Local embeddable profiles can opt into community personalization and safely leave/purge their contributions. New types:
SignalScope,CommunityId,Membership,MembershipEpoch,PolicyMetadata. Community signal reconciliation viaCrdtSignalStatewith commutative/associative/idempotent merge laws; membership-epoch revocation purges a departed member's contributed signals.
M10 — Governance & Agent Rights
- Community rules and agent-scoped permissions control what signals influence ranking: policy-metadata enforcement and agent-rights scoping wired into the signal-write and ranking paths.
Cluster mode (m8p10): true multi-process region nodes + full tier-3 UAT — M8 COMPLETE
- Multi-process cluster mode:
tidal-server cluster --region <name> [--data-dir <p>](envTIDAL_REGION) runs one process per region (RegionClusterState), each owning oneTidalDband oneGrpcTransportthat binds this region'sgrpc_addrand dials every sibling's realgrpc_addr— real process/host isolation. The topology requires per-regiongrpc_addrANDhttp_addrin this mode; single-process mode (no--region) is unchanged as the dev/demo default. Both modes stay behind the experimental gate (--experimental-cluster/TIDAL_ALLOW_EXPERIMENTAL_CLUSTER=1) with mode-specific WARN text. - New routes / behaviors on the multi-process surface:
GET /cluster/status/local(per-node status),GET /cluster/statusaggregates ALL regions with areachablefield (unreachable ⇒reachable:false+ worst-case lag),POST /cluster/reconcile {region}(cross-process CRDT snapshot exchange; idempotent — a repeat is an exact no-op on scores),POST /hardnegs {user_id,item_id}(user-scoped hide; converges via reconcile, filtered from/feed?user_id). Writes on a non-leader forward to the leader;?region=reads forward to the owning region; default reads are LOCAL in multi-process mode. Items/embeddings are leader-applied + HTTP-broadcast to peers with a per-peer report:POST /items→ 201{replicated_to, failed},POST /embeddings→ 200 with the same report on the leader path (NOT 204 — a 204 cannot carry the report; the forwarded/internal path stays 204)./sharded/*fans out across processes (degraded semantics preserved).promotefans out to all peers ({ok, leader, acked, failed}). /cluster/healis the single recovery verb: redelivers missed signal segments (gap-aware) AND re-broadcasts item metadata + embeddings to the healed region (idempotent upserts). After a partition the per-peer gRPC circuit breaker (threshold 5, reset 30s) is open, so re-issue/cluster/healuntil/cluster/statusshows lag 0.TIDAL_HLC_SKEW_MS(multi-process): signed ms offset applied to the process's HLC (reconcile LWW stamping only — not signal-decay timestamps); a test/ops escape hatch.- Tier-3 UAT over real OS processes (feature
cluster-e2e):cluster_multiproc(5),cluster_chaos(3, REAL network-partition injection via a root-free in-harness TCP relay proxy — the toxiproxy-style alternative the ROADMAP sanctions; iptables/pfctl remain an operator option),cluster_lifecycle(2, ±500ms clock skew + rolling upgrade with zero acknowledged-write loss),cluster_runbook(9, everydocs/runbooks/cluster.md§5–§11 operation), pluscluster_e2e(2, single-process smoke). Measured (localhost): replication p99 ~110–133ms (< 2s SLA), failover ~31–34ms (< 10s SLA), reconcile 0–1ms/side (< 100ms SLA).
Fixed
Four production bugs surfaced and fixed by the m8p10 tier-3 UAT
- Silent data loss on out-of-order ships. The replication applied high-water-mark swallowed sequence gaps when eager ships arrived out of order. The applied frontier is now contiguous with a bounded ahead-buffer, so a gap can never be skipped.
- Non-idempotent reconcile (score creep).
take_crdt_snapshotattributed replicated signal streams per-node, so reconciling already-converged nodes crept the decayed scores (0.5 → 0.375 → …). Contributions are now attributed to one canonical replication shard (ShardId::SINGLE), makingmergeidempotent — reconcile of converged nodes is an exact fixpoint. - Lag gauge conflated leader streams across a promotion. A converged node reported a
permanent phantom lag after a
/cluster/promotemoved leadership to a different shard. The lag gauge now tracks the leader high-water-mark per source shard (ReplicationLagGauge::leader_seqno_for), computing lag against the current leader. - Items missed during a broadcast were never backfilled. A node down/partitioned
during an item/embedding broadcast was permanently missing that data even at signal
lag 0.
/cluster/healnow re-broadcasts item metadata + embeddings to the healed region (idempotent upserts), making heal the single recovery verb.
Changed
Cluster mode (m8p8): real gRPC replication
tidal-server'sClusterStatenow wires each follower region to a realtidal-netGrpcTransport(self-loop over loopback gRPC) instead of in-process crossbeam channels — replication between regions traverses real gRPC/TCP (serialization, circuit breaker, HTTP/2). Closes M8 gap G1.- Follower gRPC ports are auto-allocated from the topology (
grpc_addroptional per region) and self-heal a transient bind race by retrying on a fresh port. - Blocking gRPC ships triggered by
POST /signalsandPOST /cluster/healare offloaded to a dedicated thread so they never block the axum reactor. ClusterStateis constructed off the async reactor (GrpcTransport::newblocks on its own runtime).- The experimental opt-in gate and
docker/cluster/Dockerfileare updated: single-process cluster mode is honest that it replicates over real gRPC but runs all regions in one process (no host/process isolation). (True multi-process region nodes shipped subsequently in m8p10 — see the m8p10 entry above.)
Docker build fixes (all three images now that tidal-server pulls tidal-net)
- Install
protobuf-compilerin the builder stage —tidal-net's build script runstonic-build, which needsprotocto compile the WAL-shipping.proto. - Pin the builder base to
bookworm(rust:1.91-bookworm/rust:1.91-slim-bookworm) so its glibc matches thedebian:bookworm-slimruntime; the default trixie base emitted alibmvec.so.1dependency absent on bookworm, aborting the binary at startup. Verified:docker runof the cluster image serves a functional 3-region cluster (write replicates to both followers over gRPC; region-pinned reads serve replicated data; SIGTERM exits 0). - New tests:
tidal-server/tests/cluster_grpc.rs(in-process gRPC replication + HTTP offload path) and a hardened tier-3cluster_e2e.rs(multi-process smoke- promote over real OS processes, feature-gated).
[0.1.0] - 2026-02-23
Added
Core Database Engine
TidalDbembeddable database withephemeral()andwith_data_dir()open modesSchemaBuilderfor defining signal types, decay parameters, and ranking profilesTidalDbBuilderfluent builder with schema, data directory, metrics, and rate limiter configuration
Signal System
- Typed signal recording with exponential decay scoring
- Hot-tier (DashMap) and warm-tier (BucketedCounter) signal storage
- Windowed aggregation:
OneHour,TwentyFourHours,SevenDays,AllTime - Signal velocity tracking
- WAL-backed signal durability with crash recovery
- Periodic signal checkpointing to fjall (every 30s)
- WAL compaction after each checkpoint
Retrieval (RETRIEVE query)
- 5-stage pipeline: universe, filter, score, diversify, return
- Filter expressions:
Eq,In,Gt,Lt,And,Or,Not,InCollection,InProgress,MinSignal,MaxSignal,NearLocation - Built-in ranking profiles:
trending,for_you,new,popular,recent, and 20+ more - Custom ranking profiles via
SchemaBuilder - Diversity enforcement (max N per category/creator)
- Sort modes:
Relevance,Trending,Newest,MostLiked,MostViewed,MostFollowed,AlphabeticalAsc/Desc,Shortest/Longest,LiveViewerCount,DateSaved, and more
Search (SEARCH query)
- BM25 full-text search via Tantivy
- Approximate nearest-neighbor (ANN) semantic search via USearch HNSW
- Reciprocal Rank Fusion (RRF) combining BM25 + ANN scores
- Creator search with
entity_kind(EntityKind::Creator) similar_to(EntityId)for content-based recommendations- Scope pre-filters:
Trending,CohortTrending,Following,Category,Collection - Autocomplete suggestions via
db.suggest()
Entity Model
- Three built-in entity types:
Item,User,Creator - Metadata storage as
HashMap<String, String> - Embedding slots (up to 4 per entity type) via USearch
- Relationships:
Follows,Blocks,Hide,Mute,InteractionWeight
Sessions
- Session lifecycle:
open_session,close_session - Cross-session preference vector updates (EMA blend)
- Session snapshots with signal state and preference vectors
- Session serialization format v0x03 with backward compatibility
Social Graph
- Creator follower/following indexes
- Cohort membership (user segments)
- CoEngagementIndex for co-viewing patterns with LRU eviction
- Social graph filter for "followed creator" content scoping
Collections
- Named collections with
Private,Shared,Publicvisibility create_collection,add_to_collection,remove_from_collection,list_collectionsFilterExpr::InCollectionfor collection-scoped retrieval- Saved searches with
save_search,list_saved_searches,retrieve_saved_search
Observability
enable_metrics(addr)-- Prometheus-format/metricsendpoint +/healthzJSON- 15+ metrics: signal writes, WAL lag, checkpoint age, degradation level, index health
tidaldb_checkpoint_failures_totalcounter for checkpoint monitoringTidalDb::diagnostics()-- structured health snapshot- WAL diagnostics and recovery tools
Safety
- Signal weight NaN/Inf validation (returns
TidalError::InvalidInput) - Metadata size bounds: 64 keys max, 8KB value max, 64KB total max
- Export request limit: 500K signals max per request
FilterExprcomplexity limit: 256 nodes max- Data directory lock (
tidaldb.lock) prevents dual-process corruption - Schema fingerprint persistence detects decay parameter changes on reopen
- Bounded
closed_sessionscache (10K max, LRU eviction) - Metrics server non-loopback bind warning
CLI (tidalctl)
tidalctlbinary for database inspection and diagnostics
RLHF / ML Export
db.export_signals(ExportRequest)-- WAL-based signal export for training datadb.user_session_summary(user_id, since_ns)-- aggregated session statistics
Stability
tidalDB 0.1.0 is pre-1.0. No API or data format stability guarantees are made for 0.x releases. Upgrade guides will be provided for each minor version bump. Do not upgrade 0.x to 0.y on a live data directory without reading the release notes.
Format based on Keep a Changelog