281 lines
7.5 KiB
JavaScript
281 lines
7.5 KiB
JavaScript
#!/usr/bin/env node
|
|
/**
|
|
* Auto-Retry Usage Examples
|
|
*
|
|
* Shows how to integrate auto-retry into existing cron jobs
|
|
*/
|
|
|
|
const { executeWithRetry, executeWithNotifications } = require('../lib/auto-retry');
|
|
const https = require('https');
|
|
|
|
// ============================================================================
|
|
// Example 1: Simple API Call with Retry
|
|
// ============================================================================
|
|
|
|
async function fetchStockPrice(symbol) {
|
|
return new Promise((resolve, reject) => {
|
|
const options = {
|
|
hostname: 'query1.finance.yahoo.com',
|
|
path: `/v8/finance/chart/${symbol}`,
|
|
method: 'GET',
|
|
timeout: 5000
|
|
};
|
|
|
|
const req = https.request(options, (res) => {
|
|
let data = '';
|
|
res.on('data', chunk => data += chunk);
|
|
res.on('end', () => {
|
|
if (res.statusCode === 200) {
|
|
resolve(JSON.parse(data));
|
|
} else {
|
|
const error = new Error(`HTTP ${res.statusCode}`);
|
|
error.statusCode = res.statusCode;
|
|
reject(error);
|
|
}
|
|
});
|
|
});
|
|
|
|
req.on('error', reject);
|
|
req.on('timeout', () => {
|
|
req.destroy();
|
|
const error = new Error('Request timeout');
|
|
error.code = 'ETIMEDOUT';
|
|
reject(error);
|
|
});
|
|
|
|
req.end();
|
|
});
|
|
}
|
|
|
|
async function example1_SimpleRetry() {
|
|
console.log('Example 1: Simple Retry\n');
|
|
|
|
try {
|
|
const result = await executeWithRetry(
|
|
() => fetchStockPrice('TQQQ'),
|
|
{
|
|
maxRetries: 3,
|
|
backoff: 'exponential',
|
|
context: { task: 'fetch TQQQ price' }
|
|
}
|
|
);
|
|
|
|
console.log('✅ Success:', result.success);
|
|
console.log('📊 Attempts:', result.attempts);
|
|
console.log('⏱️ Duration:', result.totalDuration + 'ms');
|
|
} catch (error) {
|
|
console.error('❌ Failed:', error.message);
|
|
}
|
|
}
|
|
|
|
// ============================================================================
|
|
// Example 2: With Discord Notifications
|
|
// ============================================================================
|
|
|
|
async function example2_WithNotifications() {
|
|
console.log('\nExample 2: With Discord Notifications\n');
|
|
|
|
const WEBHOOK = 'https://discord.com/api/webhooks/1468429341154214049/arTEGUkhIZ5bpE63AefMnyneomjwf1zDzCpzCwbdlzKpH7KgNzcMpFNX9G-DPW5HRojU';
|
|
|
|
try {
|
|
const result = await executeWithNotifications(
|
|
() => fetchStockPrice('TQQQ'),
|
|
{
|
|
maxRetries: 3,
|
|
backoff: 'exponential',
|
|
discordWebhook: WEBHOOK,
|
|
taskName: 'TQQQ 가격 조회',
|
|
context: { cron: 'TQQQ 15분 모니터링' }
|
|
}
|
|
);
|
|
|
|
console.log('✅ Success with notifications');
|
|
} catch (error) {
|
|
console.error('❌ Failed with notifications');
|
|
}
|
|
}
|
|
|
|
// ============================================================================
|
|
// Example 3: Wrap Existing Cron Function
|
|
// ============================================================================
|
|
|
|
async function originalCronFunction() {
|
|
// 기존 cron 로직
|
|
const price = await fetchStockPrice('TQQQ');
|
|
const rate = await fetchExchangeRate();
|
|
|
|
return {
|
|
price: price.chart.result[0].meta.regularMarketPrice,
|
|
rate
|
|
};
|
|
}
|
|
|
|
async function fetchExchangeRate() {
|
|
// 환율 API 호출
|
|
return 1320.5;
|
|
}
|
|
|
|
async function example3_WrapCron() {
|
|
console.log('\nExample 3: Wrap Existing Cron\n');
|
|
|
|
// Before: 재시도 없음
|
|
// const result = await originalCronFunction();
|
|
|
|
// After: 자동 재시도 추가
|
|
const result = await executeWithRetry(
|
|
originalCronFunction,
|
|
{
|
|
maxRetries: 3,
|
|
backoff: 'exponential'
|
|
}
|
|
);
|
|
|
|
console.log('✅ Cron result:', result.result);
|
|
}
|
|
|
|
// ============================================================================
|
|
// Example 4: Custom Retry Logic
|
|
// ============================================================================
|
|
|
|
const { AutoRetry } = require('../lib/auto-retry');
|
|
|
|
async function example4_CustomLogic() {
|
|
console.log('\nExample 4: Custom Retry Logic\n');
|
|
|
|
const retry = new AutoRetry({
|
|
maxRetries: 5,
|
|
backoff: 'linear',
|
|
baseDelay: 2000, // 2초
|
|
|
|
onRetry: async (attempt, error, analysis, delay) => {
|
|
console.log(` 🔄 Retry ${attempt}:`);
|
|
console.log(` Error: ${error.message}`);
|
|
console.log(` Category: ${analysis.category}`);
|
|
console.log(` Suggestion: ${analysis.suggestedFix}`);
|
|
console.log(` Next attempt in: ${delay}ms\n`);
|
|
},
|
|
|
|
onSuccess: async (attempt, result) => {
|
|
if (attempt > 1) {
|
|
console.log(` ✅ Recovered after ${attempt} attempts!`);
|
|
}
|
|
},
|
|
|
|
onFinalFailure: async (attempts, analysis) => {
|
|
console.log(` ❌ Gave up after ${attempts.length} attempts`);
|
|
console.log(` Suggestion: ${analysis.suggestedFix}`);
|
|
|
|
// 사람에게 알림 (예: Discord, Email 등)
|
|
// await notifyHuman(analysis);
|
|
}
|
|
});
|
|
|
|
try {
|
|
const result = await retry.execute(
|
|
() => fetchStockPrice('TQQQ'),
|
|
{ task: 'custom retry test' }
|
|
);
|
|
|
|
console.log('\n✅ Final success');
|
|
} catch (error) {
|
|
console.error('\n❌ Final failure');
|
|
}
|
|
}
|
|
|
|
// ============================================================================
|
|
// Example 5: Integration with TQQQ Monitor
|
|
// ============================================================================
|
|
|
|
async function example5_TQQQIntegration() {
|
|
console.log('\nExample 5: TQQQ Monitor Integration\n');
|
|
|
|
const WEBHOOK = 'https://discord.com/api/webhooks/1468429341154214049/arTEGUkhIZ5bpE63AefMnyneomjwf1zDzCpzCwbdlzKpH7KgNzcMpFNX9G-DPW5HRojU';
|
|
|
|
// TQQQ 모니터링 로직 (기존 코드와 동일)
|
|
async function monitorTQQQ() {
|
|
const [priceData, rate] = await Promise.all([
|
|
fetchStockPrice('TQQQ'),
|
|
fetchExchangeRate()
|
|
]);
|
|
|
|
const price = priceData.chart.result[0].meta.regularMarketPrice;
|
|
const shares = 47;
|
|
const avgPrice = 52.52;
|
|
|
|
const dollarProfit = (price - avgPrice) * shares;
|
|
const wonProfit = dollarProfit * rate;
|
|
|
|
return {
|
|
price,
|
|
dollarProfit,
|
|
wonProfit,
|
|
rate
|
|
};
|
|
}
|
|
|
|
try {
|
|
// 자동 재시도 + Discord 알림
|
|
const result = await executeWithNotifications(
|
|
monitorTQQQ,
|
|
{
|
|
maxRetries: 3,
|
|
backoff: 'exponential',
|
|
discordWebhook: WEBHOOK,
|
|
taskName: 'TQQQ 15분 모니터링',
|
|
context: {
|
|
cron: 'TQQQ 15분 모니터링',
|
|
schedule: '*/15 * * * *'
|
|
}
|
|
}
|
|
);
|
|
|
|
console.log('✅ TQQQ Monitor completed');
|
|
console.log(' Price:', result.result.price);
|
|
console.log(' Dollar P/L:', result.result.dollarProfit.toFixed(2));
|
|
console.log(' Won P/L:', result.result.wonProfit.toFixed(0));
|
|
} catch (error) {
|
|
console.error('❌ TQQQ Monitor failed after all retries');
|
|
}
|
|
}
|
|
|
|
// ============================================================================
|
|
// Run Examples
|
|
// ============================================================================
|
|
|
|
async function main() {
|
|
const args = process.argv.slice(2);
|
|
const example = args[0] || 'all';
|
|
|
|
if (example === 'all' || example === '1') {
|
|
await example1_SimpleRetry();
|
|
}
|
|
|
|
if (example === 'all' || example === '2') {
|
|
await example2_WithNotifications();
|
|
}
|
|
|
|
if (example === 'all' || example === '3') {
|
|
await example3_WrapCron();
|
|
}
|
|
|
|
if (example === 'all' || example === '4') {
|
|
await example4_CustomLogic();
|
|
}
|
|
|
|
if (example === 'all' || example === '5') {
|
|
await example5_TQQQIntegration();
|
|
}
|
|
}
|
|
|
|
if (require.main === module) {
|
|
main().catch(console.error);
|
|
}
|
|
|
|
module.exports = {
|
|
example1_SimpleRetry,
|
|
example2_WithNotifications,
|
|
example3_WrapCron,
|
|
example4_CustomLogic,
|
|
example5_TQQQIntegration
|
|
};
|