fix: retry transactions on AccountInUse errors

This commit is contained in:
Michael Vines
2018-10-23 13:10:08 -07:00
parent 96c685eb5d
commit 90c9df15ef
3 changed files with 38 additions and 24 deletions

View File

@ -41,7 +41,11 @@ declare module '@solana/web3.js' {
userdata: Buffer, userdata: Buffer,
} }
declare export type SignatureStatus = 'Confirmed' | 'SignatureNotFound' | 'ProgramRuntimeError' | 'GenericFailure'; declare export type SignatureStatus = 'Confirmed'
| 'AccountInUse'
| 'SignatureNotFound'
| 'ProgramRuntimeError'
| 'GenericFailure';
declare export class Connection { declare export class Connection {
constructor(endpoint: string): Connection; constructor(endpoint: string): Connection;

View File

@ -101,10 +101,11 @@ const ConfirmTransactionRpcResult = jsonRpcResult('boolean');
* Expected JSON RPC response for the "getSignatureStatus" message * Expected JSON RPC response for the "getSignatureStatus" message
*/ */
const GetSignatureStatusRpcResult = jsonRpcResult(struct.enum([ const GetSignatureStatusRpcResult = jsonRpcResult(struct.enum([
'AccountInUse',
'Confirmed', 'Confirmed',
'SignatureNotFound',
'ProgramRuntimeError',
'GenericFailure', 'GenericFailure',
'ProgramRuntimeError',
'SignatureNotFound',
])); ]));
/** /**
@ -152,7 +153,11 @@ type AccountInfo = {
* *
* @typedef {string} SignatureStatus * @typedef {string} SignatureStatus
*/ */
export type SignatureStatus = 'Confirmed' | 'SignatureNotFound' | 'ProgramRuntimeError' | 'GenericFailure'; export type SignatureStatus = 'Confirmed'
| 'AccountInUse'
| 'SignatureNotFound'
| 'ProgramRuntimeError'
| 'GenericFailure';
/** /**
* A connection to a fullnode JSON RPC endpoint * A connection to a fullnode JSON RPC endpoint

View File

@ -15,31 +15,36 @@ export async function sendAndConfirmTransaction(
transaction: Transaction, transaction: Transaction,
runtimeErrorOk: boolean = false runtimeErrorOk: boolean = false
): Promise<void> { ): Promise<void> {
let sendRetries = 3;
for (;;) {
const start = Date.now(); const start = Date.now();
const signature = await connection.sendTransaction(from, transaction); const signature = await connection.sendTransaction(from, transaction);
// Wait up to a couple seconds for a confirmation // Wait up to a couple seconds for a confirmation
let i = 4; let status = 'SignatureNotFound';
let statusRetries = 4;
for (;;) { for (;;) {
const status = await connection.getSignatureStatus(signature); status = await connection.getSignatureStatus(signature);
switch (status) { if (status !== 'SignatureNotFound') {
case 'Confirmed':
return;
case 'ProgramRuntimeError':
if (runtimeErrorOk) return;
//fall through
case 'GenericError':
default:
throw new Error(`Transaction ${signature} failed (${status})`);
case 'SignatureNotFound':
break; break;
} }
await sleep(500); await sleep(500);
if (--i < 0) { if (--statusRetries <= 0) {
const duration = (Date.now() - start) / 1000; const duration = (Date.now() - start) / 1000;
throw new Error(`Transaction '${signature}' was not confirmed in ${duration.toFixed(2)} seconds (${status})`); throw new Error(`Transaction '${signature}' was not confirmed in ${duration.toFixed(2)} seconds (${status})`);
} }
} }
if ( (status === 'Confirmed') ||
(status === 'ProgramRuntimeError' && runtimeErrorOk) ) {
return;
}
if (status !== 'AccountInUse' || --sendRetries <= 0) {
throw new Error(`Transaction ${signature} failed (${status})`);
}
}
} }