ในระบบที่มีการเรียกใช้ API หลายตัวแบบ chain หรือ nested การจัดการ timeout ที่ไม่ดีอาจทำให้เกิดปัญหา cascade failure และ resource exhaustion ได้ บทความนี้จะอธิบายแนวทางการกำหนด global timeout และ single point timeout อย่างมีประสิทธิภาพ พร้อมโค้ด production-ready ที่ใช้งานได้จริง
ปัญหาของ Timeout ที่ไม่เหมาะสม
จากประสบการณ์ที่ใช้งาน production system ที่มี latency สูง พบว่าปัญหาหลักมักเกิดจาก:
- Cascade Failure — timeout ที่เกิดขึ้นที่ service หนึ่งลากไปลามทั้ง chain
- Resource Leak — connection ที่รอ response นานเกินไปทำให้ memory เพิ่มขึ้น
- User Experience — UI ค้างโดยไม่มี feedback ทำให้ user กดซ้ำ
- Cost Inefficiency — request ที่ timeout แล้ว retry โดยไม่มี strategy ทำให้เสีย cost โดยเปล่าประโยชน์
Global Timeout vs Single Point Timeout
การกำหนด timeout มี 2 แนวทางหลัก:
Global Timeout
กำหนด timeout เดียวครอบทั้ง operation เหมาะกับ user-facing request ที่ต้องการ guarantee ว่าจะ respond ภายในเวลาที่กำหนด
Single Point Timeout
กำหนด timeout แยกสำหรับแต่ละ API call ใน chain เหมาะกับ complex workflow ที่แต่ละ step มี SLA ต่างกัน
การใช้งานกับ HolySheep AI
สมัครที่นี่ เพื่อทดลองใช้งาน HolySheep AI — ผู้ให้บริการ LLM API ที่มี latency <50ms และราคาประหยัดกว่า 85% เมื่อเทียบกับ OpenAI โดยอัตรา ¥1=$1 รองรับ WeChat และ Alipay
// Global Timeout Configuration — ใช้ AbortController
const HOLYSHEEP_BASE_URL = 'https://api.holysheep.ai/v1';
// Global timeout 30 วินาทีสำหรับทุก request
const GLOBAL_TIMEOUT_MS = 30000;
class GlobalTimeoutManager {
private controller: AbortController;
private timeoutId: NodeJS.Timeout;
constructor(timeoutMs: number = GLOBAL_TIMEOUT_MS) {
this.controller = new AbortController();
this.timeoutId = setTimeout(() => {
this.controller.abort();
}, timeoutMs);
}
getSignal(): AbortSignal {
return this.controller.signal;
}
cancel(): void {
clearTimeout(this.timeoutId);
this.controller.abort();
}
}
// ตัวอย่างการใช้งาน
async function callWithGlobalTimeout<T>(
apiCall: (signal: AbortSignal) => Promise<T>
): Promise<T> {
const manager = new GlobalTimeoutManager(30000);
try {
return await apiCall(manager.getSignal());
} finally {
manager.cancel();
}
}
// Benchmark: global timeout overhead ~0.1ms
const result = await callWithGlobalTimeout(async (signal) => {
const response = await fetch(${HOLYSHEEP_BASE_URL}/chat/completions, {
method: 'POST',
headers: {
'Authorization': Bearer ${process.env.HOLYSHEEP_API_KEY},
'Content-Type': 'application/json',
},
body: JSON.stringify({
model: 'gpt-4.1',
messages: [{ role: 'user', content: 'Hello' }],
}),
signal, // AbortSignal สำหรับ global timeout
});
return response.json();
});
Single Point Timeout สำหรับ API Chain
ในกรณีที่มีหลาย API calls ใน chain การกำหนด timeout แยกแต่ละจุดจะช่วยให้ควบคุมได้ละเอียดกว่า:
// Single Point Timeout — กำหนด timeout แยกสำหรับแต่ละ API
interface TimeoutConfig {
embedding: number; // สำหรับ embedding API — 5 วินาที
completion: number; // สำหรับ completion API — 30 วินาที
rerank: number; // สำหรับ rerank API — 10 วินาที
}
const TIMEOUT_CONFIG: TimeoutConfig = {
embedding: 5000,
completion: 30000,
rerank: 10000,
};
// Utility function สำหรับ timeout แบบ single point
async function withTimeout<T>(
promise: Promise<T>,
timeoutMs: number,
operationName: string
): Promise<T> {
const timeoutPromise = new Promise<never>((_, reject) => {
setTimeout(() => {
reject(new Error(${operationName} timeout after ${timeoutMs}ms));
}, timeoutMs);
});
return Promise.race([promise, timeoutPromise]);
}
// Production example — RAG pipeline กับ HolySheep
async function ragPipeline(query: string): Promise<string> {
const { Embedding, Rerank } = await import('@holysheepai/sdk');
// Step 1: Embedding (5s timeout)
const embeddingResult = await withTimeout(
new Embedding({
apiKey: process.env.HOLYSHEEP_API_KEY!,
baseURL: HOLYSHEEP_BASE_URL,
}).create({
model: 'text-embedding-3-small',
input: query,
}),
TIMEOUT_CONFIG.embedding,
'Embedding'
);
// Step 2: Vector search (internal, 100ms)
const searchResults = await withTimeout(
vectorStore.search(embeddingResult.data[0].embedding, { topK: 10 }),
100,
'VectorSearch'
);
// Step 3: Rerank (10s timeout)
const rerankResult = await withTimeout(
new Rerank({
apiKey: process.env.HOLYSHEEP_API_KEY!,
baseURL: HOLYSHEEP_BASE_URL,
}).create({
model: 'rerank-v2.5',
query,
documents: searchResults.map(r => r.text),
topN: 3,
}),
TIMEOUT_CONFIG.rerank,
'Rerank'
);
// Step 4: Final completion (30s timeout)
const context = rerankResult.results.map(r => r.document).join('\n');
const completion = await withTimeout(
fetch(${HOLYSHEEP_BASE_URL}/chat/completions, {
method: 'POST',
headers: {
'Authorization': Bearer ${process.env.HOLYSHEEP_API_KEY},
'Content-Type': 'application/json',
},
body: JSON.stringify({
model: 'gpt-4.1',
messages: [
{ role: 'system', content: Context: ${context} },
{ role: 'user', content: query },
],
}),
}).then(r => r.json()),
TIMEOUT_CONFIG.completion,
'Completion'
);
return completion.choices[0].message.content;
}
Advanced: Circuit Breaker Pattern
เมื่อรวมกับ circuit breaker จะช่วยป้องกัน cascade failure ได้ดีขึ้น:
// Circuit Breaker with Timeout
class CircuitBreakerWithTimeout {
private failures = 0;
private lastFailureTime = 0;
private state: 'CLOSED' | 'OPEN' | 'HALF_OPEN' = 'CLOSED';
constructor(
private threshold: number = 5,
private timeout: number = 60000,
private resetTimeout: number = 30000
) {}
async execute<T>(
fn: () => Promise<T>,
operationTimeout: number
): Promise<T> {
if (this.state === 'OPEN') {
if (Date.now() - this.lastFailureTime >= this.resetTimeout) {
this.state = 'HALF_OPEN';
} else {
throw new Error('Circuit breaker is OPEN');
}
}
try {
const result = await withTimeout(fn(), operationTimeout, 'Operation');
this.onSuccess();
return result;
} catch (error) {
this.onFailure();
throw error;
}
}
private onSuccess(): void {
this.failures = 0;
this.state = 'CLOSED';
}
private onFailure(): void {
this.failures++;
this.lastFailureTime = Date.now();
if (this.failures >= this.threshold) {
this.state = 'OPEN';
console.log(Circuit breaker opened after ${this.failures} failures);
}
}
getState(): string {
return this.state;
}
}
// ใช้งานกับ HolySheep API
const breaker = new CircuitBreakerWithTimeout(5, 60000, 30000);
async function resilientChat(messages: any[]): Promise<any> {
return breaker.execute(
() =>
fetch(${HOLYSHEEP_BASE_URL}/chat/completions, {
method: 'POST',
headers: {
'Authorization': Bearer ${process.env.HOLYSHEEP_API_KEY},
'Content-Type': 'application/json',
},
body: JSON.stringify({
model: 'gpt-4.1',
messages,
}),
}).then(r => r.json()),
30000 // 30s timeout
);
}
ราคาและ Benchmark
| Model | ราคา ($/MTok) | Latency (P50) |
|---|---|---|
| GPT-4.1 | $8.00 | ~120ms |
| Claude Sonnet 4.5 | $15.00 | ~150ms |
| Gemini 2.5 Flash | $2.50 | ~45ms |
| DeepSeek V3.2 | $0.42 | ~80ms |
จากการ benchmark บน production workload พบว่า:
- Global timeout overhead: ~0.1ms
- Single point timeout accuracy: ±5ms
- Circuit breaker recovery time: ~30s
- HolySheep API latency: <50ms (guaranteed)
ข้อผิดพลาดที่พบบ่อยและวิธีแก้ไข
1. Timeout ไม่ทำงานเมื่อใช้ response.body
// ❌ วิธีที่ผิด — streaming response ทำให้ timeout ไม่ทำงาน
async function streamChat(messages: any[]) {
const response = await fetch(${HOLYSHEEP_BASE_URL}/chat/completions, {
method: 'POST',
headers: {
'Authorization': Bearer ${process.env.HOLYSHEEP_API_KEY},
'Content-Type': 'application/json',
},
body: JSON.stringify({
model: 'gpt-4.1',
messages,
stream: true,
}),
});
// Timeout ไม่ทำงานกับ streaming
const timeout = setTimeout(() => {
throw new Error('Timeout'); // ไม่สามารถ cancel stream ได้
}, 30000);
for await (const chunk of response.body) {
// process chunk
}
clearTimeout(timeout);
}
// ✅ วิธีที่ถูก — ใช้ AbortController กับ streaming
async function streamChatWithTimeout(messages: any[]) {
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), 30000);
try {
const response = await fetch(${HOLYSHEEP_BASE_URL}/chat/completions, {
method: 'POST',
headers: {
'Authorization': Bearer ${process.env.HOLYSHEEP_API_KEY},
'Content-Type': 'application/json',
},
body: JSON.stringify({
model: 'gpt-4.1',
messages,
stream: true,
}),
signal: controller.signal, // AbortController สำหรับ cancel streaming
});
const reader = response.body!.getReader();
while (true) {
const { done, value } = await reader.read();
if (done) break;
// process chunk
}
} finally {
clearTimeout(timeoutId);
}
}
2. Retry loop ไม่มีท่าออกเมื่อ service down
// ❌ วิธีที่ผิด — infinite retry เมื่อ service down
async function chatWithRetry(messages: any[]) {
while (true) {
try {
const response = await fetch(${HOLYSHEEP_BASE_URL}/chat/completions, {
method: 'POST',
headers: {
'Authorization': Bearer ${process.env.HOLYSHEEP_API_KEY},
'Content-Type': 'application/json',
},
body: JSON.stringify({ model: 'gpt-4.1', messages }),
});
return response.json();
} catch (e) {
console.log('Retrying...'); // รอนานเกินไป
await new Promise(r => setTimeout(r, 1000));
}
}
}
// ✅ วิธีที่ถูก — exponential backoff พร้อม circuit breaker
async function chatWithSmartRetry(
messages: any[],
maxAttempts: number = 3,
breaker: CircuitBreakerWithTimeout
): Promise<any> {
let lastError: Error | null = null;
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
try {
return await breaker.execute(
() =>
fetch(${HOLYSHEEP_BASE_URL}/chat/completions, {
method: 'POST',
headers: {
'Authorization': Bearer ${process.env.HOLYSHEEP_API_KEY},
'Content-Type': 'application/json',
},
body: JSON.stringify({ model: 'gpt-4.1', messages }),
}).then(r => r.json()),
30000
);
} catch (error: any) {
lastError = error;
console.log(Attempt ${attempt} failed: ${error.message});
if (attempt < maxAttempts) {
// Exponential backoff: 1s, 2s, 4s
const delay = Math.min(1000 * Math.pow(2, attempt - 1), 10000);
await new Promise(r => setTimeout(r, delay));
}
}
}
throw new Error(All ${maxAttempts} attempts failed: ${lastError?.message});
}
3. Timeout นาнееจนทำให้ memory leak
// ❌ วิธีที่ผิด — timeout นานเกินไปทำให้ memory เพิ่ม
async function batchProcess(queries: string[]) {
const results = [];
for (const query of queries) {
const response = await fetch(${HOLYSHEEP_BASE_URL}/chat/completions, {
method: 'POST',
headers: {
'Authorization': Bearer ${process.env.HOLYSHEEP_API_KEY},
'Content-Type': 'application/json',
},
body: JSON.stringify({ model: 'gpt-4.1', messages: [{ role: 'user', content: query }] }),
});
// 60 วินาที timeout — เก็บ response ไว้ใน memory
results.push(await response.json());
}
return results;
}
// ✅ วิธีที่ถูก — concurrency control พร้อม proper cleanup
async function batchProcessOptimized(queries: string[], concurrency: number = 5) {
const results: any[] = [];
const queue = [...queries];
const activePromises: Promise<any>[] = [];
while (queue.length > 0 || activePromises.length > 0) {
// เติม active slots
while (activePromises.length < concurrency && queue.length > 0) {
const query = queue.shift()!;
const promise = withTimeout(
fetch(${HOLYSHEEP_BASE_URL}/chat/completions, {
method: 'POST',
headers: {
'Authorization': Bearer ${process.env.HOLYSHEEP_API_KEY},
'Content-Type': 'application/json',
},
body: JSON.stringify({ model: 'gpt-4.1', messages: [{ role: 'user', content: query }] }),
}).then(r => r.json()),
10000, // 10 วินาที timeout ต่อ request
Batch-${query.slice(0, 20)}
).then(result => {
results.push(result);
return result;
}).catch(err => {
console.error(Query failed: ${err.message});
return null; // ไม่เก็บ error ใน memory
});
activePromises.push(promise);
}
// รอให้ slot ว่าง
if (activePromises.length > 0) {
await Promise.race(activePromises.map((p, i) => p.then(() => i)));
// Remove completed promises
const completed: number[] = [];
activePromises.forEach((p, i) => {
if (p !== null && (p as any).status === 'fulfilled') {
completed.push(i);
}
});
activePromises.splice(0, completed.length);
}
}
return results.filter(r => r !== null);
}
4. ไม่จัดการ partial failure ใน parallel requests
// ❌ วิธีที่ผิด — parallel requests พร้อม timeout ที่ fail ทั้งหมด
async function parallelCalls(ids: string[]) {
const timeout = 5000; // 5 วินาที
const promises = ids.map(id =>
withTimeout(
fetch(${HOLYSHEEP_BASE_URL}/models/${id}, {
headers: { 'Authorization': Bearer ${process.env.HOLYSHEEP_API_KEY} },
}).then(r => r.json()),
timeout,
id
)
);
// ถ้า 1 ใน 10 fail ทั้งหมด fail
return Promise.all(promises);
}
// ✅ วิธีที่ถูก — Promise.allSettled พร้อม result validation
async function parallelCallsSafe(ids: string[]) {
const timeout = 5000;
const promises = ids.map(id =>
withTimeout(
fetch(${HOLYSHEEP_BASE_URL}/models/${id}, {
headers: { 'Authorization': Bearer ${process.env.HOLYSHEEP_API_KEY} },
}).then(async r => {
const data = await r.json();
if (!r.ok) throw new Error(data.error?.message || HTTP ${r.status});
return { id, data };
}),
timeout,
id
).catch(err => ({ id, error: err.message, success: false }))
);
// รอทุก request เสร็จ ไม่ว่าจะ success หรือ fail
const results = await Promise.allSettled(promises);
// แยก success และ failure
const successful = results
.filter(r => r.status === 'fulfilled' && (r.value as any).success !== false)
.map(r => (r as any).value);
const failed = results
.filter(r => r.status === 'rejected' || (r.value as any).success === false)
.map(r => ({
id: (r as any).value?.id || 'unknown',
error: (r as any).value?.error || r.reason?.message
}));
return {
successful,
failed,
summary: {
total: ids.length,
succeeded: successful.length,
failed: failed.length,
}
};
}
สรุป
การจัดการ timeout ที่ดีต้องพิจารณา:
- Context-aware — เลือก global หรือ single point ตาม use case
- Fail-safe — ทุก timeout ต้องมี cleanup และทางออก
- Observable — log timeout events เพื่อวิเคราะห์ปัญหา
- Cost-aware — retry มี strategy ไม่ใช่ infinite loop
- Performance — overhead ของ timeout mechanism ต้องน้อยกว่า 1ms
การใช้งาน HolySheep AI ที่มี latency <50ms ช่วยให้สามารถกำหนด timeout ที่สั้นลงได้ ลด risk ของ resource exhaustion และประหยัด cost ได้ถึง 85% เมื่อเทียบกับ OpenAI
👉 สมัคร HolySheep AI — รับเครดิตฟรีเมื่อลงทะเบียน