You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
The overall architecture is solid — leader election via epoch-fenced CAS on udb_lease, SKIP-LOCKED group-commit batching, versioned BARE wire format, and a dedicated LISTEN connection for wakeup fanout. The design correctly mirrors several FDB invariants (monotone watermark, conflict-range OCC, versionstamp ordering). The issues below range from correctness concerns to style violations per CLAUDE.md and minor dead code.
Bug / Correctness
1. wait_for_leader polls with sleep — should use watch::changed()
PostgresShared already holds a watch::Sender<Option<LeaseInfo>> (lease_tx). CLAUDE.md explicitly bans loop { check; sleep } poll patterns: "Never poll a shared-state counter with loop { if ready; sleep(Nms).await; }. Pair the counter with a … watch::channel that every … site pings."wait_for_leader should subscribe to shared.lease_rx with changed().await (plus a timeout) instead of sleeping. This is the hot path during leader transitions — every pending commit blocks on it.
2. udb_commit_requests_pending index missing epoch column — suboptimal under failover
CREATEINDEXIF NOT EXISTS udb_commit_requests_pending
ON udb_commit_requests (id) WHERE status ='pending';
The drain query filters WHERE status = 'pending' AND epoch = $1. The partial index covers only status = 'pending', so Postgres scans all pending rows to filter by epoch. After a failover, stale-epoch pending rows accumulate until GC (up to 60 s), and the new leader's drain scans all of them on every batch. A composite index (epoch, id) WHERE status = 'pending' would fix this.
3. connect_and_run listener race — new subscriptions can be silently skipped
let poll_handle = tokio::spawn(asyncmove{Self::poll_connection(connection, ...).await});// ... re-LISTEN loop ...*client.lock().await = Some(new_client);// stored only after all LISTENs
client is set to Some(new_client) only after the re-LISTEN loop completes. If listen() is called concurrently while the loop is mid-execution, client is still None and the LISTEN is silently skipped. The subscription won't be registered until the next reconnect. Consider storing the client earlier or tracking pending channels and replaying them once the client is set.
4. table_stats CTE in handle_get_estimated_range_size is dead code
table_stats AS (
SELECT reltuples::bigintas total_rows
FROM pg_class WHERE relname ='kv'AND relkind ='r'
)
SELECT CASE WHEN r.estimated_count=0 THEN 0
ELSE (r.sample_size*100)::bigint
END
FROM range_stats r, table_stats t
total_rows from table_stats is never referenced in the projection. The CTE and cross-join add noise and the cross-join's single-row assumption would silently corrupt results if the table were renamed or absent. Either remove table_stats entirely, or wire total_rows into the estimation logic as intended.
5. GC query deletes all rows, but comment says "terminal and orphaned"
/// Terminal and orphaned commit-request rows older than this are garbage collected.
..."DELETE FROM udb_commit_requests WHERE created_at < now() - ($1::bigint * interval '1 second')"
The query has no status filter — it deletes all rows (including pending) older than 60 s. The comment is misleading. While await_result handles a GC'd row gracefully (Ok(None) → NotCommitted), the comment should say "all rows" or the query should filter to terminal statuses only if pending rows should be spared. At minimum the doc comment must match the implementation.
let _ = response.send(Err(anyhow!("postgres transaction connection failed")));
CLAUDE.md: "Prefer .context() over the anyhow! macro." Use anyhow::Error::msg("…") or attach .context("…") to the originating error where available. There are multiple repetitions of this inside fail_receiver that should all be updated.
7. commit() and commit_ref() have identical bodies
let status = match status.as_str(){"committed" => Status::Committed,"conflict" => Status::Conflict,
_ => Status::Pending,// silently handles unknown statuses};
Any unexpected string (e.g., a future 'claimed' intermediate status) is silently treated as Pending, causing waiters to hang until timeout. At minimum add an explicit "pending" arm and log a tracing::warn! for the fallback. CLAUDE.md prefers exhaustive matching on closed value sets.
Performance
9. seq_high reads last_value before first nextval — off-by-one at startup
let seq_high:i64 = conn
.query_one("SELECT last_value FROM udb_version_seq",&[])
...
Ok(durable.max(seq_high).max(0)asu64)
Postgres sequences initialized with START WITH 1 return last_value = 1 even before the first nextval (when is_called = false). On a fresh database, recovery_floor returns 1, which causes every early commit with read_version = 0 to hit the cold-window guard unnecessarily. Check is_called from pg_sequences before trusting last_value, or start the sequence at 0.
10. Unused dependencies: base64 and tokio-util in Cargo.toml
engine/packages/universaldb/Cargo.toml
Both crates appear to have been added in this PR but are not referenced in any new source files. These should be removed to avoid bloating build times.
Minor / Non-blocking
11. LEASE_TTL_SECS passed as string for interval arithmetic
now() + ($3||' seconds')::interval
Using $3 * interval '1 second' with $3 typed as BIGINT is more idiomatic Postgres and avoids the string cast.
12. recovery_floor opens two sequential pool connections
Both current_durable_version and seq_high reads each check out a pool connection. A single query joining udb_lease and reading udb_version_seq would reduce round-trips and pool pressure at startup.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
No description provided.