함수 내 오류 처리
이 페이지에서는 Atlas Functions에서 오류를 처리하는 방법을 설명합니다.
참고
AWS EventBridge를 사용한 데이터베이스 트리거에 대한 사용자 지정 오류 처리
AWS EventBridge를 사용하여 데이터베이스 트리거에 대한 사용자 지정 오류 핸들러를 생성할 수 있습니다. 자세한 내용은 사용자 지정 오류 처리를 참조하세요.
기본 오류 처리
try...catch 문과같은 표준 JavaScript 오류 처리 기술을 사용하여 함수 오류를 처리하다 할 수 있습니다.
function willThrowAndHandleError() { try { throw new Error("This will always happen"); } catch (err) { console.error("An error occurred. Error message:" + err.message); } } exports = willThrowAndHandleError;
로그 보기
App Service 로그에서 오류로 인해 성공적으로 실행되지 못한 함수를 포함한 모든 함수 실행 기록을 볼 수 있습니다.
함수가 호출되는 방식에 따라 로그에 다르게 표시됩니다. 예를 들어, Atlas 트리거에 의해 호출된 함수에 대한 로그는 로그에 'Triggers'로 표시되고, Realm 클라이언트 SDK에서 호출된 함수에 대한 로그는 'Functions'로 로그에 표시됩니다. 자세한 내용은 로그 입력 유형 문서를 참조하세요.
재시도 함수
Atlas Functions에는 재시도 동작이 내장되어 있지 않습니다. 사용자 지정 재시도 동작을 추가할 수 있습니다. 예를 들어 함수가 호출하는 타사 서비스가 간헐적으로 연결되는 경우 재시도 동작을 추가하고, 타사 서비스가 일시적으로 다운되더라도 함수가 다시 실행되도록 하고 싶을 수 있습니다.
이 섹션에서는 함수에 재시도 동작을 추가하기 위한 다음 전략을 설명합니다.
오류 처리 블록에서 재귀적으로 호출하는 함수
함수를 재귀적으로 호출하여 실패할 수 있는 작업을 처리할 수 있습니다.
이 프로세스는 크게 다음과 같은 구성 요소를 포함합니다:
재시도하려는 작업을
try
문에서 실행하고 함수 자체가catch
문에서 호출되도록 하세요.무기한 실행되는 것을 방지하려면 최대 재시도 횟수를 설정하세요. 함수가 실패하고
catch
문을 입력할 때마다 현재 재시도 횟수가 증가합니다. 함수의 현재 재시도 횟수가 최대 재시도 횟수에 도달하면 재귀 실행을 중지합니다.특정 기간의 총 실행 횟수를 줄이기 위해 재시도를 제한할 수도 있습니다.
다음 표에서는 재귀 호출 전략을 사용하여 함수 재시도를 처리할 때의 몇 가지 장점과 단점을 설명합니다.
장점 | 단점 |
---|---|
|
|
다음 코드 예시에서는 오류 처리 블록에서 재귀를 사용하여 함수를 다시 시도하는 작업의 구현을 설명합니다.
// Utility function to suspend execution of current process async function sleep(milliseconds) { await new Promise((resolve) => setTimeout(resolve, milliseconds)); } // Set variables to be used by all calls to `mightFail` // Tip: You could also store `MAX_RETRIES` and `THROTTLE_TIME_MS` // in App Services Values const MAX_RETRIES = 5; const THROTTLE_TIME_MS = 5000; let currentRetries = 0; let errorMessage = ""; async function mightFail(...inputVars) { if (currentRetries === MAX_RETRIES) { console.error( `Reached maximum number of retries (${MAX_RETRIES}) without successful execution.` ); console.error("Error Message:", errorMessage); return; } let res; try { // operation that might fail res = await callFlakyExternalService(...inputVars); } catch (err) { errorMessage = err.message; // throttle retries await sleep(THROTTLE_TIME_MS); currentRetries++; res = await mightFail(...inputVars); } return res; } exports = mightFail;
데이터베이스 트리거를 사용하여 재시도
또한 데이터베이스 트리거를 사용하여 함수를 재시도하고, MongoDB 컬렉션이 이전에 실패한 실행 항목을 추적하도록 할 수 있습니다.
이 프로세스는 크게 다음과 같은 구성 요소를 포함합니다:
재시도하려는 로직을 실행하는 메인 함수로, 핸들러 함수에 래핑되어 있습니다(아래 글머리 기호 항목 참조).
메인 함수의 실패한 실행 항목을 추적하는 실패한 실행 항목 추적기 MongoDB 컬렉션입니다.
메인 함수를 호출하고 함수가 실패한 경우 실패한 실행 항목 추적기 컬렉션에 이를 로그하는 핸들러 함수입니다.
핸들러 함수가 실패한 실행 항목 추적기 컬렉션에 오류를 추가할 때마다 핸들러 함수를 다시 실행하는 데이터베이스 트리거 함수입니다.
하나의 핸들러 함수, 실행 항목 추적기 컬렉션, 데이터베이스 트리거 함수 세트로 여러 메인 함수를 지원할 수 있습니다.
장점 | 단점 |
---|---|
|
|
실행 재시도를 처리하는 함수 만들기
먼저 메인 함수를 호출하는 핸들러 함수 handleRetry
를 만듭니다.
handleRetry
다음 매개 변수를 허용합니다:
Parameter | 유형 | 설명 |
---|---|---|
functionToRetry | JavaScript 함수 | 다시 시도하는 함수입니다. |
functionName | 문자열 | 다시 시도할 함수의 이름입니다. |
operationId | ObjectId | 재시도를 포함하는 메인 함수 실행을 위한 고유 식별자입니다. |
previousRetries | 번호 | 이전에 메인 함수를 시도한 횟수입니다. |
...args | 미사용 매개 변수 | 메인 함수에 전달되는 인수의 무한 수입니다. |
handleRetry
다음 작업을 수행합니다:
try
문에서functionToRetry
를 실행하려고 시도합니다. 실행이 성공하면handleRetry
에서functionToRetry
가 반환한 값을 반환합니다.이전 단계에서
functionToRetry
실행 시 오류가 발생하는 경우catch
문은 다음과 같이 오류를 처리합니다.이전 재시도 횟수가 허용된 최대 재시도 횟수와 동일한지 확인합니다. 두 숫자가 동일하면 최대 재시도 횟수에 도달했기 때문에 함수에서 오류가 발생합니다. 이 함수는 더 이상 재시도를 시도하지 않습니다.
데이터베이스에 삽입할 함수 실행 로그 항목 객체를 빌드합니다.
실패한 실행 항목 추적기 컬렉션에 대한 참조를 가져옵니다.
실패한 실행 항목 추적기 컬렉션에 함수 로그 실행 로그 항목을 삽입합니다. 이 삽입 작업으로 인해 다음 단계에서 만들 데이터베이스 트리거 함수가 실행됩니다.
메인 함수는 functionToRetry
인수로 전달됩니다. handleRetry
가 메인 함수를 실행하려고 시도합니다. 실행에 실패하면 이 함수는 메인 함수를 다시 시도합니다.
Functions 로 이동합니다. Create New Function 버튼을 클릭합니다.
Name 필드에 handleRetry
을(를) 추가합니다.
Function Editor 에 다음 코드를 추가한 다음 함수를 저장합니다:
// Tip: You could also put this in an App Services Value const MAX_FUNC_RETRIES = 5; async function handleRetry( functionToRetry, functionName, operationId, previousRetries, ...args ) { try { // Try to execute the main function const response = await functionToRetry(...args); return response; } catch (err) { // Evaluates if should retry function again. // If no retry, throws error and stops retrying. if (previousRetries === MAX_FUNC_RETRIES) { throw new Error( `Maximum number of attempts reached (${MAX_FUNC_RETRIES}) for function '${functionName}': ${err.message}` ); } // Build function execution log entry for insertion into database. const logEntry = { operationId, errorMessage: err.message, timestamp: new Date(), retries: previousRetries + 1, args, functionName, }; // Get reference to database collection const executionLog = context.services .get("mongodb-atlas") .db("logs") .collection("failed_execution_logs"); // Add execution log entry to database await executionLog.insertOne(logEntry); return; } } exports = handleRetry;
팁
App Services CLI 설치 및 설정
CLI 를 사용하여 App Services App 을 업데이트 하는 경우 먼저 App Services CLI를 설치하고 설정하다 해야 합니다 .
functions/config.json
에 다음을 추가합니다.
[ { "name": "handleRetry", "private": true, "run_as_system": true } // ...other configuration ]
함수 functions/handleRetry.js
에 대한 파일 을 만듭니다.
// Tip: You could also put this in an App Services Value const MAX_FUNC_RETRIES = 5; async function handleRetry( functionToRetry, functionName, operationId, previousRetries, ...args ) { try { // Try to execute the main function const response = await functionToRetry(...args); return response; } catch (err) { // Evaluates if should retry function again. // If no retry, throws error and stops retrying. if (previousRetries === MAX_FUNC_RETRIES) { throw new Error( `Maximum number of attempts reached (${MAX_FUNC_RETRIES}) for function '${functionName}': ${err.message}` ); } // Build function execution log entry for insertion into database. const logEntry = { operationId, errorMessage: err.message, timestamp: new Date(), retries: previousRetries + 1, args, functionName, }; // Get reference to database collection const executionLog = context.services .get("mongodb-atlas") .db("logs") .collection("failed_execution_logs"); // Add execution log entry to database await executionLog.insertOne(logEntry); return; } } exports = handleRetry;
변경 사항을 App Services 에 푸시합니다.
appservices push
데이터베이스 재시도 트리거 생성
앱의 UI에서 Triggers 로 이동합니다.
Add a Trigger 버튼을 클릭합니다.
다음 구성으로 트리거를 만듭니다:
필드값이름원하는 이름(예:retryOperation
)활성화됨예이벤트 건너뛰기 또는 재활성화예이벤트 주문예클러스터 이름원하는 이름(예:mongodb-atlas
)데이터베이스 이름원하는 이름(예:logs
)컬렉션 이름원하는 이름(예:failed_execution_logs
)작업 유형Insert전체 문서예문서 미리보기 이미지No이벤트 유형 선택기능기능+ New Function를 클릭합니다. 함수의 내용에 대해서는 다음 정보를 참조하십시오.고급 구성고급 구성이 필요하지 않습니다.
데이터베이스 trigger 에 대한 구성을 추가합니다. 자세한 내용은 trigger 구성 참조를 참조하세요.
{ "name": "retry", "type": "DATABASE", "config": { "operation_types": ["INSERT"], "database": "logs", "collection": "failed_execution_logs", "service_name": "mongodb-atlas", "project": {}, "full_document": true, "full_document_before_change": false, "unordered": false, "skip_catchup_events": false }, "disabled": false, "event_processors": { "FUNCTION": { "config": { "function_name": "retryOperationDbTrigger" } } } }
이제 트리거가 호출하는 함수에 대한 코드를 추가합니다.
retryOperation
함수는 logEntry
매개 변수를 사용합니다. 해당 매개 변수는 재시도 핸들러가 실패한 실행 항목 추적기 컬렉션에 게시한 문서입니다. 그런 다음 retryOperation
이 context.functions.execute()를 사용하여 logEntry
의 정보로 메인 함수를 호출합니다.
Function Name 필드에 retryOperationDbTrigger
을(를) 추가합니다.
필드 Function에 다음 코드를 추가한 다음, 트리거를 저장합니다:
async function retryOperation({ fullDocument: logEntry }) { // parse values from log entry posted to database const { args, retries, functionName, operationId } = logEntry; // Re-execute the main function await context.functions.execute(functionName, ...args, operationId, retries); } exports = retryOperation;
함수 메타데이터 를 functions/config.json
에 추가합니다.
[ // ...other configuration { "name": "retryOperationDbTrigger", "private": true } ]
functions/retryOperationDbTrigger.js
파일 에 다음 코드를 추가합니다.
async function retryOperation({ fullDocument: logEntry }) { // parse values from log entry posted to database const { args, retries, functionName, operationId } = logEntry; // Re-execute the main function await context.functions.execute(functionName, ...args, operationId, retries); } exports = retryOperation;
변경 사항을 App Services 에 푸시합니다.
appservices push
재시도를 위한 쓰기 함수
이제 함수 핸들러와 재시도 데이터베이스 트리거 함수가 있으므로 메인 함수를 작성할 수 있습니다.
다음 예에서는 합계 작업을 수행할 때마다 함수에서 무작위로 오류를 발생시킵니다. 이 로직를 실행하는 JavaScript 함수는 다음과 같습니다.
getRandomOneTwoThree()
: 예시에 대한 오류를 생성하기 위한 헬퍼 함수입니다.additionOrFailure()
: 메인 로직이 있는 함수입니다.
재시도 핸들러에 의해 래핑된 additionOrFailure()
호출은 내보낸 함수 additionWithRetryHandler()
에서 발생합니다. 재시도 핸들러 함수를 사용하는 모든 함수는 이 함수와 유사해야 합니다.
이 함수가 나머지 재시도 로직과 함께 작동하도록 하려면 올바른 매개 변수를 포함해야 합니다. 이러한 매개 변수는 다음과 같습니다:
Parameter | 유형 | 설명 |
---|---|---|
...args | 미사용 매개 변수 | 기본 로직을 사용하여 함수에 전달할 매개 변수가 0개 이상입니다. 이 예시에서는 additionOrFailure() 함수 내에서 더해진 두 숫자 num1 및 num2 를 사용합니다. |
operationId | 함수 호출 및 재시도에 대한 고유 식별자입니다. 기본값을 new BSON.ObjectId() 로 설정합니다. | |
retries | 번호 | 기본값을 0으로 설정합니다. |
additionWithRetryHandler
의 본문은 context.functions.execute()
가 호출한 재시도 핸들러 handleRetry
이며, 이 핸들러는 다시 additionOrFailure
를 호출합니다. context.functions.execute()에 전달하는 인수는 다음과 같습니다:
Argument | 유형 | 설명 |
---|---|---|
"handleRetry" | 문자열 | 메인 함수가 제대로 실행되지 않은 경우 메인 함수를 호출하고 이를 재시도 로그에 게시하기 위해 정의한 함수의 이름입니다. |
additionOrFailure | JavaScript 함수 | handleRetry() 가 호출하는 메인 함수입니다. |
operationId | BSON.ObjectId | additionWithRetryHandler() 의 매개 변수 operationId 에서 인수로 전달됩니다. |
retries | 번호 | additionWithRetryHandler() 의 매개 변수 retries 에서 인수로 전달됩니다. |
...args | 인수 확산 | 기본 로직을 사용하여 함수에 전달할 인수가 0개 이상입니다. additionWithRetryHandler() 의 매개 변수 ...args 에서 인수로 전달됩니다. |
Function Name 필드에 additionWithRetryHandler
을(를) 추가합니다.
Function 필드에 다음 코드를 추가하고 함수를 저장합니다:
// randomly generates 1, 2, or 3 function getRandomOneTwoThree() { return Math.floor(Math.random() * 3) + 1; } function additionOrFailure(num1, num2) { // Throw error if getRandomOneTwoThree returns 1 const rand = getRandomOneTwoThree(); if (rand === 1) throw new Error("Uh oh!!"); const sum = num1 + num2; console.log(`Successful addition of ${num1} + ${num2}. Result: ${sum}`); // Otherwise return the sum return sum; } async function additionWithRetryHandler( inputVar1, inputVar2, // create a new `operation_id` if one not provided operationId = new BSON.ObjectId(), // count number of attempts retries = 0 ) { const res = await context.functions.execute( "handleRetry", additionOrFailure, "additionWithRetryHandler", // MUST BE NAME OF FUNCTION operationId, retries, inputVar1, inputVar2 ); return res; } exports = additionWithRetryHandler;
함수 메타데이터 를 functions/config.json
에 추가합니다.
[ // ...other configuration { "name": "additionWithRetryHandler", "private": false } ]
functions/additionWithRetryHandler.js
파일 에 다음 코드를 추가합니다.
// randomly generates 1, 2, or 3 function getRandomOneTwoThree() { return Math.floor(Math.random() * 3) + 1; } function additionOrFailure(num1, num2) { // Throw error if getRandomOneTwoThree returns 1 const rand = getRandomOneTwoThree(); if (rand === 1) throw new Error("Uh oh!!"); const sum = num1 + num2; console.log(`Successful addition of ${num1} + ${num2}. Result: ${sum}`); // Otherwise return the sum return sum; } async function additionWithRetryHandler( inputVar1, inputVar2, // create a new `operation_id` if one not provided operationId = new BSON.ObjectId(), // count number of attempts retries = 0 ) { const res = await context.functions.execute( "handleRetry", additionOrFailure, "additionWithRetryHandler", // MUST BE NAME OF FUNCTION operationId, retries, inputVar1, inputVar2 ); return res; } exports = additionWithRetryHandler;
변경 사항을 App Services 에 푸시합니다.
appservices push
이제 additionWithRetryHandler
를 호출할 때 함수가 실패하면 다시 시도합니다.