Shred Delivery and Sender are now live! Get earliest access to raw Solana data and optimized transaction sending. Learn about Shred Delivery | Learn about Sender
Shred Delivery and Sender are now live! Get earliest access to raw Solana data and optimized transaction sending. Learn about Shred Delivery | Learn about Sender
优化 Solana RPC 性能,降低成本,提高可靠性。交易优化、数据检索模式和最佳实践指南。
const testTransaction = new VersionedTransaction(/* your transaction */);
const simulation = await connection.simulateTransaction(testTransaction, {
replaceRecentBlockhash: true,
sigVerify: false
});
const unitsConsumed = simulation.value.unitsConsumed;
const computeUnitLimit = Math.ceil(unitsConsumed * 1.1);
const computeUnitIx = ComputeBudgetProgram.setComputeUnitLimit({
units: computeUnitLimit
});
instructions.unshift(computeUnitIx); // Add at beginning
const response = await fetch(`https://mainnet.helius-rpc.com/?api-key=${API_KEY}`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
method: 'getPriorityFeeEstimate',
params: [{
accountKeys: ['11111111111111111111111111111112'], // System Program
options: { recommended: true }
}]
})
});
const { priorityFeeEstimate } = await response.json().result;
const priorityFeeIx = ComputeBudgetProgram.setComputeUnitPrice({
microLamports: priorityFeeEstimate
});
instructions.unshift(priorityFeeIx);
// Serialize and encode
const serializedTx = transaction.serialize();
const signature = await connection.sendRawTransaction(serializedTx, {
skipPreflight: true, // Saves ~100ms
maxRetries: 0 // Handle retries manually
});
getProgramAccountsV2
和 getTokenAccountsByOwnerV2
为处理大型数据集的应用程序提供了显著的性能改进:changedSinceSlot
进行实时同步// ❌ Old approach - could timeout with large datasets
const allAccounts = await connection.getProgramAccounts(programId, {
encoding: 'base64',
filters: [{ dataSize: 165 }]
});
// ✅ New approach - paginated with better performance
let allAccounts = [];
let paginationKey = null;
do {
const response = await fetch(`https://mainnet.helius-rpc.com/?api-key=${API_KEY}`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
jsonrpc: '2.0',
id: '1',
method: 'getProgramAccountsV2',
params: [
programId,
{
encoding: 'base64',
filters: [{ dataSize: 165 }],
limit: 5000,
...(paginationKey && { paginationKey })
}
]
})
});
const data = await response.json();
allAccounts.push(...data.result.accounts);
paginationKey = data.result.paginationKey;
} while (paginationKey);
// Get only accounts modified since a specific slot
const incrementalUpdate = await fetch(`https://mainnet.helius-rpc.com/?api-key=${API_KEY}`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
jsonrpc: '2.0',
id: '1',
method: 'getProgramAccountsV2',
params: [
programId,
{
encoding: 'jsonParsed',
limit: 1000,
changedSinceSlot: lastProcessedSlot // Only get recent changes
}
]
})
});
// Use dataSlice to reduce payload size
const accountInfo = await connection.getAccountInfo(pubkey, {
encoding: 'base64',
dataSlice: { offset: 0, length: 100 }, // Only get needed data
commitment: 'confirmed'
});
// Don't do this - requires N+1 RPC calls
const tokenAccounts = await connection.getTokenAccountsByOwner(owner, {
programId: TOKEN_PROGRAM_ID
});
const balances = await Promise.all(
tokenAccounts.value.map(acc =>
connection.getTokenAccountBalance(acc.pubkey)
)
);
// ~500ms + (100ms * N accounts)
// Avoid sequential transaction fetching
const signatures = await connection.getSignaturesForAddress(address, { limit: 100 });
const transactions = await Promise.all(
signatures.map(sig => connection.getTransaction(sig.signature))
);
// ~1s + (200ms * 100 txs) = ~21s
// Avoid polling - wastes resources
setInterval(async () => {
const accountInfo = await connection.getAccountInfo(pubkey);
// Process updates...
}, 1000);
// Monitor specific program accounts with filters
connection.onProgramAccountChange(
programId,
(accountInfo, context) => {
// Handle program account changes
},
'confirmed',
{
filters: [
{ dataSize: 1024 },
{ memcmp: { offset: 0, bytes: ACCOUNT_DISCRIMINATOR }}
],
encoding: 'base64'
}
);
// Subscribe to transaction logs for real-time monitoring
const ws = new WebSocket(`wss://mainnet.helius-rpc.com/?api-key=${API_KEY}`);
ws.on('open', () => {
ws.send(JSON.stringify({
jsonrpc: '2.0',
id: 1,
method: 'logsSubscribe',
params: [
{ mentions: [programId] },
{ commitment: 'confirmed' }
]
}));
});
ws.on('message', (data) => {
const message = JSON.parse(data);
if (message.params) {
const signature = message.params.result.value.signature;
// Process transaction signature
}
});
class RetryManager {
private backoff = new ExponentialBackoff({
min: 100,
max: 5000,
factor: 2,
jitter: 0.2
});
async executeWithRetry<T>(operation: () => Promise<T>): Promise<T> {
while (true) {
try {
return await operation();
} catch (error) {
if (error.message.includes('429')) {
// Rate limit - wait and retry
await this.backoff.delay();
continue;
}
throw error;
}
}
}
}
// Process large datasets in chunks
function chunk<T>(array: T[], size: number): T[][] {
return Array.from({ length: Math.ceil(array.length / size) }, (_, i) =>
array.slice(i * size, i * size + size)
);
}
// Process program accounts in batches
const allAccounts = await connection.getProgramAccounts(programId, {
dataSlice: { offset: 0, length: 32 }
});
const chunks = chunk(allAccounts, 100);
for (const batch of chunks) {
const detailedAccounts = await connection.getMultipleAccountsInfo(
batch.map(acc => acc.pubkey)
);
// Process batch...
}
class ConnectionPool {
private connections: Connection[] = [];
private currentIndex = 0;
constructor(rpcUrls: string[]) {
this.connections = rpcUrls.map(url => new Connection(url));
}
getConnection(): Connection {
const connection = this.connections[this.currentIndex];
this.currentIndex = (this.currentIndex + 1) % this.connections.length;
return connection;
}
}
const pool = new ConnectionPool([
'https://mainnet.helius-rpc.com/?api-key=YOUR_API_KEY',
'https://mainnet-backup.helius-rpc.com/?api-key=YOUR_API_KEY'
]);
class RPCMonitor {
private metrics = {
calls: 0,
errors: 0,
totalLatency: 0
};
async monitoredCall<T>(operation: () => Promise<T>): Promise<T> {
const start = Date.now();
this.metrics.calls++;
try {
const result = await operation();
this.metrics.totalLatency += Date.now() - start;
return result;
} catch (error) {
this.metrics.errors++;
throw error;
}
}
getStats() {
return {
...this.metrics,
averageLatency: this.metrics.totalLatency / this.metrics.calls,
errorRate: this.metrics.errors / this.metrics.calls
};
}
}
// Implement robust error handling
async function robustRPCCall<T>(operation: () => Promise<T>): Promise<T> {
try {
return await operation();
} catch (error) {
if (error.code === -32602) {
// Invalid params - fix request
throw new Error('Invalid RPC parameters');
} else if (error.code === -32005) {
// Node behind - retry with different node
throw new Error('Node synchronization issue');
} else if (error.message.includes('429')) {
// Rate limit - implement backoff
throw new Error('Rate limited');
}
throw error;
}
}
confirmed
足够时使用 finalized
承诺