Run contract executions one at a time - #36
Merged
Merged
Conversation
A contract instance keeps the state of the running call on the instance itself: address, validator_address, is_feature, is_message, tx, op, value and storage are set on entry to Contract.execute(), and contract handlers re-read those fields after every await. Two executions in flight on one instance therefore interleave their working state. Autobase apply is sequential, but Protocol.simulateTransaction() executes on the same live contract instance from outside the apply loop, so a simulation and an apply do overlap on a busy peer. Observed on a live subnet writer: a simulated command returned a record with two of its fields missing, and the operation that resumed a moment later found the fields already reset and threw "get(key): storage undefined", which ended the process. The execution body moves unchanged to executeQueued(), and execute() now queues calls so a call starts only after the previous one has finished and releases the next one in its finally block, so a throwing handler cannot wedge the queue. Apply order and results are unchanged because apply was already sequential; simulations wait behind whatever is applying instead of interleaving with it. New unit tests in tests/unit/contractExecuteQueue.test.js: two overlapping executions each keep their own operation, sender, transaction and storage and match a sequential run byte for byte; a throwing handler leaves the queue usable; a sub-class override that calls super.execute() still runs.
The trac-msb dependency now resolves from its GitHub repository at the exact commit that carries version 0.2.21, so every consumer builds against the same settlement bus code instead of whatever the registry range picks up.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
A contract instance keeps the state of the running call on the instance itself:
address,validator_address,is_feature,is_message,tx,op,valueand
storageare set on entry toContract.execute(), and contract handlersre-read those fields after every await. Two executions in flight on one instance
therefore interleave their working state.
Autobase apply is sequential, but
Protocol.simulateTransaction()executes onthe same live contract instance from outside the apply loop, so a simulation and
an apply do overlap on a busy peer. Observed on a live subnet writer: a simulated
command returned a record with two of its fields missing, and the operation that
resumed a moment later found the fields already reset and threw
get(key): storage undefined, which ended the process.The execution body moves unchanged to
executeQueued(), andexecute()nowqueues calls so a call starts only after the previous one has finished and
releases the next one in its
finallyblock, so a throwing handler cannot wedgethe queue. Apply order and results are unchanged because apply was already
sequential; simulations wait behind whatever is applying instead of interleaving
with it.
New unit tests in
tests/unit/contractExecuteQueue.test.js: two overlappingexecutions each keep their own operation, sender, transaction and storage and
match a sequential run byte for byte; a throwing handler leaves the queue usable;
a sub-class override that calls
super.execute()still runs.The second commit pins
trac-msbto its GitHub repository at the commit thatcarries version 0.2.21, so every consumer builds against the same settlement bus
code instead of whatever the registry range picks up.
Local runs on this branch:
npm run test:unit:node43/43 tests, 125/125 asserts(40/40 and 89/89 on main);
npm run test:acceptance:node4/4 tests, 21/21asserts.