fix: add lastId caching

This commit is contained in:
Michael Vines 2018-10-22 20:03:44 -07:00
parent aaf86ff2ba
commit 83d12f50df
2 changed files with 58 additions and 18 deletions

View File

@ -159,7 +159,13 @@ export type SignatureStatus = 'Confirmed' | 'SignatureNotFound' | 'ProgramRuntim
*/
export class Connection {
_rpcRequest: RpcRequest;
_lastId: null | TransactionId;
_lastIdInfo: {
lastId: TransactionId | null,
seconds: number,
transactionSignatures: Array<string>,
};
_disableLastIdCaching: boolean = false
/**
* Establish a JSON RPC connection
@ -171,7 +177,11 @@ export class Connection {
throw new Error('Connection endpoint not specified');
}
this._rpcRequest = createRpcRequest(endpoint);
this._lastId = null;
this._lastIdInfo = {
lastId: null,
seconds: -1,
transactionSignatures: [],
};
}
/**
@ -301,27 +311,50 @@ export class Connection {
* Sign and send a transaction
*/
async sendTransaction(from: Account, transaction: Transaction): Promise<TransactionSignature> {
let attempts = 0;
for (;;) {
transaction.lastId = await this.getLastId();
// Attempt to use the previous last id for up to 1 second
const seconds = (new Date()).getSeconds();
if ( (this._lastIdInfo.lastId != null) &&
(this._lastIdInfo.seconds === seconds) ) {
// TODO: Waiting for the next lastId is really only necessary if a second
// transaction with the raw input bytes as an in-flight transaction
// is issued.
if (this._lastId != transaction.lastId) {
this._lastId = transaction.lastId;
break;
transaction.lastId = this._lastIdInfo.lastId;
transaction.sign(from);
if (!transaction.signature) {
throw new Error('!signature'); // should never happen
}
// If the signature of this transaction has not been seen before with the
// current lastId, all done.
const signature = transaction.signature.toString();
if (!this._lastIdInfo.transactionSignatures.includes(signature)) {
this._lastIdInfo.transactionSignatures.push(signature);
if (this._disableLastIdCaching) {
this._lastIdInfo.seconds = -1;
}
break;
}
}
if (attempts === 20) {
throw new Error('Unable to obtain new last id');
// Fetch a new last id
let attempts = 0;
for (;;) {
const lastId = await this.getLastId();
if (this._lastIdInfo.lastId != lastId) {
this._lastIdInfo = {
lastId,
seconds: (new Date()).getSeconds(),
transactionSignatures: [],
};
break;
}
if (attempts === 8) {
throw new Error('Unable to obtain new last id');
}
await sleep(250);
++attempts;
}
// TODO: Add a pubsub notification for obtaining the next last id instead
// of polling?
await sleep(100);
++attempts;
}
transaction.sign(from);
const wireTransaction = transaction.serialize();
const unsafeRes = await this._rpcRequest('sendTransaction', [[...wireTransaction]]);

View File

@ -52,6 +52,7 @@ let initialOwnerTokenAccount: PublicKey;
test('create new token', async () => {
const connection = new Connection(url);
connection._disableLastIdCaching = mockRpcEnabled;
initialOwner = await newAccountWithTokens(connection);
@ -168,6 +169,7 @@ test('create new token', async () => {
test('create new token account', async () => {
const connection = new Connection(url);
connection._disableLastIdCaching = mockRpcEnabled;
const destOwner = await newAccountWithTokens(connection);
{
@ -224,6 +226,7 @@ test('create new token account', async () => {
test('transfer', async () => {
const connection = new Connection(url);
connection._disableLastIdCaching = mockRpcEnabled;
const destOwner = await newAccountWithTokens(connection);
{
@ -319,6 +322,7 @@ test('transfer', async () => {
test('approve/revoke', async () => {
const connection = new Connection(url);
connection._disableLastIdCaching = mockRpcEnabled;
const delegateOwner = await newAccountWithTokens(connection);
{
@ -453,6 +457,7 @@ test('invalid approve', async () => {
}
const connection = new Connection(url);
connection._disableLastIdCaching = mockRpcEnabled;
const owner = await newAccountWithTokens(connection);
const account1 = await testToken.newAccount(owner);
@ -488,6 +493,7 @@ test('fail on approve overspend', async () => {
}
const connection = new Connection(url);
connection._disableLastIdCaching = mockRpcEnabled;
const owner = await newAccountWithTokens(connection);
const account1 = await testToken.newAccount(owner);
@ -552,6 +558,7 @@ test('set owner', async () => {
}
const connection = new Connection(url);
connection._disableLastIdCaching = mockRpcEnabled;
const owner = await newAccountWithTokens(connection);
const newOwner = await newAccountWithTokens(connection);