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.
122 lines • 3.76 kB
JavaScript
/**
* 並列実行制御ユーティリティ
*/
/**
* 複数のPromiseを並列実行数を制限して実行
*/
export async function runConcurrent(tasks, options = {}) {
const { concurrency = 10, onProgress } = options;
if (tasks.length === 0) {
return [];
}
const results = new Array(tasks.length);
const errors = [];
let completed = 0;
let index = 0;
// ワーカー関数
async function worker() {
while (index < tasks.length) {
const currentIndex = index++;
const task = tasks[currentIndex];
try {
if (task) {
results[currentIndex] = await task();
}
}
catch (error) {
errors.push({ index: currentIndex, error });
}
completed++;
if (onProgress) {
onProgress(completed, tasks.length);
}
}
}
// 指定された並列数でワーカーを起動
const workers = Array(Math.min(concurrency, tasks.length))
.fill(null)
.map(() => worker());
await Promise.all(workers);
// エラーがある場合は集約エラーをスロー
if (errors.length > 0) {
throw new AggregateError(errors.map((e) => e.error), `${errors.length} out of ${tasks.length} tasks failed`);
}
return results;
}
/**
* 配列の要素に対して並列処理を実行
*/
export async function mapConcurrent(items, mapper, options = {}) {
const tasks = items.map((item, index) => () => mapper(item, index));
return runConcurrent(tasks, options);
}
/**
* 並列処理の進捗を追跡するクラス
*/
export class ProgressTracker {
total;
onUpdate;
completed = 0;
startTime;
constructor(total, onUpdate) {
this.total = total;
this.onUpdate = onUpdate;
this.startTime = Date.now();
}
increment() {
this.completed++;
if (this.onUpdate) {
this.onUpdate(this.getProgress());
}
}
getProgress() {
const elapsed = Date.now() - this.startTime;
const rate = this.completed / (elapsed / 1000); // items per second
const remaining = this.total - this.completed;
const eta = (remaining / rate) * 1000; // milliseconds
return {
completed: this.completed,
total: this.total,
percentage: Math.round((this.completed / this.total) * 100),
elapsed,
rate,
eta: Math.round(eta),
remaining,
};
}
}
/**
* バッチ処理ユーティリティ
*/
export async function processBatch(items, batchSize, processor) {
const results = [];
for (let i = 0; i < items.length; i += batchSize) {
const batch = items.slice(i, i + batchSize);
const batchResults = await processor(batch);
results.push(...batchResults);
}
return results;
}
/**
* リトライ付き並列実行
*/
export async function runConcurrentWithRetry(tasks, options = {}) {
const { maxRetries = 3, retryDelay = 1000, ...concurrentOptions } = options;
const tasksWithRetry = tasks.map((task) => async () => {
let lastError;
for (let attempt = 0; attempt <= maxRetries; attempt++) {
try {
return await task();
}
catch (error) {
lastError = error;
if (attempt < maxRetries) {
await new Promise((resolve) => setTimeout(resolve, retryDelay * (attempt + 1)));
}
}
}
throw lastError;
});
return runConcurrent(tasksWithRetry, concurrentOptions);
}
//# sourceMappingURL=concurrent.js.map