tables.X.create(record) single-argument form silently escapes the caller's request transaction
Nobody has claimed this yet.
Assessment
- Difficulty
- 2/5
- Estimated time
- 1-3 hours
- Newbie friendliness
- 88/100
- Issue type
- Bug
- Clarity
- Clearly specified
- Activity status
- Active
- Tech stack
- javascript, node.js
Research direction
Start in resources/Resource.ts at static create(), specifically the default loadAsInstance !== false two-argument shift branch, and then read resources/transaction.ts to understand how context determines transaction ownership. Preserve the caller's ambient request transaction for the single-argument form while retaining the empty-context fallback; verify that a write followed by a throw rolls back rather than persisting.
Written by the indexing model from the issue text.
Description
tables.X.create(record) — the single-argument form — silently runs outside the caller's request
transaction. A write followed by a throw therefore persists, while the same write issued as
tables.X.create(record, this.getContext()) correctly rolls back. Verified in both directions in one
run.
Mechanism (harper, origin/main)
resources/Resource.ts, static create(), the default (loadAsInstance !== false) "two argument
form, shift" branch:
context = record || {}; // single-arg call: record is undefined -> context = {}
record = idPrefix;
{} is truthy, so the subsequent if (context) { … } else { context = contextStorage.getStore() ?? {}; }
takes the truthy branch and the ambient-context lookup is never reached.
resources/transaction.ts's transaction(context, cb) then sees a context with no .transaction
property, so isJoinableScope is false and it allocates a fresh DatabaseTransaction({ scopeOwned: true })
that commits itself via onComplete / doneWriting: true, independent of the caller's
request-scoped transaction.
Why it matters
It only manifests on the error path, which is exactly where atomicity is supposed to hold: the
caller's transaction rolls back, the escaped write stays committed. Nothing logs, and the
single-argument form reads like a supported convenience shape.
Fix shape
In that shift branch, only fabricate {} when there is genuinely no ambient store — mirroring the
fallback the very next lines already contain:
context = record ?? contextStorage.getStore() ?? {};
Confined to the loadAsInstance !== false default-Table path.
- Dominant language
- JavaScript
- Stars
- 89
- Forks
- 10
- Avg merge
- 1d 15h
- Merged PRs (30d)
- 196
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
More from HarperFast/harper
-
Derived index logs [error] "backend has no durable cursor undefined" at startup on a brand-new index Open
Difficulty 2/5 1-3 hours Newbie friendliness 74/100
HarperFast/harper#2696 ·
-
Difficulty 2/5 1-3 hours Newbie friendliness 82/100
HarperFast/harper#2650 ·
-
Runtime guard on HierarchicalNavigableSmallWorld.search() options argument (follow-up to #2165) Open
Difficulty 2/5 1-3 hours Newbie friendliness 90/100
HarperFast/harper#2611 ·
-
Difficulty 2/5 1-3 hours Newbie friendliness 76/100
HarperFast/harper#2547 ·
-
Difficulty 2/5 1-3 hours Newbie friendliness 82/100
HarperFast/harper#2503 ·
All issues in HarperFast/harper
Similar issues
-
Difficulty 2/5 1-3 hours Newbie friendliness 88/100
HarperFast/skills#96 ·
-
[Block] Latest Posts [Type] Bug
Difficulty 2/5 1-3 hours Newbie friendliness 76/100
-
Difficulty 2/5 1-3 hours Newbie friendliness 78/100
Automattic/studio#4908 ·
-
Difficulty 2/5 1-3 hours Newbie friendliness 74/100
-
Difficulty 2/5 1-3 hours Newbie friendliness 86/100
sugarlabs/musicblocks#8847 ·