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,
}
declare export type SignatureStatus = 'Confirmed' | 'SignatureNotFound' | 'ProgramRuntimeError' | 'GenericFailure';
declare export type SignatureStatus = 'Confirmed'
| 'AccountInUse'
| 'SignatureNotFound'
| 'ProgramRuntimeError'
| 'GenericFailure';
declare export class Connection {
constructor(endpoint: string): Connection;

View File

@ -101,10 +101,11 @@ const ConfirmTransactionRpcResult = jsonRpcResult('boolean');
* Expected JSON RPC response for the "getSignatureStatus" message
*/
const GetSignatureStatusRpcResult = jsonRpcResult(struct.enum([
'AccountInUse',
'Confirmed',
'SignatureNotFound',
'ProgramRuntimeError',
'GenericFailure',
'ProgramRuntimeError',
'SignatureNotFound',
]));
/**
@ -152,7 +153,11 @@ type AccountInfo = {
*
* @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

View File

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