-
Notifications
You must be signed in to change notification settings - Fork 215
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
don't reschedule auction's price notifier if we already have one #10615
Changes from 3 commits
3e19377
a12a965
ba64933
77f2260
ee6c0a1
bff3484
59406eb
e88aebe
466d3b8
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -6,7 +6,11 @@ import { Fail } from '@endo/errors'; | |
import { E } from '@endo/captp'; | ||
import { AmountMath, RatioShape } from '@agoric/ertp'; | ||
import { mustMatch } from '@agoric/store'; | ||
import { M, prepareExoClassKit } from '@agoric/vat-data'; | ||
import { | ||
M, | ||
prepareExoClassKit, | ||
provideDurableMapStore, | ||
} from '@agoric/vat-data'; | ||
|
||
import { assertAllDefined, makeTracer } from '@agoric/internal'; | ||
import { | ||
|
@@ -118,6 +122,12 @@ export const makeOfferSpecShape = (bidBrand, collateralBrand) => { | |
export const prepareAuctionBook = (baggage, zcf, makeRecorderKit) => { | ||
const makeScaledBidBook = prepareScaledBidBook(baggage); | ||
const makePriceBook = preparePriceBook(baggage); | ||
// a map from collateralBrand to true when the quoteNotifier has an observer | ||
// the brand is absent when there's no observer. | ||
const quoteNotifierFlags = provideDurableMapStore( | ||
baggage, | ||
'quoteNotifierFlags', | ||
); | ||
|
||
const AuctionBookStateShape = harden({ | ||
collateralBrand: M.any(), | ||
|
@@ -454,38 +464,59 @@ export const prepareAuctionBook = (baggage, zcf, makeRecorderKit) => { | |
}); | ||
return state.bookDataKit.recorder.write(bookData); | ||
}, | ||
observeQuoteNotifier() { | ||
// Ensure that there is an observer monitoring the quoteNotifier. We | ||
// assume that all failure modes for quoteNotifier eventually lead to | ||
// fail or finish. | ||
Comment on lines
+459
to
+461
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should I read this as "We rely on SOMETHING to see that all failure modes for quoteNotifier eventually lead to fail or finish"? What is the SOMETHING? If we don't rely on something else, we have to ensure it here, no? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The assumption is baked into the design of the notifiers and observers. There's no way to interrupt it from the consumer end, and no way to verify that it's still alive. Its contract says if it every ceases to continue producing new values it will either call |
||
ensureQuoteNotifierObserved() { | ||
const { state, facets } = this; | ||
const { collateralBrand, bidBrand, priceAuthority } = state; | ||
|
||
if (quoteNotifierFlags.has(collateralBrand)) { | ||
return; | ||
} | ||
quoteNotifierFlags.init(collateralBrand, true); | ||
trace('observing'); | ||
|
||
const quoteNotifier = E(priceAuthority).makeQuoteNotifier( | ||
const quoteNotifierP = E(priceAuthority).makeQuoteNotifier( | ||
AmountMath.make(collateralBrand, QUOTE_SCALE), | ||
bidBrand, | ||
); | ||
void observeNotifier(quoteNotifier, { | ||
updateState: quote => { | ||
trace( | ||
`BOOK notifier ${priceFrom(quote).numerator.value}/${ | ||
priceFrom(quote).denominator.value | ||
}`, | ||
); | ||
state.updatingOracleQuote = priceFrom(quote); | ||
}, | ||
fail: reason => { | ||
trace(`Failure from quoteNotifier (${reason}) setting to null`); | ||
// lack of quote will trigger restart | ||
state.updatingOracleQuote = null; | ||
}, | ||
finish: done => { | ||
trace( | ||
`quoteNotifier invoked finish(${done}). setting quote to null`, | ||
); | ||
// lack of quote will trigger restart | ||
|
||
void E.when( | ||
quoteNotifierP, | ||
quoteNotifier => | ||
observeNotifier(quoteNotifier, { | ||
updateState: quote => { | ||
trace( | ||
`BOOK notifier ${priceFrom(quote).numerator.value}/${ | ||
priceFrom(quote).denominator.value | ||
}`, | ||
); | ||
state.updatingOracleQuote = priceFrom(quote); | ||
}, | ||
fail: reason => { | ||
trace( | ||
`Failure from quoteNotifier (${reason}) setting to null`, | ||
); | ||
// lack of quote will trigger restart | ||
state.updatingOracleQuote = null; | ||
quoteNotifierFlags.delete(collateralBrand); | ||
}, | ||
finish: done => { | ||
trace( | ||
`quoteNotifier invoked finish(${done}). setting quote to null`, | ||
); | ||
// lack of quote will trigger restart | ||
state.updatingOracleQuote = null; | ||
quoteNotifierFlags.delete(collateralBrand); | ||
}, | ||
}), | ||
e => { | ||
trace('makeQuoteNotifier failed, resetting', e); | ||
state.updatingOracleQuote = null; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why is it necessary to reset the state here? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's safe because we'll check for a notifier again at the next auction. It's a good idea because we can't count on the notifier continuing to produce events if there's an error at this level. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I just don't quite understand in which case this isn't already There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We didn't explore it deeply, but I think if a previous version was happily updating the state, then it upgrades, then anything which samples the price at the beginning of the new incarnation (before any new updates happen) will get the old price. If an auction then starts and initiates the observer process, but fails (maybe because the price feed vat is getting upgraded too?), then we'll hit this case, clear the ephemeral flag (so the next auction can initiate a new one), and also clear the durable I suspect that's a bit weird. Not sure if it's a problem though. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. On line 469, we add the brand to the set, and on line 472, we attempt to create the notifier. On line 477, we await the notifier. If the creation promise fails, we end up here, and want to ensure the brand is removed from the set, or we'll never try again to create a notifier. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
I have no problem with the ephemeral set being reset on error. My concern is with how the durable Brian mentions a case where this durable state may not be But beyond that question, the problem is that the IMO, we need to switch the call to Regarding the early bail out of There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Oh, yeah, if the auctionBook has a price (in durable I agree with @mhofman that the |
||
quoteNotifierFlags.delete(collateralBrand); | ||
}, | ||
}); | ||
); | ||
|
||
void facets.helper.publishBookData(); | ||
}, | ||
|
@@ -645,8 +676,8 @@ export const prepareAuctionBook = (baggage, zcf, makeRecorderKit) => { | |
|
||
trace(`capturing oracle price `, state.updatingOracleQuote); | ||
if (!state.updatingOracleQuote) { | ||
// if the price has feed has died, try restarting it. | ||
facets.helper.observeQuoteNotifier(); | ||
// if the price feed has died, restart it. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. maybe add "(or hasn't been started for this incarnation yet)" |
||
facets.helper.ensureQuoteNotifierObserved(); | ||
return; | ||
} | ||
|
||
|
@@ -750,7 +781,7 @@ export const prepareAuctionBook = (baggage, zcf, makeRecorderKit) => { | |
const { collateralBrand, bidBrand, priceAuthority } = state; | ||
assertAllDefined({ collateralBrand, bidBrand, priceAuthority }); | ||
|
||
facets.helper.observeQuoteNotifier(); | ||
facets.helper.ensureQuoteNotifierObserved(); | ||
}, | ||
stateShape: AuctionBookStateShape, | ||
}, | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -384,6 +384,14 @@ export const distributeProportionalSharesWithLimits = ( | |
return transfers; | ||
}; | ||
|
||
// auctionBook changed to create a sub-baggage for each book (to distinguish | ||
// their quoteNotifier flags) so older auctions will not be upgradable to this | ||
// version. We believe this version is saving all the necesary state to be | ||
// upgraded, but that hasn't been verified, so we don't mark it as `canUpgrade'. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. My understanding of those My opinion is that So maybe add ", so we do not mark this as There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
If we had verified it, it would be 'canUpgrade There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I thought If I understand it correctly, There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I never did know the difference. Ooh! we have docs now: * - `canUpgrade` means this code can perform an upgrade
* - `canBeUpgraded` means that the contract stores kinds durably such that the next version can upgrade So I concur that |
||
export const meta = { | ||
upgradable: 'canBeUpgraded', | ||
}; | ||
|
||
/** | ||
* @param {ZCF< | ||
* GovernanceTerms<typeof auctioneerParamTypes> & { | ||
|
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.
oh, hey, should this just be a
DurableMapSet
?