Skip to content

Commit 02bfef5

Browse files
committed
fix: simplify retry logic, avoid retrying on top of next push attempt
1 parent d4bf8e9 commit 02bfef5

File tree

5 files changed

+67
-55
lines changed

5 files changed

+67
-55
lines changed

apps/price_pusher/src/solana/command.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -232,7 +232,8 @@ export default {
232232
jitoClients,
233233
jitoBundleSize,
234234
updatesPerJitoBundle,
235-
60000, // Default max retry time of 60 seconds
235+
// Set max retry time to pushing frequency, since we want to stop retrying before the next push attempt
236+
pushingFrequency * 1000,
236237
lookupTableAccount,
237238
);
238239

apps/price_pusher/src/solana/solana.ts

Lines changed: 10 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -169,7 +169,7 @@ export class SolanaPricePusherJito implements IPricePusher {
169169
private searcherClients: SearcherClient[],
170170
private jitoBundleSize: number,
171171
private updatesPerJitoBundle: number,
172-
private maxRetryTimeMs: number = 60000, // Default to 60 seconds max retry time
172+
private maxRetryTimeMs: number,
173173
private addressLookupTableAccount?: AddressLookupTableAccount,
174174
) {}
175175

@@ -195,10 +195,6 @@ export class SolanaPricePusherJito implements IPricePusher {
195195
}
196196
}
197197

198-
private async sleep(ms: number): Promise<void> {
199-
return new Promise((resolve) => setTimeout(resolve, ms));
200-
}
201-
202198
async updatePriceFeed(priceIds: string[]): Promise<void> {
203199
const recentJitoTip = await this.getRecentJitoTipLamports();
204200
const jitoTip =
@@ -244,41 +240,15 @@ export class SolanaPricePusherJito implements IPricePusher {
244240
jitoBundleSize: this.jitoBundleSize,
245241
});
246242

247-
try {
248-
this.logger.info("Sending Jito transactions...");
249-
await sendTransactionsJito(
250-
transactions,
251-
this.searcherClients,
252-
this.pythSolanaReceiver.wallet,
253-
{ maxRetryTimeMs: this.maxRetryTimeMs },
254-
);
255-
} catch (err: any) {
256-
if (err.code === 8 && err.details?.includes("Rate limit exceeded")) {
257-
this.logger.warn("Rate limit hit, waiting before retry...");
258-
await this.sleep(1100); // Wait slightly more than 1 second
259-
try {
260-
this.logger.info("Sending Jito transactions...");
261-
await sendTransactionsJito(
262-
transactions,
263-
this.searcherClients,
264-
this.pythSolanaReceiver.wallet,
265-
{ maxRetryTimeMs: this.maxRetryTimeMs },
266-
);
267-
} catch (retryErr: any) {
268-
this.logger.error("Failed after rate limit retry");
269-
throw retryErr;
270-
}
271-
} else {
272-
this.logger.error(
273-
{ err },
274-
"Failed to send transactions via all JITO endpoints",
275-
);
276-
throw err;
277-
}
278-
}
279-
280-
// Add a delay between bundles to avoid rate limiting
281-
await this.sleep(1100);
243+
await sendTransactionsJito(
244+
transactions,
245+
this.searcherClients,
246+
this.pythSolanaReceiver.wallet,
247+
{
248+
maxRetryTimeMs: this.maxRetryTimeMs,
249+
},
250+
this.logger,
251+
);
282252
}
283253
}
284254
}

pnpm-lock.yaml

Lines changed: 3 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

target_chains/solana/sdk/js/solana_utils/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@
4949
"@coral-xyz/anchor": "^0.29.0",
5050
"@solana/web3.js": "^1.90.0",
5151
"bs58": "^5.0.0",
52-
"jito-ts": "^3.0.1"
52+
"jito-ts": "^3.0.1",
53+
"ts-log": "^2.2.7"
5354
}
5455
}

target_chains/solana/sdk/js/solana_utils/src/jito.ts

Lines changed: 50 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { dummyLogger, Logger } from "ts-log";
12
import { Wallet } from "@coral-xyz/anchor";
23
import {
34
PublicKey,
@@ -45,8 +46,10 @@ export async function sendTransactionsJito(
4546
searcherClients: SearcherClient | SearcherClient[],
4647
wallet: Wallet,
4748
options: {
48-
maxRetryTimeMs?: number;
49+
maxRetryTimeMs?: number; // Max time to retry sending transactions
50+
delayBetweenCyclesMs?: number; // Delay between cycles of sending transactions to all searcher clients
4951
} = {},
52+
logger: Logger = dummyLogger, // Optional logger to track progress of retries
5053
): Promise<string> {
5154
const clients = Array.isArray(searcherClients)
5255
? searcherClients
@@ -57,6 +60,8 @@ export async function sendTransactionsJito(
5760
}
5861

5962
const maxRetryTimeMs = options.maxRetryTimeMs || 60000; // Default to 60 seconds
63+
const delayBetweenCyclesMs = options.delayBetweenCyclesMs || 1000; // Default to 1 second
64+
6065
const startTime = Date.now();
6166

6267
const signedTransactions = [];
@@ -80,22 +85,54 @@ export async function sendTransactionsJito(
8085
const bundle = new Bundle(signedTransactions, 2);
8186

8287
let lastError: Error | null = null;
83-
let clientIndex = 0;
88+
let totalAttempts = 0;
8489

8590
while (Date.now() - startTime < maxRetryTimeMs) {
86-
const currentClient = clients[clientIndex];
87-
try {
88-
await currentClient.sendBundle(bundle);
89-
return firstTransactionSignature;
90-
} catch (err: any) {
91-
lastError = err;
92-
clientIndex = (clientIndex + 1) % clients.length;
93-
await new Promise((resolve) => setTimeout(resolve, 500));
91+
// Try all clients in this cycle
92+
for (let i = 0; i < clients.length; i++) {
93+
const currentClient = clients[i];
94+
totalAttempts++;
95+
96+
try {
97+
await currentClient.sendBundle(bundle);
98+
logger.info(
99+
{ clientIndex: i, totalAttempts },
100+
`Successfully sent bundle to Jito client after ${totalAttempts} attempts`,
101+
);
102+
return firstTransactionSignature;
103+
} catch (err: any) {
104+
lastError = err;
105+
logger.error(
106+
{ clientIndex: i, totalAttempts, err: err.message },
107+
`Attempt ${totalAttempts}: Error sending bundle to Jito client ${i}`,
108+
);
109+
}
110+
111+
// Check if we've run out of time
112+
if (Date.now() - startTime >= maxRetryTimeMs) {
113+
break;
114+
}
115+
}
116+
117+
// If we've tried all clients and still have time, wait before next cycle
118+
const timeRemaining = maxRetryTimeMs - (Date.now() - startTime);
119+
if (timeRemaining > delayBetweenCyclesMs) {
120+
await new Promise((resolve) => setTimeout(resolve, delayBetweenCyclesMs));
94121
}
95122
}
96123

97-
throw (
98-
lastError ||
99-
new Error("Failed to send transactions via JITO after maximum retry time")
124+
const totalTimeMs = Date.now() - startTime;
125+
const errorMsg = `Failed to send transactions via JITO after ${totalAttempts} attempts over ${totalTimeMs}ms (max: ${maxRetryTimeMs}ms)`;
126+
127+
logger.error(
128+
{
129+
totalAttempts,
130+
totalTimeMs,
131+
maxRetryTimeMs,
132+
lastError: lastError?.message,
133+
},
134+
errorMsg,
100135
);
136+
137+
throw lastError || new Error(errorMsg);
101138
}

0 commit comments

Comments
 (0)