• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

databendlabs / openraft / 24497325976
86%

Build:
DEFAULT BRANCH: main
Ran 16 Apr 2026 07:19AM UTC
Jobs 1
Files 262
Run time 1min
Badge
Embed ▾
README BADGES
x

If you need to use a raster PNG badge, change the '.svg' to '.png' in the link

Markdown

Textile

RDoc

HTML

Rst

16 Apr 2026 07:16AM UTC coverage: 86.513% (+0.04%) from 86.478%
24497325976

push

github

drmingdrmer
fix: clear replication backoff after success in stream session

Thanks to @AustenSchunk for reporting this bug (#1723) with a
precise root-cause analysis that made the fix straightforward.

Cause:

Backoff state was split across two pieces that were updated in
different scopes and never coordinated:

  1. `ReplicationCore::backoff_rank` — a counter incremented on
     every RPC error and reset to 0 on every successful response.
     Both updates happen inside the stream response-handling loop.

  2. `Arc<Mutex<Option<Backoff>>>` — the shared `Backoff` iterator
     sampled by `StreamState::backoff_if_enabled` before each
     request. This was only created by `enable_backoff()` and
     only cleared by `disable_backoff()`, both called from the
     outer main loop.

In `LogsSince` (pipeline) mode the outer main loop does not
iterate while a stream session is alive — `handle_response_stream`
consumes responses in a `while let` loop while the request-stream
generator concurrently emits requests. `disable_backoff` therefore
never runs between sessions. So after a transient error spike
followed by recovery:

  - `backoff_rank` is reset to 0 on the first successful response.
  - The `Backoff` object remains `Some(...)` indefinitely.
  - Every subsequent request sleeps for ~200ms before being sent.
  - The throttle persists until the session ends for unrelated
    reasons (network error, cancellation, higher vote).

Fix:

Encapsulate both pieces behind a new `BackoffState` type that
maintains the invariant `rank == 0 => inner == None`. `on_success`
now clears both `rank` and the shared iterator in one place, so
reconciliation no longer depends on the outer loop. `StreamState`
receives a read-only `BackoffConsumer` handle that can only sample
the next delay — it cannot enable, disable, or replace the
backoff, so the ownership of these transitions is clear in the
type system.

Changes:
- Add `BackoffState` owning both the rank counter and the shared
  `Backof... (continued)

105 of 106 new or added lines in 4 files covered. (99.06%)

7 existing lines in 3 files now uncovered.

16408 of 18966 relevant lines covered (86.51%)

119081.72 hits per line

Uncovered Changes

Lines Coverage ∆ File
1
96.6
-0.26% openraft/src/replication/mod.rs

Coverage Regressions

Lines Coverage ∆ File
3
82.8
-3.23% openraft/src/replication/snapshot_transmitter.rs
2
92.02
-0.22% openraft/src/core/raft_core.rs
2
81.48
-7.41% openraft/src/core/sm/handle.rs
Jobs
ID Job ID Ran Files Coverage
1 24497325976.1 16 Apr 2026 07:19AM UTC 262
86.51
GitHub Action Run
Source Files on build 24497325976
  • Tree
  • List 262
  • Changed 6
  • Source Changed 2
  • Coverage Changed 6
Coverage ∆ File Lines Relevant Covered Missed Hits/Line
  • Back to Repo
  • Github Actions Build #24497325976
  • 50f7269d on github
  • Prev Build on main (#24496293744)
  • Next Build on main (#24501237597)
  • Delete
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc