-
Notifications
You must be signed in to change notification settings - Fork 208
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
feat(vow): Support reliable retry-send #9608
base: master
Are you sure you want to change the base?
Conversation
Deploying agoric-sdk with Cloudflare Pages
|
b4bb440
to
95fe951
Compare
packages/vat-data/package.json
Outdated
@@ -23,7 +23,6 @@ | |||
"@agoric/base-zone": "^0.1.0", | |||
"@agoric/store": "^0.9.2", | |||
"@agoric/swingset-liveslots": "^0.10.2", | |||
"@agoric/vow": "^0.1.0", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks like a drive-by, since it wasn't needed by vat-data anyway. But until removed, the new devDependence of @agoric/vow on @agoric/zone, for durable zone testing, created a dependency cycle.
// TODO do I need to wait for a new incarnation | ||
// using isRetryableReason instead? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@michaelfig , I'm especially interested in your take on this question. Thanks
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Very much so. Which means you need to store any previous rejection seen. See how watch
implements this.
t.log('ping at', count); | ||
}, | ||
}); | ||
const v = zone.makeOnce('v', () => retry(bob, 'foo', [carol])); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note that the call to retry
only happens in the first incarnation, but it continues retrying into the second without further preparation.
See #9621 for an experiment of a different approach. |
I doubt it will be that simple given the requirement that the retry helper sees the host objects when used inside an asyncFlow guest function. While some usages of these helpers might be independent, we need to make sure we plumb things in a way that composes well. |
(target, optVerb, args) => { | ||
const { vow, resolver } = makeVowKit(); | ||
return harden({ | ||
target, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
target
should support a promise, which is not durably storable. Instead I believe we need a watchHandler for the target, and use watch(target)
on init to get at the final target. It would also allow us to transparently handle vows as targets too :)
// TODO do I need to wait for a new incarnation | ||
// using isRetryableReason instead? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Very much so. Which means you need to store any previous rejection seen. See how watch
implements this.
if (optResolver === undefined) { | ||
return; | ||
} | ||
// TODO `heapVowE` is likely too fragile under upgrade. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe not, if the target send is idempotent, it's probably ok to just retrigger if we get upgraded ourselves.
4ef923f
to
d3c4db8
Compare
fd21ac6
to
915a7d7
Compare
915a7d7
to
3dc8fc6
Compare
closes: #9541
Description
Retry idempotent eventual sends with reliable durable result vows that mask upgrade trauma.
The goal here is to support something like
E(bob).foo(carol)
but with several differences:.foo(carol)
to bob is rejected with anUpgradeDisconnection
, the message is resent, until finally the result of such a send settles in some other way, i.e., it is either fulfilled, or rejected by anything else.bob
, and the arguments, .e.g.,carol
, must themselves be storable in the zone in question --- typically meaning they must be durable as well.In this first PR, you can only express a retry-send of
const p = E(bob).foo(carol);
by sayingbut the intention is that the next PR will provide a more natural
E
-like syntax.Note we already have variations on
E
, likeheapVowE
that is scoped to the heap zone and have extra static methods. We can make similar variants ofE
that are scoped to the durable zone and have an extraretry
method. Suppose thisE
variant is calleddurableVowE
.We already have static methods on
E
that express variants ofE
, such asE.sendOnly(bob).foo(carol)
saying to just send the message tobob
as a one-way send without any result. Similarly, I imagine that the next PR may provide something likeBut hopefully we can invent something shorter but just as clear.
The internal API also provides a means to cancel the retry loop, so it doesn't go on forever after it is no longer relevant. Using the precedent of
E
taking an options bag as second argument, I imagine something likeonce we have adapted a cancellation token proposal such as https://github.com/tc39/proposal-cancellation . So this would be farther into the future.
Security Considerations
unpredicable resends of non-idempotent operations may be accidents threatening integrity. We currently have no in-band way to mark an operation as idempotent, and so no current means to check the consistency of these assumptions.
Scaling Considerations
Perpetual retries threaten availability and scalability. Though not much if they are only triggered by upgrade. Nevertheless, it would be nice to better support cancellation.
Documentation Considerations
Will need to be explained, including the caution to avoid retry-send on non-idempotent operations, which requires explaining idempotence.
Testing Considerations
As with the current state of async-flow, this only uses the lightweight low-fidelity upgrade testing framework. See #9303
Upgrade Considerations
The point. Retry-sent messages will be retried across upgrade at either end until they settle to something else, masking upgrade trauma. Rather than a promise, a durable retry-send returns a reliable durable vow that itself survives upgrades.