From f9075d187949572c2395d24cc4a9b92e2d42f5bd Mon Sep 17 00:00:00 2001 From: Mathieu Hofman Date: Tue, 23 Jul 2024 07:36:11 +0000 Subject: [PATCH] feat(vow): retriable tools --- .../snapshots/send-anywhere.test.ts.md | 6 + .../snapshots/send-anywhere.test.ts.snap | Bin 1215 -> 1313 bytes .../snapshots/staking-combinations.test.ts.md | 6 + .../staking-combinations.test.ts.snap | Bin 2471 -> 2573 bytes .../snapshots/unbond.contract.test.ts.md | 6 + .../snapshots/unbond.contract.test.ts.snap | Bin 1887 -> 1979 bytes packages/vow/src/retriable.js | 218 ++++++++++++++++++ packages/vow/src/tools.js | 22 +- 8 files changed, 241 insertions(+), 17 deletions(-) create mode 100644 packages/vow/src/retriable.js diff --git a/packages/orchestration/test/examples/snapshots/send-anywhere.test.ts.md b/packages/orchestration/test/examples/snapshots/send-anywhere.test.ts.md index 5f4dbdca9a3..ba2a78f5e78 100644 --- a/packages/orchestration/test/examples/snapshots/send-anywhere.test.ts.md +++ b/packages/orchestration/test/examples/snapshots/send-anywhere.test.ts.md @@ -31,6 +31,9 @@ Generated by [AVA](https://avajs.dev). chainInfos: {}, connectionInfos: {}, denom: {}, + lookupChainInfo_kindHandle: 'Alleged: kind', + lookupChainsAndConnection_kindHandle: 'Alleged: kind', + lookupConnectionInfo_kindHandle: 'Alleged: kind', }, contract: { 'ChainHub Admin_kindHandle': 'Alleged: kind', @@ -72,8 +75,11 @@ Generated by [AVA](https://avajs.dev). }, }, vows: { + AdminRetriableFlow_kindHandle: 'Alleged: kind', + AdminRetriableFlow_singleton: 'Alleged: AdminRetriableFlow', PromiseWatcher_kindHandle: 'Alleged: kind', VowInternalsKit_kindHandle: 'Alleged: kind', WatchUtils_kindHandle: 'Alleged: kind', + retriableFlowForOutcomeVow: {}, }, } diff --git a/packages/orchestration/test/examples/snapshots/send-anywhere.test.ts.snap b/packages/orchestration/test/examples/snapshots/send-anywhere.test.ts.snap index bab47226e8b51cb8fa66f49fa275be951a16c45b..95e0daca779778a0434cdb6658d02fc9bd21b2ba 100644 GIT binary patch literal 1313 zcmV++1>X8WRzVG z(ODUNZ6AvW00000000A>SWAc_MHv1nNhh65lFsbR?#^p92LuSydJ$3ZDw@u8 zrn}k=(=(?q-}ir2|Fiz&=1$8KcGutCW6D(?Yqw~-P20@%Iqmx$q5R!F$DxWoSNk%H z0!DwtBxV!9I)JwUd=20i0RI43Ai!w?%#r*o$)9h%z-&dxQB4sO4sGiIm#W6g|te;^H`fXPt-@4rZKn0Y#|*c4`Em( zWP<=9T%`UrZZ|zK44~@~$h14u<>v;i0QotF2(BcxRe&sI4v>nFc8B>&QsoLBz|TkE z+*L93M^P-lmjP~MfIAuB{w#1M3*5{Ce`J9NazHx=yqN<&$pPQxw5Vp{Q8h`aWB@e- zSTlfE4dA8$d|?1T7{K2K@Ms=*C9lckb(#FFJa9V?6brz!1>p4p@Kr%Go6*gRKNW!A z3c!;l@P-L|V*;fjuwDe(MPRq6NzCdJg&RfS{r~8{7J<_x;A#o@yafDN(oByhDr!cn zhQ)@{b9p1oYuaL3(9C_8w>_o=4+$qH)^Vn$Gk?Y%-O?32?G09%10EP&jO{X^At zOkp{xkZ@yZB9I{K?X@o2+e%25_8N=ZiYk`Iiz*?fW)Avge%ze{Za+oaOkSp4CjEf* zc!V`OVKu4i4I~>=E=MUlo)|WTJU>vj=&?(QY!wH=EjMUC{Lk^p)rO3AH)uZyK~Ql; zJwU7blOk<^j!g^`>6x9_twzVtAmUW1B`J5#GA?=n_D&QFhSwV35#wNd8o?}Ea6T^0 z1H!ivg5iucV|QKhgy;_XtD$aMU+;_9`wi}_PA-vF>E3;%m=}i-sel-Ig@(Z$_j2l4 zp_xya6>+9s2iuH0_04A5^Jhc5#KEkZo$ z1Fq`lgm<_nH>UaEy0EEt7s(^M{mX$~5ZxxVsgrt0ojkaTNbSwa7V8Pc(j8wVWWF3( zL4sC&hW1zhJraRTw`E@t!W*|JjUY39YIm912P+_X?PjNfI@@AOx}){#C@iI{gl;gp<7nSSzhfC6Hk$5l3sAJko zVvbi7qah`|MzY~2%d#d|xW44H%_oi;Q_;dy)SQYIr=lf&AIp4D20kilyH|Ot$T3|c zcdHECE(2Bt*sN$`bBQyrSAad8Hb3>u6Nzn*1gZP6a*(>b&$zRu0yL{XC2MC^FG$M$ z9VSPElF6ftAh%_2F@NBxqgHl8Akk)v6MLN-dabqE!FV{FM<&UZtH7};uwK=2P)S5N zqc6KO#kyT3q*INoDvUc~*kfD;UyU+vRe^W)D^%~2=no@8!YZA9kIFb%8h;2qw_t}DGK4sgAGnq$Q%zJSq40(P3 Xr;a+^QB2l-MWy-|&ldcK4-o(WiRNs~ literal 1215 zcmV;w1VH;iRzVSIci3MI8Rd_O92D^`l8>(gv{L1cwSvNQg_C;wEa+gghGc zgvPrQdzH&!hLU16We*ogpL&Xs!!~t~THK4j97E^D`FyG`57@j31J9U=YOhqg^6y05e3d6Mds~gISW0@z+|d24aHbL8_P`nOTwr(dy)x zOMQ>FT-Mm^w_L~XG_E-E%>#$qE0o(V3u)$(>#{bpUu_IA#baiRS;DiKcLHIBkaYrt zaDnuOK!(+!4qxfF0_0%~5nOuIk^xf5Ob}CeR)_i0qtX#PfZvF~xwpi? zA7-(9D+9cn0lvxrW)`@d1@2^lhgqPO12%F%KL>o610Lj*tY+d_HAwz(4){9e${~Ibl`>#9O}T2x*{v7vceM`!(w7L58TTGzvY4F4S*TI zEd%(-P%P@IMee=oE*^h|n;?c_e_>hpUEw!2qRhsUWr;qQjTPaZ z>@JFVzPKhV>KspqPFCD^d`xM->TK>7z;1VoN~X$QEe)kpNbk&(ZW=;sNVXSUy8u* zMdh|HPc=D{csig2=q2Dv3AkHQ%w`g6{!jvbR%x?|7X}Hs>FM&Mn{=OXdsY5#BR#u% zae2q1+}~&3@GN28X{OX^SzFBSyYjUCm@r5jc?)B`$_=Ad24pfpCyU4+*_ASIwG6yh zR8-PYEkVRJ(HE$d2vN!H(EwH1(X#EAlB1k~ovOw8h-F d7sHe{j{YV$=)PoL!P{{b7NPT@-r007%iSZV+O diff --git a/packages/orchestration/test/examples/snapshots/staking-combinations.test.ts.md b/packages/orchestration/test/examples/snapshots/staking-combinations.test.ts.md index afcd6ed2fd1..55579d3b9f2 100644 --- a/packages/orchestration/test/examples/snapshots/staking-combinations.test.ts.md +++ b/packages/orchestration/test/examples/snapshots/staking-combinations.test.ts.md @@ -96,6 +96,9 @@ Generated by [AVA](https://avajs.dev). chainName: 'agoric', }, }, + lookupChainInfo_kindHandle: 'Alleged: kind', + lookupChainsAndConnection_kindHandle: 'Alleged: kind', + lookupConnectionInfo_kindHandle: 'Alleged: kind', }, contract: { 'ChainHub Admin_kindHandle': 'Alleged: kind', @@ -208,8 +211,11 @@ Generated by [AVA](https://avajs.dev). }, }, vows: { + AdminRetriableFlow_kindHandle: 'Alleged: kind', + AdminRetriableFlow_singleton: 'Alleged: AdminRetriableFlow', PromiseWatcher_kindHandle: 'Alleged: kind', VowInternalsKit_kindHandle: 'Alleged: kind', WatchUtils_kindHandle: 'Alleged: kind', + retriableFlowForOutcomeVow: {}, }, } diff --git a/packages/orchestration/test/examples/snapshots/staking-combinations.test.ts.snap b/packages/orchestration/test/examples/snapshots/staking-combinations.test.ts.snap index 406237967cd19ffb5aa5ec256a4d339d32f20cb7..8135c2bb2f8f8c9609f9cf748d941147bdbdc0d3 100644 GIT binary patch literal 2573 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 diff --git a/packages/orchestration/test/examples/snapshots/unbond.contract.test.ts.md b/packages/orchestration/test/examples/snapshots/unbond.contract.test.ts.md index 2a64227520d..0b95c9a7efc 100644 --- a/packages/orchestration/test/examples/snapshots/unbond.contract.test.ts.md +++ b/packages/orchestration/test/examples/snapshots/unbond.contract.test.ts.md @@ -114,6 +114,9 @@ Generated by [AVA](https://avajs.dev). }, }, denom: {}, + lookupChainInfo_kindHandle: 'Alleged: kind', + lookupChainsAndConnection_kindHandle: 'Alleged: kind', + lookupConnectionInfo_kindHandle: 'Alleged: kind', }, contract: { orchestration: { @@ -144,8 +147,11 @@ Generated by [AVA](https://avajs.dev). }, }, vows: { + AdminRetriableFlow_kindHandle: 'Alleged: kind', + AdminRetriableFlow_singleton: 'Alleged: AdminRetriableFlow', PromiseWatcher_kindHandle: 'Alleged: kind', VowInternalsKit_kindHandle: 'Alleged: kind', WatchUtils_kindHandle: 'Alleged: kind', + retriableFlowForOutcomeVow: {}, }, } diff --git a/packages/orchestration/test/examples/snapshots/unbond.contract.test.ts.snap b/packages/orchestration/test/examples/snapshots/unbond.contract.test.ts.snap index 9bf82ebc441f6257cf784eaba05cfb98d3c92f83..4f33076c337f2c46b1c53c7d7c656c10fa38c0c2 100644 GIT binary patch literal 1979 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 VowKit} makeVowKit + * @property {IsRetryableReason} isRetryableReason + */ + +/** + * @template {Passable[]} [TArgs=Passable[]] + * @template {any} [TRet=any] + * @typedef {(...args: TArgs) => Promise} RetriableFunc + */ + +const { defineProperties } = Object; + +const RetriableFlowIKit = harden({ + flow: M.interface('Flow', { + restart: M.call().returns(), + getOutcome: M.call().returns(VowShape), + }), + resultWatcher: PromiseWatcherI, +}); + +const AdminRetriableFlowI = M.interface('RetriableFlowAdmin', { + getFlowForOutcomeVow: M.call(VowShape).returns(M.opt(M.remotable('flow'))), +}); + +/** + * @param {Zone} outerZone + * @param {PreparationOptions} [outerOptions] + */ +export const prepareRetriableTools = (outerZone, outerOptions = {}) => { + const { makeVowKit, isRetryableReason } = outerOptions; + + /** + * So we can give out wrapper functions easily and recover flow objects + * for their activations later. + */ + const flowForOutcomeVowKey = outerZone.mapStore( + 'retriableFlowForOutcomeVow', + { + keyShape: M.remotable('toPassableCap'), + valueShape: M.remotable('flow'), // isDone === false + }, + ); + + /** + * @param {Zone} zone + * @param {string} tag + * @param {RetriableFunc} retriableFunc + */ + const prepareRetriableFlowKit = (zone, tag, retriableFunc) => { + typeof retriableFunc === 'function' || + Fail`retriableFunc must be a callable function ${retriableFunc}`; + + const internalMakeRetriableFlowKit = zone.exoClassKit( + tag, + RetriableFlowIKit, + activationArgs => { + harden(activationArgs); + + return { + activationArgs, // restarting the retriable function uses the original args + outcomeKit: makeVowKit(), // outcome of activation as vow + lastRetryReason: undefined, + runs: 0n, + isDone: false, // persistently done + }; + }, + { + flow: { + /** + * Calls the retriable function, either for the initial run or when + * the result of the previous run fails with a retriable reason. + */ + restart() { + const { state, facets } = this; + const { activationArgs, isDone } = state; + const { flow, resultWatcher } = facets; + + !isDone || + // separate line so I can set a breakpoint + Fail`Cannot restart a done retriable flow ${flow}`; + + const runId = state.runs + 1n; + state.runs = runId; + + let resultP; + try { + resultP = Promise.resolve(retriableFunc(...activationArgs)); + } catch (err) { + resultP = Promise.resolve(() => Promise.reject(err)); + } + + outerZone.watchPromise(harden(resultP), resultWatcher, runId); + }, + getOutcome() { + const { state } = this; + const { outcomeKit } = state; + return outcomeKit.vow; + }, + }, + resultWatcher: { + onFulfilled(value, runId) { + const { state } = this; + const { runs, outcomeKit } = state; + if (runId !== runs) return; + !state.isDone || + Fail`Cannot resolve a done retriable flow ${this.facets.flow}`; + outcomeKit.resolver.resolve(value); + flowForOutcomeVowKey.delete(toPassableCap(outcomeKit.vow)); + state.isDone = true; + }, + onRejected(reason, runId) { + const { state } = this; + const { runs, outcomeKit } = state; + if (runId !== runs) return; + !state.isDone || + Fail`Cannot reject a done retriable flow ${this.facets.flow}`; + const retryReason = isRetryableReason( + reason, + state.lastRetryReason, + ); + if (retryReason) { + state.lastRetryReason = retryReason; + this.facets.flow.restart(); + } else { + outcomeKit.resolver.reject(reason); + flowForOutcomeVowKey.delete(toPassableCap(outcomeKit.vow)); + state.isDone = true; + } + }, + }, + }, + ); + const makeRetriableFlowKit = activationArgs => { + const retriableKit = internalMakeRetriableFlowKit(activationArgs); + const { flow } = retriableKit; + + const vow = flow.getOutcome(); + flowForOutcomeVowKey.init(toPassableCap(vow), flow); + flow.restart(); + return retriableKit; + }; + return harden(makeRetriableFlowKit); + }; + + /** + * @template {RetriableFunc} F + * @param {Zone} zone + * @param {string} tag + * @param {F} retriableFunc + */ + const retriable = (zone, tag, retriableFunc) => { + const makeRetriableKit = prepareRetriableFlowKit(zone, tag, retriableFunc); + const wrapperFuncName = `${tag}_retriable`; + + const wrapperFunc = { + /** @type {(...args: Parameters) => Vow>>} */ + [wrapperFuncName](...args) { + const { flow } = makeRetriableKit(args); + return flow.getOutcome(); + }, + }[wrapperFuncName]; + defineProperties(wrapperFunc, { + length: { value: retriableFunc.length }, + }); + return harden(wrapperFunc); + }; + + const adminRetriableFlow = outerZone.exo( + 'AdminRetriableFlow', + AdminRetriableFlowI, + { + getFlowForOutcomeVow(outcomeVow) { + return flowForOutcomeVowKey.get(toPassableCap(outcomeVow)); + }, + }, + ); + + return harden({ + prepareRetriableFlowKit, + adminRetriableFlow, + retriable, + }); +}; +harden(prepareRetriableTools); + +/** + * @typedef {ReturnType} RetriableTools + */ + +/** + * @typedef {RetriableTools['adminRetriableFlow']} AdminRetriableFlow + */ + +/** + * @typedef {ReturnType} MakeRetriableFlowKit + */ + +/** + * @typedef {ReturnType} RetriableFlowKit + */ + +/** + * @typedef {RetriableFlowKit['flow']} RetriableFlow + */ diff --git a/packages/vow/src/tools.js b/packages/vow/src/tools.js index 275d274c615..2207da0f0e4 100644 --- a/packages/vow/src/tools.js +++ b/packages/vow/src/tools.js @@ -3,6 +3,7 @@ import { makeAsVow } from './vow-utils.js'; import { prepareVowKit } from './vow.js'; import { prepareWatchUtils } from './watch-utils.js'; import { prepareWatch } from './watch.js'; +import { prepareRetriableTools } from './retriable.js'; import { makeWhen } from './when.js'; /** @@ -34,23 +35,10 @@ export const prepareBasicVowTools = (zone, powers = {}) => { const watchUtils = makeWatchUtils(); const asVow = makeAsVow(makeVowKit); - /** - * TODO FIXME make this real - * Create a function that retries the given function if the underlying - * functions rejects due to upgrade disconnection. - * - * @template {(...args: any[]) => Promise} F - * @param {Zone} fnZone - the zone for the named function - * @param {string} name - * @param {F} fn - * @returns {F extends (...args: infer Args) => Promise ? (...args: Args) => Vow : never} - */ - const retriable = - (fnZone, name, fn) => - // @ts-expect-error cast - (...args) => { - return watch(fn(...args)); - }; + const { retriable } = prepareRetriableTools(zone, { + makeVowKit, + isRetryableReason, + }); /** * Vow-tolerant implementation of Promise.all that takes an iterable of vows