함수 내 오류 처리
이 페이지에서는 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;
로그 보기
Logs 페이지에서 Atlas trigger 에 의해 호출된 모든 함수 실행 의 기록을 볼 수 있습니다. 여기에는 오류로 인해 성공적으로 실행하지 못한 함수가 포함됩니다.
자세한 내용은 trigger 로그를 참조하세요.
재시도 함수
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 컬렉션입니다.
메인 함수를 호출하고 함수가 실패한 경우 실패한 실행 항목 추적기 컬렉션에 이를 로그하는 핸들러 함수입니다.
핸들러 함수가 실패한 실행 항목 추적기 컬렉션에 오류를 추가할 때마다 핸들러 함수를 다시 실행하는 데이터베이스 트리거 함수입니다.
하나의 핸들러 함수, 실행 항목 추적기 컬렉션, 데이터베이스 트리거 함수 세트로 여러 메인 함수를 지원할 수 있습니다.
장점 | 단점 |
---|---|
|
|
Atlas UI 에서 또는 App Services CLI 를 사용하여 함수에 대한 재시도 메커니즘을 만들 수 있습니다. 다음 절차에서는 핸들러, 재시도 데이터베이스 trigger 함수 및 기본 함수를 만드는 과정을 안내합니다.
핸들러 함수 만들기
먼저 메인 함수를 호출하는 핸들러 함수 handleRetry
를 만듭니다.
handleRetry
다음 매개 변수를 허용합니다:
Parameter | 유형 | 설명 |
---|---|---|
functionToRetry | JavaScript 함수 | 다시 시도하는 함수입니다. |
functionName | 문자열 | 다시 시도할 함수의 이름입니다. |
operationId | ObjectId | 재시도를 포함하는 메인 함수 실행을 위한 고유 식별자입니다. |
previousRetries | 번호 | 이전에 메인 함수를 시도한 횟수입니다. |
...args | 미사용 매개 변수 | 메인 함수에 전달되는 인수의 무한 수입니다. |
handleRetry
다음 작업을 수행합니다:
try
문에서functionToRetry
를 실행하려고 시도합니다. 실행이 성공하면handleRetry
에서functionToRetry
가 반환한 값을 반환합니다.이전 단계에서
functionToRetry
실행 시 오류가 발생하는 경우catch
문은 다음과 같이 오류를 처리합니다.이전 재시도 횟수가 허용된 최대 재시도 횟수와 동일한지 확인합니다. 두 숫자가 동일하면 최대 재시도 횟수에 도달했기 때문에 함수에서 오류가 발생합니다. 이 함수는 더 이상 재시도를 시도하지 않습니다.
데이터베이스에 삽입할 함수 실행 로그 항목 객체를 빌드합니다.
실패한 실행 항목 추적기 컬렉션에 대한 참조를 가져옵니다.
실패한 실행 추적기 컬렉션 에 함수 로그 실행 로그 항목을 삽입합니다. 이 삽입 작업으로 인해 다음 단계에서 만들 데이터베이스 trigger 함수가 실행됩니다.
메인 함수는 functionToRetry
인수로 전달됩니다. handleRetry
가 메인 함수를 실행하려고 시도합니다. 실행에 실패하면 이 함수는 메인 함수를 다시 시도합니다.
Functions(으)로 이동합니다. Create New Function 버튼을 클릭합니다.
Name 필드에
handleRetry
을(를) 추가합니다.Function Editor 에 다음 코드를 추가한 다음 함수를 저장합니다:
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;
functions/config.json
에 다음을 추가합니다.functions/config.json[ { "name": "handleRetry", "private": true, "run_as_system": true } // ...other configuration ] 함수
functions/handleRetry.js
에 대한 파일 을 만듭니다.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; 변경 사항을 배포합니다:
다음 명령을 실행하여 변경 사항을 배포 합니다.
appservices push
데이터베이스 재시도 trigger만들기
Triggers 페이지로 이동합니다.
아직 표시되지 않은 경우 탐색 표시줄의 Organizations 메뉴에서 프로젝트가 포함된 조직을 선택합니다.
아직 표시되지 않은 경우 내비게이션 바의 Projects 메뉴에서 프로젝트를 선택합니다.
사이드바에서 Services 제목 아래의 Triggers를 클릭합니다.
트리거 페이지가 표시됩니다.
Add a Trigger를 클릭합니다.
다음 구성으로 트리거를 만듭니다:
필드값이름원하는 이름(예:retryOperation
)활성화됨예이벤트 건너뛰기 또는 재활성화예이벤트 주문예클러스터 이름원하는 이름(예:mongodb-atlas
)데이터베이스 이름원하는 이름(예:logs
)컬렉션 이름원하는 이름(예:failed_execution_logs
)작업 유형Insert전체 문서예문서 미리보기 이미지No이벤트 유형 선택기능기능+ New Function를 클릭합니다. 함수의 내용에 대해서는 다음 정보를 참조하십시오.고급 구성N/A - 고급 구성이 필요하지 않습니다.이제 trigger 가 호출하는 함수에 코드를 추가합니다.
retryOperation
함수는 재시도 핸들러가 실패한 실행 추적기 컬렉션 에 게시한 문서 인logEntry
를 매개 변수로 사용합니다. 그런 다음retryOperation
는 context.functions.execute() 를 사용하여logEntry
의 정보로 메인 함수를 호출합니다.Function Name 필드에
retryOperationDbTrigger
을(를) 추가합니다.Function 필드 에 다음 코드를 추가한 다음 trigger 를 저장합니다.
functions/retryOperationDbTrigger.jsasync 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;
MongoDB Atlas 사용자를 인증합니다.
MongoDB Atlas 관리 API 키 를 사용하여 App Services CLI 에 로그인 합니다.
appservices login --api-key="<API KEY>" --private-api-key="<PRIVATE KEY>" 앱의 최신 구성 파일을 가져옵니다.
다음 명령을 실행하여 구성 파일의 로컬 복사본을 가져옵니다.
appservices pull --remote=<App ID> 기본값 으로 이 명령은 파일을 현재 작업 디렉토리 로 가져옵니다. 선택 사항인
--local
플래그를 사용하여 디렉토리 경로를 지정할 수 있습니다.데이터베이스 trigger 에 대한 구성을 추가합니다. 자세한 내용은 trigger 구성 참조를 참조하세요.
triggers/retryOperation.json{ "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" } } } } 이제 trigger 가 호출하는
functions/config.json
함수에 코드를 추가합니다.retryOperation
함수는 재시도 핸들러가 실패한 실행 추적기 컬렉션 에 게시한 문서 인logEntry
를 매개 변수로 사용합니다. 그런 다음retryOperation
는 context.functions.execute() 를 사용하여logEntry
의 정보로 메인 함수를 호출합니다.functions/config.json[ // ...other configuration { "name": "retryOperationDbTrigger", "private": true } ] functions/retryOperationDbTrigger.js
파일 에 다음 코드를 추가합니다.retryOperationDbTrigger.jsasync 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; 변경 사항을 배포합니다:
다음 명령을 실행하여 변경 사항을 배포 합니다.
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 필드 에 다음 코드를 추가합니다.
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; Save를 클릭합니다.
함수 메타데이터 를
functions/config.json
에 추가합니다.functions/config.json[ // ...other configuration { "name": "additionWithRetryHandler", "private": false } ] functions/additionWithRetryHandler.js
파일 에 다음 코드를 추가합니다.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; 변경 사항을 배포합니다:
다음 명령을 실행하여 변경 사항을 배포 합니다.
appservices push
이제 additionWithRetryHandler
를 호출할 때 함수가 실패하면 다시 시도합니다.