Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Explicit async-flow wake #10208

Open
wants to merge 15 commits into
base: master
Choose a base branch
from
Open

Explicit async-flow wake #10208

wants to merge 15 commits into from

Conversation

mhofman
Copy link
Member

@mhofman mhofman commented Oct 3, 2024

closes: #9377
closes: #9383
refs: #9753

best reviewed commit-by-commit

Description

This PR adds a lot of helpers to more ergonomically write async flow / liveslots upgrade tests, uses these to add a bunch of test cases around async-flow wake, then changes the behavior of async-flow to require an explicit wakeAll call once all flows are redefined. It updates the withOrchestration helper to call wakeAll.

Security Considerations

To avoid the risk of stuck flows if wakeAll is not called, a "finalize gate" is introduced that would cause the upgrade to fail if the function is not called.

Scaling Considerations

None

Documentation Considerations

The change in behavior is a breaking change, and marked as such, however no deployed code directly uses async-flow.

Testing Considerations

Lots of testing infra updates.
Full unit testing of the wake logic, including the new explicit wakeAll and its gate.

TODO: I am looking at further streamlining the test helpers to make it more obvious the tests are all serial and carry state between each test definition.

Upgrade Considerations

Deployable as new NPM packages. I have to check whether the breaking change commit would also cause the orchestration package to get a semver major change, as that may be unnecessary.
TODO: potentially split that commit

Copy link

cloudflare-workers-and-pages bot commented Oct 3, 2024

Deploying agoric-sdk with  Cloudflare Pages  Cloudflare Pages

Latest commit: 332eb05
Status: ✅  Deploy successful!
Preview URL: https://4bd08da4.agoric-sdk.pages.dev
Branch Preview URL: https://mhofman-9377-wake-all.agoric-sdk.pages.dev

View logs

@mhofman mhofman marked this pull request as ready for review October 3, 2024 22:50
@mhofman mhofman requested a review from a team as a code owner October 3, 2024 22:50
Copy link
Member

@michaelfig michaelfig left a comment

Choose a reason for hiding this comment

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

It's a really good improvement! I'd be happy to see this land given your attention to my change suggestions.

Comment on lines -526 to -532
// Cannot call this until everything is prepared, so postpone to a later
// turn. (Ideally, we'd postpone to a later crank because prepares are
// allowed anytime in the first crank. But there's currently no pleasant
// way to postpone to a later crank.)
// See https://github.com/Agoric/agoric-sdk/issues/9377
const allWokenP = E.when(null, () => adminAsyncFlow.wakeAll());

Copy link
Member

Choose a reason for hiding this comment

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

Good to see this code go away. wakeUpgradeGate has the right semantics and is much more understandable.

* @import {Zone} from '@agoric/base-zone';
* @import {PreparationOptions} from '../src/types.js';
*/

/** @typedef {(e: any, t: ExecutionContext) => void} TestAsyncFlowPanicHandler */
Copy link
Member

Choose a reason for hiding this comment

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

💯

Copy link
Member

Choose a reason for hiding this comment

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

👍

packages/async-flow/test/async-flow-wake.test.js Outdated Show resolved Hide resolved
// new incarnation, but otherwise should wait until a wake watcher is triggered.

import { Fail } from '@endo/errors';
import { testAsyncLife } from './prepare-test-env-ava.js';
Copy link
Member

Choose a reason for hiding this comment

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

I suggest there could be an initial testAsyncLife, which returns a new testAsyncLife with its reincarnation state curried in:

Suggested change
import { testAsyncLife } from './prepare-test-env-ava.js';
import { testAsyncLifeFromScratch } from './prepare-test-env-ava.js';

return { resolver, waiter };
};

testAsyncLife(
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
testAsyncLife(
const testAsyncLifeAfterSetup = testAsyncLifeFromScratch(

},
);

testAsyncLife(
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
testAsyncLife(
const testAsyncLifeAfterSleeping = testAsyncLifeAfterSetup(

Copy link
Member

Choose a reason for hiding this comment

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

Nice mechanical simplification.

packages/async-flow/src/async-flow.js Outdated Show resolved Hide resolved
packages/async-flow/src/async-flow.js Outdated Show resolved Hide resolved
* upgrade. In the first incarnation, the state is assumed to be born already
* finalized, and there is no failure if the "finalize" is not explicitly
* performed. In future incarnation the upgrade will fail if the "finalize" step
* is not explicitly performed.
Copy link
Member

Choose a reason for hiding this comment

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

please name the entity that is responsible for performing the finalization

Copy link
Member Author

@mhofman mhofman Oct 4, 2024

Choose a reason for hiding this comment

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

I'm not sure how to shoe horn that in this comment. While defined within async-flow.js, this is written as a generic utility.

How about something along the lines:

Helper to enforce that a post-redefinition "finalize" step has been performed on upgrade. In some cases, it may be necessary to execute some operations on upgraded durable objects, which can only happen after the objects' kinds and their dependencies have all been redefined. A "finalize upgrade gate" helps enforce that this finalize step is performed by the contract on upgrade. It returns a function which should be called by the finalize step to indicate the operations were performed. In the first incarnation, it assumes that no finalizer step is necessary, and there is no failure if the function is not called. However, in a future incarnation, the upgrade will fail if the function is not called.

@mhofman
Copy link
Member Author

mhofman commented Oct 4, 2024

I'm gonna need to force push to split one of the commits in 2, sorry for the inconvenience.

Done: 198ad9e..332eb05

mhofman added a commit to endojs/endo that referenced this pull request Oct 17, 2024
Refs: Agoric/agoric-sdk#10208

## Description

the `ava` test functions can be nested more than one level deep. This
change recurses test functions with known names until none is found.

### Security Considerations

None

### Scaling Considerations

None

### Documentation Considerations

Relaxes the type of acceptable test functions as it only really cares
about it being a test function, regardless of whatever chain expandos it
may have.

### Testing Considerations

Manually verified that a `test.serial.only` does show errors details as
expected. Not sure if we have any automated coverage for this.

### Compatibility Considerations

None

### Upgrade Considerations

This is used by tests only, and can be consumed as a non breaking change
simply by using the new NPM package including this fix.
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.

async-flow: deeper wake testing async-flow: postpone wakeAll after first crank rather than first turn.
3 participants