UNPKG

dnsweeper

Version:

Advanced CLI tool for DNS record risk analysis and cleanup. Features CSV import for Cloudflare/Route53, automated risk assessment, and parallel DNS validation.

202 lines (170 loc) 4.79 kB
/** * 高性能バッチ処理ユーティリティ * DNS解決やCSV処理の大量データを効率的に処理 */ export interface BatchProcessorOptions { batchSize: number; concurrency: number; retries: number; retryDelay: number; onProgress?: (processed: number, total: number) => void; onError?: (error: Error, item: any) => void; } export interface BatchResult<T, R> { successful: R[]; failed: Array<{ item: T; error: Error }>; totalProcessed: number; duration: number; } export class BatchProcessor<T, R> { private options: BatchProcessorOptions; constructor(options: Partial<BatchProcessorOptions> = {}) { this.options = { batchSize: 50, concurrency: 10, retries: 3, retryDelay: 1000, ...options, }; } /** * アイテムのバッチを並列処理 */ async process(items: T[], processor: (item: T) => Promise<R>): Promise<BatchResult<T, R>> { const startTime = Date.now(); const successful: R[] = []; const failed: Array<{ item: T; error: Error }> = []; let processedCount = 0; // アイテムをバッチに分割 const batches = this.createBatches(items); for (const batch of batches) { const batchResults = await this.processBatch(batch, processor); successful.push(...batchResults.successful); failed.push(...batchResults.failed); processedCount += batch.length; // 進捗報告 this.options.onProgress?.(processedCount, items.length); } return { successful, failed, totalProcessed: processedCount, duration: Date.now() - startTime, }; } /** * 単一バッチの並列処理 */ private async processBatch( batch: T[], processor: (item: T) => Promise<R>, ): Promise<{ successful: R[]; failed: Array<{ item: T; error: Error }> }> { const successful: R[] = []; const failed: Array<{ item: T; error: Error }> = []; // 並行処理制限 const semaphore = new Semaphore(this.options.concurrency); const promises = batch.map(async (item) => { await semaphore.acquire(); try { const result = await this.processWithRetry(item, processor); successful.push(result); } catch (error) { const err = error instanceof Error ? error : new Error(String(error)); failed.push({ item, error: err }); this.options.onError?.(err, item); } finally { semaphore.release(); } }); await Promise.all(promises); return { successful, failed }; } /** * リトライ機能付きアイテム処理 */ private async processWithRetry(item: T, processor: (item: T) => Promise<R>): Promise<R> { let lastError: Error; for (let attempt = 0; attempt <= this.options.retries; attempt++) { try { return await processor(item); } catch (error) { lastError = error instanceof Error ? error : new Error(String(error)); if (attempt < this.options.retries) { await this.delay(this.options.retryDelay * Math.pow(2, attempt)); } } } throw lastError!; } /** * アイテムをバッチに分割 */ private createBatches(items: T[]): T[][] { const batches: T[][] = []; for (let i = 0; i < items.length; i += this.options.batchSize) { batches.push(items.slice(i, i + this.options.batchSize)); } return batches; } /** * 指定時間待機 */ private delay(ms: number): Promise<void> { return new Promise((resolve) => setTimeout(resolve, ms)); } } /** * セマフォクラス(並行処理制限) */ class Semaphore { private available: number; private waiters: Array<() => void> = []; constructor(count: number) { this.available = count; } async acquire(): Promise<void> { if (this.available > 0) { this.available--; return; } return new Promise<void>((resolve) => { this.waiters.push(resolve); }); } release(): void { if (this.waiters.length > 0) { const resolve = this.waiters.shift()!; resolve(); } else { this.available++; } } } /** * DNS解決専用のバッチプロセッサー */ export class DNSBatchProcessor extends BatchProcessor<string, any> { constructor(options: Partial<BatchProcessorOptions> = {}) { super({ batchSize: 100, concurrency: 20, retries: 2, retryDelay: 500, ...options, }); } } /** * CSV処理専用のバッチプロセッサー */ export class CSVBatchProcessor<T> extends BatchProcessor<T, T> { constructor(options: Partial<BatchProcessorOptions> = {}) { super({ batchSize: 1000, concurrency: 4, retries: 1, retryDelay: 100, ...options, }); } }