Skip to content

Commit

Permalink
fix: modify retry logic to only occur on dryrun exceptions
Browse files Browse the repository at this point in the history
  • Loading branch information
kunstmusik authored and dtfiedler committed Feb 24, 2025
1 parent 2397612 commit c578893
Show file tree
Hide file tree
Showing 2 changed files with 80 additions and 50 deletions.
116 changes: 66 additions & 50 deletions src/common/contracts/ao-process.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,12 @@
*/
import { connect } from '@permaweb/aoconnect';

import { AOContract, AoClient, AoSigner } from '../../types/index.js';
import {
AOContract,
AoClient,
AoSigner,
DryRunResult,
} from '../../types/index.js';
import { getRandomText } from '../../utils/base64.js';
import { errorMessageFromOutput } from '../../utils/index.js';
import { safeDecode } from '../../utils/json.js';
Expand Down Expand Up @@ -60,72 +65,83 @@ export class AOProcess implements AOContract {
retries?: number;
fromAddress?: string;
}): Promise<K> {
this.logger.debug(`Evaluating read interaction on process`, {
tags,
processId: this.processId,
});
// map tags to inputs
const dryRunInput = {
process: this.processId,
tags,
};
if (fromAddress !== undefined) {
dryRunInput['Owner'] = fromAddress;
}

let attempts = 0;
let lastError: Error | undefined;
let result: DryRunResult | undefined = undefined;

while (attempts < retries) {
try {
this.logger.debug(`Evaluating read interaction on process`, {
tags,
processId: this.processId,
});
// map tags to inputs
const dryRunInput = {
process: this.processId,
tags,
};
if (fromAddress !== undefined) {
dryRunInput['Owner'] = fromAddress;
}
const result = await this.ao.dryrun(dryRunInput);
this.logger.debug(`Read interaction result`, {
result,
processId: this.processId,
});

const error = errorMessageFromOutput(result);
if (error !== undefined) {
throw new Error(error);
}

if (result.Messages === undefined || result.Messages.length === 0) {
this.logger.debug(
`Process ${this.processId} does not support provided action.`,
{
result,
tags,
processId: this.processId,
},
);
throw new Error(
`Process ${this.processId} does not support provided action.`,
);
}
const messageData = result.Messages?.[0]?.Data;

// return undefined if no data is returned
if (this.isMessageDataEmpty(messageData)) {
return undefined as K;
}

const response: K = safeDecode<K>(messageData);
return response;
} catch (error: any) {
result = await this.ao.dryrun(dryRunInput);
// break on successful return of result
break;
} catch (error) {
attempts++;
this.logger.debug(`Read attempt ${attempts} failed`, {
error: error?.message,
stack: error?.stack,
tags,
processId: this.processId,
});
lastError = error;

if (attempts === retries) {
throw error;
}

// exponential backoff
await new Promise((resolve) =>
setTimeout(resolve, 2 ** attempts * 1000),
);
}
}
throw lastError;

if (result === undefined) {
throw new Error('Unexpected error when evaluating read interaction');
}

this.logger.debug(`Read interaction result`, {
result,
processId: this.processId,
});

const error = errorMessageFromOutput(result);
if (error !== undefined) {
throw new Error(error);
}

if (result.Messages === undefined || result.Messages.length === 0) {
this.logger.debug(
`Process ${this.processId} does not support provided action.`,
{
result,
tags,
processId: this.processId,
},
);
throw new Error(
`Process ${this.processId} does not support provided action.`,
);
}
const messageData = result.Messages?.[0]?.Data;

// return undefined if no data is returned
if (this.isMessageDataEmpty(messageData)) {
return undefined as K;
}

const response: K = safeDecode<K>(messageData);
return response;
}

async send<K>({
Expand Down
14 changes: 14 additions & 0 deletions src/types/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -141,3 +141,17 @@ export type AoWriteAction<P, R = AoMessageResult> = (
params: P,
options?: WriteOptions,
) => Promise<R>;

// the following are from @permaweb/aoconnect which does not export these types directly
export type DryRunResult = {
Output: any;
Messages: any[];
Spawns: any[];
Error?: any;
};
export type MessageResult = {
Output: any;
Messages: any[];
Spawns: any[];
Error?: any;
};

0 comments on commit c578893

Please sign in to comment.