fixed getaddrinfo EAI_AGAIN exprofile.bangkok.go.th
All checks were successful
Build & Deploy on Dev / build (push) Successful in 1m2s
All checks were successful
Build & Deploy on Dev / build (push) Successful in 1m2s
This commit is contained in:
parent
d3e1707cc2
commit
22ea54aeb3
1 changed files with 45 additions and 8 deletions
|
|
@ -43,6 +43,35 @@ class TokenCache {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// network/DNS error codes ที่ถือว่าเป็น transient (ควร retry)
|
||||||
|
const TRANSIENT_NETWORK_CODES = [
|
||||||
|
"EAI_AGAIN", // DNS lookup ล้มเหลวชั่วคราว
|
||||||
|
"ENOTFOUND",
|
||||||
|
"ECONNRESET",
|
||||||
|
"ETIMEDOUT",
|
||||||
|
"ECONNREFUSED",
|
||||||
|
"EHOSTUNREACH",
|
||||||
|
"ENETUNREACH",
|
||||||
|
];
|
||||||
|
|
||||||
|
// ตรวจว่า error ควร retry: ครอบ network/DNS (ไม่มี response หรือ code ตรง TRANSIENT) และ HTTP 5xx
|
||||||
|
function isTransientError(error: any): boolean {
|
||||||
|
if (!error) return false;
|
||||||
|
if (!error.response || TRANSIENT_NETWORK_CODES.includes(error.code)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return error.response?.status >= 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
// หน่วงเวลา (backoff) ระหว่าง retry — ใช้รูปแบบ setTimeout เดียวกับ keycloak/index.ts
|
||||||
|
function sleep(ms: number): Promise<void> {
|
||||||
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
||||||
|
}
|
||||||
|
|
||||||
|
// config retry สำหรับเรียก Exprofile
|
||||||
|
const EXPROFILE_MAX_RETRIES = 3; // จากเดิม 2
|
||||||
|
const EXPROFILE_BACKOFF_BASE_MS = 1000; // backoff = base * 2^retryCount → 1s, 2s
|
||||||
|
|
||||||
@Route("api/v1/org/ex/retirement")
|
@Route("api/v1/org/ex/retirement")
|
||||||
@Tags("ExRetirement")
|
@Tags("ExRetirement")
|
||||||
@Security("bearerAuth")
|
@Security("bearerAuth")
|
||||||
|
|
@ -60,7 +89,7 @@ export class ExRetirementController extends Controller {
|
||||||
},
|
},
|
||||||
) {
|
) {
|
||||||
let retryCount = 0;
|
let retryCount = 0;
|
||||||
const maxRetries = 2;
|
const maxRetries = EXPROFILE_MAX_RETRIES;
|
||||||
|
|
||||||
while (retryCount < maxRetries) {
|
while (retryCount < maxRetries) {
|
||||||
try {
|
try {
|
||||||
|
|
@ -92,8 +121,9 @@ export class ExRetirementController extends Controller {
|
||||||
// return res.data;
|
// return res.data;
|
||||||
return new HttpSuccess(res.data.data);
|
return new HttpSuccess(res.data.data);
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
if (error.response?.status === 500 && retryCount < maxRetries - 1) {
|
if (isTransientError(error) && retryCount < maxRetries - 1) {
|
||||||
TokenCache.delete(`${clientId}:${clientSecret}`);
|
TokenCache.delete(`${clientId}:${clientSecret}`);
|
||||||
|
await sleep(EXPROFILE_BACKOFF_BASE_MS * 2 ** retryCount);
|
||||||
retryCount++;
|
retryCount++;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
@ -107,7 +137,7 @@ export class ExRetirementController extends Controller {
|
||||||
@Get("/document/{documentId}")
|
@Get("/document/{documentId}")
|
||||||
async getDocument(@Path("documentId") officerDocumentID: string, @Request() req: any) {
|
async getDocument(@Path("documentId") officerDocumentID: string, @Request() req: any) {
|
||||||
let retryCount = 0;
|
let retryCount = 0;
|
||||||
const maxRetries = 2;
|
const maxRetries = EXPROFILE_MAX_RETRIES;
|
||||||
while (retryCount < maxRetries) {
|
while (retryCount < maxRetries) {
|
||||||
try {
|
try {
|
||||||
const token = await getToken(clientId, clientSecret);
|
const token = await getToken(clientId, clientSecret);
|
||||||
|
|
@ -136,8 +166,9 @@ export class ExRetirementController extends Controller {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
if (error.response?.status === 500 && retryCount < maxRetries - 1) {
|
if (isTransientError(error) && retryCount < maxRetries - 1) {
|
||||||
TokenCache.delete(`${clientId}:${clientSecret}`);
|
TokenCache.delete(`${clientId}:${clientSecret}`);
|
||||||
|
await sleep(EXPROFILE_BACKOFF_BASE_MS * 2 ** retryCount);
|
||||||
retryCount++;
|
retryCount++;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
@ -171,8 +202,13 @@ async function getToken(ClientID: string, ClientSecret: string): Promise<string>
|
||||||
TokenCache.set(cacheKey, token);
|
TokenCache.set(cacheKey, token);
|
||||||
return token;
|
return token;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('getToken error:', error);
|
// log แบบกระชับ (ลด log noise จากการ dump object ใหญ่ตามที่เห็นใน log จริง)
|
||||||
return Promise.reject({ message: "Error occurred", error });
|
console.error(
|
||||||
|
"getToken error:",
|
||||||
|
error instanceof Error ? `${error.name}: ${error.message}` : error,
|
||||||
|
);
|
||||||
|
// โยน AxiosError ตัวจริงออกไปให้ caller อ่าน code / response / status เพื่อตัดสินใจ retry
|
||||||
|
throw error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -198,7 +234,7 @@ export async function PostRetireToExprofile(
|
||||||
}
|
}
|
||||||
|
|
||||||
let retryCount = 0;
|
let retryCount = 0;
|
||||||
const maxRetries = 2;
|
const maxRetries = EXPROFILE_MAX_RETRIES;
|
||||||
|
|
||||||
while (retryCount < maxRetries) {
|
while (retryCount < maxRetries) {
|
||||||
try {
|
try {
|
||||||
|
|
@ -235,8 +271,9 @@ export async function PostRetireToExprofile(
|
||||||
|
|
||||||
return res.data;
|
return res.data;
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
if (error.response?.status === 500 && retryCount < maxRetries - 1) {
|
if (isTransientError(error) && retryCount < maxRetries - 1) {
|
||||||
TokenCache.delete(`${clientId}:${clientSecret}`);
|
TokenCache.delete(`${clientId}:${clientSecret}`);
|
||||||
|
await sleep(EXPROFILE_BACKOFF_BASE_MS * 2 ** retryCount);
|
||||||
retryCount++;
|
retryCount++;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue