HarperFast / HarperFast/harper
CRDT counter increments can be lost under multi-worker contention
- Dominant language
- JavaScript
- Stars
- 89
- Forks
- 10
- Avg merge
- 2d 6h
- Merged PRs (30d)
- 200
Description
## Defect
The multi-worker CRDT counter path can acknowledge an increment with HTTP 200 and later persist a lower count than the number of acknowledged increments.
`integrationTests/resources/ttl-rate-limiter-concurrent.test.ts` seeds ten 500 ms-TTL `RateCounter` rows, sends 50 concurrent `addTo('hits', 1)` requests to each row through four HTTP workers, and compares each stored count with that row's 200 responses. CI has failed the same assertion twice:
- [Integration Tests 2/6 uWS HTTP, run 31113724037](https://github.com/HarperFast/harper/actions/runs/31113724037) on 2026-08-06
- [Integration Tests 2/6 Node.js v22, run 31212214591](https://github.com/HarperFast/harper/actions/runs/31212214591) on 2026-08-07
Each run reported one window with `stored < acked`; other runs passed. The test treats a final 404 as expired and excludes it, so a successfully read lower count is not an expected TTL rollover.
The request resource awaits `updatable.save()` before returning 200. The invariant is therefore exact: every acknowledged increment must be represented in the stored counter. The observed logs show concurrent coordinated retries interleaved with CRDT update application for the affected key. Investigation will trace the retry/CRDT merge and the TTL eviction/recreate interaction, then add a regression test that fails on the base revision.
Contributor guide
Assessment
This issue has not been assessed yet.