이 글은 여러 파트로 구성된 시리즈 중 첫 번째 글입니다. 카우치베이스 이벤트 서비스 를 사용하여 특정 반복 간격으로 여러 예약된 작업을 실행할 수 있습니다. cron 단일 범용 이벤트 함수를 통해 추가 인프라 없이도 데이터베이스 내부에서 패션과 같은 기능을 완전히 구현할 수 있습니다.
이번 편에서는 이벤트 함수 안에 정의된 자바스크립트 함수인 고정 사용자 루틴을 실행하는 데 중점을 두겠습니다.
이후 글에서는 다음 글에서 확장된 cron 이벤트 함수와 같이 데이터베이스 기반 동적 N1QL 문을 예약하고 실행하는 방법을 살펴보고, 마지막으로 데이터베이스 기반 동적 JavaScript 함수를 예약하는 방법을 살펴봅니다.
배경
카우치베이스 이벤트 서비스는 문서 변경을 처리하기 위한 간단한 JavaScript 함수인 자체 루틴을 작성할 수 있는 프레임워크를 제공합니다. 이 서비스는 확장 가능하고 강력한 클라우드 기반 함수를 만드는 데 필요한 모든 인프라를 제공하여 데이터의 변화에 거의 실시간으로 상호작용하는 순수한 비즈니스 로직을 개발하는 데 집중할 수 있도록 해줍니다. 함수는 Couchbase 데이터 서비스(KV), Couchbase 쿼리 서비스(N1QL) 및 Couchbase 시스템 외부의 REST 엔드포인트에 액세스할 수 있습니다.

그리고 JSON 카우치베이스의 데이터 모델은 자바스크립트따라서 이벤트 서비스에서 삽입, 업데이트, 병합, 삭제(총칭하여 변경이라고 함)를 포함한 모든 유형의 변경 이벤트에 대해 JSON 문서를 분석하고 조작하기 위한 JavaScript 코드를 작성하는 기능을 노출하는 것은 당연한 일입니다.
이벤트 함수를 사용하면 일반적으로 문서에서 초당 수천, 수백만 건의 변경에 반응하는 사용자 지정 코드 조각을 배포하고 실행할 수 있습니다. 몇 가지 일반적인 사용 사례는 다음과 같습니다. 문서화 를 사용하여 Couchbase 문서의 변이에 반응하는 고속 대규모 이벤트 함수를 개발할 수 있습니다.

이 글에서는 대신 안정적인 "데이터베이스 내" 분산 크론탭을 구축하여 정기적으로 주기적으로 Couchbase 서비스와 상호 작용하는 JavaScript 함수를 실행할 수 있는 매우 낮은 속도의 이벤트 서비스 사용 사례에 초점을 맞출 것입니다.
지정된 날짜 또는 시간에 실행되도록 비즈니스 로직 예약하기
Cron의 이름을 따서 "크로노스시간을 뜻하는 그리스어인 "시간"은 Linux 시스템에서 가장 유용한 유틸리티 중 하나입니다. Linux에서 cron 유틸리티는 주어진 일정에 따라 주기적으로 실행할 셸 명령을 지정하는 구성 파일인 크론탭(크론 테이블) 파일에 의해 구동됩니다.
러닝의 한 가지 단점 cron 는 분산 서비스로 설계되지 않았기 때문에 단일 박스에서 실행되므로 단일 장애 지점이 존재합니다. 시스템이 몇 시간 동안 오프라인 상태이면 예약된 모든 작업을 놓치게 됩니다.
예, 일부 배포된 cron Google의 클라우드 서비스, AWS의 예약 작업, Azure 함수/시간 트리거와 같은 구현을 사용할 수 있습니다. 그러나 각 클라우드 공급업체의 서비스에는 고유한 관용구가 있으며 직접 이식할 수 없습니다.
또한, 예를 들어 분산된 애플리케이션을 제어하는 경우와 같이 구성 및 제어 방법론의 보안이 필요합니다. cron 시스템을 HTTP/S를 통해 REST API로 연결하려면 보안 계획에 이를 고려해야 합니다.
카우치베이스 자체를 사용하여 주기적 명령 실행하기
약간의 코드와 계획만 있으면 Couchbase의 Eventing 서비스를 활용하여 유연한 cron 예약된 데이터베이스 작업이나 유지 관리를 위한 기능처럼요. 스케줄러를 데이터베이스에 구축하면 다음과 같은 이점을 얻을 수 있습니다:
- 클라우드 제공업체 간 이동성, Couchbase 클러스터를 리호스팅하는 경우 스케줄러는 영향을 받지 않습니다.
- 지원 가능성, Couchbase를 사용하는 경우 지원 및 기타 서비스를 제공하는 단일 공급업체가 있습니다.
- 단일 장애 지점이 없는 분산형이며 모든 Couchbase 서비스는 분산 복제본을 지원합니다.
- 실행이 보장되어 노드 장애에서 복구된 후에도 작업이 실행됩니다.
카우치베이스 스케줄링, 타이머의 비밀 소스
타이머는 개발자가 미래의 시간에 트리거될 루틴(비즈니스 로직)을 지정할 수 있는 카우치베이스 이벤트 서비스 구성입니다. 이 기능을 사용하여 순수한 Couchbase 구성 가능 크론탭 시스템을 사용하면 간단한 N1QL 쿼리를 실행하든 복잡한 규칙 엔진을 구축하든 워크플로우의 일부로 반복 작업을 트리거할 수 있습니다.
이후 모든 디자인에서 우리는 cron 구현을 15초 이상의 해상도로 구현할 수 없습니다. 이러한 제한이 있는 이유는 타이머가 수백만 단위로 확장되고 실행 및 실행이 보장되지만 현재 14초 미만의 정상 상태 지연으로 인해 벽시계 정확도가 떨어지기 때문입니다. [1].
물론 더 짧은 일정, 즉 15초 미만이 필요한 경우에는 타이머 구조를 사용하지 않고 이벤트 로직에서 돌연변이 자체를 처리하여 향후 콜백을 예약해야 합니다.
이 글을 쓰는 시점에서 현재 Couchbase 릴리스는 버전 6.5.1입니다. 해결해야 할 두 가지 제한 사항 강력한 cron 시스템.
- 5.5.x, 6.0.x 및 6.5.x 릴리스에서는 타이머 콜백으로 호출되는 함수가 새 타이머를 안정적으로 생성할 수 없습니다(두 번째 협력 함수를 통해 사용자 공간 우회 방법을 수행할 수 있음).
- 6.5.x 릴리스에서는 유휴 시스템에서 향후(1시간 이상) 타이머를 생성하면 메타데이터 버킷 작업의 수가 증가하여 결국 주어진 이벤트 함수에 대한 변이를 차단할 수 있습니다(6.5.X에서는 두 번째 협력 함수를 통해 사용자 공간 워크어라운드가 수행될 수 있습니다). 심각도는 다음에 의해 관리됩니다:
- 활성 타이머를 보유하고 있는 vBucket의 수입니다. 따라서 향후 타이머가 몇 개만 있는 경우 문제가 눈에 띄지 않거나 구체화되지 않을 수 있습니다. 이것은 몇 가지 크론 일정의 경우이지만 날짜 기능을 추가 할 경우를 대비하여이 문서에 제공된 코드에 대한이 문제에 대한 수정 사항을 추가했습니다..
- 이벤트 타이머가 최근에 vBucket에서 실행되었는지 여부(기능별로 지정된 vBucket에 대한 문제를 지웁니다). 따라서 단기 타이머 활동이 많은 시스템에서는 타이머가 먼 미래로 예약되어 있어도 이 문제가 발생하지 않습니다.
다행히도 버전 6.6.0에서는 위의 두 가지 문제나 제한 사항이 모두 해결되어 하나의 간단한 통합 이벤트 함수에서 스케줄러를 만들 수 있습니다.

전제 조건
이 문서에서는 최신 GA 버전, 즉 Couchbase 버전 6.5.1을 사용합니다(이전 Couchbase 버전에 대해 설명된 이벤트 함수를 일부 변경해야 할 수도 있습니다). 이 문서의 예제는 Couchbase 서버와 함께 제공되는 여행 샘플 데이터 세트에 대해 실행됩니다.
프로 팁: 고급 사용자 전용, 카우치베이스 이벤트 및 CLI/ REST 도구에 익숙하다면 이 블로그의 대부분을 건너뛰고 아래 제시된 스케줄러 시스템을 빠르게 설정하고 실행할 수 있는 ZIP 파일을 다운로드할 수 있습니다. 다음 링크를 마우스 오른쪽 버튼으로 클릭하고 다른 이름으로 링크 저장 를 클릭하여 파일을 다운로드합니다. cron_impl_2func_CLI.zip, 이벤트 노드로 이동하고 ZIP 파일을 추출한 다음 추출된 README.txt 파일을 참조하세요.
그러나 카우치베이스 또는 이벤트 서비스에 익숙하지 않은 경우 시작하기와 이벤트 예시를 구체적으로 참조하세요:
- 의 지침에 따라 작동하는 Couchbase 6.5.1 서버를 설정합니다. 여기서 시작하세요!
- 에 대해 N1QL 쿼리를 실행할 수 있는지 확인합니다. 여행 샘플 의 지침에 따라 데이터 세트를 첫 번째 N1QL 쿼리 실행.
- 의 지침에 따라 기본 이벤트 기능을 배포하는 방법을 이해합니다. 문서 보관 예제를 사용하는 여행 샘플 데이터 집합입니다.
- 다음 사항이 있는지 확인합니다. 여행 샘플 버킷을 클릭합니다.
- 라는 버킷이 있는지 확인합니다. 메타데이터 UI의 버킷 보기에서 최소 크기가 200MB여야 합니다.
- UI의 버킷 보기에서 다음과 같은 버킷을 만듭니다. 크론데이터 최소 크기는 200MB입니다. 버킷을 만드는 방법에 대한 자세한 단계는 다음을 참조하세요. 버킷 만들기.
- 설정 허용_인터버킷_재귀 에 true 를 사용하여 두(2) 개의 이벤트 함수가 동일한 KV 문서를 변경할 수 있도록 허용합니다. [2].
1curl -X POST -u "$CB_USERNAME:$CB_PASSWORD" 'http://localhost:8091/_p/event/api/v1/config' -d '{ "allow_interbucket_recursion":true }'
구현 #1, 스케줄링과 같은 하드 코딩된 '크론'
첫 번째 구현(예: 시리즈 1편)에서는 KV JSON 문서에 불과한 간단한 제어 구조와 제어 구조의 정보에 응답하고 이에 따라 동작하는 이벤트 함수 2개를 설계할 것입니다.
아래는 여러 개의 예약된 "이벤트"를 가질 수 있도록 하는 JSON 문서 또는 제어 구조의 디자인입니다. 각 스케줄 이벤트에는 recurring_event::1, recurring_event::1, ... recurring_event::N과 같은 고유한 KEY가 있는 자체 제어 문서가 있습니다. 스케줄링 시스템이 작업을 활성화 또는 비활성화하기 위해 "활성" 상태를 토글하거나 로깅의 양과 스타일을 제어하는 "상세" 필드를 변경하는 등 제어 문서의 변경 또는 업데이트(변이)에 응답하므로 JSON 구조 자체에는 "키 재구성"을 위한 정보가 포함되어 있습니다.
다음은 KEY를 사용한 제어 문서 예시입니다. 반복_이벤트::1 자바스크립트 함수를 실행하는 doCronActionA 매일 14:54(오후 2시 30분).
| JSON 제어 레코드 | 설명 |
|---|---|
| { | |
| "유형":"반복_이벤트", | KEY는 <>::<>입니다. |
| "id":1, | |
| "시간":14, | 0-23, *, *2X, *4X를 트리거하는 하루 중 시간입니다. |
| "min":54, | 시 0-59분, *, *2X, *4X를 트리거하려면 |
| "action":"doCronActionA", | 타이머가 실행될 때 실행할 자바스크립트 함수 |
| "active":true, | 이 일정을 활성화 또는 비활성화하려면 플래그 지정 |
| "verbose": { | [선택 사항] 로깅 제어 |
| "user_func":2, | 액션 로직의 로깅 수준 : 0=없음 등 등 |
| "스케줄러":3 | 크론 로직의 로깅 수준: 0=없음 등 등 |
| }, | |
| "동적": { | [동적] 시스템 제어 및 통계 |
| "상태":"팔", | "ARM"|"다시 무장"|"보류 중" 임의의 값 != "보류 중" 일정 시작 |
| "next_sched": 0, | 에포크 이후 다음 원하는 일정까지 걸린 시간(초) |
| "prev_sched": 0, | 이전 일정의 에포크 이후 경과 시간(초) |
| "prev_time": 0, | 이전 스케줄 실제 실행 시간에 대한 에포크 이후 시간(초) |
| "prev_delay": 0, | 타이머가 일정에서 지연된 시간(초) |
| "prev_atime": 0 | 사용자 '액션'에 걸린 시간(초) |
| } | |
| } |
기존 Linux와 마찬가지로 크론탭 시와 분을 법정 정수로 설정할 수 있으며, 또한 시간 를 "*"로 설정하여 모든 시간 동안 처리하거나 분 를 "*"로 설정하여 모든 분을 처리합니다.
전체 기능을 지원하지는 않지만 크론탭 구문을 사용하는 경우 다음과 같은 두 가지 비표준 설정을 지원합니다. 둘 다 설정 시간 그리고 분 를 "*4X"로 설정하면 1분에 네(4) 번 실행하고 다시 무장하며, 두 가지를 모두 "*2X"로 설정하면 1분에 두(2) 번 실행하고 다시 무장합니다. 아래는 설명과 함께 지원되는 스케줄 표입니다:
| 시간 | 분 | 값은 숫자 또는 문자열일 수 있습니다. |
|---|---|---|
| 13 | 32 | 13:32(또는 오후 1시 32분) 실행 |
| * | 15 | 매시간 15분 경과 시 실행 |
| 8 | 12 | 하루에 한 번 8시 32분(또는 오전 8시 32분)에 실행합니다. |
| * | * | 1분에 한 번 실행 |
| *2X | *2X | 1분에 두 번 실행 - 시와 분을 모두 "*2X"로 설정해야 합니다. |
| *4X | *4X | 1분에 4번 실행 - 시와 분을 모두 "*2X"로 설정해야 합니다. |
결국 쿼리 워크벤치를 사용하여 다음을 삽입합니다. cron 제어 문서에는 모두 고유한 키가 있어야 합니다. recurring_event::# 를 실행 예정 시간인 14:54(오후 2시 54분)로 변경하려면 doCronActionA 작업에 대해 다음 N1QL 문을 사용할 수 있습니다.
지금 당장 N1QL 문을 실제로 실행하는 것에 대해 걱정하지 마세요, 이벤트 함수를 빌드하고 배포한 후 나중에 N1QL 문을 수행하겠습니다.
버킷에서 제어 레코드(또는 레코드)를 만들 수 있습니다. 여행 샘플를 클릭한 다음 다음과 같이 나열, 무장, 무장 해제, 따르는 일정 조정, 로깅의 상세도 수준 변경 또는 삭제합니다:
| 액션 | N1QL 문 |
|---|---|
| 일정 만들기 | 삽입 여행 샘플 (키,값) 값 ("RECURRING_EVENT::1", { "유형":"반복_이벤트", "id":1, "시간":"14", "min":"54″, "action":"doCronActionA", "active":true } ); |
| 키를 지정하지 않고도 데이터를 쿼리할 수 있는 인덱스 만들기 | 다음에 기본 인덱스 만들기 크론데이터 ; |
| ID별로 모든 일정 순서 표시 | SELECT * FROM 크론데이터 WHERE type="recurring_event" order by id ; |
| 특정 일정 표시 | SELECT * FROM 크론데이터 WHERE type="recurring_event" AND id=1 ; |
| 팔 또는 활성 설정 | 업데이트 크론데이터 SET active = true WHERE type="recurring_event" AND id=1 ; |
| 비활성화 또는 비활성 설정 | 업데이트 크론데이터 SET active = false WHERE type="recurring_event" AND id=1 ; |
| 트리거 시간 조정 | 업데이트 크론데이터 SET hour = 11, min = 30 WHERE type="recurring_event" AND id=1 ; |
| "작업" 로깅 조정 | 업데이트 크론데이터 SET verbose.user_data = 0 WHERE type="recurring_event" AND id=1 ; |
| 스케줄러 로직의 로깅 조정하기 | 업데이트 크론데이터 SET verbose.scheduler = 0 WHERE type="recurring_event" AND id=1 ; |
| 일정 삭제 | 다음에서 삭제 크론데이터 WHERE type="recurring_event" AND id=1 ; |
4개의 활성 일정이 있다고 가정하고, 위의 첫 번째 N1QL 문을 실행하면 다음과 같이 모든 일정이 나열됩니다.
|
1 2 |
SELECT active, action, hour, min, type, id, verbose.user_func, verbose.scheduler FROM `crondata` where type="recurring_event" order by id ; |
는 다음과 같은 출력을 반환합니다(쿼리 워크벤치의 테이블 보기):
| 활성 | 액션 | 시간 | id | 분 | 스케줄러 | 유형 | user_func |
|---|---|---|---|---|---|---|---|
| true | "doCronActionA" | 14 | 1 | 54 | 1 | "recurring_event" | 2 |
| true | "doCronActionB" | * | 2 | * | 1 | "recurring_event" | 1 |
| true | "doCronActionC" | *2X | 3 | *2X | 4 | "recurring_event" | 4 |
| true | "doCronActionD" | * | 4 | 0 | 0 | "recurring_event" | 1 |
위의 표에서 첫 번째 동작은 하루에 한 번, 두 번째 동작은 1분마다, 세 번째 동작은 30초마다, 네 번째 동작은 1시간에 한 번 실행되는 네 가지 동작이 있습니다. 이 시리즈의 다음 편에서는 '요일' 기능을 추가할 예정입니다.
JSON 컨트롤 레코드의 중첩된 개체 "verbose"를 제공하지 않으면 기본값은 { "user_func":1, "scheduler":1 }로 동작 함수 및 스케줄링 로직에 대한 로깅 수준이 낮거나 간결함을 나타냅니다. 값이 0이면 모든 로그 메시지(예: doCronActionD)가 억제되고, 값이 클수록 doCronActionC에 정의된 대로 더 자세한 내용이 기록됩니다.
JSON 컨트롤 레코드의 중첩된 개체 "동적"(일반적으로 제공되지 않는 경우 기본값은 { "state")로 설정됩니다: "arm", "next_sched": 0, "prev_sched": 0, "prev_time": 0, "prev_delay": 0, "prev_atime": 0 } 이것은 실행 중인 이벤트 로직 스케줄을 위한 스크래치 패드이며 실행 시간에 대한 유용한 통계도 제공하므로 읽기 전용으로 취급해야 합니다.
이 시점에서 우리는 높은 수준의 제어 설계를 가지고 있지만 제어 구조를 처리할 로직이 필요하며, 바로 이 지점에서 Couchbase Eventing 서비스, 특히 Eventing 함수가 작동합니다.
이벤트 함수
이 디자인에는 두 개의 이벤트 함수, 즉 메인 자바스크립트 함수 "cron_impl_2func_651"과 작은 헬퍼 자바스크립트 함수 "cron_impl_2func_651_help"가 필요합니다. 초기 구현을 구성하는 자바스크립트 함수의 각 섹션은 약 610줄(줄 중 약 441줄은 주석과 공백)의 자바스크립트 코드를 합친 것입니다.
지금 바로 잘라내어 붙여넣기에 대해 걱정하지 마세요.나중에 두 개의 필수 이벤트 함수와 모든 필수 설정을 "cron_impl_2func_651.json" "cron_impl_2func_651_help.json"이라는 두 개의 파일에 다운로드(가져오기용) 링크를 제공할 것이며, 직접 잘라 붙여넣기할 수 있는 완전한 통합 함수 2개를 선호하는 경우 이 두 개의 링크를 제공할 것입니다.
메인 이벤트 함수 "cron_impl_2func_651"은 9개의 자바스크립트 함수로 구성됩니다.
- 비즈니스 로직 함수 3개(2개는 빈 셸).
- doCronActionA(doc) - 실행할 N1QL 예제 사용자 작업
- doCronActionB(doc) - 실험을 위한 빈 사용자 액션 셸
- doCronActionC(doc) - 실험을 위한 빈 사용자 액션 셸
- 이벤트에 대한 하나의(1) 진입점.
- OnUpdate(문서, 메타) - 삽입 또는 업데이트를 위한 표준 이벤트 엔트리 포인트입니다.
- 하나(1) cron 구문 파서를 사용하여 다음 일정을 생성합니다.
- getNextRecurringDate(hour_str, min_str) - 다음 예약 날짜를 찾는 크론 로직입니다.
- 비즈니스 로직이 존재하는지 확인하거나 결과 서식을 지정하는 세 가지 지원 기능을 제공합니다.
- verifyFunctionExistsViaEval(curDoc, id) - 실행할 함수가 있는지 확인합니다.
- toNumericFixed(숫자, 정밀도) - 부동 소수점을 콤팩트한 스타일로 포맷합니다.
- toLocalISOTime(d) - 날짜를 간결한 스타일로 포맷합니다.
- 타이머가 실행될 때 콜백 함수가 하나(1) 있습니다.
- Callback(doc) - 예약된 타이머를 위한 콜백 함수
헬퍼 이벤트 함수 "cron_impl_2func_651_help"는 하나의(1) 자바스크립트 함수로 구성됩니다.
- 이벤트에 대한 하나의(1) 진입점.
- OnUpdate(문서, 메타) - 삽입 또는 업데이트를 위한 표준 이벤트 엔트리 포인트입니다.
다음 섹션에서는 위의 각 자바스크립트 함수를 하나씩 살펴보겠습니다.
주기적인 일정에 따라 실행되는 비즈니스 로직과 같은 자바스크립트 함수가 필요합니다.
가장 먼저 원하는 것은 크론탭 규칙에 따라 실행할 비즈니스 로직이 있는 루틴 또는 함수입니다. 자바스크립트 메서드 doCronActionA(doc)라고 부르지만, 예를 들어 doPeriodicLedgerBalance(doc), 예약된 비즈니스 로직을 구현하는 '액션' 기능에 대한 유일한 요구 사항은 다음과 같습니다:
- 하나의 매개변수가 있습니다: doc, 위에서 설명한 유형="recurring_event"의 제어 문서.
- 실제 자바스크립트 이름은 제어 문서의 'action' 필드와 일치합니다.
- 반환 true 성공과 false 실패 시
- 활용 doc.verbose.user_func 로깅을 제어하기 위해 0이면 무음, 1이면 한 줄, 2이면 함수 디버깅에 필요한 모든 로그 정보 등을 출력합니다.
함수를 작성하겠습니다. doCronActionA(doc))를 사용하여 내장된 N1QL 쿼리를 실행하여 국가별 항공사 수를 결합한 다음 계산된 데이터의 단일 KV 문서를 만듭니다.
|
1 |
SELECT country, count( * ) AS cnt FROM `travel-sample` WHERE `type` = 'airline' GROUP BY 국가; |
내 테스트 시스템에서는 작은 단일 노드 비MDS 서버(모든 Couchbase 서비스 실행)에서 위의 N1QL은 약 20ms가 소요됩니다. (명확성을 위해 매우 복잡하다고 가정하면 완료하는 데 10초가 걸립니다).
여기서 아이디어는 쿼리 서비스 노드와의 추가 오버헤드 통신이나 각 변이에 대한 N1QL 문을 처리하지 않고도 초당 10만(또는 백만) 개의 이벤트 변이에 의해 최종 계산 및 요약된 KV 문서를 빠르게 로드할 수 있다는 것입니다.
이 특정 비즈니스 로직의 목표는 분명해야 합니다, doCronActionA(doc)는 일정에 따라 주기적으로 업데이트되는 반정적 캐시를 생성하는 것입니다.
우리가 실제로 하는 일은 여행 샘플 문서 세트에서 국가별 항공사 수를 가져오는 것뿐입니다(매우 빠릅니다). N1QL을 사용하면서 문서를 작성하고 최종적으로 요약된 문서로 KV에 작성합니다. 여기서 핵심은 수백만 개의 변이에 대해 각각 동일한 작업을 반복하고 싶지 않다는 것입니다. 특히 일부 계산은 이벤트 함수에서 임베디드 N1QL 쿼리를 시작할 때마다 10초의 쿼리 서비스 계산 시간이 걸릴 수 있기 때문입니다.
아래에는 하루에 한 번(또는 한 시간에 한 번 등) 실행하려는 JavaScript 함수가 나와 있습니다. 함수 이름은 제어 구조 액션 필드에 있는 이름과 일치합니다. 이벤트 용어 및 언어 구조에 대한 자세한 내용은 다음에서 Couchbase 문서 및 예제를 참조하세요. 이벤트 서비스: 기본 사항.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 |
함수 doCronActionA(doc) { 시도 { // 문서에 원하는 값이 있는지 확인 만약 (!doc.유형 || doc.유형 !== "recurring_event" || !doc.활성 || doc.활성 !== true) 반환; 만약 (doc.verbose.user_func >= 1) 로그(doc.액션 + ' 에 의해 제어되는 사용자 작업 + doc.유형 + '::' + doc.id); // 이것은 6.5 N1QL 쿼리입니다(6.5 이전 GA에서는 사용할 수 없는 기능). // SELECT 문을 실행하여 임베디드 N1QL 이터레이터를 생성하여 다음을 가져옵니다. // 국가별 항공사 수입니다. 새 문서를 만들어 KV에 작성합니다. // 반복자를 사용하여 반복자의 결과를 나타내는 KV 문서를 생성합니다. // 길이가 긴 임베디드 N1QL 쿼리를 작성하고 이를 다시 KV에 쓰는 것은 // 하루에 한 번 '빠르게' 읽을 수 있도록 계산을 최신으로 업데이트합니다. // 다른 이벤트 함수, 다른 카우치베이스 서비스 또는 SDK에 의해 호출됩니다. // 1분에 백만 개의 문서가 있다고 가정했을 때 정말 N1QL을 사용해야 할까요? //를 사용하여 100만 개의 모든 문서에 대해 거의 정적인 것을 다시 계산합니다. // 물론 아니므로 Eventing으로 읽을 수 있는 중간 값을 만듭니다. // 단일 '경량' KV 읽기를 통해 사용됩니다. var q_iter = 선택 국가, 카운트( * ) cnt FROM `여행-샘플` 어디 `유형` = '항공사' 그룹 BY 국가; // 결과 집합을 반복하고 '누적' 맵을 업데이트합니다. var 누적 = {}; var idx = 0; 에 대한 (var val 의 q_iter) { 만약 (doc.verbose.user_func >= 2) 로그(doc.액션 + ' N1QL idx ' + idx + ', 국가 ' + val.국가 + " cnt " + val.cnt); 누적[val.국가] = val.cnt; idx++; } // 임베디드 N1QL 이터레이터 닫기 q_iter.닫기(); // 이제 HARD 길이의 임베디드 N1QL을 나타내는 캐시된 KV 문서를 만들어 보겠습니다. // 쿼리하고 KV에 다시 쓰려면 KEY와 유형 및 ID가 필요합니다. // 'travel-sample' 버킷에 넣습니다. var cachedoc = {}; cachedoc.유형 = "cron_cache"; cachedoc.id = "항공사별_국가"; cachedoc.날짜 = new 날짜(); cachedoc.데이터 = 누적; var ckey = cachedoc.유형 + '::' + cachedoc.id; ts_bkt[ckey] = cachedoc; 만약 (doc.verbose.user_func >= 2) { 로그(doc.액션 + ' 키로 KV로 업서트 ' + ckey + ' 캐시독 ', cachedoc); } } catch (e) { 로그(doc.액션 + ' 오류 예외:', e); 반환 false; } 반환 true; } |
위의 함수는 1) 여행 샘플 버킷을 쿼리하여 데이터(이 경우 각 국가별 항공사 수)를 추출하고, 2) 새 KV 문서와 키를 생성하여 나중에 사용할 수 있도록 여행 샘플 버킷에 기록하기만 하면 됩니다.
또한 이 예제에서는 숫자 상세 정보 설정에 응답하는 로깅을 구축하여, a) 제어 문서에 doc.verbose.user_func == 1 값이 있는 경우 한 줄을 기록하거나 b) doc.verbose.user_func 값이 >= 2인 경우 더 많은 정보를 기록합니다.
이것은 하나(1)를 실행할 수 있는 일반적인 프레임워크입니다. cron 액션 또는 천(1000) 개의 cron 액션. 따라서 앞서 지적한 대로 두 개의 "빈" 함수 셸을 추가로 제공했는데, 이 셸에는 아무 이름도 붙일 수 없었습니다.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
함수 doCronActionB(doc) { 시도 { // 문서에 원하는 값이 있는지 확인 만약 (doc.유형 !== "recurring_event" || doc.활성 !== true) 반환; 만약 (doc.verbose.user_func >= 1) 로그(doc.액션 + ' 에 의해 제어되는 사용자 작업 + doc.유형 + '::' + doc.id); // 여기에 당신의 로직 } catch (e) { 로그(doc.액션 + ' 오류 예외:', e); 반환 false; } 반환 true; } |
그리고
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
함수 doCronActionC(doc) { 시도 { // 문서에 원하는 값이 있는지 확인 만약 (doc.유형 !== "recurring_event" || doc.활성 !== true) 반환; 만약 (doc.verbose.user_func >= 1) 로그(doc.액션 + ' 에 의해 제어되는 사용자 작업 + doc.유형 + '::' + doc.id); // 여기에 당신의 로직 } catch (e) { 로그(doc.액션 + ' 오류 예외:', e); 반환 false; } 반환 true; } |
위의 doCronActionB 및 doCronActionC 함수는 이벤트 함수의 이벤트 애플리케이션 로그에 정보를 기록하기만 하므로 사소한 함수입니다. 다음을 참조하세요. 로깅 기능 를 참조하세요. 물론 실제로 활성화하고 실행하려면 type="recurring_event", active=true의 제어 문서와 action = "doCronActionB" 같은 액션이 필요합니다.
이벤트 진입점 또는 핸들러가 필요합니다.
버전 6.5부터 이벤트 서비스에서 지원하는 두 가지 엔트리 포인트 또는 핸들러는 다음과 같습니다. 온업데이트(문서, 메타) 그리고 OnDelete(메타) 우리는 오직 온업데이트(문서,메타) 이 예제에서는
그리고 온업데이트(문서,메타) 핸들러는 소스 버킷의 문서가 생성되거나 수정(변경)될 때 호출되며, 관심 없는 문서를 즉시 필터링합니다. [3]
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 |
함수 온업데이트(doc, 메타) { // 6.5.X 버킷 작업 증가에 대한 수정 만약 (doc.유형 === "_tmp_vbs") genNoopTimers(doc, 메타, 30); 만약 (!크론_bkt["수정_타이머_스캔_이슈::1"]) { 크론_bkt["수정_타이머_스캔_이슈::1"] = {}; } 시도 { // 추가 분석이 필요한 경우 활성 recurring_event에서만 트리거합니다. 만약 (doc.유형 !== "recurring_event" || doc.활성 !== true) 반환; var update_doc = false; 만약 (!doc.동적) { // 기본값으로 doc.dynamic이 누락된 경우 추가 doc.동적 = { "state": "arm", "next_sched": 0, "prev_sched": 0, "prev_time": 0, "prev_delay": 0, "prev_atime": 0 }; // 다음 일정이 잡히면 문서를 업데이트해야 합니다. update_doc = true; } 만약 (!doc.verbose) { // 기본값으로 doc.dynamic이 누락된 경우 추가 doc.verbose = { "user_func": 1, "스케줄러": 1 }; // 다음 일정이 잡히면 문서를 업데이트해야 합니다. update_doc = true; } // dynamic.state 보류 중 처리하지 않음 만약 (!doc.동적 || !doc.동적.상태 || doc.동적.상태 === "pending") 반환; var mid = doc.유형 + "::" + doc.id; // 이것은 meta.id 또는 KEY와 동일합니다. var 시간 = doc.시간; var 분 = doc.분; // 자바스크립트 함수가 존재하는지 확인합니다. 평가는 일반적인 // 유틸리티 함수는 RecurringCallback과 공유됩니다. 만약 (!검증 함수 존재 여부 확인 값(doc, mid)) { // doc.action이 존재하지 않는 경우 이미 문제를 기록했습니다. 반환; } // 다음 유효한 실행 시간 가져오기 var 날짜_타이머 = getNextRecurringDate(시간, 분); var next_sched = 수학.라운드(날짜_타이머.getTime() / 1000); 만약 (!update_doc && next_sched !== doc.동적.next_sched) { // 다음_sched는 도우미 애플리케이션의 설정과 동일해야 합니다. // 배포/배포를 취소하거나 일시 중지/재시작하는 경우 다음 시간 슬롯으로 일정을 다시 잡아야 할 수 있습니다. 로그('온업데이트 유 ' + mid + ' 계산된 next_sched !== doc.dynamic.next_sched, 델타 ' + (next_sched - doc.동적.next_sched) + ', reschedule'); update_doc = true; } 만약 (update_doc) { // 이 돌연변이는 재귀적이며 억제될 것이며, 동적 구조를 갖도록 합니다. doc.동적.next_sched = next_sched; // 함수를 호출하여 리소스 문제가 있는 경우 트래핑하고 다시 시도합니다. // cron_bkt[mid] = doc; 만약 (!tryBucketKvWriteWithLog('OnUpdate F', mid, doc)) { // 크론_bkt[키]에 문서 쓰기에 실패했습니다. 오류가 기록되었습니다. // 더 이상 할 수 있는 일이 없습니다. 반환; } } // 이벤트 타이머 예약하기 var timer_id = createTimer(콜백, 날짜_타이머, null, doc); 만약 (doc.verbose.스케줄러 >= 1) { 로그('온업데이트 A ' + mid + ' RCV 돌연변이(초기 또는 재 무장) 스케줄 타이머의 ' + toLocalISOTime(날짜_타이머)); } 만약 (doc.verbose.스케줄러 >= 2) { 로그('온업데이트 B ' + mid + ' 반복 타이머가 생성되었습니다, timer_id ' + timer_id); } } catch (e) { 로그('온업데이트 E ' + 메타.id + ', 오류 예외:', e); } } |
여기서 핵심은 cron 핸들러의 로직은 doc.type이 'recurring_event이고 doc.active가 true인 문서에만 관심을 갖습니다. 또한, 이 예제에서는 cron 제어 문서에 doc.verbose >= 3 값이 있는 경우에만 애플리케이션 로그에 기록되는 하우스키핑 로직입니다.
몇 가지 일정만 실행하는 경우 사용자 공간 작업을 끄거나 "6.5.X 버킷 작업 증가에 대한 수정 사항"크론_임플_2func_651"에 대한 위의 OnUpdate 블록에서 다음과 같이 네 줄의 코드를 주석으로 추가합니다:
|
1 2 3 4 5 6 |
함수 온업데이트(doc, 메타) { // 6.5.X 버킷 작업 증가에 대한 수정 // if (doc.type === "_tmp_vbs") genNoopTimers(doc, meta, 30); // if (!cron_bkt["fix_timer_scan_issue::1"]) { // cron_bkt["fix_timer_scan_issue::1"] = {}; // } |
6.5.X에서 증가하는 버킷 작업을 해결할 수 있는 코드가 필요합니다.
버전 6.5.X부터는 "6.5.X 버킷 작업 증가에 대한 수정 사항"라는 타이머가 향후 예약된 많은 타이머가 있는 유휴 시스템에서 발생합니다. 이 코드는 이벤트 타이머가 vBucket에서 최근에 실행되었는지 확인합니다(기능별로 지정된 vBucket의 문제를 해결합니다).
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 |
// fixup: addin functon 함수 noopTimer(컨텍스트) { // 6.5.X 버킷 작업 증가에 대한 수정 시도 { 만약 (컨텍스트.유형 === "_tmp_vbs" && 컨텍스트.vb === 0) { // log("noopTimer 타이머 실행 중, vBucket 0에 대해서만 인쇄"); } } catch (e) { 로그("콜백의 온업데이트 예외 noopTimer:", e); } } // fixup: addin functon 함수 재 무장 타이머(컨텍스트) { // 6.5.X 버킷 작업 증가에 대한 수정 시도 { 만약 (컨텍스트.유형 === "_tmp_vbs" && 컨텍스트.vb === 0) { // 헬퍼_버킷의 모든 문서를 업데이트/터치하면 헬퍼 함수는 다음과 같이 실행됩니다. // 유형 == vbs_seed의 1024(MacOS에서는 64)를 모두 변경하여 리커링 주기를 생성합니다. // log("noopTimer 타이머가 1024개의 vBucket을 모두 실행하고, vb 0만 로깅", context); // HELPER 함수를 다시 무장시키기 위한 돌연변이 생성: fix_scan_issue // 이 함수에 대한 새로운 돌연변이를 만듭니다. var cur = 크론_bkt[컨텍스트.키]; 만약 (cur && cur.ts_millis === 컨텍스트.ts_millis) { // log("0에 대해서만 헬퍼_버킷 별칭으로 fix_timer_scan_issue::1을 업데이트하는 rearmTimer 업데이트"); var 지금 = new 날짜(); 크론_bkt["수정_타이머_스캔_이슈::1"] = { "last_update": 지금 }; } else { // 아니요 타이머 주기가 여러 번 있었으니 이 타이머만 조용히 멈추게 하세요. } } } catch (e) { 로그("콜백 재암호화 타이머의 온업데이트 예외:", e); } } // fixup: addin functon 함수 genNoopTimers(doc, 메타, 초) { // 6.5.X 버킷 작업 증가에 대한 수정 시도 { // 중복되지만 안전하게 플레이 만약 (doc.유형 === "_tmp_vbs") { // 다른 함수를 사용하고 있기 때문에 모든 vBuckets의 타이머가 즉시 실행됩니다(최대 15초까지 걸릴 수 있음). // 교차 버킷 재귀를 사용하여 모든 타이머를 반복 방식으로 재장전하는 경우 최소 40초의 지연이 추가됩니다. createTimer(noopTimer, new 날짜(), null, doc); 만약 (doc.vb === 0) { // 헬퍼_버킷의 모든 문서를 업데이트/터치하면 헬퍼 함수는 다음과 같이 실행됩니다. // 유형 == vbs_seed의 1024(MacOS에서는 64)를 모두 변경하여 리커링 주기를 생성합니다. // log("noopTimer 타이머가 1024개의 vBucket을 모두 실행하고, vb 0만 로깅", context); // HELPER 함수를 다시 무장시키기 위한 돌연변이 생성: fix_scan_issue // 이 함수에 대한 새로운 돌연변이를 만듭니다. // log("genNoopTimers가 타이머를 재장전하여 fix_timer_scan_issue::1을 만듭니다."); createTimer(재 무장 타이머, new 날짜(new 날짜().getTime() + 초 * 1000), null, doc); } } } catch (e) { 로그("genNoopTimers의 온업데이트 예외:", e); } } |
일정의 다음 시간을 계산하는 유틸리티가 필요합니다.
다음 함수 getNextRecurringDate(시간, 분)는 일정의 일부로 정의된 대로 작업을 실행할 시간을 결정합니다. 이것은 cron대신 하루에 한 번, 한 시간에 한 번, 1분에 한 번 실행하는 주요 표준 기능이 포함되어 있습니다. 또한 1분에 두 번 또는 1분에 네 번 실행하는 기능을 제공하기 위한 비표준 구문도 포함되어 있습니다.
앞서 기능에 대해 설명한 것처럼 다음 반복 날짜(시간, 분) 는 다음을 허용하며(표는 아래에 중복되어 있습니다), 마지막 두 개는 비표준입니다.[4]
| 시간 | 분 | 값은 숫자 또는 문자열일 수 있습니다. |
|---|---|---|
| 13 | 32 | 13:32(또는 오후 1시 32분) 실행 |
| * | 15 | 매시간 15분 경과 시 실행 |
| 8 | 12 | 하루에 한 번 8시 32분(또는 오전 8시 32분)에 실행합니다. |
| * | * | 1분에 한 번 실행 |
| *2X | *2X | 1분에 두 번 실행 - 시와 분을 모두 "*2X"로 설정해야 합니다. |
| *4X | *4X | 1분에 4번 실행 - 시와 분을 모두 "*2X"로 설정해야 합니다. |
다음은 첫 번째 예제의 사용자 로직에서 스케줄에서 이벤트 타이머를 트리거할 다음 시간을 결정하는 데 필요한 로직의 구현입니다. doCronActionA(doc) 가 실시간 오버런 등의 이유로 제때 완료되지 않으면 다음 스케줄이 선택됩니다. 타이머와 그 부모 함수를 모두 주목하세요. 따라서 이벤트 함수의 기본 실행 시간 제한이 60초인 경우, 필요한 경우 이 설정을 조정하거나 높일 수 있습니다.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 |
함수 getNextRecurringDate(hour_str, min_str) { // 참고 자바스크립트 날짜는 밀리초 단위입니다. var date_now = new 날짜(); var date_ret = new 날짜(); var 시간; var 분; 시도 { 시간 = parseInt(hour_str); } catch (e) {} 시도 { 분 = parseInt(min_str); } catch (e) {} // 이것은 약간의 확장이 있는 간단한 부분적인 '크론탭' 구문일 뿐입니다. // 하루에 한 번, 한 시간에 한 번, 1분에 한 번 허용합니다. 또한 몇 가지 비표준 // 구문을 사용하여 1분에 두 번 또는 1분에 네 번 실행할 수 있는 기능을 제공합니다. 만약 (hour_str === '*4X' && min_str === '*4X') { // 15초에 한 번 또는 1분에 네 번 date_ret.설정 밀리초(0); date_ret.setSeconds(15); 동안 (date_ret.getTime() < date_now.getTime()) { date_ret.setSeconds(date_ret.getSeconds() + 15); } 반환 date_ret; } else 만약 (hour_str === '*2X' && min_str === '*2X') { // 30초에 한 번 또는 1분에 두 번 date_ret.설정 밀리초(0); date_ret.setSeconds(30); 동안 (date_ret.getTime() < date_now.getTime()) { date_ret.setSeconds(date_ret.getSeconds() + 30); } 반환 date_ret; } else 만약 (hour_str === '*' && min_str === '*') { // 1분에 한 번 date_ret.설정 밀리초(0); date_ret.setSeconds(0); date_ret.setMinutes(date_ret.getMinutes() + 1); } else 만약 (hour_str !== '*' && isNaN(시간) === false && min_str === '*') { // 지정된 시간 동안 1분에 한 번만 date_ret.설정 밀리초(0); date_ret.setSeconds(0); date_ret.setMinutes(date_ret.getMinutes() + 1); 만약 (date_ret.getTime() < date_now.getTime()) { date_ret.setHours(시간); } 만약 (date_ret.getTime() > date_now.getTime()) { date_ret.setDate(date_ret.getDate() + 1); date_ret.setSeconds(0); date_ret.setMinutes(0); date_ret.setHours(시간); } } else 만약 (hour_str === '*' && min_str !== '*' && isNaN(분) === false) { // 한 시간에 한 번씩 주어진 분에 date_ret.설정 밀리초(0); date_ret.setSeconds(0); date_ret.setMinutes(분); // 다음 시간 예약 date_ret.setHours(date_ret.getHours() + 1); } else 만약 (isNaN(시간) === false && isNaN(분) === false) { // 하루에 한 번 주어진 시간과 주어진 분 동안 date_ret.설정 밀리초(0); date_ret.setSeconds(0); date_ret.setMinutes(분); date_ret.setHours(시간); 만약 (date_ret.getTime() < date_now.getTime()) { // 내일 일정 date_ret.setDate(date_ret.getDate() + 1); } } else { 로그('getNextRecurringDate 잘못된 입력 시간_str <' + hour_str + '> min_str <' + min_str + '>'); throw new 오류('getNextRecurringDate 잘못된 입력 시간_str <' + hour_str + '> min_str <' + min_str + '>'); 반환 null; } 반환 date_ret; } |
몇 가지 작은 유틸리티가 필요합니다.
자바스크립트의 존재 여부만 확인하는 공통 유틸리티 함수는 두 가지 모두에서 사용됩니다. 온업데이트(문서,메타), 와 타이머 콜백(문서), 나중에 표시됩니다. 아래는 verifyFunctionExistsViaEval(curDoc, id) 는 두 개의 인자로 JSON 제어 문서와 해당 문서의 KEY를 받습니다.
이를 통해 배포 시 JSON 제어 레코드 문서와 JavaScript 코드의 비즈니스 로직 함수의 실제 이름 간에 명명 불일치 문제가 있는 경우 즉시 알 수 있습니다.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
함수 검증 함수 존재 여부 확인 값(curDoc, id) { var 결과 = false; 시도 { // 이것이 누락된 경우 잘못된 반환 결과인지 함수 확인 결과 = 평가("typeof" + curDoc.액션 + " === 'function';"); 만약 (결과 === false) { 만약 (curDoc.verbose.스케줄러 >= 1) 로그("경고/비활성화(조치 없음 및 재암호화 없음)의 '조치'가 필요하기 때문에" + curDoc.액션 + "(문서)가 존재하지 않습니다, ID는", id); 반환 결과; } } catch (e) { 로그('verifyFunctionExistsViaEval 오류 예외:', e); } 반환 결과; } |
존재하지 않는 함수를 실행하려고 시도하면 최종 사용자에게 애플리케이션 로그에 경고가 표시됩니다. cron_impl_2func_651.log 를 클릭해 문제를 해결하세요.
2020-04-22T16:20:38.725-07:00 [INFO] "경고/비활성화(동작 없음 및 재암호화 없음), doCronMyNewFunction(doc)의 필수 '동작'이 존재하지 않으므로 ID는""recurring_event::1""입니다.
이 수정은 일시 중지/재시작을 통해 함수를 추가한 다음 지정된 ID 또는 KEY로 제어 문서를 조정하거나(토글을 활성화하여 거짓에서 참으로 전환), 핸들러의 기존 함수를 가리키도록 제어 문서를 조정하여 수행할 수 있습니다.
다음 유틸리티 toNumericFixed(숫자, 정밀도) 를 사용하면 로그 메시지의 플로트 형식을 간결하게 지정할 수 있습니다.
|
1 2 3 4 |
함수 숫자 고정(숫자, 정밀도) { var 멀티 = 수학.pow(10, 정밀도); 반환 수학.라운드((번호 * 멀티).toFixed(정밀도 + 1)) / 멀티; } |
마지막으로, 유틸리티 toLocalISOTime(d) 로그 메시지의 날짜 서식을 간결하게 지정할 수 있습니다.
|
1 2 3 4 |
함수 toLocalISOTime(d) { var tzoffset = (new 날짜()).getTimeZoneOffset() * 60000; //오프셋(밀리초) 반환 (new 날짜(d.getTime() - tzoffset)).toISOString().슬라이스(0, -1); } |
사용자 로직을 실행하고 타이머를 재장전하려면 타이머 콜백이 필요합니다.
"cron_impl_2func_651"의 마지막 JavaScript 함수는 예약된 타이머가 실행될 때 호출되는 타이머 콜백입니다. 콜백 함수는 컨텍스트라는 단일 인수를 받는 최상위 함수여야 합니다.
이 경우 OnUpdate 핸들러에서 자바스크립트 함수인 콜백(문서) 문서(활성 스케줄러 제어 문서 유형="recurring_event")의 컨텍스트가 있습니다.
버전 6.6에서는 타이머 내에 다른 타이머를 만들 수 있지만 이전 버전에서는 모두 "헬퍼" 함수로의 변형을 트리거해야 합니다(무한 재귀를 조심스럽게 피합니다). 6.6에서는 헬퍼 함수가 필요하지 않으며 로직이 상당히 단순화되었습니다.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 |
함수 콜백(doc) { 시도 { var fired_at = new 날짜(); // 추가 분석이 필요한 경우 활성 상태인 recurring_event에서만 트리거합니다. 만약 (doc.유형 !== "recurring_event") 반환; // 문서에 'action', 'dynamic {}', verbose {}, dynamic.state가 있어야 합니다. 만약 (!doc.액션 || !doc.동적 || !doc.verbose || !doc.동적.상태) 반환; // 모든 doc.dynamic.state BUT 보류 중 처리 만약 (doc.동적.상태 === "pending") 반환; // ================== // 아직 활성 상태인지 확인 // KV에서 'doc'가 여전히 존재하고 활성 상태인지 확인합니다. // 반환하여 동작을 건너뛰고 타이머를 재장전하지 않습니다. 참고 `travel-sample`은 // 맵 'cron_bkt'에 별칭 지정 var mid = doc.유형 + '::' + doc.id; // 키 만들기 var curDoc = null; 시도 { // KV에서 현재 버전의 문서를 읽습니다(예: curDoc). curDoc = 크론_bkt[mid]; } catch (e) {} // 6.5 이전 버전에 필요, 순수 6.5 이상 배포는 예외 없이 null을 반환합니다. var 이유 = null; 만약 (!curDoc || curDoc === null) { 이유 = "크론 문서가 누락되었습니다"; } else 만약 (!curDoc.활성) { 이유 = "크론 문서가 활성 = 거짓"; } else 만약 (!curDoc.동적.상태 || curDoc.동적.상태 !== doc.동적.상태) { 이유 = "크론 문서 잘못된 dynamic.state 예상" + doc.동적.상태; } else 만약 (crc64(doc) !== crc64(curDoc)) { 이유 = "크론 문서 변경"; } 만약 (이유 !== null) { 만약 (!curDoc || curDoc === null || curDoc.verbose.스케줄러 >= 1) { 로그('콜백 X ' + mid + " 때문에 이 타이머의 일정을 무시/중지합니다." + 이유); } 만약 (!curDoc || curDoc === null || curDoc.verbose.스케줄러 >= 4) { 로그('콜백 Y ' + mid + ' 타이머 문서', doc); 로그('콜백 Z ' + mid + ' KV curDoc', curDoc); } 반환; } // ================== // 사용자 루틴이 존재하는지 확인하고 존재하는 경우 평가합니다. // curDoc.action에 "doCronActionA"와 같은 함수가 포함되어 있고 // 이 핸들러는 "doCronActionA(doc)"와 같습니다. 아래에서는 최종 사용자가 다음과 같아야 하므로 curDoc을 사용합니다. // 평가된 자바스크립트 함수를 변경할 수 있습니다. 두(2) 개의 평가를 실행합니다. // 먼저 자바스크립트 함수가 존재하는지 확인합니다. 평가는 일반적인 // 콜백과 공유되는 유틸리티 함수 만약 (!검증 함수 존재 여부 확인 값(curDoc, mid)) { // curDoc.action이 존재하지 않는 경우 이미 문제를 기록했습니다. 반환; } // 두 번째 평가 정의된 함수를 실행하는 사용자 함수를 실행하고 처리합니다. // curDoc의 인수와 함께 var beg_act = new 날짜(); var 결과 = null; 평가("결과 = " + curDoc.액션 + "(curDoc);"); var end_act = new 날짜(); var atime_ms = end_act.getTime() - beg_act.getTime(); 만약 (curDoc.verbose.스케줄러 >= 2) 로그('콜백 R ' + mid + ' 조치 취함 ' + 숫자 고정((atime_ms / 1000), 3) + ' 초, 반환됨 ' + 결과); // ================== // 다음 번에 계산하고 헬퍼 함수에 대한 제어 문서를 변경합니다. // 이 함수의 OnUpdate가 다음을 선택하도록 또 다른 변이를 생성합니다. // 타이머를 생성합니다(MB-38554 문제 방지). var 시간 = curDoc.시간; var 분 = curDoc.분; var 날짜_타이머 = getNextRecurringDate(시간, 분); curDoc.동적.이전_지연 = 숫자 고정(((fired_at.getTime() / 1000) - curDoc.동적.next_sched), 3); curDoc.동적.prev_sched = curDoc.동적.next_sched; curDoc.동적.이전 시간 = 수학.라운드(fired_at.getTime() / 1000); curDoc.동적.이전 시간 = 숫자 고정((atime_ms / 1000), 3); curDoc.동적.상태 = "pending"; curDoc.동적.next_sched = 수학.라운드(날짜_타이머.getTime() / 1000); 시도 { 크론_bkt[mid] = curDoc; } catch (e) { 로그('콜백 도움말: F ' + mid + ' FATAL이 KV 크론 주기를 업데이트할 수 없습니다. + curDoc.액션); 반환; } 만약 (curDoc.verbose.스케줄러 >= 1) { 로그('콜백 A ' + mid + ' 유전자 돌연변이 #1을 문서로 변경하여 ' + toLocalISOTime(날짜_타이머)); } 만약 (curDoc.verbose.스케줄러 >= 2) { 로그('콜백 B ' + mid + ' sched ' + curDoc.동적.prev_sched + ', 실제 ' + curDoc.동적.이전 시간 + ', 지연 ' + curDoc.동적.이전_지연 + ', ' + curDoc.동적.이전 시간); } 만약 (curDoc.verbose.스케줄러 >= 3) { 로그('콜백 C ' + mid + ' curDoc', curDoc); } } catch (e) { var mid = doc.유형 + '::' + doc.id; // 키 만들기 로그('콜백 E ' + mid + ' 오류 예외:', e); } } |
새로운 돌연변이를 트리거하는 도우미 함수가 필요합니다.
6.6(아직 릴리즈되지 않은) 이전에는 실행 중인 타이머의 콜백 내에서 타이머를 만들 수 없기 때문에 메인 Eventing 함수의 OnUpdate(doc,meta) 진입점에서 모든 타이머를 생성할 수 있도록 변형을 트리거하려면 "allow_interbucket_recursion":true와 함께 두 번째 Eventing 함수("allow_interbucket_recursion":true)가 있어야 합니다. 이 작업은 다음과 같이 수행합니다:
- 크론_임플_2func_651 온업데이트(문서,메타) 는 변이를 수신하고 타이머를 예약합니다.
- 크론_임플_2func_651 타이머가 일정 시간이 지나면 콜백(문서) 루틴이 실행되면 먼저 원하는 사용자 작업을 실행한 다음 제어 문서에 변이 #1을 생성합니다(재귀를 방지하기 위해 생성 함수에서는 보이지 않음).
- cron_impl_2func_651_help 온업데이트(문서,메타) 가 돌연변이를 받으면 제어 문서에 또 다른 돌연변이 #2를 만들어 위의 1을 무한 순환으로 트리거합니다.
Couchbase 릴리스 6.6에서는 실행 중인 타이머 내에서 타이머를 생성할 수 있으므로 헬퍼 함수가 전혀 필요하지 않습니다. 이렇게 하면 필요한 로직이 크게 단순화됩니다. cron 시스템[2].
"cron_impl_2func_651_help"의 유일한 자바스크립트 함수 온업데이트(문서,메타) 는 아래와 같습니다.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 |
함수 온업데이트(doc, 메타) { // 6.5.X 버킷 작업 증가에 대한 수정 만약 (메타.id.startsWith("수정_타이머_스캔_문제:")) 버킷당 원도큐퍼(doc, 메타); 시도 { // 문서에 원하는 값이 있는지 확인 만약 (!doc.유형 || doc.유형 !== "recurring_event" || !doc.활성 || doc.활성 != true) 반환; // 문서에 'action', 'dynamic {}', verbose {}, dynamic.state가 있어야 합니다. 만약 (!doc.액션 || !doc.동적 || !doc.verbose || !doc.동적.상태) 반환; // 대기 중인 프로세스 상태만 '간단한' 시간 동안만 존재합니다. 만약 (doc.동적.상태 !== "pending") 반환; var mid = doc.유형 + '::' + doc.id; // 키 만들기 var newdoc = null; 시도 { // KV에서 현재 버전의 문서를 읽습니다(예: curDoc). newdoc = 크론_bkt[mid]; } catch (e) {} // 6.5 이전 버전에 필요, 순수 6.5 이상 배포는 예외 없이 null을 반환합니다. var 이유 = null; 만약 (!newdoc || newdoc == null) { 이유 = "크론 문서가 누락되었습니다"; } else 만약 (!newdoc.활성) { 이유 = "크론 문서가 활성 = 거짓"; } else 만약 (!newdoc.동적.상태 || newdoc.동적.상태 !== doc.동적.상태) { 이유 = "크론 문서 잘못된 dynamic.state 예상" + doc.동적.상태; } else 만약 (crc64(doc) !== crc64(newdoc)) { 이유 = "크론 문서 변경"; } 만약 (이유 != null) { 만약 (!newdoc || newdoc == null || newdoc.verbose.스케줄러 >= 1) { 로그('온업데이트 도움말: X 때문에 일정이 중지되었습니다. + 이유 + ',', newdoc) 반환; } } newdoc.동적.상태 = "무장"; // cron_bkt[mid] = newdoc; 만약 (!tryBucketKvWriteWithLog('온업데이트 도움말: F', mid, newdoc)) { // cron_bkt[키]에 newdoc을 쓰지 못했습니다. 오류가 기록되었습니다. // 더 이상 할 수 있는 일이 없습니다. 반환; } 만약 (newdoc.verbose.스케줄러 >= 1) { 로그('온업데이트 도움말: A ' + mid + ' 돌연변이 #2를 문서로 변경하여 강제 일정 재 무장 '); } 만약 (newdoc.verbose.스케줄러 >= 3) { 로그('온업데이트 도움말: B ' + mid + ',', newdoc); } } catch (e) { 로그('온업데이트 도움말: E ' + 메타.id + ', 오류 예외:', e); } } 함수 tryBucketKvWriteWithLog(태그, 키, doc) { var 성공 = false; var 시도 = 0; 동안 (시도 < 10) { 시도++; 시도 { // 그렇지 않으면 크론 사이클이 중단되므로 아래 내용이 성공하는 것이 중요합니다. 크론_bkt[키] = doc; 성공 = true; break; } catch (e) { 로그(태그 + ' ' + 키 + ' 경고 KV 업데이트 시도가 실패했습니다. + 시도, e); } } 만약 (!성공) { 로그(태그 + ' ' + +키 + ' FATAL이 KV 크론 주기를 업데이트 할 수 없습니다. + 시도 + ', ' + curDoc.액션); } 반환 성공; } |
도우미 기능에는 몇 가지 유틸리티가 필요합니다.
이러한 유틸리티는 6.5.X 버킷 작업 증가에 대한 수정 사항 모든 vBucket에서 이벤트 타이머가 적시에 실행되도록 하여 시간을 절약할 수 있습니다.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 |
// fixup: addin functon // 6.5.X 버킷 작업 증가에 대한 수정 함수 버킷당 원도큐퍼(doc, 메타) { var crcTable = makeCRC32Table(); // 버킷당 하나의 문서 만들기 var isVerbose = 0; var isMacOS = false; //가 이벤트에 노출된 상수라면 좋을 것 같습니다. var numvbs = 1024; // 기본값은 리눅스/PC 만약 (isMacOS) { numvbs = 64; } var beg = (new 날짜).getTime(); var 결과 = getKeysToCoverAllPartitions(crcTable, "_tmp_vbs:", numvbs); 에 대한 (var vb=0; vb<numvbs; vb++) { // 무차별 대입을 통해 키 접두사를 vBucket에 맞추는 방법 var tst = 결과[vb]; 만약 (isVerbose > 1 || (isVerbose == 1) && (vb < 3 || vb > numvbs -4)) { 로그("KEY: " + tst); } else { 만약 (vb == 5) 콘솔.로그("\t*\n\t*\n\t*"); } // PRIMARY 함수에 대한 변이를 트리거하도록 항목을 업데이트합니다. 크론_bkt[tst] = { "type": "_tmp_vbs", "vb": vb, "ts_millis": beg, "key": tst }; } var 끝 = (new 날짜).getTime(); 로그("primary_bucket 별칭의 각 vBucket에 하나의 문서 시딩(가져온 " + (끝 - beg) + " 밀릴리스)"); } // fixup: addin functon // 6.5.X 버킷 작업 증가에 대한 수정 함수 showHex(n) { 반환 n.toString(16); } // fixup: addin functon // 6.5.X 버킷 작업 증가에 대한 수정 함수 makeCRC32Table() { var crcTable = []; var c; 에 대한(var n =0; n < 256; n++){ c = n; 에 대한(var k =0; k < 8; k++){ c = ((c&1) ? (0xEDB88320 ^ (c >>> 1)) : (c >>> 1)); } crcTable[n] = c; } 반환 crcTable; } // fixup: addin functon // 6.5.X 버킷 작업 증가에 대한 수정 함수 crc32(crcTable,str) { var crc = 0 ^ (-1); 에 대한 (var i = 0; i < str.길이; i++ ) { crc = (crc >>> 8) ^ crcTable[(crc ^ str.charCodeAt(i)) & 0xFF]; } 반환 (crc ^ (-1)) >>> 0; } // fixup: addin functon // 6.5.X 버킷 작업 증가에 대한 수정 함수 getKeysToCoverAllPartitions(crcTable,키 접두사,파티션 수) { var 결과 = []; var 남은 = 파티션 수; 에 대한 (var i = 0; 남은 > 0; i++) { var 키 = 키 접두사 + i; var rv = (crc32(crcTable,키) >> 16) & 0x7fff; var 실제 파티션 = rv & 파티션 수 - 1; 만약 (!결과[실제 파티션] || 결과[실제 파티션] === 정의되지 않음) { 결과[실제 파티션] = 키; 남은--; } } 반환 결과; } |
이제 두 개의 이벤트 함수를 배포해 보겠습니다.
많은 코드와 초기 스케줄러의 디자인을 검토했으니 이제 모든 것이 함께 작동하는 것을 확인할 차례입니다.
이 예제에서는 버킷 세 개를 기억하세요. travel-sample (샘플 기본 데이터 세트), 메타데이터(메타데이터 버킷은 Eventing을 위한 스크래치 패드이며 다른 Eventing 함수와 공유할 수 있음), 마지막으로 크론데이터 (크론 일정을 보관하는). 그리고 여행 샘플 버킷의 크기는 100MB이고 나머지 두 버킷은 메타데이터 그리고 크론데이터 의 크기는 모두 200MB여야 하며 '전제 조건'의 지침에 따라 이미 존재해야 합니다.
- 에 액세스하여 현재 버킷 구성을 확인합니다. Couchbase 웹 콘솔 > 버킷 페이지로 이동합니다:

이벤트 기능 배포하기 "크론_임플_2func_651'를 입력하면 두 가지 방법 중 하나를 따를 수 있습니다:
- 기본 복잡도, 방법 #1 다운로드/가져오기
- 중간 복잡도, 방법 #2 수동으로 함수 추가, 잘라내어 붙여넣기 자바스크립트
방법 #1 다운로드/가져오기
첫 번째 함수 "cron_impl_2func_651" 가져오기
필요한 모든 설정이 포함된 첫 번째 이벤트 함수를 다운로드하고 다음 링크를 마우스 오른쪽 버튼으로 클릭한 후 다음을 선택합니다. 다른 이름으로 링크 저장 를 클릭하여 파일을 다운로드합니다. cron_impl_2func_651.json 를 로컬 파일 시스템에 저장합니다.
에서 Couchbase 웹 콘솔 > 이벤트 설정 페이지에서 가져오기를 클릭하고 파일로 이동합니다. cron_impl_2func_651.json를 클릭하고 선택하여 엽니다. 를 선택하고 기능 추가 대화 상자가 나타납니다.
에서 기능 추가 대화 상자에서 개별 함수 요소에 대해 아래 정보를 제공합니다. JSON 파일에 주목하세요. cron_impl_2func_651.json 는 이 예제에 대한 모든 설정을 올바르게 사전 구성합니다:
- 의 경우 소스 버킷 드롭다운에서 다음과 같이 설정되어 있는지 확인합니다. 크론데이터.
- 의 경우 메타데이터 버킷 드롭다운에서 다음과 같이 설정되어 있는지 확인합니다. 메타데이터.
- 다음을 확인합니다. 크론_임플_2func_651 에서 생성하는 함수의 이름입니다. 함수 이름 텍스트 상자.
- [선택 단계] 텍스트 입력 크론 같은 스케줄러 파트 1에서 설명 텍스트 상자.
- 의 경우 설정 옵션을 사용하지 않으려면 기본값을 사용합니다.
- 의 경우 바인딩 옵션을 사용하여 두 개의 바인딩이 있는지 확인합니다.
- 바인딩의 경우, "버킷 별칭"은 다음을 지정합니다. 크론_bkt 을 버킷의 "별칭 이름"으로 입력하고, 버킷의 "별칭 이름"으로
크론데이터 를 연결된 버킷으로 설정하고 모드는 "읽기 및 쓰기"로 설정해야 합니다. - 바인딩의 경우, "버킷 별칭"은 다음을 지정합니다. ts_bkt 을 버킷의 "별칭 이름"으로 입력하고, 버킷의 "별칭 이름"으로
여행 샘플 를 연결된 버킷으로 설정하고 모드는 "읽기 및 쓰기"로 설정해야 합니다. - 대화 상자의 설정은 다음과 같이 표시되어야 합니다:

- 함수 추가 대화 상자에서 필요한 모든 정보를 확인한 후 다음을 클릭합니다: 코드 추가를 클릭합니다. 다음 크론_임플_2func_651 대화 상자가 나타납니다(자바스크립트 코드가 미리 로드된 상태).

- 이벤트 화면으로 돌아가려면 '< 이벤트로 돌아가기' 링크(편집기 아래)를 클릭하거나 이벤트 탭을 클릭합니다.
두 번째 함수 "cron_impl_2func_651_help" 가져오기
필요한 모든 설정이 포함된 두 번째 이벤트 기능을 다운로드하고 다음 링크를 마우스 오른쪽 버튼으로 클릭한 후 다음을 선택합니다. 다른 이름으로 링크 저장 를 클릭하여 파일을 다운로드합니다. 크론_impl_2func_651_help.json 를 로컬 파일 시스템에 저장합니다.
에서 Couchbase 웹 콘솔 > 이벤트 설정 페이지에서 가져오기를 클릭하고 파일로 이동합니다. 크론_impl_2func_651_help.json를 클릭하고 선택하여 엽니다. 를 선택하고 기능 추가 대화 상자가 나타납니다.
에서 기능 추가 대화 상자에서 개별 함수 요소에 대해 아래 정보를 제공합니다. JSON 파일에 주목하세요. 크론_impl_2func_651_help.json 는 이 예제에 대한 모든 설정을 올바르게 사전 구성합니다:
- 의 경우 소스 버킷 드롭다운에서 다음과 같이 설정되어 있는지 확인합니다. 크론데이터.
- 의 경우 메타데이터 버킷 드롭다운에서 다음과 같이 설정되어 있는지 확인합니다. 메타데이터.
- 다음을 확인합니다. cron_impl_2func_651_help 에서 생성하는 함수의 이름입니다. 함수 이름 텍스트 상자.
- [선택 단계] 텍스트 입력 크론과 같은 스케줄러 도우미 파트 1에서 설명 텍스트 상자.
- 의 경우 설정 옵션을 사용하지 않으려면 기본값을 사용합니다.
- 의 경우 바인딩 옵션을 선택하면 바인딩이 하나만 존재하는지 확인합니다.
- 바인딩의 경우, "버킷 별칭"은 다음을 지정합니다. 크론_bkt 을 버킷의 "별칭 이름"으로 입력하고, 버킷의 "별칭 이름"으로
크론데이터 를 연결된 버킷으로 설정하고 모드는 "읽기 및 쓰기"로 설정해야 합니다. - 대화 상자의 설정은 다음과 같이 표시되어야 합니다:

- 함수 추가 대화 상자에서 필요한 모든 정보를 확인한 후 다음을 클릭합니다: 코드 추가를 클릭합니다. 다음 cron_impl_2func_651_help 대화 상자가 나타납니다(자바스크립트 코드가 미리 로드된 상태).

- 이벤트 화면으로 돌아가려면 '< 이벤트로 돌아가기' 링크(편집기 아래)를 클릭하거나 이벤트 탭을 클릭합니다.
방법 #2 수동으로 함수 추가, 자바스크립트 잘라내기 및 붙여넣기
수동으로 "cron_impl_2func_651" 만들기
에서 첫 번째 이벤트 함수를 추가하려면 Couchbase 웹 콘솔 > 이벤트 설정 페이지에서 기능 추가를 클릭하여 새 함수를 추가합니다. 의 기능 추가 대화 상자가 나타납니다.
에서 기능 추가 대화 상자에서 개별 함수 요소에 대해 아래 정보를 제공합니다:
- 의 경우 소스 버킷 드롭다운에서 크론데이터.
- 의 경우 메타데이터 버킷 드롭다운에서 메타데이터.
- 만들기 크론_임플_2func_651 에서 생성하는 함수의 이름입니다. 함수 이름 텍스트 상자.
- [선택 단계] 텍스트 입력 크론 같은 스케줄러 파트 1에서 설명 텍스트 상자.
- 의 경우 설정 옵션을 사용하지 않으려면 기본값을 사용합니다.
- 의 경우 바인딩 옵션을 사용하여 두 개의 바인딩을 만듭니다:
- 바인딩의 경우, "버킷 별칭"은 다음을 지정합니다. 크론_bkt 을 버킷의 "별칭 이름"으로 입력하고, 버킷의 "별칭 이름"으로
크론데이터 를 연결된 버킷으로 설정하고 모드는 "읽기 및 쓰기"로 설정해야 합니다. - 바인딩의 경우, "버킷 별칭"은 다음을 지정합니다. ts_bkt 을 버킷의 "별칭 이름"으로 입력하고, 버킷의 "별칭 이름"으로
여행 샘플 를 연결된 버킷으로 설정하고 모드는 "읽기 및 쓰기"로 설정해야 합니다. - 설정을 구성한 후 대화 상자는 다음과 같이 표시됩니다:

- 에 필요한 모든 정보를 제공한 후 기능 추가 대화 상자에서 다음: 코드 추가. . 크론_임플_2func_651 대화 상자가 나타납니다. 그러면 크론_임플_2func_651 대화 상자에는 처음에 플레이스홀더 코드 블록이 포함됩니다. 실제 크론_임플_2func_651 코드를 추가합니다.

- 다음 이벤트 함수 자바스크립트 소스(618줄)를 복사하여 다음 플레이스홀더 코드 블록에 붙여넣습니다. 크론_임플_2func_651
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618/*함수 "크론_임플_2func_651"에는 "크론_임플_2func_651_help"도 필요합니다.이벤트 발생을 사용하여 기본 크론 시스템을 만들면 반복 함수가 다음에서 활동을 실행할 수 있습니다.매일, 매시간, 분, 30초, 15초 단위로 지정된 시간을 입력합니다. '크론데이터'라는 버킷을 사용합니다.유형 = "recurring_event"의 제어 문서를 하나 이상 저장할 수 있는 'cron_bkt'로 별칭을 지정합니다.다음 타이머 사용은 Couchbase 버전 6.5 및 6.5.1에서 안정적으로 작동하지 않습니다.a) 타이머의 콜백 내에서 이벤트 타이머 예약하기b) 아이디로 기존 타이머 덮어쓰기또한 타이머를 취소하는 기능은 카우치베이스 버전 6.5 및 6.5.1에 존재하지 않습니다.이 예제에서는 반복되는 '정적' 캐시 문서를 빌드하는 실제 사용자 함수 하나를 제공합니다.버킷 `travel-sample`에서 N1QL 쿼리를 통해 결과를 다시 `travel-sample`에 저장합니다.알라이 'ts_bkt'. 이 자바스크립트 함수는 doCronActionA()이며, 두 개의 자리 표시자를 제공합니다.doCronActionB() 및 doCronActionC()를 사용하여 추가 실험을 할 수 있습니다.테스트 문서:{"type":"recurring_event", // KEY는 <>::<>가 됩니다."id":1, //"hour":14, // 트리거할 0-23, *, *2X, *4X의 하루 중 시간입니다."min":54, // 0-59시간의 분, *, *2X, *4X를 트리거합니다."action":"doCronActionA", // 트리거에서 실행할 함수"active":false, // 이 스케줄을 활성화 또는 비활성화하려면 플래그를 지정합니다."verbose" : {"user_func":2, // 액션 로직의 로깅 수준 : 0=없음 등 등"scheduler":3 // 크론 로직의 로깅 수준 : 0=없음 등 등},"dynamic" : {"state":"arm", // "pending" 이외의 값으로 스케줄을 시작하면 "arm"|"rearm"|"pending" 상태가 됩니다."next_sched": 0, // 에포크 이후 다음 원하는 스케줄까지의 시간(초)"prev_sched": 0, // 이전 스케줄의 에포크 이후 시간(초)"prev_time": 0, // 이전 스케줄 실제 실행 시간의 에포크 이후 시간(초)"prev_delay": 0, // 타이머가 스케줄에서 지연된 시간(초)"prev_atime": 0 // 사용자 '액션'에 걸린 시간(초)}}INSERT INTO `crondata` (KEY,VALUE) VALUES ("recurring_event::1",{"유형":"반복_이벤트","id":1,"시간":14,"min":54,"action":"doCronActionA","verbose" : {"user_func":2,"스케줄러":3},"active":false,"dynamic" : {"state": "arm","next_sched": 0,"prev_sched": 0,"prev_time": 0,"prev_delay": 0,"prev_atime": 0}});버보스{}와 다이내믹{}는 이 메인 이벤트에 의해 자동으로 생성되므로 생략할 수 있습니다.Function "cron_impl_2func_651". verbose{}가 누락되면 로깅 수준은 기본값으로verbose" : { "user_func":1, "scheduler":1 }INSERT INTO `crondata` (KEY,VALUE) VALUES ("recurring_event::1",{"유형":"반복_이벤트","id":1,"시간":14,"min":54,"action":"doCronActionA","active":false});N1QL : 키를 지정하지 않고 데이터를 쿼리할 수 있는 인덱스 만들기크론데이터`에 기본 인덱스를 생성합니다;N1QL: 일정의 설정 확인 또는 검사SELECT * FROM `crondata` WHERE type="recurring_event";N1QL: 암 또는 설정 활성화UPDATE `crondata` SET active = true WHERE type="recurring_event" AND id=1 ;N1QL: 무장 해제 또는 비활성 설정UPDATE `crondata` SET active = false WHERE type="recurring_event" AND id=1 ;N1QL: 트리거 시간 조정UPDATE `crondata` SET hour = 11, min = 30 WHERE type="recurring_event" AND id=1 ;N1QL: 로깅 조정UPDATE `crondata` SET verbose.user_func = 1, verbose.scheduler = 0 WHERE type="recurring_event" AND id=1 ;N1QL: 일정 삭제DELETE FROM `crondata` WHERE type="recurring_event" AND id=1 ;액션 필드는 이 이벤트 함수에서 '존재해야' 하는 것이 중요하며, 어떤 것이든 될 수 있습니다.자바스크립트 이름(예: MyFunc)과 doCronActionA(doc) 예제처럼 구현해야 합니다.doc는 별칭 버킷에서 읽은 유형 = 'recurring_event'의 현재 활성 항목이 됩니다.타이머가 실행될 때 'cron_bkt'를 반환합니다. 액션 자바스크립트 함수는 참 또는또는 로깅 목적으로 잘못 사용되었습니다. 작업이 존재하지 않으면 오류 및 경고입니다.가 기록되고 타이머가 비활성화됩니다.Couchbase 버전 6.5 이상에서 새로운 크론과 같은 일일 기능을 추가하려면 활성 핸들러를 일시 중지하면 됩니다.새 함수 doCronActionB(doc) {...}를 삽입한 다음 이벤트 발생 핸들러를 재개합니다. 좋은타이머가 실행될 경우 기능이 일시 중지되더라도 손실되지 않습니다.기능을 다시 시작하면 다음 사용 가능한 시간 슬롯에서 처리됩니다.제어 구조를 변경하면 새 반복 일정 또는 타이머가 생성되고 반복 일정 또는 타이머가 취소됩니다.현재 이전 스케줄의 상세도 수준을 변경합니다. 이전 타이머는계속 실행되지만 실행되면 현재 제어 구조에 대한 체크섬을 수행합니다.를 전달된 컨텍스트와 비교하여 다르면 콜백은 이전 일정을 무시합니다.일정 검색이 만료된 경우 즉시 처리하도록 이 로직을 변경할 수 있습니다.문자열 "OnUpdate U"를 아래 코드에 추가합니다.*/// ==================/* 하루에 한 번, 매시간, 매분 등 원하는 대로 사용자 함수를 실행하도록 요청하세요.함수 doCronActionA(doc) {시도 {// 문서에 원하는 값이 있는지 확인만약 (!doc.유형 || doc.유형 !== "recurring_event" || !doc.활성 || doc.활성 !== true) 반환;만약 (doc.verbose.user_func >= 1)로그(doc.액션 + ' 에 의해 제어되는 사용자 작업 + doc.유형 + '::' + doc.id);// 이것은 6.5 N1QL 쿼리입니다(6.5 이전 GA에서는 사용할 수 없는 기능).// SELECT 문을 실행하여 임베디드 N1QL 이터레이터를 생성하여 다음을 가져옵니다.// 국가별 항공사 수입니다. 새 문서를 만들어 KV에 작성합니다.// 반복자를 사용하여 반복자의 결과를 나타내는 KV 문서를 생성합니다.// 길이가 긴 임베디드 N1QL 쿼리를 작성하고 이를 다시 KV에 쓰는 것은// 하루에 한 번 '빠르게' 읽을 수 있도록 계산을 최신으로 업데이트합니다.// 다른 이벤트 함수, 다른 카우치베이스 서비스 또는 SDK에 의해 호출됩니다.// 1분에 백만 개의 문서가 있다고 가정했을 때 정말 N1QL을 사용해야 할까요?//를 사용하여 100만 개의 모든 문서에 대해 거의 정적인 것을 다시 계산합니다.// 물론 아니므로 Eventing으로 읽을 수 있는 중간 값을 만듭니다.// 단일 '경량' KV 읽기를 통해 사용됩니다.var q_iter = 선택 국가,카운트( * ) cntFROM `여행-샘플`어디 `유형` = '항공사'그룹 BY 국가;// 결과 집합을 반복하고 '누적' 맵을 업데이트합니다.var 누적 = {};var idx = 0;에 대한 (var val 의 q_iter) {만약 (doc.verbose.user_func >= 2)로그(doc.액션 + ' N1QL idx ' + idx + ', 국가 ' + val.국가 + " cnt " + val.cnt);누적[val.국가] = val.cnt;idx++;}// 임베디드 N1QL 이터레이터 닫기q_iter.닫기();// 이제 HARD 길이의 임베디드 N1QL을 나타내는 캐시된 KV 문서를 만들어 보겠습니다.// 쿼리하고 KV에 다시 쓰려면 KEY와 유형 및 ID가 필요합니다.// 'travel-sample' 버킷에 넣습니다.var cachedoc = {};cachedoc.유형 = "cron_cache";cachedoc.id = "항공사별_국가";cachedoc.날짜 = new 날짜();cachedoc.데이터 = 누적;var ckey = cachedoc.유형 + '::' + cachedoc.id;ts_bkt[ckey] = cachedoc;만약 (doc.verbose.user_func >= 2) {로그(doc.액션 + ' 키로 KV로 업서트 ' + ckey + ' 캐시독 ', cachedoc);}} catch (e) {로그(doc.액션 + ' 오류 예외:', e);반환 false;}반환 true;}함수 doCronActionB(doc) {시도 {// 문서에 원하는 값이 있는지 확인만약 (doc.유형 !== "recurring_event" || doc.활성 !== true) 반환;만약 (doc.verbose.user_func >= 1)로그(doc.액션 + ' 에 의해 제어되는 사용자 작업 + doc.유형 + '::' + doc.id);// 여기에 당신의 로직} catch (e) {로그(doc.액션 + ' 오류 예외:', e);반환 false;}반환 true;}함수 doCronActionC(doc) {시도 {// 문서에 원하는 값이 있는지 확인만약 (doc.유형 !== "recurring_event" || doc.활성 !== true) 반환;만약 (doc.verbose.user_func >= 1)로그(doc.액션 + ' 에 의해 제어되는 사용자 작업 + doc.유형 + '::' + doc.id);// 여기에 당신의 로직} catch (e) {로그(doc.액션 + ' 오류 예외:', e);반환 false;}반환 true;}/* 하루에 한 번, 시간, 분 단위로 실행하는 최종 사용자 기능 - 위에 원하는 모든 것 */// ==================// fixup: addin functon함수 noopTimer(컨텍스트) {// 6.5.X 버킷 작업 증가에 대한 수정시도 {만약 (컨텍스트.유형 === "_tmp_vbs" && 컨텍스트.vb === 0) {// log("noopTimer 타이머 실행 중, vBucket 0에 대해서만 인쇄");}} catch (e) {로그("콜백의 온업데이트 예외 noopTimer:", e);}}// fixup: addin functon함수 재 무장 타이머(컨텍스트) {// 6.5.X 버킷 작업 증가에 대한 수정시도 {만약 (컨텍스트.유형 === "_tmp_vbs" && 컨텍스트.vb === 0) {// 헬퍼_버킷의 모든 문서를 업데이트/터치하면 헬퍼 함수는 다음과 같이 실행됩니다.// 유형 == vbs_seed의 1024(MacOS에서는 64)를 모두 변경하여 리커링 주기를 생성합니다.// log("noopTimer 타이머가 1024개의 vBucket을 모두 실행하고, vb 0만 로깅", context);// HELPER 함수를 다시 무장시키기 위한 돌연변이 생성: fix_scan_issue// 이 함수에 대한 새로운 돌연변이를 만듭니다.var cur = 크론_bkt[컨텍스트.키];만약 (cur && cur.ts_millis === 컨텍스트.ts_millis) {// log("0에 대해서만 헬퍼_버킷 별칭으로 fix_timer_scan_issue::1을 업데이트하는 rearmTimer 업데이트");var 지금 = new 날짜();크론_bkt["수정_타이머_스캔_이슈::1"] = { "last_update": 지금 };} else {// 아니요 타이머 주기가 여러 번 있었으니 이 타이머만 조용히 멈추게 하세요.}}} catch (e) {로그("콜백 재암호화 타이머의 온업데이트 예외:", e);}}// fixup: addin functon함수 genNoopTimers(doc, 메타, 초) {// 6.5.X 버킷 작업 증가에 대한 수정시도 {// 중복되지만 안전하게 플레이만약 (doc.유형 === "_tmp_vbs") {// 다른 함수를 사용하고 있기 때문에 모든 vBuckets의 타이머가 즉시 실행됩니다(최대 15초까지 걸릴 수 있음).// 교차 버킷 재귀를 사용하여 모든 타이머를 반복 방식으로 재장전하는 경우 최소 40초의 지연이 추가됩니다.createTimer(noopTimer, new 날짜(), null, doc);만약 (doc.vb === 0) {// 헬퍼_버킷의 모든 문서를 업데이트/터치하면 헬퍼 함수는 다음과 같이 실행됩니다.// 유형 == vbs_seed의 1024(MacOS에서는 64)를 모두 변경하여 리커링 주기를 생성합니다.// log("noopTimer 타이머가 1024개의 vBucket을 모두 실행하고, vb 0만 로깅", context);// HELPER 함수를 다시 무장시키기 위한 돌연변이 생성: fix_scan_issue// 이 함수에 대한 새로운 돌연변이를 만듭니다.// log("genNoopTimers가 타이머를 재장전하여 fix_timer_scan_issue::1을 만듭니다.");createTimer(재 무장 타이머, new 날짜(new 날짜().getTime() + 초 * 1000), null, doc);}}} catch (e) {로그("genNoopTimers의 온업데이트 예외:", e);}}함수 온업데이트(doc, 메타) {// 6.5.X 버킷 작업 증가에 대한 수정만약 (doc.유형 === "_tmp_vbs") genNoopTimers(doc, 메타, 30);만약 (!크론_bkt["수정_타이머_스캔_이슈::1"]) {크론_bkt["수정_타이머_스캔_이슈::1"] = {};}시도 {// 추가 분석이 필요한 경우 활성 recurring_event에서만 트리거합니다.만약 (doc.유형 !== "recurring_event" || doc.활성 !== true) 반환;var update_doc = false;만약 (!doc.동적) {// 기본값으로 doc.dynamic이 누락된 경우 추가doc.동적 = {"state": "arm","next_sched": 0,"prev_sched": 0,"prev_time": 0,"prev_delay": 0,"prev_atime": 0};// 다음 일정이 잡히면 문서를 업데이트해야 합니다.update_doc = true;}만약 (!doc.verbose) {// 기본값으로 doc.dynamic이 누락된 경우 추가doc.verbose = {"user_func": 1,"스케줄러": 1};// 다음 일정이 잡히면 문서를 업데이트해야 합니다.update_doc = true;}// dynamic.state 보류 중 처리하지 않음만약 (!doc.동적 || !doc.동적.상태 || doc.동적.상태 === "pending") 반환;var mid = doc.유형 + "::" + doc.id; // 이것은 meta.id 또는 KEY와 동일합니다.var 시간 = doc.시간;var 분 = doc.분;// 자바스크립트 함수가 존재하는지 확인합니다. 평가는 일반적인// 유틸리티 함수는 RecurringCallback과 공유됩니다.만약 (!검증 함수 존재 여부 확인 값(doc, mid)) {// doc.action이 존재하지 않는 경우 이미 문제를 기록했습니다.반환;}// 다음 유효한 실행 시간 가져오기var 날짜_타이머 = getNextRecurringDate(시간, 분);var next_sched = 수학.라운드(날짜_타이머.getTime() / 1000);만약 (!update_doc && next_sched !== doc.동적.next_sched) {// 다음_sched는 도우미 애플리케이션의 설정과 동일해야 합니다.// 배포/배포를 취소하거나 일시 중지/재시작하는 경우 다음 시간 슬롯으로 일정을 다시 잡아야 할 수 있습니다.로그('온업데이트 유 ' + mid + ' 계산된 next_sched !== doc.dynamic.next_sched, 델타 ' +(next_sched - doc.동적.next_sched) + ', reschedule');update_doc = true;}만약 (update_doc) {// 이 돌연변이는 재귀적이며 억제될 것이며, 동적 구조를 갖도록 합니다.doc.동적.next_sched = next_sched;// 함수를 호출하여 리소스 문제가 있는 경우 트래핑하고 다시 시도합니다.// cron_bkt[mid] = doc;만약 (!tryBucketKvWriteWithLog('OnUpdate F', mid, doc)) {// 크론_bkt[키]에 문서 쓰기에 실패했습니다. 오류가 기록되었습니다.// 더 이상 할 수 있는 일이 없습니다.반환;}}// 이벤트 타이머 예약하기var timer_id = createTimer(콜백, 날짜_타이머, null, doc);만약 (doc.verbose.스케줄러 >= 1) {로그('온업데이트 A ' + mid + ' RCV 돌연변이(초기 또는 재 무장) 스케줄 타이머의 ' +toLocalISOTime(날짜_타이머));}만약 (doc.verbose.스케줄러 >= 2) {로그('온업데이트 B ' + mid + ' 반복 타이머가 생성되었습니다, timer_id ' + timer_id);}} catch (e) {로그('온업데이트 E ' + 메타.id + ', 오류 예외:', e);}}함수 getNextRecurringDate(hour_str, min_str) {// 참고 자바스크립트 날짜는 밀리초 단위입니다.var date_now = new 날짜();var date_ret = new 날짜();var 시간;var 분;시도 {시간 = parseInt(hour_str);} catch (e) {}시도 {분 = parseInt(min_str);} catch (e) {}// 이것은 약간의 확장이 있는 간단한 부분적인 '크론탭' 구문일 뿐입니다.// 하루에 한 번, 한 시간에 한 번, 1분에 한 번 허용합니다. 또한 몇 가지 비표준// 구문을 사용하여 1분에 두 번 또는 1분에 네 번 실행할 수 있는 기능을 제공합니다.만약 (hour_str === '*4X' && min_str === '*4X') {// 15초에 한 번 또는 1분에 네 번date_ret.설정 밀리초(0);date_ret.setSeconds(15);동안 (date_ret.getTime() < date_now.getTime()) {date_ret.setSeconds(date_ret.getSeconds() + 15);}반환 date_ret;} else만약 (hour_str === '*2X' && min_str === '*2X') {// 30초에 한 번 또는 1분에 두 번date_ret.설정 밀리초(0);date_ret.setSeconds(30);동안 (date_ret.getTime() < date_now.getTime()) {date_ret.setSeconds(date_ret.getSeconds() + 30);}반환 date_ret;} else만약 (hour_str === '*' && min_str === '*') {// 1분에 한 번date_ret.설정 밀리초(0);date_ret.setSeconds(0);date_ret.setMinutes(date_ret.getMinutes() + 1);} else만약 (hour_str !== '*' && isNaN(시간) === false && min_str === '*') {// 지정된 시간 동안 1분에 한 번만date_ret.설정 밀리초(0);date_ret.setSeconds(0);date_ret.setMinutes(date_ret.getMinutes() + 1);만약 (date_ret.getTime() < date_now.getTime()) {date_ret.setHours(시간);}만약 (date_ret.getTime() > date_now.getTime()) {date_ret.setDate(date_ret.getDate() + 1);date_ret.setSeconds(0);date_ret.setMinutes(0);date_ret.setHours(시간);}} else만약 (hour_str === '*' && min_str !== '*' && isNaN(분) === false) {// 한 시간에 한 번씩 주어진 분에date_ret.설정 밀리초(0);date_ret.setSeconds(0);date_ret.setMinutes(분);// 다음 시간 예약date_ret.setHours(date_ret.getHours() + 1);} else만약 (isNaN(시간) === false && isNaN(분) === false) {// 하루에 한 번 주어진 시간과 주어진 분 동안date_ret.설정 밀리초(0);date_ret.setSeconds(0);date_ret.setMinutes(분);date_ret.setHours(시간);만약 (date_ret.getTime() < date_now.getTime()) {// 내일 일정date_ret.setDate(date_ret.getDate() + 1);}} else {로그('getNextRecurringDate 잘못된 입력 시간_str <' +hour_str + '> min_str <' + min_str + '>');throw new 오류('getNextRecurringDate 잘못된 입력 시간_str <' +hour_str + '> min_str <' + min_str + '>');반환 null;}반환 date_ret;}함수 검증 함수 존재 여부 확인 값(curDoc, id) {var 결과 = false;시도 {// 이것이 누락된 경우 잘못된 반환 결과인지 함수 확인결과 = 평가("typeof" + curDoc.액션 + " === 'function';");만약 (결과 === false) {만약 (curDoc.verbose.스케줄러 >= 1)로그("경고/비활성화(조치 없음 및 재암호화 없음)의 '조치'가 필요하기 때문에" +curDoc.액션 + "(문서)가 존재하지 않습니다, ID는", id);반환 결과;}} catch (e) {로그('verifyFunctionExistsViaEval 오류 예외:', e);}반환 결과;}함수 숫자 고정(숫자, 정밀도) {var 멀티 = 수학.pow(10, 정밀도);반환 수학.라운드((숫자 * 멀티).toFixed(정밀도 + 1)) / 멀티;}함수 toLocalISOTime(d) {var tzoffset = (new 날짜()).getTimeZoneOffset() * 60000; //오프셋(밀리초)반환 (new 날짜(d.getTime() - tzoffset)).toISOString().슬라이스(0, -1);}함수 tryBucketKvWriteWithLog(태그, 키, doc) {var 성공 = false;var 시도 = 0;동안 (시도 < 10) {시도++;시도 {// 그렇지 않으면 크론 사이클이 중단되므로 아래 내용이 성공하는 것이 중요합니다.크론_bkt[키] = doc;성공 = true;break;} catch (e) {로그(태그 + ' ' + 키 + ' 경고 KV 업데이트 시도가 실패했습니다. + 시도, e);}}만약 (!성공) {로그(태그 + ' ' + +키 + ' FATAL이 KV 크론 주기를 업데이트 할 수 없습니다. + 시도 + ', ' + curDoc.액션);}반환 성공;}함수 콜백(doc) {시도 {var fired_at = new 날짜();// 추가 분석이 필요한 경우 활성 상태인 recurring_event에서만 트리거합니다.만약 (doc.유형 !== "recurring_event") 반환;// 문서에 'action', 'dynamic {}', verbose {}, dynamic.state가 있어야 합니다.만약 (!doc.액션 || !doc.동적 || !doc.verbose || !doc.동적.상태) 반환;// 모든 doc.dynamic.state BUT 보류 중 처리만약 (doc.동적.상태 === "pending") 반환;// ==================// 아직 활성 상태인지 확인// KV에서 'doc'가 여전히 존재하고 활성 상태인지 확인합니다.// 반환하여 동작을 건너뛰고 타이머를 재장전하지 않습니다. 참고 `travel-sample`은// 맵 'cron_bkt'에 별칭 지정var mid = doc.유형 + '::' + doc.id; // 키 만들기var curDoc = null;시도 {// KV에서 현재 버전의 문서를 읽습니다(예: curDoc).curDoc = 크론_bkt[mid];} catch (e) {} // 6.5 이전 버전에 필요, 순수 6.5 이상 배포는 예외 없이 null을 반환합니다.var 이유 = null;만약 (!curDoc || curDoc === null) {이유 = "크론 문서가 누락되었습니다";} else만약 (!curDoc.활성) {이유 = "크론 문서가 활성 = 거짓";} else만약 (!curDoc.동적.상태 || curDoc.동적.상태 !== doc.동적.상태) {이유 = "크론 문서 잘못된 dynamic.state 예상" + doc.동적.상태;} else만약 (crc64(doc) !== crc64(curDoc)) {이유 = "크론 문서 변경";}만약 (이유 !== null) {만약 (!curDoc || curDoc === null || curDoc.verbose.스케줄러 >= 1) {로그('콜백 X ' + mid + " 때문에 이 타이머의 일정을 무시/중지합니다." + 이유);}만약 (!curDoc || curDoc === null || curDoc.verbose.스케줄러 >= 4) {로그('콜백 Y ' + mid + ' 타이머 문서', doc);로그('콜백 Z ' + mid + ' KV curDoc', curDoc);}반환;}// ==================// 사용자 루틴이 존재하는지 확인하고 존재하는 경우 평가합니다.// curDoc.action에 "doCronActionA"와 같은 함수가 포함되어 있고// 이 핸들러는 "doCronActionA(doc)"와 같습니다. 아래에서는 최종 사용자가 다음과 같아야 하므로 curDoc을 사용합니다.// 평가된 자바스크립트 함수를 변경할 수 있습니다. 두(2) 개의 평가를 실행합니다.// 먼저 자바스크립트 함수가 존재하는지 확인합니다. 평가는 일반적인// 유틸리티 함수는 RecurringCallback과 공유됩니다.만약 (!검증 함수 존재 여부 확인 값(curDoc, mid)) {// curDoc.action이 존재하지 않는 경우 이미 문제를 기록했습니다.반환;}// 두 번째 평가 정의된 함수를 실행하는 사용자 함수를 실행하고 처리합니다.// curDoc의 인수와 함께var beg_act = new 날짜();var 결과 = null;평가("결과 = " + curDoc.액션 + "(curDoc);");var end_act = new 날짜();var atime_ms = end_act.getTime() - beg_act.getTime();만약 (curDoc.verbose.스케줄러 >= 2)로그('콜백 R ' + mid + ' 조치 취함 ' + 숫자 고정((atime_ms / 1000), 3) +' 초, 반환됨 ' + 결과);// ==================// 다음 번에 계산하고 헬퍼 함수에 대한 제어 문서를 변경합니다.// 이 함수의 OnUpdate가 다음을 선택하도록 또 다른 변이를 생성합니다.// 타이머를 생성합니다(MB-38554 문제 방지).var 시간 = curDoc.시간;var 분 = curDoc.분;var 날짜_타이머 = getNextRecurringDate(시간, 분);curDoc.동적.이전_지연 =숫자 고정(((fired_at.getTime() / 1000) - curDoc.동적.next_sched), 3);curDoc.동적.prev_sched = curDoc.동적.next_sched;curDoc.동적.이전 시간 = 수학.라운드(fired_at.getTime() / 1000);curDoc.동적.이전 시간 = 숫자 고정((atime_ms / 1000), 3);curDoc.동적.상태 = "pending";curDoc.동적.next_sched = 수학.라운드(날짜_타이머.getTime() / 1000);// 함수를 호출하여 리소스 문제가 있는 경우 트래핑하고 다시 시도합니다.// cron_bkt[mid] = curDoc;만약 (!tryBucketKvWriteWithLog('콜백 F', mid, curDoc)) {// curDoc을 cron_bkt[key]에 쓰지 못했습니다. 오류가 기록되었습니다.// 더 이상 할 수 있는 일이 없습니다.반환;}만약 (curDoc.verbose.스케줄러 >= 1) {로그('콜백 A ' + mid + ' 유전자 돌연변이 #1을 문서로 변경하여 ' +toLocalISOTime(날짜_타이머));}만약 (curDoc.verbose.스케줄러 >= 2) {로그('콜백 B ' + mid + ' sched ' + curDoc.동적.prev_sched +', 실제 ' + curDoc.동적.이전 시간 +', 지연 ' + curDoc.동적.이전_지연 +', ' + curDoc.동적.이전 시간);}만약 (curDoc.verbose.스케줄러 >= 3) {로그('콜백 C ' + mid + ' curDoc', curDoc);}} catch (e) {var mid = doc.유형 + '::' + doc.id; // 키 만들기로그('콜백 E ' + mid + ' 오류 예외:', e);}}
- 붙여넣기를 마치면 아래와 같은 화면이 나타납니다:

- 클릭 저장.
- 이벤트 화면으로 돌아가려면 '< 이벤트로 돌아가기' 링크(편집기 아래)를 클릭하거나 이벤트
"cron_impl_2func_651_help"를 수동으로 생성합니다.
에서 두 번째 이벤트 함수를 추가하려면 Couchbase 웹 콘솔 > 이벤트 설정 페이지에서 기능 추가를 클릭하여 새 함수를 추가합니다. 의 기능 추가 대화 상자가 나타납니다.
에서 기능 추가 대화 상자에서 개별 함수 요소에 대해 아래 정보를 제공합니다:
- 의 경우 소스 버킷 드롭다운에서 크론데이터.
- 의 경우 메타데이터 버킷 드롭다운에서 메타데이터.
- 만들기 cron_impl_2func_651_help 에서 생성하는 함수의 이름입니다. 함수 이름 텍스트 상자.
- [선택 단계] 텍스트 입력 크론과 같은 스케줄러 도우미 파트 1에서 설명 텍스트 상자.
- 의 경우 설정 옵션을 사용하지 않으려면 기본값을 사용합니다.
- 의 경우 바인딩 옵션을 사용하여 하나의 바인딩을 만듭니다:
- 바인딩의 경우, "버킷 별칭"은 다음을 지정합니다. 크론_bkt 을 버킷의 "별칭 이름"으로 입력하고, 버킷의 "별칭 이름"으로
크론데이터 를 연결된 버킷으로 설정하고 모드는 "읽기 및 쓰기"로 설정해야 합니다. - 설정을 구성한 후 대화 상자는 다음과 같이 표시됩니다:

- 에 필요한 모든 정보를 제공한 후 기능 추가 대화 상자에서 다음: 코드 추가. . cron_impl_2func_651_help 대화 상자가 나타납니다. 그러면 cron_impl_2func_651_help 대화 상자에는 처음에 플레이스홀더 코드 블록이 포함됩니다. 실제 cron_impl_2func_651_help 코드를 추가합니다.

- 다음 이벤트 함수 자바스크립트 소스(187줄)를 복사하여 다음 플레이스홀더 코드 블록에 붙여넣습니다. cron_impl_2func_651_help
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187/*함수 "cron_impl_2func_651_help"에도 "cron_impl_2func_651"이 필요합니다.테스트 문서:{"type":"recurring_event", // KEY는 <>::<>가 됩니다."id":1, //"hour":14, // 트리거할 0-23, *, *2X, *4X의 하루 중 시간입니다."min":54, // 0-59시간의 분, *, *2X, *4X를 트리거합니다."action":"doCronActionA", // 트리거에서 실행할 함수"active":false, // 이 스케줄을 활성화 또는 비활성화하려면 플래그를 지정합니다."verbose" : {"user_func":2, // 액션 로직의 로깅 수준 : 0=없음 등 등"scheduler":3 // 크론 로직의 로깅 수준 : 0=없음 등 등},"dynamic" : {"state":"arm", // "pending" 이외의 값으로 스케줄을 시작하면 "arm"|"rearm"|"pending" 상태가 됩니다."next_sched": 0, // 에포크 이후 다음 원하는 스케줄까지의 시간(초)"prev_sched": 0, // 이전 스케줄의 에포크 이후 시간(초)"prev_time": 0, // 이전 스케줄 실제 실행 시간의 에포크 이후 시간(초)"prev_delay": 0, // 타이머가 스케줄에서 지연된 시간(초)"prev_atime": 0 // 사용자 '액션'에 걸린 시간(초)}}버보스{} 및 다이내믹{}은 메인 이벤트에 의해 자동 생성되므로 생략할 수 있습니다.Function "cron_impl_2func_651". verbose{}가 누락되면 로깅 수준은 기본값으로verbose" : { "user_func":1, "scheduler":1 }*/함수 tryBucketKvWriteWithLog(태그, 키, doc) {var 성공 = false;var 시도 = 0;동안 (시도 < 10) {시도++;시도 {// 그렇지 않으면 크론 사이클이 중단되므로 아래 내용이 성공하는 것이 중요합니다.크론_bkt[키] = doc;성공 = true;break;} catch (e) {로그(태그 + ' ' + 키 + ' 경고 KV 업데이트 시도가 실패했습니다. + 시도, e);}}만약 (!성공) {로그(태그 + ' ' + +키 + ' FATAL이 KV 크론 주기를 업데이트 할 수 없습니다. + 시도 + ', ' + curDoc.액션);}반환 성공;}함수 온업데이트(doc, 메타) {// 6.5.X 버킷 작업 증가에 대한 수정만약 (메타.id.startsWith("수정_타이머_스캔_문제:")) 버킷당 원도큐퍼(doc, 메타);시도 {// 문서에 원하는 값이 있는지 확인만약 (!doc.유형 || doc.유형 !== "recurring_event" || !doc.활성 || doc.활성 != true) 반환;// 문서에 'action', 'dynamic {}', verbose {}, dynamic.state가 있어야 합니다.만약 (!doc.액션 || !doc.동적 || !doc.verbose || !doc.동적.상태) 반환;// 대기 중인 프로세스 상태만 '간단한' 시간 동안만 존재합니다.만약 (doc.동적.상태 !== "pending") 반환;var mid = doc.유형 + '::' + doc.id; // 키 만들기var newdoc = null;시도 {// KV에서 현재 버전의 문서를 읽습니다(예: curDoc).newdoc = 크론_bkt[mid];} catch (e) {} // 6.5 이전 버전에 필요, 순수 6.5 이상 배포는 예외 없이 null을 반환합니다.var 이유 = null;만약 (!newdoc || newdoc == null) {이유 = "크론 문서가 누락되었습니다";} else만약 (!newdoc.활성) {이유 = "크론 문서가 활성 = 거짓";} else만약 (!newdoc.동적.상태 || newdoc.동적.상태 !== doc.동적.상태) {이유 = "크론 문서 잘못된 dynamic.state 예상" + doc.동적.상태;} else만약 (crc64(doc) !== crc64(newdoc)) {이유 = "크론 문서 변경";}만약 (이유 != null) {만약 (!newdoc || newdoc == null || newdoc.verbose.스케줄러 >= 1) {로그('온업데이트 도움말: X 때문에 일정이 중지되었습니다. + 이유 + ',', newdoc)반환;}}newdoc.동적.상태 = "무장";// cron_bkt[mid] = newdoc;만약 (!tryBucketKvWriteWithLog('온업데이트 도움말: F', mid, newdoc)) {// cron_bkt[키]에 newdoc을 쓰지 못했습니다. 오류가 기록되었습니다.// 더 이상 할 수 있는 일이 없습니다.반환;}만약 (newdoc.verbose.스케줄러 >= 1) {로그('온업데이트 도움말: A ' + mid + ' 돌연변이 #2를 문서로 변경하여 강제 일정 재 무장 ');}만약 (newdoc.verbose.스케줄러 >= 3) {로그('온업데이트 도움말: B ' + mid + ',', newdoc);}} catch (e) {로그('온업데이트 도움말: E ' + 메타.id + ', 오류 예외:', e);}}// fixup: addin functon// 6.5.X 버킷 작업 증가에 대한 수정함수 버킷당 원도큐퍼(doc, 메타) {var crcTable = makeCRC32Table();// 버킷당 하나의 문서 만들기var isVerbose = 0;var isMacOS = false; //가 이벤트에 노출된 상수라면 좋을 것 같습니다.var numvbs = 1024; // 기본값은 리눅스/PC만약 (isMacOS) {numvbs = 64;}var beg = (new 날짜).getTime();var 결과 = getKeysToCoverAllPartitions(crcTable, "_tmp_vbs:", numvbs);에 대한 (var vb=0; vb<numvbs; vb++) {// 무차별 대입을 통해 키 접두사를 vBucket에 맞추는 방법var tst = 결과[vb];만약 (isVerbose > 1 || (isVerbose == 1) && (vb < 3 || vb > numvbs -4)) {로그("KEY: " + tst);} else {만약 (vb == 5) 콘솔.로그("\t*\n\t*\n\t*");}// PRIMARY 함수에 대한 변이를 트리거하도록 항목을 업데이트합니다.크론_bkt[tst] = { "type": "_tmp_vbs", "vb": vb, "ts_millis": beg, "key": tst };}var 끝 = (new 날짜).getTime();로그("primary_bucket 별칭의 각 vBucket에 하나의 문서 시딩(가져온 " + (끝 - beg) + " 밀릴리스)");}// fixup: addin functon// 6.5.X 버킷 작업 증가에 대한 수정함수 showHex(n) {반환 n.toString(16);}// fixup: addin functon// 6.5.X 버킷 작업 증가에 대한 수정함수 makeCRC32Table() {var crcTable = [];var c;에 대한(var n =0; n < 256; n++){c = n;에 대한(var k =0; k < 8; k++){c = ((c&1) ? (0xEDB88320 ^ (c >>> 1)) : (c >>> 1));}crcTable[n] = c;}반환 crcTable;}// fixup: addin functon// 6.5.X 버킷 작업 증가에 대한 수정함수 crc32(crcTable,str) {var crc = 0 ^ (-1);에 대한 (var i = 0; i < str.길이; i++ ) {crc = (crc >>> 8) ^ crcTable[(crc ^ str.charCodeAt(i)) & 0xFF];}반환 (crc ^ (-1)) >>> 0;}// fixup: addin functon// 6.5.X 버킷 작업 증가에 대한 수정함수 getKeysToCoverAllPartitions(crcTable,키 접두사,파티션 수) {var 결과 = [];var 남은 = 파티션 수;에 대한 (var i = 0; 남은 > 0; i++) {var 키 = 키 접두사 + i;var rv = (crc32(crcTable,키) >> 16) & 0x7fff;var 실제 파티션 = rv & 파티션 수 - 1;만약 (!결과[실제 파티션] || 결과[실제 파티션] === 정의되지 않음) {결과[실제 파티션] = 키;남은--;}}반환 결과;}
- 붙여넣기를 마치면 아래와 같은 화면이 나타납니다:

- 클릭 저장.
- 이벤트 화면으로 돌아가려면 '< 이벤트로 돌아가기' 링크(편집기 아래)를 클릭하거나 이벤트
두 가지 기능 배포
이제 이벤트 기능을 시작할 준비가 되었습니다. 에서 Couchbase 웹 콘솔 > 이벤트 설정 화면으로 이동합니다:
- 함수 이름을 클릭합니다. cron_impl_2func_651_help 을 클릭해 함수 컨트롤을 확장하고 노출합니다.

- 클릭 배포.
- 에서 배포 기능 확인 대화 상자에서 "지금부터" 를 피드 경계 옵션에서 선택합니다.

다른 이벤트 함수를 시작해 보겠습니다. 에서 Couchbase 웹 콘솔 > 이벤트 설정 화면으로 이동합니다:
- 함수 이름을 클릭합니다. 크론_임플_2func_651 을 클릭해 함수 컨트롤을 확장하고 노출합니다.

- 클릭 배포.
- 에서 배포 기능 확인 대화 상자에서 "지금부터" 를 피드 경계 옵션에서 선택합니다.

설정 cron 작업을 분당 네(4) 번 실행합니다.
이 시점에서 이벤트 함수는 특히 활성=true 필드가 있는 type="recurring_event"의 모든 문서에 대한 변형을 기다리고 있습니다.
에서 Couchbase 웹 콘솔 > 쿼리 페이지에서 N1QL을 사용하여 '여행 샘플' 버킷에 예약된 작업을 새로 만들겠습니다:
- 다음 N1QL 문을 잘라내어 붙여넣기하여 쿼리 편집기
123456789101112131415삽입 INTO `크론데이터` (KEY,VALUE) 가치 ("RECURRING_EVENT::1",{"type": "recurring_event","id":1,"시간":"*","min":"0","action": "doCronActionA","verbose": {"user_func": 2,"스케줄러": 3},"active": false});
- 클릭 실행

첫 번째 cron 작업
이전에 만든 제어 문서는 "active":false를 지정했기 때문에 활성화되지 않았고, 또한 위의 일정은 한 시간에 한 번만 실행되지만 가까운 시일 내에 테스트하여 작동하는지 확인하려고 합니다.
먼저 N1QL에서 제어 문서를 조작할 수 있는 인덱스가 필요하며, 이 작업은 한 번만 수행하면 됩니다.
- 다음 N1QL 문을 잘라내어 붙여넣기하여 쿼리 편집기
1만들기 기본 INDEX on `crondata` ;
다음 N1QL 문을 잘라내어 붙여넣기하여 쿼리 편집기이제 작업을 활성화하되 반복 일정을 15초마다로 조정하여 시스템이 어떻게 작동하는지 정확히 확인하겠습니다. 이를 위해 KEY recurring_event::1로 제어 문서를 수정합니다.
-
123업데이트 `crondata`SET 활성=TRUE, 시간="*4X", 분="*4X"어디 유형="recurring_event" AND id=1 ;
- 클릭 실행

비표준 구문인 ="*4X"를 사용하여 1분에 네 번 반복되는 항목을 예약하여 작업 함수를 볼 수 있습니다. doCronActionA 를 실행하고 로그 문을 통해 함수를 예약하는 하우스키핑 로직도 verbose=3으로 설정했기 때문입니다.
이제 스케줄러가 1분에 4번 실행되고 있습니다. 통계 및 이벤트 함수에 대한 애플리케이션 로그 파일에서 활동을 확인할 수 있습니다. 크론_임플_2func_651 그리고 cron_impl_2func_651_help.
- 액세스 Couchbase 웹 콘솔 > 대시보드 를 선택하면 15초마다 활동이 폭발적으로 증가하는 것을 볼 수 있습니다:

- 액세스 Couchbase 웹 콘솔 > 이벤트 설정 을 클릭하고 로그 배포된 링크의 크론_임플_2func_651 이벤트 함수. 이 함수 로그 대화 상자는 로그 문을 역순으로 나열합니다(최신 항목부터). 초기 출력은 다음과 비슷합니다:
2020-05-20T18:34:33.340-07:00 [INFO] "OnUpdate B recurring_event::1 반복 타이머가 생성되었습니다, timer_id 570927555481258455388"
2020-05-20T18:34:33.340-07:00 [INFO] "2020-05-20T18:34:45.000에 recurring_event::1 rcv 변이(초기 또는 재 무장) 스케줄 타이머 업데이트"
2020-05-20T18:34:33.233-07:00 [INFO] "doCronActionA를 KEY로 KV에 업서트 cron_cache::airlines_by_country cachedoc " {"type':"cron_cache',"id':"airlines_by_country',"date':"2020-05-21T01:34:33.232Z',"data':{"미국":127,"영국":39,"프랑스":21}}}}
2020-05-20T18:34:33.233-07:00 [INFO] "콜백 R recurring_event::1 동작에 0.013초가 걸렸습니다. 참을 반환했습니다."
2020-05-20T18:34:33.233-07:00 [INFO] "콜백 C recurring_event::1 curDoc" {"action":"doCronActionA","active":true,"hour":"*4X","id":1,"min":"*4X","type":"recurring_event","verbose":{"scheduler":3,"user_func":2},"dynamic":{"state":"pending","next_sched":1590024885,"prev_sched":1590024870,"prev_time":1590024873,"prev_delay":3.218,”prev_atime”:0.013}}
2020-05-20T18:34:33.233-07:00 [INFO] "콜백 B recurring_event::1 sched 1590024870, 실제 1590024873, 지연 3.218, 걸린 시간 0.013"
2020-05-20T18:34:33.233-07:00 [INFO] "2020-05-20T18:34:45.000에 스케줄 재장전을 강제하기 위해 문서에 recurring_event::1 세대 돌연변이 #1 콜백"
2020-05-20T18:34:33.232-07:00 [INFO] "doCronActionA N1QL idx 2, 국가 프랑스 cnt 21"
2020-05-20T18:34:33.232-07:00 [INFO] "doCronActionA N1QL idx 1, 국가 영국 cnt 39"
2020-05-20T18:34:33.232-07:00 [INFO] "doCronActionA N1QL idx 0, 국가 미국 cnt 127"
2020-05-20T18:34:33.220-07:00 [INFO] "doCronActionA 사용자 동작이 recurring_event::1에 의해 제어됨"
2020-05-20T18:34:19.340-07:00 [INFO] "OnUpdate B recurring_event::1 반복 타이머가 생성되었습니다, timer_id 381384185845112994486"
2020-05-20T18:34:19.340-07:00 [INFO] "2020-05-20T18:34:30.000에 recurring_event::1 rcv 변이(초기 또는 재 무장) 스케줄 타이머 업데이트"가장 아래쪽의 가장 오래된 줄은 스케줄을 시작한(또는 스케줄을 다시 무장시킨) 변이(예: OnUpdate 메시지)이며, 코딩한 비즈니스 로직의 처음 두 개의 전체 실행을 볼 수 있습니다. doCronActionA
- 또한 "6.5.X 버킷 작업 증가에 대한 수정 사항"에 기록되지만 cron_impl_2func_651_help 를 클릭하면 약 30초마다 다음과 같은 메시지가 표시됩니다:
2020-05-20T18:34::49.185-07:00 [INFO] "primary_bucket 별칭의 각 vBucket에 하나의 문서 시드하기(221밀리초 소요)"
이 특정 작업의 빈도와 장황함을 모두 조정해 보겠습니다. 표준 cron 둘 다에 '*' 구문 시간 그리고 분 로 설정하여 현재 실행 중인 빈도보다 4배 느린 1분에 한 번씩 반복되는 스케줄을 가져옵니다. 또한 스케줄러 로직의 상세도 수준을 0으로 낮추고 사용자 함수를 1로 낮추어 호출당 하나의 메시지만 표시되도록 합니다.
에서 Couchbase 웹 콘솔 > 쿼리 페이지에서 N1QL을 사용하여 '여행 샘플' 버킷에 예약된 작업을 새로 만들겠습니다:
- 다음 N1QL 문을 잘라내어 붙여넣기하여 쿼리 편집기
1234업데이트 `크론데이터`SET verbose.스케줄러 = 0, verbose.user_func = 1,활성=true, 시간="*", 분="*"어디 유형="recurring_event" AND id=1 ;
- 클릭 실행
2분 또는 3분 후에 Couchbase 웹 콘솔 > 이벤트 설정 을 클릭하고 로그 배포된 링크의 크론_임플_2func_651 이벤트 기능.
- 이제 반복 일정이 1분으로 줄어들고 훨씬 덜 장황해졌습니다. 각 함수 실행마다 로그 메시지 또는 줄이 한 개만 생성됩니다(다시 한 번 시간 역순으로).
2020-05-20T18:43:04.231-07:00 [INFO] "doCronActionA 사용자 동작이 recurring_event::1로 제어됨"
2020-05-20T18:42:08.233-07:00 [INFO] "doCronActionA 사용자 동작이 recurring_event::1로 제어됨"예약된 각 사용자 함수 실행에 대해 하나의 로그 메시지 또는 줄만 전송됩니다. doCronActionA (다시 한 번 시간 역순으로).
수행 중인 작업을 살펴보겠습니다.
이 코드는 반복 일정에 따라 자바스크립트 함수를 실행하기 위한 실용적인 프레임워크와 함수 doCronActionA 는 계산된 캐시 KV 문서를 1분에 한 번씩 업서트(삽입 또는 업데이트)합니다.
배포된 이벤트 함수의 결과를 확인하려면, 이벤트 함수의 Couchbase 웹 콘솔 > 버킷 페이지의 문서 링크를 클릭하고 여행 샘플 버킷.
- 텍스트 상자에 "N1QL 어디'를 입력합니다.
1유형="cron_cache"
- 클릭 문서 검색
이제 예약 함수에 의해 1분에 한 번씩 업데이트되는 캐시 문서 cron_cache::airlines_by_country가 하나의 문서로 표시됩니다. doCronActionA.

- 아이디를 클릭"크론 캐시::국가별 항공사"의 비즈니스 로직에 의해 업데이트되는 캐시된 문서를 볼 수 있습니다. doCronActionA.

잠시 후 문서를 다시 편집하면 '날짜' 필드가 업데이트되는 것을 볼 수 있으며, 물론 지금은 소스 데이터가 '정적'이므로 개수는 동일하게 유지됩니다.
제어 문서를 살펴보겠습니다.
이 코드는 각 실행 일정에 대한 약간의 통계를 유지하기 위한 프레임워크를 제공합니다.
배포된 이벤트 함수의 통계를 확인하려면 이벤트 함수의 Couchbase 웹 콘솔 > 버킷 페이지의 문서 링크를 클릭하고 크론데이터 버킷.
- 텍스트 상자에 "N1QL 어디'를 입력합니다.
1유형="recuring_event"
- 클릭 문서 검색
이제 예약된 함수를 구동하는 제어 문서 recurring_event::1이 하나의 문서로 표시되어야 합니다. doCronActionA.
- 아이디를 클릭"반복_이벤트::1"를 입력하면 스케줄러 로직에 의해 업데이트되는 제어 문서가 JSON 개체 동적에 몇 가지 통계와 함께 표시됩니다.

자동으로 추가되는 문서의 '동적' 부분에는 일부 디버그 통계가 유지됩니다: - prev_sched: 마지막으로 실행된 스케줄의 이전 UNIX 타임스탬프입니다.
- 이전_시간: 스케줄이 마지막으로 실행된 실제 UNIX 타임스탬프입니다.
- 이전_지연: 이전_스케줄에서 이전_시간까지의 지연 시간입니다.
- 이전_시간: 이 작업을 실행하는 데 걸린 시간, 즉 doCronActionA를 실행하는 데 걸린 시간입니다.
- next_sched: 이 작업에 대한 다음 예약된 실행입니다.
각 일정에 대해 JSON 동적 하위 개체에 보관된 이러한 통계는 스케줄링 시스템이 정상이고 실행 중인 작업이 적시에 완료되고 있는지 확인하는 데 유용합니다.
데이터 변경 시 캐시가 업데이트되는지 확인합니다.
의 전체 목적은 doCronActionA 는 예약된 시간 또는 그 근처에서 작업을 수행하고 "cron_cache::airlines_by_country" KEY로 캐시 문서를 업데이트하는 것입니다.
쿼리 모니터에서 1) 캐시 문서를 보고, 2) 여행 샘플 문서 세트에서 일부 항공사를 삭제하고, 3) 함수에 의해 캐시 문서가 업데이트되는지 확인하여 캐시가 업데이트되고 있는지 몇 가지 검증을 수행해 보겠습니다. doCronActionA.
에서 Couchbase 웹 콘솔 > 쿼리 페이지에서는 N1QL을 사용하여 '여행 샘플' 버킷의 데이터를 보고 조작할 것입니다:
- 다음 N1QL 문을 잘라내어 붙여넣기하여
- 쿼리 편집기
12선택 데이터 FROM `여행-샘플`어디 `유형` = 'cron_cache' AND id== 'airlines_by_country'; - 클릭 실행
쿼리 워크벤치의 JSON 보기에서 다음을 볼 수 있습니다:
123456789[{"데이터": {"프랑스": 21,"영국": 39,"미국": 127}}] - 다음 N1QL 문을 잘라내어 붙여넣기하여 쿼리 편집기
12삭제 FROM `여행-샘플`어디 `유형` = '항공사' AND 콜사인 좋아요 'U%' - 클릭 실행
쿼리 워크벤치의 JSON 보기에 다음과 같은 내용이 표시됩니다(방금 일부 데이터를 삭제했습니다).
123{"결과": []} - 잠시만 기다려주세요.
- 다음 N1QL 문을 잘라내어 붙여넣기하여 쿼리 편집기
12선택 데이터 FROM `여행-샘플`어디 `유형` = 'cron_cache' AND id== 'airlines_by_country'; - 클릭 실행
쿼리 워크벤치의 JSON 보기에서 4개의 항공사가 더 이상 존재하지 않는 것을 볼 수 있습니다.
123456789[{"데이터": {"프랑스": 21,"영국": 39,"미국": 123}}]
두 번째 예약된 작업 시작
이 코드는 반복 일정에 따라 1~N개의 자바스크립트 함수를 실행하기 위한 실용적인 프레임워크를 제공합니다.
함수 doCronActionA 는 계속 1분 스케줄로 실행되지만 이제 doCronActionB 를 30초 간격(1분에 두 번)으로 호출합니다. 이 함수는 빈 셸이며 호출되었다는 사실만 기록합니다.
에서 Couchbase 웹 콘솔 > 쿼리 페이지의 데이터를 보고 조작하기 위해 N1QL을 사용하겠습니다.크론데이터' 버킷으로 이동합니다:
- 다음 N1QL 문을 잘라내어 붙여넣기하여 쿼리 편집기
123456789101112131415삽입 INTO `crondata` (KEY,VALUE) 가치 ("RECURRING_EVENT::2",{"type":"recurring_event","id":2,"시간":"*2X","min":"*2X","action":"doCronActionB","verbose": {"user_func": 1,"스케줄러": 0},"active": true}); - 클릭 실행
이 시점에서 각각 다른 스케줄로 실행되는 두(2) 개의 서로 다른 작업을 실행하여 2~3분 정도 기다렸다가 로그 파일을 다시 검사합니다.
- 액세스 Couchbase 웹 콘솔 > 이벤트 설정 을 클릭하고 로그 배포된 링크의 크론_임플_2func_651 이벤트 기능.
각 함수 실행에 대해 하나의 로그 메시지 또는 줄만 생성됩니다(다시 한 번 시간 순서가 역순으로). 다음을 확인할 수 있습니다. doCronActionA 1분에 한 번씩 발사되는 동안 doCronActionB 는 30초에 한 번씩 두 번 실행됩니다.2020-05-20T19:16:05.259-07:00 [INFO] "doCronActionA 사용자 동작이 recurring_event::1로 제어됨"
2020-05-20T19:16:05.255-07:00 [INFO] "recurring_event::2에 의해 제어되는 doCronActionB 사용자 작업"
2020-05-20T19:15:37.253-07:00 [INFO] "recurring_event::2에 의해 제어되는 doCronActionB 사용자 작업"
2020-05-20T19:15:09.250-07:00 [INFO] "doCronActionA 사용자 동작이 recurring_event::1에 의해 제어됨"
2020-05-20T19:15:09.249-07:00 [INFO] "recurring_event::2에 의해 제어되는 doCronActionB 사용자 작업"
2020-05-20T19:14:34.255-07:00 [INFO] "recurring_event::2에 의해 제어되는 doCronActionB 사용자 작업"
[선택 사항] 자바스크립트 일시 중지/편집/재개
이번 편은 본질적으로 끝났으니 자유롭게 실험하고 수정하고 실험해 보세요:
에서 Couchbase 웹 콘솔 > 이벤트 설정 화면으로 이동합니다:
- 함수 이름을 클릭합니다. 크론_임플_2func_651 을 클릭해 함수 컨트롤을 확장하고 노출합니다.
- 클릭 일시 중지.
- 에서 일시 정지 기능 확인 대화 상자에서 "일시 정지 기능".
- "자바스크립트 편집"
- 자신감이 생겼다면 doCronActionB 함수를 사용하여 일부 KV 연산을 수행하거나 cURL과 통합할 수 있습니다.
함수에 간단한 변경 사항만 추가하고 싶다면 아래와 같이 해보세요:
1234567891011121314151617함수 doCronActionB(doc) {시도 {// 문서에 원하는 값이 있는지 확인만약 (doc.유형 !== "recurring_event" || doc.활성 !== true) 반환;만약 (doc.verbose.user_func >= 1)로그(doc.액션 + ' 에 의해 제어되는 사용자 작업 + doc.유형 + '::' + doc.id);// 여기에 당신의 로직var a = 1 + 7;로그('이것은 내 로직입니다, a = 1 +7 = ' + a);} catch (e) {로그(doc.액션 + ' 오류 예외:', e);반환 false;}반환 true;} - 클릭 저장.
- 이벤트 화면으로 돌아가려면 '< 이벤트로 돌아가기' 링크(편집기 아래)를 클릭하거나 이벤트
- 클릭 이력서.
- 에서 재개 기능 확인 대화 상자에서 "기능 재개".
- 약 1 분 정도 기다린 후 기능 크론_임플_2func_651 배포하려면
- 를 클릭합니다. 로그 배포된 링크의 크론_임플_2func_651 이벤트 기능.
2020-05-20T19:20:41.343-07:00 [INFO] "이것이 내 논리입니다, a = 1 +7 = 8"
2020-05-20T19:20:41.343-07:00 [INFO] "recurring_event::2에 의해 제어되는 doCronActionB 사용자 작업"
정리
정리에는 함수 배포를 취소하고 삭제한 다음 생성한 두 개의 버킷을 제거하는 작업이 포함됩니다. 이것으로 예제를 마칩니다.
기능 제거
에서 Couchbase 웹 콘솔 > 이벤트 설정 화면으로 이동합니다:
- 함수 이름을 클릭합니다. 크론_임플_2func_651 을 클릭해 함수 컨트롤을 확장하고 노출합니다.
- 클릭 배포 취소.
- 에서 배포 취소 기능 확인 대화 상자에서 "배포 취소 기능".
- 기능 대기 크론_임플_2func_651 를 클릭하여 배포를 취소합니다.
- 클릭 삭제.
- 에서 삭제 기능 확인 대화 상자에서 "삭제 기능".
에서 Couchbase 웹 콘솔 > 이벤트 설정 화면으로 이동합니다:
- 함수 이름을 클릭합니다. cron_impl_2func_651_help 을 클릭해 함수 컨트롤을 확장하고 노출합니다.
- 클릭 배포 취소.
- 에서 배포 취소 기능 확인 대화 상자에서 "배포 취소 기능".
- 기능 대기 cron_impl_2func_651_help 를 클릭하여 배포를 취소합니다.
- 클릭 삭제.
- 에서 삭제 기능 확인 대화 상자에서 "삭제 기능".
버킷 제거
다음 버킷 삭제 '메타데이터' '크론데이터', '여행 샘플'(언제든지 다시 만들 수 있음).
에서 Couchbase 웹 콘솔 > 버킷 페이지의 문서 링크를 클릭하고 여행 샘플 버킷.
- 클릭 버킷 이름에 "메타데이터"를 클릭하여 컨트롤을 확장하고 노출합니다.
- 클릭 삭제
- 에서 버킷 삭제 확인 대화 상자에서 "버킷 삭제".
- 클릭 버킷 이름에 "크론데이터"를 클릭하여 컨트롤을 확장하고 노출합니다.
- 클릭 삭제
- 에서 버킷 삭제 확인 대화 상자에서 "버킷 삭제".
- 클릭 버킷 이름에 "여행 샘플"를 클릭하여 컨트롤을 확장하고 노출합니다.
- 클릭 삭제
- 에서 버킷 삭제 확인 대화 상자에서 "버킷 삭제".
최종 생각
이 가이드가 도움이 되셨기를 바라며, 카우치베이스 이벤트 서비스 전반에 대한 이해도가 높아지셨기를 바랍니다.
앞서 Eventing은 함수와 관련된 소스 버킷의 DCP 스트림에서 초당 수백만 건의 고속 변형을 처리하도록 설계되었다고 언급했습니다. 이 Eventing 함수 또는 스케줄러는 스케줄러 문서의 최소한의 변경에만 반응하면 됩니다.
제어 문서를 추가하는 것만으로 이 코드를 변경하지 않고 5,000개의 스케줄을 시작했습니다. 심지어 이 구현을 테스트하기 위해 매분 120,000개의 스케줄을 실행하기도 했습니다(예, 120,000개는 상당히 많은 양의 독립적인 스케줄입니다. cron 스케줄을 실행하도록 설정했습니다.) 또한 리소스 저하 문제가 발생하지 않도록 120,000개의 sc3스케줄을 2일 후로 미뤄두었습니다.
메인 함수인 스케줄링 시스템을 만들기 위해 두 개의 저녁 함수를 사용해야 합니다. 크론_임플_2func_651 그리고 간단한 도우미 cron_impl_2func_651_help 는 제가 기대했던 것만큼 우아하지 않았습니다. 게다가 모든 vBucket에서 타이머를 실행하여 버킷 작업 누수를 해결해야 하는 것은 아무리 말해도 실망스러웠습니다. 다행히도 이러한 노력으로 인해 변경 사항이 구현되었고 곧 출시될 6.6.0 릴리스에서는 저녁 함수 하나를 사용하여 더 깔끔한 스케줄러를 만들 수 있습니다. 6.6의 주요 개선 사항은 1) 타이머 콜백 내에서 새 타이머를 생성하는 기능, 2) 참조로 기존 타이머를 취소하거나 덮어쓰는 기능, 3) 향후 타이머가 예약된 유휴 시스템에서 리소스 사용량이 증가하는 것을 제거하는 기능입니다.
독립형 버킷 '크론데이터' 를 사용하여 일정이나 제어 문서를 보관합니다( '메타데이터' 버킷에 배치하여 다른 버킷의 데이터를 최대한 유연하게 조작할 수 있도록 했습니다. 제어 문서를 다른 버킷에 배치했다면 해당 버킷에서 N1QL 작업을 할 수 없었을 것이고(이벤트 함수에 대한 소스 버킷이므로), 배치된 버킷의 데이터를 조작하기 위한 KV 작업으로만 제한되었을 것입니다.
다른 크론 사용 사례도 직접 시도해 보시고 예약 서비스를 활용하는 다른 방법도 생각해 보시기 바랍니다:
- '사용량이 적은 시간대'에 대규모 데이터 세트의 항목 수를 확인하고 증분 제거를 수행합니다.
- N1QL을 통해 예약된 문서 보강을 수행합니다.
- 정기적으로 주식 포트폴리오를 다시 계산합니다.
- 반복 일정에 따라 N1QL을 통해 TTL 또는 만료 시간 관리하기는 다음을 참조하세요. Couchbase N1QL로 TTL(Time-To-Live) 문서를 관리하는 방법.
- 반복되는 일정에 따라 외부 REST 엔드포인트와 통합하려면 다음을 참조하세요. 이벤트 서비스와 함께 cURL 사용하기: 업데이트.
- 의 자바스크립트 업데이트 크론_임플_2func_651 를 사용하여 이전에 실행된 사용자 작업에서 반환된 참/거짓 결과 플래그를 저장하기 위해 JSON 동적 객체에 새 필드 'prev_astatus'를 추가합니다.
업데이트
이 블로그는 2020년 7월 24일에 업데이트되어 메타데이터 버킷 작업 수가 증가하는 6.5.x 릴리스에 대한 해결 방법을 추가하여 향후 유휴 시스템에서 타이머를 생성할 때(1시간 이상) 특정 이벤트 함수에 대한 변이를 차단할 수 있습니다(예: 1시간 이상).
다음 단계
몇 주 후 "Couchbase Eventing을 통한 강력한 휴대용 크론 같은 스케줄러 구현하기(2부)"에서는 이벤트 함수를 편집하거나 이벤트 함수 내에서 하드코딩된 "액션" 스크립트를 정의할 필요 없이 일련의 데이터베이스 기반 동적 N1QL 문을 실행하는 방법을 살펴볼 예정입니다.
리소스
- 다운로드: 카우치베이스 서버 6.5.1 다운로드
- 이벤트 함수: cron_impl_2func_651.json
- 이벤트 도우미 기능: 크론_impl_2func_651_help.json
참조
- 카우치베이스 이벤트 문서:
https://docs.couchbase.com/server/current/eventing/eventing-overview.html - 카우치베이스 서버 6.5의 새로운 기능:
https://docs.couchbase.com/server/6.5/introduction/whats-new.html - 이벤트에 대한 카우치베이스 블로그:
https://www.couchbase.com/blog/tag/eventing/
6.5의 기능이 마음에 드셨는지, 앞으로 비즈니스에 어떤 도움이 될지 여러분의 의견을 듣고 싶습니다. 댓글을 통해 의견을 공유해 주세요. 포럼.
각주
[1] Eventing Service의 타이머 구현은 수백만 개의 분산 타이머를 빠른 속도로 처리하도록 설계되었습니다. 단일 이벤트 노드는 초당 10만 개 이상의 타이머를 처리할 수 있으며, 타이머가 손실되지 않도록 가능한 한 빨리 타이머를 실행하는 것만이 유일한 약속입니다. 현재 스캔 간격은 실행 준비가 완료된 타이머를 수집하는 데 7초이므로 약간의 지연을 예상해야 합니다. 타이머 스케줄링에 대한 자세한 내용은 다음을 참조하세요. 타이머: 벽시계 정확도 를 참조하세요.
[2] 조정하여 허용_인터버킷_재귀 에 true 를 사용하면 무한 재귀 루프를 시작할 수 있는 이벤트 로직이 실수로 발생하는 것을 방지하기 위해 Couchbase 서버에 설정된 가드를 제거하게 됩니다. 이것은 잘못된 것은 아니지만 재귀를 활용할 때 실수하기 쉽습니다. Couchbase 버전 6.6에서는 이벤트 로직을 조정할 때 두(2) 개의 이벤트 함수에서 하나의 단순화된 이벤트 함수(1)로 축소할 수 있으며, 조정할 필요가 없습니다. 허용_인터버킷_재귀 설정합니다.
[3] 두 가지 주요 제한 사항이 존재합니다. 첫째, 문서가 단기간에 여러 번 수정되는 경우 중복 제거로 인해 호출이 단일 이벤트로 합쳐질 수 있습니다. 둘째, 만들기 작업과 업데이트 작업을 구분할 수 없습니다. 제안의 경우 cron 함수에 대한 제한은 문제가 되지 않습니다.
[4] 정확한 크론탭 시맨틱을 구현하지 않은 이유는 구현할 수 있었지만 코드의 양이 너무 많았기 때문입니다. https://github.com/kelektiv/node-cron 패키지를 모멘트 및 모멘트 시간대의 종속성(모두 매우 큰 패키지)과 함께 사용해야 합니다. getNextRecurringDate(hour_str, min_str)는 유연성은 떨어지지만 간단하고 사용 사례에 적합합니다.
단일 기능을 사용하는 6.6.x 업데이트에 많은 관심을 갖고 있습니다! 크론과 유사한 스케줄러를 위한 이 구현이 마음에 듭니다.
깃허브에서 이 기능을 사용할 수 있다는 사실을 방금 알았습니다! 한번 살펴볼게요! 링크를 통해 이 블로그 게시물을 업데이트해 주세요 :) 고마워요!
안녕하세요 Alex,
곧 6.6.0 버전과 6.6.1 버전(고급 버킷 접근자 사용)으로 "크론과 유사한 스케줄러"를 업데이트할 예정입니다.
하지만 후속 블로그(2부)에서 6.6.0 프로토타입을 GitHub에서 찾아주셔서 감사드리며, 두 블로그를 교차 링크할 예정입니다.