diff --git a/web3.js/src/connection.js b/web3.js/src/connection.js index 7e9e5896b4..7125b5e4ac 100644 --- a/web3.js/src/connection.js +++ b/web3.js/src/connection.js @@ -7,8 +7,9 @@ import jayson from 'jayson/lib/client/browser'; import {struct} from 'superstruct'; import {Client as RpcWebSocketClient} from 'rpc-websockets'; -import {Transaction} from './transaction'; +import {DEFAULT_TICKS_PER_SLOT, NUM_TICKS_PER_SECOND} from './timing'; import {PublicKey} from './publickey'; +import {Transaction} from './transaction'; import {sleep} from './util/sleep'; import type {Blockhash} from './blockhash'; import type {Account} from './account'; @@ -351,11 +352,11 @@ export class Connection { ...signers: Array ): Promise { for (;;) { - // Attempt to use the previous last id for up to 1 second + // Attempt to use a recent blockhash for up to 30 seconds const seconds = new Date().getSeconds(); if ( this._blockhashInfo.recentBlockhash != null && - this._blockhashInfo.seconds === seconds + this._blockhashInfo.seconds < seconds + 30 ) { transaction.recentBlockhash = this._blockhashInfo.recentBlockhash; transaction.sign(...signers); @@ -375,7 +376,7 @@ export class Connection { } } - // Fetch a new last id + // Fetch a new blockhash let attempts = 0; const startTime = Date.now(); for (;;) { @@ -391,10 +392,14 @@ export class Connection { } if (attempts === 16) { throw new Error( - `Unable to obtain a new last id after ${Date.now() - startTime}ms`, + `Unable to obtain a new blockhash after ${Date.now() - + startTime}ms`, ); } - await sleep(250); + + // Sleep for approximately half a slot + await sleep((500 * DEFAULT_TICKS_PER_SLOT) / NUM_TICKS_PER_SECOND); + ++attempts; } } diff --git a/web3.js/src/loader.js b/web3.js/src/loader.js index 9b3706c592..52ec6282d3 100644 --- a/web3.js/src/loader.js +++ b/web3.js/src/loader.js @@ -4,8 +4,10 @@ import * as BufferLayout from 'buffer-layout'; import {Account} from './account'; import {PublicKey} from './publickey'; +import {NUM_TICKS_PER_SECOND} from './timing'; import {Transaction} from './transaction'; import {sendAndConfirmTransaction} from './util/send-and-confirm-transaction'; +import {sleep} from './util/sleep'; import type {Connection} from './connection'; /** @@ -81,6 +83,10 @@ export class Loader { sendAndConfirmTransaction(this.connection, transaction, program), ); + // Delay ~1 tick between write transactions in an attempt to reduce AccountInUse errors + // since all the write transactions modify the same program account + await sleep(1000 / NUM_TICKS_PER_SECOND); + // Run up to 8 Loads in parallel to prevent too many parallel transactions from // getting rejected with AccountInUse. // diff --git a/web3.js/src/timing.js b/web3.js/src/timing.js new file mode 100644 index 0000000000..6c541d5d77 --- /dev/null +++ b/web3.js/src/timing.js @@ -0,0 +1,7 @@ +// @flow + +// These constants should match the values in +// https://github.com/solana-labs/solana/blob/master/sdk/src/timing.rs +export const NUM_TICKS_PER_SECOND = 10; +export const DEFAULT_TICKS_PER_SLOT = 80; +export const DEFAULT_SLOTS_PER_EPOCH = 64;