-
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
VowTools retryable #9785
VowTools retryable #9785
Conversation
Codecov ReportAttention: Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## master #9785 +/- ##
==========================================
+ Coverage 56.37% 56.39% +0.02%
==========================================
Files 667 668 +1
Lines 207526 207871 +345
Branches 339 368 +29
==========================================
+ Hits 116986 117227 +241
- Misses 90422 90526 +104
Partials 118 118
|
harden(isUpgradeDisconnection); | ||
|
||
/** | ||
* Returns whether a reason is a 'vat terminated' error generated when an object |
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.
I understand this as an expedient measure, until we distinguish this rejection condition some other way. Either, like UpgradeDisconnection, use something other than an Error as a rejection to be tested. For an error, the only content that a program should make a semantic decision on is .name
. Most emphatically, code should not make a semantic decision on an error .message
.
So, an encapsulated temporary expedient measure is fine if clearly marked as such, and with a filed issue to fix it.
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.
const error = harden(Error('vat terminated')); | ||
const remoteError = fromCapData(toCapData(error)); | ||
|
||
t.true(isAbandonedError(remoteError)); |
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.
Nice. But to reiterate, this probably depends on stability beyond what I expect to propose for ocapn to mandate.
packages/orchestration/test/examples/snapshots/sendAnywhere.test.ts.md
Outdated
Show resolved
Hide resolved
import { makeHeapZone } from '@agoric/base-zone/heap.js'; | ||
import { makeE, prepareVowTools as rawPrepareVowTools } from './src/index.js'; | ||
|
||
/** @type {import('./src/types.js').IsRetryableReason} */ | ||
const isRetryableReason = (reason, priorRetryValue) => { | ||
if ( | ||
isUpgradeDisconnection(reason) && | ||
(!priorRetryValue || | ||
(!isUpgradeDisconnection(priorRetryValue) || |
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.
What is the significance of this change? (including lines 24-28 below)
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.
When shortening a vow, or checking whether to retry a retriable flow, we will now consider that we're not eligible for a retry when:
- The current and previous reasons were both "upgrade disconnection" records, but only if different incarnation
- The current and previous reasons were both "vat terminated" errors
In particular the following situations are still eligible for retries:
- Going from a "vat terminated" error to an "upgrade disconnection" record, or vice versa
- Going from an "upgrade disconnection" record to another, if the incarnation number changes
Before this change, only "upgrade disconnection" records were recorded as "previous retry reason" so we need to narrow down the check here.
As mentioned IMO we ultimately want to implement a .cause
resolution and only root checks in disconnection records, as described in #9582
Deploying agoric-sdk with Cloudflare Pages
|
20417e3
to
ea795cd
Compare
refs: #9541 ## Description Extracted the chain hub changes from #9785 Changed `makeChainHub` to accept a zone instead of it implicitly creating a heap zone. A durable zone is required for `retriable`. ### Security Considerations None ### Scaling Considerations This store the map in durable storage instead of keeping it in the heap, which uses more persistent storage. ### Documentation Considerations Not sure if any docs need to be updated besides the JS doc for `makeChainHub` ### Testing Considerations Updated tests and examples ### Upgrade Considerations This moves into durable storage some info that was heap only before, effectively commiting ourselves to the shape of the data in these maps.
refs: #9303 ## Description This adds a test for upgrading send-anywhere. It doesn't yet pass without these in-flight PRs, - #9736 - #9785 We've agreed to land this without those to reduce work-in-flight. ### Security Considerations none ### Scaling Considerations none ### Documentation Considerations none ### Testing Considerations The failing test has a link to the issue to make it pass. ### Upgrade Considerations none
515a4e3
to
09980df
Compare
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.
I like this implementation; it is clear and direct. I didn't come across any issues that I think would prevent merging it.
LGTM!
34980e5
to
f38e095
Compare
f38e095
to
0237350
Compare
@@ -100,6 +100,27 @@ export type AsPromiseFunction< | |||
watcherArgs?: C | undefined, | |||
) => Promise<TResult1 | TResult2>; | |||
|
|||
export interface RetryableTool { | |||
/** |
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.
thanks for the jsdoc!
|
||
zone.exo('DurableVowTestWatcher', undefined, { | ||
onFulfilled(value) { | ||
t.is(value, 42, 'vow resolved with value 42'); |
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.
consider losing the t.plan
to make the logic clearer. E.g. in the test closer define a promise kit and resolve it with the when(result)
. Then at the end of the test have await t.is(await resultP, 24)
, something like that.
Not a biggy but would make the logic easier to read and modify
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.
A promise kit wouldn't help because it doesn't guarantee the other handlers didn't trigger (as promise resolve logic ignores any subsequent resolution). Of course we could set a counter to check how many times the handlers got triggered, but at that point, isn't that what plan
does?
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.
because it doesn't guarantee the other handlers didn't trigger
i was thinking
t.is(value, 42, 'vow resolved with value 42'); | |
if (value === 42) { | |
p.resolve('success'); | |
} |
But you're right that something else could resolve it with success
even without success, so I suppose that t.plan()
is safer
t.false(zone.isStorable(nonStorableArg), 'arg is actually non storable'); | ||
|
||
let resultV; | ||
t.notThrows(() => { |
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.
thanks for these checks that help describe the expected behavior
refs: #9541
Description
Adds a
retryable
helper which retries an idempotent function on upgrade disconnection reasons.Update the upgrade disconnection
isRetryableReason
predicate to acceptvat terminated
errors created by liveslots when trying to message an ephemeral object disconnected by an upgrade (until #9582 is addressed)Remove the stub
retriable
helper, and use theretryable
implementation.Security Considerations
To avoid infinite loops, the
vat terminated
detection logic does not allow retrying such consecutive reasons.Scaling Considerations
None
Documentation Considerations
JSDoc for
retryable
Testing Considerations
Full unit test and liveslots upgrade test coverage
Upgrade Considerations
The current
retriable
implementation was just a stub that was advertised as not actually working with upgrades. This introduces the actual implementation, which can be picked up by contract authors by simply updating to the next NPM release containing this change. Going forward, defined retryable flows will need to be redefined on upgrade like any other durable exo defined by a contract.