Repository navigation
Conversation
When the TSO stream failed (PD stalled, restarted, lost leadership or closed
the stream), the oracle's background task exited and dropped the request
receiver, so every later `get_timestamp` on that oracle failed at once with
"TimestampRequest channel is closed". Only `RetryClient::reconnect` could
replace the oracle, and it skips reconnecting within a second of the previous
reconnect. The ten retries of one `get_timestamp` then ran out within
microseconds, and `begin` failed although PD was already serving again.
The oracle now survives the loss of its stream:
- the request receiver is shared by the oracle's successive streams, so the
request channel stays open for as long as the oracle exists;
- when a stream fails, the requests waiting on it fail with the stream's
error ("TSO stream failed: ..."), and a stream marked dead takes no more
requests from the channel;
- the background task reopens the stream for the next request, with backoff
(50 ms doubling to 1 s, reset after a stream that answered), so an idle
oracle does not keep dialling an unavailable PD;
- the task ends when every oracle handle is dropped, as before.
The oneshot channel now carries `Result<Timestamp>` so a failed stream reports
its cause. Opening the stream goes through a small private `TsoConnector`
trait so the unit tests can drive the oracle with in-memory streams.
Tested with unit tests for a failing stream, a refused open, concurrent
callers across repeated failures, and shutdown, and against a tiup playground
(v8.5.8) by SIGSTOPping PD for 5 s and 12 s under a begin/commit loop: before
the change two begins failed with "TimestampRequest channel is closed" after
PD resumed; after it, none failed.
Signed-off-by: Dinakaran <dinakaranvijayakumar@outlook.com>
|
[APPROVALNOTIFIER] This PR is NOT APPROVED This pull-request has been approved by: The full list of commands accepted by this bot can be found here. DetailsNeeds approval from an approver in each of these files:Approvers can indicate their approval by writing |
|
Navigate logical layers of code changes, visualize relationships, and explore their blast radius. No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Advanced Run ID: 📒 Files selected for processing (1)
Included review availability: This review used your included allowance. Your plan provides up to 2 included reviews per hour; 1 remain after this review. 📝 WalkthroughWalkthroughTimestampOracle now uses a persistent background task to manage TSO streams. The task reports stream failures to pending requests, reconnects with capped exponential backoff, and opens a replacement stream when a request is available. Added tests cover reconnects, concurrent requests, refused stream opens, and shutdown. ChangesTimestamp Oracle reconnect handling
Priority: ➖ Normal Estimated code review effort: 3 (Moderate) | ~25 minutes Change: Bug fix Sequence Diagram(s)sequenceDiagram
participant Caller
participant run_tso
participant TsoConnector
participant TsoRequestStream
participant PD_TSO
Caller->>run_tso: Submit timestamp request
run_tso->>TsoConnector: Open stream when a request is available
TsoConnector->>PD_TSO: Create TSO stream
run_tso->>TsoRequestStream: Serve stream
TsoRequestStream->>PD_TSO: Send request
PD_TSO->>run_tso: Return response
run_tso->>Caller: Send Ok(timestamp)
PD_TSO->>run_tso: Report stream failure
run_tso->>Caller: Fail pending requests
Merge Risk: ⚪ Minimal · up to No actionable merge-blocking risk was established for the reconnect change. It is mergeable after normal checks. Security Architecture ReviewSecurity architecture risk: 🔵 Low · up to The new recovery path reuses the existing PD connection and does not appear to expose a new endpoint or weaken its configured connection controls. The principal remaining availability limitations also existed before this change, although recovery under every failure mode has not been verified. Retained concerns Security review detailsSecurity Blast Radius
Trust Boundaries and Controls
Resilience and Maintainability Implications
Hardening Proposals
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
Warning Some tools did not complete. Review the errors below. 🔧 Clippy (1.98.1)Clippy execution failed Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Motivation
After a PD stall, restart or leadership blip,
beginfails witheven though PD is already serving again.
The cause is in
pd/timestamp.rs. When the TSO stream ends or errors,run_tsoreturns and drops the request receiver, and theTimestampOracleis dead from then on. The only way back isRetryClient::reconnect, which builds a newClusterand oracle. Butreconnectskips reconnecting when the previous reconnect was less thanRECONNECT_INTERVAL_SEC(1 s) ago. When the fresh stream also dies right away (PD answersErrGenerateTimestamp ... requested pd is not leader of clusterfor a moment after it resumes), each later attempt fails in microseconds on the closed channel. The reconnect is skipped every time, and all ten attempts ofretry_core!are used up within one interval.Reproduced on a tiup playground (v8.5.8): a loop runs
begin/put/commitevery 500 ms while PD getsSIGSTOPfor 5 s. AfterSIGCONT, two consecutive transactions failed withTimestampRequest channel is closed(one after waiting 4.7 s, one in 111 µs) before the client recovered. An older release in the same setup never recovered without rebuilding the client.What is changed and how it works
The oracle now survives the loss of its stream:
Arc<Mutex<Receiver>>). The request channel stays open for as long as anyTimestampOraclehandle exists.StreamState::failmarks it dead and fails the requests waiting on it withTSO stream failed: <cause>. A dead stream takes no more requests from the channel: the check runs under the pending-queue lock, so a request is either failed or left in the channel for the next stream, and never lost.None, and the task tells this apart from a stream failure.Result<Timestamp>, so callers see the stream's error instead of a bareRecvError.TsoConnectortrait (production:PdClient::tso), so the oracle can be unit-tested with in-memory streams.RetryClientis unchanged. A leader change still goes throughretry_core!andreconnect, which replace the oracle. The difference is that the retries of a failedget_timestampnow wait for the oracle's reopened stream instead of failing at once on a closed channel.Tests
pd::timestamp::tests, in-memory PD):requests_after_a_stream_failure_are_served_by_a_new_streama_refused_stream_is_reopened_with_backoffconcurrent_requests_survive_repeated_stream_failures: 32 callers, streams that fail after 3, 0 and 5 requests; all timestamps unique.the_background_task_ends_when_the_oracle_is_droppedSIGSTOPped for 5 s and then 12 s. No transaction failed after either stall. The client loggedTSO stream failed: ... not leader ...; reopening it in 50ms/100msand carried on.cargo test --lib,cargo clippy --all-targets --features integration-tests -- -D clippy::all,cargo fmt --check: clean. The failpoint (9), integration (27) and sync (28) suites pass on a tiup playground v8.5.8.Notes for reviewers
TimestampRequest channel is closed(orChannel(RecvError)) toTSO stream failed: ....TimestampRequest channel is closedcan still happen, but only when every oracle handle has been dropped.async-traitandfutureswere already used).Check list
Release note: the transaction client recovers from a PD stall without failing
beginwith "TimestampRequest channel is closed".Summary by CodeRabbit