Skip to content

fix(di): return contract changes only after the contract registry applies them - #1053

Open
wolfy-j wants to merge 5 commits into
mainfrom
fix/contract-plane-acknowledged-publish
Open

wolfy-j wants to merge 5 commits into
mainfrom
fix/contract-plane-acknowledged-publish

Conversation

@wolfy-j

@wolfy-j wolfy-j commented Nov 7, 2026 •

Copy link
Copy Markdown
Contributor

The DI manager used to send contract definition and binding events to the contract registry without waiting. A registry operation or transaction commit could therefore return before contract.get saw the change. Code that re-creates a contract entry and opens it right away failed with "contract definition '' not found".

Now each commit, or each single operation outside a transaction, goes to the contract registry as one contract.changes.apply batch:

  • The registry checks every change in the batch, then applies them all under one lock and answers once with contract.accept or contract.reject. A refused batch changes nothing.
  • The manager waits for that answer through the await service and updates its own state only after the batch is accepted.
  • Each batch is sent and answered on its own path (di.changes/<seq>), so a late reply to an abandoned request cannot complete a newer one.
  • AwaitService.Stop now releases pending waiters with an error, so a commit caught by shutdown does not hang until its timeout.

Known limit: if the reply never arrives (timeout or shutdown), the batch's outcome is unknown and the manager reports it as not applied.

Tests: a batch whose second change is refused leaves the registry unchanged. Commit sends one batch and returns only after the answer. A refused commit leaves manager state unchanged. A stale reply does not complete a newer operation. A waiter returns as soon as the await service stops. A definition re-created 200 times resolves right after every commit; before this change it failed on the first iteration.

…lies them

The DI manager published contract definition and binding events to the
contract registry fire-and-forget, so a registry operation or transaction
commit returned before contract.get could resolve the change. A lookup right
after a commit, such as re-creating a contract and opening it, failed with
"contract definition not found".

Each contract-plane event is now sent through the await service and the
manager returns once the contract registry answers contract.accept, the
same request/reply shape function registration uses. Manager state is
applied only after the plane confirms the change.
…quest id

The DI manager now sends a commit, or a single operation outside a
transaction, to the contract registry as one contract.changes.apply event.
The registry checks every change in the batch, then applies them all under
one lock, and answers once on the batch's own path. A refused batch changes
nothing, so a failed commit cannot leave part of its changes behind.

Every batch path carries a per-process sequence number. A late reply to an
abandoned request therefore cannot complete a newer operation on the same
entry.

AwaitService.Stop now releases pending waiters with an error, so a commit
caught by shutdown returns at once instead of waiting out its timeout.
…ndings

A batch whose definition or binding payload is a nil pointer is now refused
during the check that runs before any change applies. Before, it applied the
earlier changes and then crashed the registry subscriber.

When a batch deletes and re-registers a definition, the default binding that
a retained binding declares for it is restored, so opening the contract still
finds its default binding.
@wolfy-j
wolfy-j requested a review from skhaz November 7, 2026 02:21
The contract registry now takes only contract.changes.apply batches. The six
per-entry event kinds are gone, along with the code that handled them as
one-change batches. Definition and binding mutations are now ChangeKind
values on contract.Change. The tests that sent the per-entry events now send
one-change batches.
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.

2 participants