Skip to content

Atomic add_relationship: collapse five transactions into one (PR 2/2)#45

Merged
david-w-t merged 6 commits into
davidwt-com:mainfrom
david-w-t:develop
Jun 21, 2026
Merged

Atomic add_relationship: collapse five transactions into one (PR 2/2)#45
david-w-t merged 6 commits into
davidwt-com:mainfrom
david-w-t:develop

Conversation

@david-w-t

Copy link
Copy Markdown
Contributor

Atomic add_relationship (PR 2 of 2)

Collapses graphdb_instance:do_add_relationship/7's five separate Mnesia
transactions into one
, spending the tier-1 in-transaction read primitives
shipped in PR 1 (#44). This is the second and final slice of the Atomic
add_relationship
transaction-seam follow-up.

The honest framing

The collapse fixes no bug — only the final phase writes today, and both
directed rows already write together in one transaction, so there is no
partial-write window. What it buys is TOCTOU isolation: endpoint
validation, class/template resolution, scope check, and the row write now
share one consistent Mnesia snapshot, closing the window where another
process retires an endpoint, deletes a class, or changes a template between
validation and write. TOCTOU isolation is a race property — it has no
deterministic test, and none was written.

What changed

  • The four single-use phase helpers (validate_arc_endpoints,
    resolve_arc_classes, resolve_template, validate_template_scope) are
    converted in place to in-transaction (mnesia:abort-based) form.
  • Private class_of_in_txn/1 added as the add-don't-rewrap twin of
    do_class_of/1 (which keeps its own txn for its public class_of caller).
  • build_connection_rows split into /6 (allocates) + /7 (pure); the
    rel-id pair is allocated up-front, outside the transaction (gen_server
    calls must never run inside an mnesia fun).
  • do_add_relationship/7 rewritten as one graphdb_mgr:transaction/1:
    validate → resolve classes → resolve template → validate scope → write.

Behaviour preservation

Every abort term is byte-identical to the pre-collapse {error, Reason}
return (transaction/1 re-adds the {error, _} wrapper); phase order is
preserved. The two previously-uncovered class-resolution atoms
(source_has_no_class, target_has_no_class) gain characterization tests.

Tests

446 CT + 105 EUnit = 551 green, zero warnings. The existing
add_relationship suite is unchanged (the behaviour-preservation proof);
+2 new instance CT cases.

Follow-ups

TASKS.md now tracks a deferred Converge default-template name search
cleanup (the do_find_template_by_name/2default_template_in_txn/1
duplication) and the still-open Batch mutate([Mutation]) tier-3 entry
point.

Design docs/designs/atomic-add-relationship-design.md; plan
docs/superpowers/plans/2026-06-21-atomic-add-relationship.md.

🤖 Generated with Claude Code

david-w-t and others added 6 commits June 21, 2026 06:24
Collapse do_add_relationship/7's five transactions into one for TOCTOU
isolation. Grep-confirmed the four phase helpers are single-use → convert
in place; do_class_of/1 has a public caller → add class_of_in_txn/1 twin.
Spends PR 1's tier-1 primitives. Behaviour-preserving; +2 error-atom tests
(source/target_has_no_class), no atomicity/race test.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EWukKCbrN8GybaScJGU2kF
3 TDD tasks: (1) characterization tests for source/target_has_no_class;
(2) collapse do_add_relationship/7 into one transaction (in-txn phase
helpers + class_of_in_txn/1 + build_connection_rows split); (3) docs incl.
deferred default-template name-search convergence task.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EWukKCbrN8GybaScJGU2kF
Characterization tests for the two previously-uncovered class-resolution
error atoms, locked in before the atomic-collapse refactor.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EWukKCbrN8GybaScJGU2kF
Validate endpoints, resolve class/template scope, and write the two
directed rows in one graphdb_mgr:transaction/1 (TOCTOU-isolated). Convert
the four single-use phase helpers in place to in-txn (abort-based) form;
add private class_of_in_txn/1 (do_class_of/1 keeps its own txn for its
public caller); split build_connection_rows into /6 (allocates) + /7
(pure) so the rel-id pair is allocated up-front outside the txn. Spends
PR 1's tier-1 primitives. Behaviour-preserving.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EWukKCbrN8GybaScJGU2kF
Mark the Atomic add_relationship follow-up implemented; add the deferred
default-template name-search convergence task; note add_relationship's
single-transaction behaviour in the graphdb worker doc.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EWukKCbrN8GybaScJGU2kF
Final-review nit: do_default_template/1 (not default_template/1) is what
already wraps its own transaction.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EWukKCbrN8GybaScJGU2kF
Comment thread apps/graphdb/src/graphdb_instance.erl

@david-w-t david-w-t left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok

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