Skip to content

fix: skip needless remote connects in local read paths - #115

Closed
lehuan5062 wants to merge 10 commits into
EpicGames:mainfrom
lehuan5062:fix/local-reads-skip-connect
Closed

fix: skip needless remote connects in local read paths#115
lehuan5062 wants to merge 10 commits into
EpicGames:mainfrom
lehuan5062:fix/local-reads-skip-connect

Conversation

@lehuan5062

Copy link
Copy Markdown
Member

Problem

#107 bounded the remote-connect stall with 5s transport timeouts, but several read verbs still drive a remote connect even when local data is what gets returned. Against an unreachable server, every such read still pays the bounded ~5-10s wait for nothing: the fetched remote value is discarded, or the connect happens before the code even checks whether the caller asked for local-only data. This slows down lore log <branch>, the desktop app, and my lore-web branch graph. Under --local/--offline the connect is also a contract violation, not just wasted time.

Change

  • revision::resolve (revision.rs): the branch@LATEST/HEAD path drove the connect before checking should_search_remote, so --local/--offline connected anyway. Reordered to match the sibling revision-number path (branch@<n>), which already did this correctly.
  • Explicit-branch revision history (revision/history.rs): connected unconditionally, then discarded the remote latest unless --remote was set. The fetch now only happens inside the --remote branch.
  • File history (file/history.rs): connected before taking the --local early-return. The local latest and the early-return now come first; default-mode reconciliation is unchanged.
  • auth::resolve_user_info (auth/userinfo.rs): the CLI resolves user display names after every history listing, and this drove a connect regardless of --local/--offline. It now skips resolution in those modes; callers fall back to the raw ids.

Validation

cargo build, cargo clippy -p lore-revision, cargo test -p lore-revision -> all clean.
New Rust unit test in lore-revision/tests/revision.rs for the local-only branch@LATEST resolve.
New integration test scripts/test/test_local_reads_skip_connect.py: seeds and pushes a branch on a dedicated server, kills it, then asserts --local reads return the correct local latest well under the connect timeout, and the default-mode read doesn't stack connect waits.
Existing suites test_branch.py, test_dirty.py, test_branch_switch_reconnect.py -> 89 passed. One failure, test_branch_merge_carries_dirty_through_clean_merge, is a pre-existing stack overflow already present on main, unrelated to this PR.
Measured against a killed server:

  • revision history --branch --local: ~4.1s -> ~150ms
  • file history --local: ~125ms
  • --remote still queries the server and fails fast when it's down

Verified default behavior with the server up is unchanged.

AI Assistance Disclosure

I used an LLM (Claude Code) to help with implementation, code comments, and documentation on this change, and to help word things clearly as a non-native English speaker. I reviewed and tested all the code myself for its correctness, security, and license compliance.

lehuan5062 and others added 9 commits July 11, 2026 21:20
Add 5-second connect timeouts to gRPC and QUIC transports, and time-box
the remote branch resolution in status() to 3 seconds. When the configured
remote is unreachable, status calls now degrade gracefully instead of
stalling for the full idle timeout (~30s for QUIC, 10s+ for retries).

Critical fixes:
1. Time-box the entire remote resolution operation (both repository.remote()
   and branch::load_remote()), not just the fast path. This ensures the
   3-second deadline covers the actual RPC calls that can hang indefinitely,
   even when the remote connection is already warm.

2. Fix available flag semantics: distinguish between "remote not reachable"
   (available=false) and "remote reachable but query failed" (available=true).
   A server that responds with a permission error or other non-branch-not-found
   error is still reachable and should not be reported as unavailable to users.

Changes:
- lore-transport: gRPC endpoint gets connect_timeout for initial connection
- lore-revision: extract resolve_remote_latest() helper that returns
  (latest, authorized, available) where available reflects connection success
  rather than query success; eliminate double remote() call on event emission
- tests: replace overfitted tautological test with two real unit tests

Signed-off-by: Huân Lê-Vương <65440815+lehuan5062@users.noreply.github.com>
clippy::doc_markdown (denied via -D warnings in the pre-commit hook)
flags bare type-name references in doc comments. CI caught this;
local clippy runs missed it because they weren't re-run after this
doc comment was added in a later revision of the same change.

Signed-off-by: Huân Lê-Vương <65440815+lehuan5062@users.noreply.github.com>
Drop the 3-second status-level deadline from resolve_remote_latest and rely
solely on transport-level connect timeouts (5s for gRPC handshake). The
status-level timeout was layering two separate concerns and timing out slow
lookups instead of bounding unreachable remotes.

The root cause is QUIC's 30-second idle timeout on unreachable remotes: UDP
offers no connection-refused signal, so every local status read would stall
~10–30s waiting for the timeout. Transport-level connect timeouts now bound
this wait and are the single timeout layer.

Update resolve_remote_latest to log previously-discarded errors at debug
level before degrading gracefully. Remove the hung-remote timeout test (now
invalid without a local timeout); add a failed-remote test mirroring terminal
state from a bounded transport connect. Update doc comments to explain the
degradation contract.

Signed-off-by: Huân Lê-Vương <65440815+lehuan5062@users.noreply.github.com>
Lore's comment standard calls for minimal, purposeful doc comments and no
restating of self-explanatory code. The rustdoc # Arguments/# Returns sections
here described obvious parameters (repository, branch_id) and are rare elsewhere
in the crate. Collapse to a terse summary that keeps only the non-obvious facts:
graceful NoRemote degradation and the available flag reflecting connectivity
rather than query success.

Signed-off-by: Huân Lê-Vương <65440815+lehuan5062@users.noreply.github.com>
Several read verbs drove a remote connect even when local data is what
gets returned, stalling for the bounded connect timeout against an
unreachable server:

- revision::resolve drove the connect before checking
  should_search_remote, so branch@LATEST under --local/--offline
  connected anyway; reorder the guard to match the @Number path.
- Explicit-branch revision history connected unconditionally and then
  discarded the remote latest unless --remote was set; move the fetch
  inside the --remote branch.
- File history connected before taking the --local early-return;
  compute the local latest and take the early-return first.
- auth::resolve_user_info (driven by the CLI after history listings to
  prettify user ids) connected regardless of --local/--offline; skip
  resolution there and fall back to raw ids.

With a killed server, revision history --local drops from ~4.1s to
~150ms; default-mode explicit-branch history no longer wastes a connect
in the history lookup itself. Guarded by a new Python integration test
against a killed server and a Rust unit test for the local-only
branch@LATEST resolve.

Signed-off-by: Huân Lê-Vương <65440815+lehuan5062@users.noreply.github.com>
@ajcarberry ajcarberry added the area:core Core library and its interfaces (lib, C API); revision, storage, transport, protocol internals label Aug 19, 2026
@mjansson

Copy link
Copy Markdown
Collaborator

@lehuan5062 Could you update this to the latest main code and resolve the conflicts?

…p-connect

Signed-off-by: Huân Lê-Vương <65440815+lehuan5062@users.noreply.github.com>

# Conflicts:
#	lore-revision/src/repository/status.rs
#	lore-revision/src/revision/history.rs
#	lore-transport/src/grpc/mod.rs
#	lore-transport/src/quic/client.rs
@mjansson mjansson added the ready-to-import Approved by Epic staff for import into Lore label Aug 31, 2026
@epic-lore-bot epic-lore-bot Bot added imported Imported into Lore for internal review and removed ready-to-import Approved by Epic staff for import into Lore labels Aug 31, 2026
@epic-lore-bot

epic-lore-bot Bot commented Aug 31, 2026

Copy link
Copy Markdown

Imported as Lore CR-499.

epic-lore-bot Bot pushed a commit that referenced this pull request Aug 31, 2026
**Problem**

#107 bounded the remote-connect stall with 5s transport timeouts, but several read verbs still drive a remote connect even when local data is what gets returned. Against an unreachable server, every such read still pays the bounded ~5-10s wait for nothing: the fetched remote value is discarded, or the connect happens before the code even checks whether the caller asked for local-only data. This slows down `lore log <branch>`, the desktop app, and [my lore-web](https://github.com/lehuan5062/HUANREAL-lore-web) branch graph. Under `--local`/`--offline` the connect is also a contract violation, not just wasted time.

**Change**

- `revision::resolve` (revision.rs): the `branch@LATEST`/`HEAD` path drove the connect before checking `should_search_remote`, so `--local`/`--offline` connected anyway. Reordered to match the sibling revision-number path (`branch@<n>`), which already did this correctly.
- Explicit-branch revision history (revision/history.rs): connected unconditionally, then discarded the remote latest unless `--remote` was set. The fetch now only happens inside the `--remote` branch.
- File history (file/history.rs): connected before taking the `--local` early-return. The local latest and the early-return now come first; default-mode reconciliation is unchanged.
- auth::resolve_user_info (auth/userinfo.rs): the CLI resolves user display names after every history listing, and this drove a connect regardless of `--local`/`--offline`. It now skips resolution in those modes; callers fall back to the raw ids.

**Validation**

cargo build, cargo clippy -p lore-revision, cargo test -p lore-revision -> all clean.
New Rust unit test in lore-revision/tests/revision.rs for the local-only `branch@LATEST` resolve.
New integration test scripts/test/test_local_reads_skip_connect.py: seeds and pushes a branch on a dedicated server, kills it, then asserts `--local` reads return the correct local latest well under the connect timeout, and the default-mode read doesn't stack connect waits.
Existing suites test_branch.py, test_dirty.py, test_branch_switch_reconnect.py -> 89 passed. One failure, test_branch_merge_carries_dirty_through_clean_merge, is a pre-existing stack overflow already present on main, unrelated to this PR.
Measured against a killed server:

- revision history --branch <name> --local: ~4.1s -> ~150ms
- file history --local: ~125ms
- --remote still queries the server and fails fast when it's down

Verified default behavior with the server up is unchanged.

**AI Assistance Disclosure**

I used an LLM (Claude Code) to help with implementation, code comments, and documentation on this change, and to help word things clearly as a non-native English speaker. I reviewed and tested all the code myself for its correctness, security, and license compliance.

```
Imported-PR: #115
Imported-From: 05d2cd4
Imported-Base: 046de55
Imported-Merge: bc580d3
Imported-Merge-Strategy: verbatim
Imported-Merged-Paths: 0
Imported-Author: Huân Lê-Vương (lehuan5062)
Signed-off-by: Huân Lê-Vương <65440815+lehuan5062@users.noreply.github.com>
GH-URL: #115
```

Lore-RevId: 850
Lore-Signature: 32ca666e6914bccad2995f2252f988a5df9f78ff5f446ba11cc3edca7c5a508f
@mjansson

Copy link
Copy Markdown
Collaborator

Closed by extended change in 1ebed42

@mjansson mjansson closed this Aug 31, 2026
@lehuan5062
lehuan5062 deleted the fix/local-reads-skip-connect branch September 1, 2026 00:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:core Core library and its interfaces (lib, C API); revision, storage, transport, protocol internals imported Imported into Lore for internal review

Development

Successfully merging this pull request may close these issues.

3 participants