Skip to content

docs: warn that UnstorageStore is not concurrency-safe - #68

Open
MathurAditya724 wants to merge 1 commit into
mainfrom
docs/unstorage-concurrency-warning
Open

docs: warn that UnstorageStore is not concurrency-safe#68
MathurAditya724 wants to merge 1 commit into
mainfrom
docs/unstorage-concurrency-warning

Conversation

@MathurAditya724

Copy link
Copy Markdown
Member

Problem

UnstorageStore.increment() does a non-atomic read-modify-write:

const record = await this.get(key);   // read
// ...compute payload...
await this.updateRecord(key, payload); // write

Unstorage exposes no atomic increment or compare-and-swap primitive, so under concurrent requests for the same key, callers can all read the same hit count before any of them writes it back. The counter under-counts and a client can exceed the configured limit. A reporter demonstrated 20/20 concurrent requests passing with limit: 5 (using a 50ms-latency driver wrapper). decrement() has the same shape.

MemoryStore (synchronous) and RedisStore (atomic Lua) are not affected.

Decision

There is no clean way to make this atomic through the unstorage API, and an in-process lock would only cover a single instance (not multi-instance deployments) while adding surprising serialization. Rather than ship a partial fix that implies a guarantee we can't keep, this PR documents the limitation and points users to RedisStore for correct counting under concurrent load.

Change

Docs-only. Adds @remarks to the UnstorageStore class and to increment() describing the race, the latency window, the multi-instance case, and the recommended alternatives. No behaviour change.

Reported alongside the RedisStore DECR issue. Thanks for the detailed PoC.

Unstorage has no atomic increment/CAS primitive, so UnstorageStore's
increment()/decrement() do a non-atomic read-modify-write. Under
concurrent requests for the same key, callers can all read the same hit
count before any writes it back, under-counting and letting a client
exceed the configured limit. Document this on the class and increment(),
and point users to RedisStore (atomic Lua) for concurrent load.

No behaviour change.
@github-actions

github-actions Bot commented Aug 6, 2026

Copy link
Copy Markdown

Semver Impact of This PR

🟢 Patch (bug fixes)

📋 Changelog Preview

This is how your changes will appear in the changelog.
Entries from this PR are highlighted with a left border (blockquote style).


Bug Fixes 🐛

  • Handle SCRIPT LOAD rejection in RedisStore constructor by jcross in #65

Documentation 📚

  • Warn that UnstorageStore is not concurrency-safe by MathurAditya724 in #68

Internal Changes 🔧

  • Add craft release system by MathurAditya724 in #66

Other

  • Fix 'succesful' typo in JSDoc by SAY-5 in #63

🤖 This preview updates automatically when you update the PR.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant