HarperFast / HarperFast/harper

Intent-based increment API with per-key coalescing for high-contention counters (token quotas)

Open
#1,715 0 comments 0 reactions 0 assignees View on GitHub
Dominant language
JavaScript
Stars
89
Forks
10
Avg merge
2d 6h
Merged PRs (30d)
200

Description

## Problem

High-contention increment workloads (canonical case: token-quota accounting — many concurrent requests incrementing a numeric property on the same record) degrade badly under optimistic concurrency: every concurrent writer to the hot key fails commit validation and retries. Coordinated retry (rocksdb-js verification table, harper#410 record-caching work) makes the retries orderly, but the workload is still serialized read-modify-write with N−1 wasted commit attempts per N writers.

Collapsing *general* conflicting transactions on retry is not safe: by commit time their writes are computed values derived from stale reads, and merging computed puts is precisely the lost-update anomaly optimistic validation exists to catch.

## Proposal: declare the intent, collapse by construction

Add an intent-based write API at the Table/Resource layer:

```js
Table.increment(id, property, delta, context)
```

Semantics and implementation sketch:

- **Per-key delta queue.** Increments for the same (table, key) coalesce in a per-worker pending map instead of each opening its own transaction.
- **Single flush.** A flush (per event-loop turn, or piggybacked on the commit-batching flush) reads the base value once, applies the queued deltas **in arrival order**, and writes the record once — one transaction, one commit, zero conflicts among the coalesced increments.
- **Per-caller post-values.** Each awaiter receives its own resulting value (base + prefix sum in arrival order), so quota checks like "did this request push usage over the limit?" remain per-caller and deterministic. Optionally support a bound (`increment(..., { max })`) that rejects the specific caller whose prefix sum crosses the limit.
- **Retry routing.** When a *regular* transaction fails commit validation and its write-set is tagged as pure increments, route it into the coalescer on retry instead of blindly re-running — "collapse on retry" restricted to the provably-safe subset.
- **Audit/replication.** The flushed write is a normal record write, so existing replication and audit machinery applies unchanged; if a per-delta audit trail is wanted, emit one audit entry per coalesced increment.

## Relation to other work

- Pairs with the rocksdb-js commit-thread + batched-commit work (dispatch overhead amortization); the coalescer reduces hot-key transactions from N to 1 before batching even starts.
- Deep-tier alternative: RocksDB Merge operator (HarperFast/rocksdb-js#692) — conflict-free at the storage layer and commutatively replicable, but far more invasive. This JS-side coalescer is the recommended first tier.

---
_Filed by KrAIs (Claude Opus 4.8) on behalf of @kriszyp, from the commit-batching feasibility research (2026-07-08)._

Contributor guide

Open the contributing guide

Research direction

Start at the Table/Resource layer and trace the existing transaction, commit-batching, retry, replication, and audit paths referenced in the proposal. Done would mean an agreed increment API with per-key coalescing, ordered per-caller results, and defined retry and bound semantics, validated against the relevant concurrency behavior.

Written by the indexing model from the issue text.

Assessment

Tech stack
javascript, node.js
Domain
backend-api-design, databases, performance
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
30/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.