Repository navigation
Conversation
A silent TCP peer and repeated reconnects could leave login callers waiting without a useful terminal result. Carry one configurable deadline through TCP sign-in, reconnects, and transport cleanup so callers receive a distinct timeout with an unknown outcome after a possible write.
Passing the parsed timeout through the TCP options constructor keeps configuration assembly consistent. The test harness now accepts a timeout override and verifies both its default and custom values.
The response-read limit does not bound lock waits, reconnects, or post-login work. Start one request budget at the public login or auto-login entry, carry it through nested TCP operations, and isolate the connection when the outcome is unknown.
Client providers and Rust examples need to pass the configured login budget into TcpClientConfig. Add the shared CLI option and wire it into both construction paths.
The login deadline must start before the high-level client lock is acquired, while the shared client interface should not depend on a TCP policy type. Route the request budget through a transport policy and keep TCP expiration behavior in the TCP client.
The public login budget already bounds the connect coordinator wait. Remove its duplicate deadline path and keep the coordinator focused on sharing connection results.
Request callers currently switch between a task-local budget handle and a runner, which makes deadline propagation hard to follow. Expose one deadline lookup and one future runner, and keep timeout handling inside the module. Box budgeted futures so nested TCP login reconnects stay within the runtime thread stack.
The timeout policy currently applies only to TCP sign-in and credentialed connect. Document that boundary, verify other transports leave the policy unset, and share the reconnectable error check.
The login and connect timeout flow had accumulated nested budget wrappers around individual waits, making the control flow difficult to follow. Keep the deadline in a task-local RequestBudget and apply it at request entry points while leaving transport-specific framing safeguards in place.
A timed-out request can leave a transport task holding the stream lock. Route deadline cleanup through the TCP request policy so a busy lock still marks the connection stale, and report cleanup lock errors.
A request can expire while the stream or state lock is held, leaving the next connection attempt with stale transport and VSR session state. Keep timeout invalidation in the TCP client, let nested budgets run one cleanup callback, and expose it through a lightweight generic request policy. Remove the extra login replay loop so reconnect retains the existing attempt behavior.
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## master #4345 +/- ##
=============================================
- Coverage 87.77% 75.25% -12.53%
- Complexity 1575 1576 +1
=============================================
Files 1288 1289 +1
Lines 227249 222599 -4650
Branches 190686 186038 -4648
=============================================
- Hits 199478 167506 -31972
- Misses 23062 50152 +27090
- Partials 4709 4941 +232
🚀 New features to boost your workflow:
|
This was referenced Oct 5, 2026
Open
Member
|
Thanks @jiengup. CI needs two small updates:
It also needs a rebase to fix the conflicts. |
This branch has not been deployed
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
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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.
Closes #4262
Relates to #3419
Rationale
A TCP connection can stay open while the server never replies to login. The caller then has no bound on the complete login operation, particularly when it waits for a lock, reconnects, or follows a leader redirect. This is the client-side failure reported in #4262. Released clients will still need a server-side response or disconnect when an older, incompatible login frame arrives.
This is an incremental implementation of #3419's timeout proposal. It introduces a place for each transport and request category to choose a deadline and define what expiration does. The configured deadline currently applies only to TCP username/password login, TCP personal-access-token login, and
connect()when configured or remembered credentials require sign-in. Extending it to other request types and transports is follow-up work.What changed?
TCP now has a nonzero
request_timeoutsetting (30 seconds by default), exposed through the TCP config builder, connection string, and shared client arguments. A task-localRequestBudgetgives nested login, reconnect, and redirect calls one deadline; a transport-neutralClientRequestPolicysupplies the timeout and expiration action without making the high-level client own TCP connection state. On expiry, TCP marks the connection invalid and reconciles its stream and VSR session before reconnecting.Deadline coverage
IggyClientlogin and credentialedconnect(), the deadline starts before acquiring the high-level client read lock. It spans the awaited login or PAT login, logout-before-relogin, leader metadata lookup and redirection, recursive sign-in, and the connect coordinator.reestablish_aftersleeps, endpoint selection, TCP dial, optional TLS handshake, automatic or remembered sign-in, and leader settlement. All attempts share the caller's deadline.The configured
request_timeoutdoes not yet apply to ordinary TCP requests or to aconnect()that has no sign-in credentials. Ordinary TCP requests retain the existing response-read limit, which does not cover the entire operation. QUIC, WebSocket, and HTTP do not yet supply a request policy. The deadline is observed by async polling, so a blocked executor or long synchronous section can delay it; arbitrary spawned tasks do not inherit the task-local budget. A caller-supplied outer budget takes precedence over nested configured budgets. A send worker that cannot make progress may also delay deferred stream cleanup and the next reconnect, even though the caller has received the timeout error.Replacing the underlying client is another uncovered case.
IggyClient::client()exposes a shared lock, and a caller can use its write guard to replace an HTTPClientWrapperwith a TCP one.IggyClientcaches the timeout policy only when it is constructed, so the cached policy remainsNoneafter this replacement. A subsequentlogin_user()waits for the shared lock's read guard without starting the new TCP client's deadline; the TCP login code cannot enforce its timeout until it acquires that guard. In the regression test, a replacement TCP client configured for 50 ms still produced no login timeout after an outer 500 ms wait.Behavior after timeout and proposed extension
For the supported TCP login and credentialed-connect paths, the caller receives
RequestTimeoutOutcomeUnknown. That error deliberately does not claim the server did nothing. Expiration sets a persistent invalidation flag and tries to drop the socket and mark the client disconnected without waiting for locks held by an in-flight send. If cleanup cannot acquire a lock immediately, the send worker or the nextconnect()discards the old stream; reconnection resets the VSR session before a new request uses it. The expired login is not replayed across a new session after its deadline.For future coverage of all TCP requests, the same full-operation deadline and invalidation path could be reused, but an operation whose outcome is unknown must not be automatically replayed under a new VSR session. For QUIC, WebSocket, and HTTP, each transport needs its own expiration policy before the same caller-facing timeout contract is enabled; the right cleanup scope may differ for multiplexed connections and independent HTTP requests.
Follow-up API and design questions
client.login(...).with_timeout(10ms). The current async trait methods return futures directly, so fluent overrides need an API change or a new request-builder/operation-specific entry point. A prototype is deliberately excluded from this PR.Local Execution
cargo fmt --all,cargo sort --no-format --workspace,cargo clippy --all-features --all-targets -- -D warnings,cargo test -p iggy_common -p iggy,./scripts/ci/taplo.sh --check, and commit-time hooks passed on the implementation before the latest rebase. The timeout tests cover silent peers, partial replies, stalled writes, stream and connect-owner lock waits, PAT and auto login, retry bounds, and reconnect after expiration. The timeout implementation is unchanged by the rebase, but the exact rebasedHEADhas not had this full sequence or a fullprek runrerun.team-review-litewas run, and the implementation was revised to address its findings.iggy-server:0.9.0. In the no-reply case, the server was paused after the TCP connection was established.upstream/mastersnapshotRequestTimeoutOutcomeUnknownin about 30.01 s.AI Usage