Skip to content

feat: upsert index nodes at their address on re-store (cljs parity) - #3

Merged
tiensonqin merged 1 commit into
mainfrom
devin/store-upsert-address
Oct 2, 2026
Merged

tiensonqin merged 1 commit into
mainfrom
devin/store-upsert-address

Conversation

@tiensonqin

Copy link
Copy Markdown
Contributor

Summary

store_node gains an optional ?address parameter so a node being re-stored can keep its original address and upsert in place, matching cljs persistent-sorted-set's store semantics. Previously every store allocated a fresh address and left the old row dead, which accumulated ~2x dead kvs rows vs cljs for the same workload.

  • PSet.store_node : ?address:string -> 'a -> 'a PSet.node — when the address is provided the node is written at that address (upsert), otherwise a new address is allocated as before.
  • Nodes carry a dirty flag so the caller can distinguish already-stored nodes from new/modified ones; only dirty nodes are written on store.
  • cljs upsert semantics replicated (verified against the cljs jar): a modified node reuses its own address; a split's first output node inherits the split node's address; a merge keeps the left operand's address; nodes orphaned by rewrites keep their addresses (no delete-buffer, matching cljs's write path without row deletion).

With the corresponding datascript-ocaml change, a 12k-block graph writes 17.6k kvs rows / 19.6MB — within ~2% of the cljs daemon's 17.3k / 19.1MB (previously 34k+ rows / 57MB).

Link to Devin session: https://app.devin.ai/sessions/f641a5e9845f45108c4a98cb82e268d3
Open in Devin Desktop: https://app.devin.ai/desktop/session/f641a5e9845f45108c4a98cb82e268d3?variant=devin
Requested by: @tiensonqin

Match the cljs persistent-sorted-set storage semantics: a modified
stored node keeps its previous address and is rewritten in place
(IStorage.store with the node's _address), instead of allocating a
fresh address and orphaning the superseded row.

- storage.store_node takes ?address for the in-place rewrite
- Leaf/Branch nodes carry a dirty flag; restored nodes start clean
- 1->1 replacement inherits the node address; split keeps it on the
  first output; merge keeps the left operand's address; borrow keeps
  each slot's own address
- store() on an unmodified stored set still writes nothing

Verified against cljs persistent-sorted-set 0.1.2 (jar from
org.clojars.tiensonqin) running the same add/remove/store scenarios:
root address stays stable, merge keeps the left address and drops the
right (we orphan the row instead of deleting it), split gives the
first output the old address.
@devin-ai-integration

Copy link
Copy Markdown

I'll fix CI failures and address comments from users with write access. I'll skip comments containing "(aside)".

  • Disable automatic comment, CI, and merge conflict monitoring

@tiensonqin
tiensonqin merged commit 695223e into main Oct 2, 2026
2 checks passed
@tiensonqin
tiensonqin deleted the devin/store-upsert-address branch October 2, 2026 17:13
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