Skip to content

Commit 88a69be

Browse files
committed
refactor(batch-snapshot): enhance batch processing and error tracking
- Increased delay between batches from 5 to 10 seconds for improved stability. - Updated batch processing logic to include network-specific data for mainnet and testnet. - Enhanced failure tracking with detailed error types and messages for better debugging. - Improved logging to provide clearer insights into wallet processing results and failures.
1 parent 41f048c commit 88a69be

File tree

3 files changed

+194
-20
lines changed

3 files changed

+194
-20
lines changed

.github/workflows/daily-balance-snapshots.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ jobs:
4141
API_BASE_URL: "https://multisig.meshjs.dev"
4242
SNAPSHOT_AUTH_TOKEN: ${{ secrets.SNAPSHOT_AUTH_TOKEN }}
4343
BATCH_SIZE: 10
44-
DELAY_BETWEEN_BATCHES: 5
44+
DELAY_BETWEEN_BATCHES: 10
4545
MAX_RETRIES: 3
4646

4747
- name: Notify on failure

scripts/batch-snapshot-orchestrator.ts

Lines changed: 110 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
* - API_BASE_URL: Base URL for the API (default: http://localhost:3000)
1616
* - SNAPSHOT_AUTH_TOKEN: Authentication token for API requests
1717
* - BATCH_SIZE: Number of wallets per batch (default: 10)
18-
* - DELAY_BETWEEN_BATCHES: Delay between batches in seconds (default: 5)
18+
* - DELAY_BETWEEN_BATCHES: Delay between batches in seconds (default: 10)
1919
* - MAX_RETRIES: Maximum retries for failed batches (default: 3)
2020
*/
2121

@@ -24,8 +24,18 @@ interface BatchProgress {
2424
walletsInBatch: number;
2525
failedInBatch: number;
2626
snapshotsStored: number;
27-
totalAdaBalance: number;
2827
totalBatches: number;
28+
// Network-specific data
29+
mainnetWallets: number;
30+
testnetWallets: number;
31+
mainnetAdaBalance: number;
32+
testnetAdaBalance: number;
33+
// Failure details
34+
failures: Array<{
35+
walletId: string;
36+
errorType: string;
37+
errorMessage: string;
38+
}>;
2939
}
3040

3141
interface BatchResponse {
@@ -40,9 +50,21 @@ interface BatchResults {
4050
failedBatches: number;
4151
totalWalletsProcessed: number;
4252
totalWalletsFailed: number;
43-
totalAdaBalance: number;
4453
totalSnapshotsStored: number;
4554
executionTime: number;
55+
// Network-specific data
56+
totalMainnetWallets: number;
57+
totalTestnetWallets: number;
58+
totalMainnetAdaBalance: number;
59+
totalTestnetAdaBalance: number;
60+
// Failure tracking
61+
allFailures: Array<{
62+
walletId: string;
63+
errorType: string;
64+
errorMessage: string;
65+
batchNumber: number;
66+
}>;
67+
failureSummary: Record<string, number>;
4668
}
4769

4870
interface BatchConfig {
@@ -70,9 +92,16 @@ class BatchSnapshotOrchestrator {
7092
failedBatches: 0,
7193
totalWalletsProcessed: 0,
7294
totalWalletsFailed: 0,
73-
totalAdaBalance: 0,
7495
totalSnapshotsStored: 0,
7596
executionTime: 0,
97+
// Network-specific data
98+
totalMainnetWallets: 0,
99+
totalTestnetWallets: 0,
100+
totalMainnetAdaBalance: 0,
101+
totalTestnetAdaBalance: 0,
102+
// Failure tracking
103+
allFailures: [],
104+
failureSummary: {},
76105
};
77106
}
78107

@@ -88,7 +117,7 @@ class BatchSnapshotOrchestrator {
88117
apiBaseUrl,
89118
authToken,
90119
batchSize: parseInt(process.env.BATCH_SIZE || '10'),
91-
delayBetweenBatches: parseInt(process.env.DELAY_BETWEEN_BATCHES || '5'),
120+
delayBetweenBatches: parseInt(process.env.DELAY_BETWEEN_BATCHES || '10'),
92121
maxRetries: parseInt(process.env.MAX_RETRIES || '3'),
93122
};
94123
}
@@ -129,6 +158,17 @@ class BatchSnapshotOrchestrator {
129158
return new Promise(resolve => setTimeout(resolve, seconds * 1000));
130159
}
131160

161+
private getFriendlyErrorName(errorType: string): string {
162+
const errorMap: Record<string, string> = {
163+
'wallet_build_failed': 'Wallet Build Failed',
164+
'utxo_fetch_failed': 'UTxO Fetch Failed',
165+
'address_generation_failed': 'Address Generation Failed',
166+
'balance_calculation_failed': 'Balance Calculation Failed',
167+
'processing_failed': 'General Processing Failed',
168+
};
169+
return errorMap[errorType] || errorType;
170+
}
171+
132172
private async processBatch(batchNumber: number, batchId: string): Promise<BatchProgress | null> {
133173
console.log(`📦 Processing batch ${batchNumber}...`);
134174

@@ -148,7 +188,8 @@ class BatchSnapshotOrchestrator {
148188
console.log(` • Processed: ${data.progress.processedInBatch}/${data.progress.walletsInBatch} wallets`);
149189
console.log(` • Failed: ${data.progress.failedInBatch}`);
150190
console.log(` • Snapshots stored: ${data.progress.snapshotsStored}`);
151-
console.log(` • Batch ADA balance: ${Math.round(data.progress.totalAdaBalance * 100) / 100} ADA`);
191+
console.log(` • Mainnet: ${data.progress.mainnetWallets} wallets, ${Math.round(data.progress.mainnetAdaBalance * 100) / 100} ADA`);
192+
console.log(` • Testnet: ${data.progress.testnetWallets} wallets, ${Math.round(data.progress.testnetAdaBalance * 100) / 100} ADA`);
152193

153194
return data.progress;
154195
} else {
@@ -193,8 +234,22 @@ class BatchSnapshotOrchestrator {
193234
this.results.completedBatches = 1;
194235
this.results.totalWalletsProcessed += firstBatch.processedInBatch;
195236
this.results.totalWalletsFailed += firstBatch.failedInBatch;
196-
this.results.totalAdaBalance += firstBatch.totalAdaBalance;
197237
this.results.totalSnapshotsStored += firstBatch.snapshotsStored;
238+
239+
// Accumulate network-specific data
240+
this.results.totalMainnetWallets += firstBatch.mainnetWallets;
241+
this.results.totalTestnetWallets += firstBatch.testnetWallets;
242+
this.results.totalMainnetAdaBalance += firstBatch.mainnetAdaBalance;
243+
this.results.totalTestnetAdaBalance += firstBatch.testnetAdaBalance;
244+
245+
// Accumulate failures
246+
firstBatch.failures.forEach(failure => {
247+
this.results.allFailures.push({
248+
...failure,
249+
batchNumber: 1
250+
});
251+
this.results.failureSummary[failure.errorType] = (this.results.failureSummary[failure.errorType] || 0) + 1;
252+
});
198253

199254
console.log(`📊 Total batches to process: ${this.results.totalBatches}`);
200255

@@ -210,8 +265,22 @@ class BatchSnapshotOrchestrator {
210265
this.results.completedBatches++;
211266
this.results.totalWalletsProcessed += batchProgress.processedInBatch;
212267
this.results.totalWalletsFailed += batchProgress.failedInBatch;
213-
this.results.totalAdaBalance += batchProgress.totalAdaBalance;
214268
this.results.totalSnapshotsStored += batchProgress.snapshotsStored;
269+
270+
// Accumulate network-specific data
271+
this.results.totalMainnetWallets += batchProgress.mainnetWallets;
272+
this.results.totalTestnetWallets += batchProgress.testnetWallets;
273+
this.results.totalMainnetAdaBalance += batchProgress.mainnetAdaBalance;
274+
this.results.totalTestnetAdaBalance += batchProgress.testnetAdaBalance;
275+
276+
// Accumulate failures
277+
batchProgress.failures.forEach(failure => {
278+
this.results.allFailures.push({
279+
...failure,
280+
batchNumber
281+
});
282+
this.results.failureSummary[failure.errorType] = (this.results.failureSummary[failure.errorType] || 0) + 1;
283+
});
215284
} else {
216285
this.results.failedBatches++;
217286
console.error(`❌ Batch ${batchNumber} failed completely`);
@@ -234,11 +303,42 @@ class BatchSnapshotOrchestrator {
234303
console.log(` • Wallets processed: ${this.results.totalWalletsProcessed}`);
235304
console.log(` • Wallets failed: ${this.results.totalWalletsFailed}`);
236305
console.log(` • Snapshots stored: ${this.results.totalSnapshotsStored}`);
237-
console.log(` • Total TVL: ${Math.round(this.results.totalAdaBalance * 100) / 100} ADA`);
238306
console.log(` • Execution time: ${this.results.executionTime}s`);
307+
308+
// Network-specific breakdown
309+
console.log(`\n🌐 Network Breakdown:`);
310+
console.log(` 📈 Mainnet:`);
311+
console.log(` • Wallets: ${this.results.totalMainnetWallets}`);
312+
console.log(` • TVL: ${Math.round(this.results.totalMainnetAdaBalance * 100) / 100} ADA`);
313+
console.log(` 🧪 Testnet:`);
314+
console.log(` • Wallets: ${this.results.totalTestnetWallets}`);
315+
console.log(` • TVL: ${Math.round(this.results.totalTestnetAdaBalance * 100) / 100} ADA`);
316+
317+
// Failure analysis
318+
if (this.results.totalWalletsFailed > 0) {
319+
console.log(`\n❌ Failure Analysis:`);
320+
console.log(` • Total failed wallets: ${this.results.totalWalletsFailed}`);
321+
322+
// Show failure summary by type
323+
Object.entries(this.results.failureSummary).forEach(([errorType, count]) => {
324+
const friendlyName = this.getFriendlyErrorName(errorType);
325+
console.log(` • ${friendlyName}: ${count} wallets`);
326+
});
327+
328+
// Show sample failures (first 3)
329+
if (this.results.allFailures.length > 0) {
330+
console.log(`\n📋 Sample Failures:`);
331+
this.results.allFailures.slice(0, 3).forEach((failure, index) => {
332+
console.log(` ${index + 1}. ${failure.walletId.slice(0, 8)}... - ${failure.errorMessage}`);
333+
});
334+
if (this.results.allFailures.length > 3) {
335+
console.log(` ... and ${this.results.allFailures.length - 3} more`);
336+
}
337+
}
338+
}
239339

240340
if (this.results.failedBatches > 0) {
241-
console.log(`⚠️ Warning: ${this.results.failedBatches} batches failed. You may need to retry those batches manually.`);
341+
console.log(`\n⚠️ Warning: ${this.results.failedBatches} batches failed. You may need to retry those batches manually.`);
242342
}
243343

244344
return this.results;

src/pages/api/v1/stats/run-snapshots-batch.ts

Lines changed: 83 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,13 @@ interface WalletBalance {
1616
balance: Record<string, string>;
1717
adaBalance: number;
1818
isArchived: boolean;
19+
network: number; // 0 = testnet, 1 = mainnet
20+
}
21+
22+
interface WalletFailure {
23+
walletId: string;
24+
errorType: string; // e.g., "wallet_build_failed", "utxo_fetch_failed", "balance_calculation_failed"
25+
errorMessage: string; // sanitized error message
1926
}
2027

2128
interface BatchProgress {
@@ -27,11 +34,17 @@ interface BatchProgress {
2734
failedInBatch: number;
2835
totalProcessed: number;
2936
totalFailed: number;
30-
totalAdaBalance: number;
3137
snapshotsStored: number;
3238
isComplete: boolean;
3339
startedAt: string;
3440
lastUpdatedAt: string;
41+
// Network-specific data
42+
mainnetWallets: number;
43+
testnetWallets: number;
44+
mainnetAdaBalance: number;
45+
testnetAdaBalance: number;
46+
// Failure details
47+
failures: WalletFailure[];
3548
}
3649

3750
interface BatchResponse {
@@ -113,21 +126,31 @@ export default async function handler(
113126
failedInBatch: 0,
114127
totalProcessed: 0,
115128
totalFailed: 0,
116-
totalAdaBalance: 0,
117129
snapshotsStored: 0,
118130
isComplete: true,
119131
startedAt: startTime,
120132
lastUpdatedAt: new Date().toISOString(),
133+
// Network-specific data
134+
mainnetWallets: 0,
135+
testnetWallets: 0,
136+
mainnetAdaBalance: 0,
137+
testnetAdaBalance: 0,
138+
// Failure details
139+
failures: [],
121140
},
122141
timestamp: new Date().toISOString(),
123142
});
124143
}
125144

126145
// Step 3: Process wallets in this batch
127146
const walletBalances: WalletBalance[] = [];
147+
const failures: WalletFailure[] = [];
128148
let processedInBatch = 0;
129149
let failedInBatch = 0;
130-
let totalAdaBalance = 0;
150+
let mainnetWallets = 0;
151+
let testnetWallets = 0;
152+
let mainnetAdaBalance = 0;
153+
let testnetAdaBalance = 0;
131154

132155
for (const wallet of wallets) {
133156
try {
@@ -163,6 +186,11 @@ export default async function handler(
163186
const mWallet = buildMultisigWallet(walletData, network);
164187
if (!mWallet) {
165188
console.error(`Failed to build multisig wallet for ${wallet.id.slice(0, 8)}...`);
189+
failures.push({
190+
walletId: wallet.id,
191+
errorType: "wallet_build_failed",
192+
errorMessage: "Unable to build multisig wallet from provided data"
193+
});
166194
failedInBatch++;
167195
continue;
168196
}
@@ -239,16 +267,49 @@ export default async function handler(
239267
balance,
240268
adaBalance: roundedAdaBalance,
241269
isArchived: wallet.isArchived,
270+
network,
242271
};
243272

244273
walletBalances.push(walletBalance);
245-
totalAdaBalance += roundedAdaBalance;
274+
275+
// Track network-specific data
276+
if (network === 1) {
277+
mainnetWallets++;
278+
mainnetAdaBalance += roundedAdaBalance;
279+
} else {
280+
testnetWallets++;
281+
testnetAdaBalance += roundedAdaBalance;
282+
}
283+
246284
processedInBatch++;
247285

248-
console.log(` ✅ Balance: ${roundedAdaBalance} ADA`);
286+
console.log(` ✅ Balance: ${roundedAdaBalance} ADA (${network === 1 ? 'mainnet' : 'testnet'})`);
249287

250288
} catch (error) {
251-
console.error(`Error processing wallet ${wallet.id.slice(0, 8)}...:`, error);
289+
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
290+
console.error(`Error processing wallet ${wallet.id.slice(0, 8)}...:`, errorMessage);
291+
292+
// Determine error type based on error message
293+
let errorType = "processing_failed";
294+
let sanitizedMessage = "Wallet processing failed";
295+
296+
if (errorMessage.includes("fetchAddressUTxOs") || errorMessage.includes("UTxO")) {
297+
errorType = "utxo_fetch_failed";
298+
sanitizedMessage = "Failed to fetch UTxOs from blockchain";
299+
} else if (errorMessage.includes("serializeNativeScript") || errorMessage.includes("address")) {
300+
errorType = "address_generation_failed";
301+
sanitizedMessage = "Failed to generate wallet address";
302+
} else if (errorMessage.includes("balance") || errorMessage.includes("lovelace")) {
303+
errorType = "balance_calculation_failed";
304+
sanitizedMessage = "Failed to calculate wallet balance";
305+
}
306+
307+
failures.push({
308+
walletId: wallet.id,
309+
errorType,
310+
errorMessage: sanitizedMessage
311+
});
312+
252313
failedInBatch++;
253314
}
254315
}
@@ -292,7 +353,8 @@ export default async function handler(
292353
console.log(` • Processed: ${processedInBatch}/${wallets.length}`);
293354
console.log(` • Failed: ${failedInBatch}`);
294355
console.log(` • Snapshots stored: ${snapshotsStored}`);
295-
console.log(` • Batch ADA balance: ${Math.round(totalAdaBalance * 100) / 100} ADA`);
356+
console.log(` • Mainnet: ${mainnetWallets} wallets, ${Math.round(mainnetAdaBalance * 100) / 100} ADA`);
357+
console.log(` • Testnet: ${testnetWallets} wallets, ${Math.round(testnetAdaBalance * 100) / 100} ADA`);
296358
console.log(` • Overall progress: ${totalProcessed}/${totalWallets} wallets`);
297359

298360
const progress: BatchProgress = {
@@ -304,11 +366,17 @@ export default async function handler(
304366
failedInBatch,
305367
totalProcessed,
306368
totalFailed,
307-
totalAdaBalance,
308369
snapshotsStored,
309370
isComplete,
310371
startedAt: startTime,
311372
lastUpdatedAt: new Date().toISOString(),
373+
// Network-specific data
374+
mainnetWallets,
375+
testnetWallets,
376+
mainnetAdaBalance,
377+
testnetAdaBalance,
378+
// Failure details
379+
failures,
312380
};
313381

314382
const response: BatchResponse = {
@@ -338,11 +406,17 @@ export default async function handler(
338406
failedInBatch: 0,
339407
totalProcessed: 0,
340408
totalFailed: 0,
341-
totalAdaBalance: 0,
342409
snapshotsStored: 0,
343410
isComplete: false,
344411
startedAt: startTime,
345412
lastUpdatedAt: new Date().toISOString(),
413+
// Network-specific data
414+
mainnetWallets: 0,
415+
testnetWallets: 0,
416+
mainnetAdaBalance: 0,
417+
testnetAdaBalance: 0,
418+
// Failure details
419+
failures: [],
346420
},
347421
timestamp: new Date().toISOString(),
348422
});

0 commit comments

Comments
 (0)