본문으로 건너뛰기

이벤트 타입 등록

이벤트 타입 X 를 읽는 문장을 배포하려면 먼저 X 를 등록해야 합니다.

등록하지 않으면 문장이 «조용히» 아무것도 매치하지 않습니다

미등록 타입 위의 문장은 컴파일되고, 배포되고, 돌고, 영영 매치하지 않습니다. 초록 잡 · 초록 파이프라인 · 멈춘 알람이고 나중에 잡히는 지점이 없습니다. 그래서 배포 시점에 이름을 대고 거부합니다.

무엇을 등록하나​

항목규칙
name^[A-Za-z_][A-Za-z0-9_]*$. 문장이 FROM X 로 쓰는 이름과 같아야 합니다
fields[]최소 하나. 빈 목록은 거부 — 「고를 것이 아무것도 없다」
필드 type스칼라 다섯 STRING · BOOLEAN · INT · LONG · DOUBLE, 시각 TIMESTAMP, 맵 다섯 MAP_STRING · MAP_BOOLEAN · MAP_INT · MAP_LONG · MAP_DOUBLE
필드 name같은 패턴. event_time 은 쓸 수 없고, 중복도 안 됩니다
event_time_field필수. 기본값이 없습니다. 리터럴 @envelope 이거나, 선언된 LONG 필드 이름(epoch ms)
event_time_field 에 기본값이 없는 것은 의도입니다

이벤트 시각을 추측한 타입은 돌면서 아무것도 매치하지 않는 문장을 만듭니다.

가리킬 수 있는 것은 LONG 필드뿐입니다(epoch ms). TIMESTAMP 는 컬럼일 수는 있어도 시각 기준의 출처는 아닙니다.

TIMESTAMP 와 MAP_*​

스칼라 다섯만 있던 시절에는 플랫폼 자신의 이벤트 클래스가 등록되지 못했습니다 — Point · PointMap · Alarm · AssetData · AssetEvent 가 저마다 날짜나 맵 멤버를 들고 있었기 때문입니다. 엔진이 못 싣는 것이 아니라 레지스트리에 이름이 없던 것이었습니다.

MAP_* 는 값 타입을 이름에 담습니다.

{ "name": "data_map", "type": "MAP_STRING" },
{ "name": "timestamp_map", "type": "MAP_LONG" }

한 자산 이벤트가 문자열 맵과 epoch 맵을 둘 다 들고 있을 수 있습니다. 하나로 뭉개 전부 문자열로 읽으면 epoch 이 문자열이 되어 비교가 사전식으로 바뀌고, 문장은 그대로 돕니다.

  • 키는 언제나 STRING 입니다 — JSON 객체의 키에 다른 종류가 없습니다.
  • 값 타입은 스칼라 다섯 중 하나뿐입니다. 값이 또 객체인 맵은 멤버 이름을 대고 거부됩니다. 문자열로 뭉개면 문장이 비교는 하되 뜻이 없는 값을 보게 됩니다.
  • MAP_* 는 항목마다 변환하고, 못 바꾸는 항목은 키를 대고 거부합니다(timestamp_map[bad]) — 컬럼 이름만 대면 항목 오십 개 중 어느 하나인지 찾을 수 없습니다.
TIMESTAMP 는 «순간» 이지 «벽시계» 가 아닙니다

투영이 받아들이는 TIMESTAMP 모양은 둘뿐입니다 — 숫자(epoch ms), 그리고 그 숫자를 담은 날짜 객체입니다.

ISO 문자열은 이름을 대고 거부합니다. 그 문자열은 쓰는 쪽 기본 시간대로 렌더된 데다 오프셋이 붙어 있지 않아, 읽는 쪽에서 파싱하면 TaskManager 의 시간대가 적용됩니다. 그 차이만큼 윈도가 어긋난 채 아무것도 빨갛지 않습니다.

⚠ 와이어 모양은 바뀌지 않았습니다. 필드는 여전히 {"name": …, "type": …} 이고 스키마 버전도 그대로입니다 — 값 타입이 «타입 이름 안» 에 있기 때문입니다.

API​

메서드경로동작
GET/api/v1/event-types목록, 이름 순
GET/api/v1/event-types/{name}하나. 없으면 404
POST/api/v1/event-types등록. 이름이 이미 있으면 409
PUT/api/v1/event-types/{name}?redeploy=false수정. 쓰는 문장이 있으면 409, redeploy=true 면 같은 호출에서 재배포
DELETE/api/v1/event-types/{name}삭제. 쓰는 문장이 있으면 409
# 등록
curl -fsS -X POST http://127.0.0.1:7400/api/v1/event-types \
-H "X-API-Key: ${PP_CEP_API_KEY}" -H 'Content-Type: application/json' \
-d '{
"name": "AlarmEvent",
"event_time_field": "occurred_at",
"fields": [
{ "name": "machineId", "type": "STRING" },
{ "name": "severity", "type": "INT" },
{ "name": "occurred_at","type": "LONG" }
]
}'

# 목록
curl -fsS -H "X-API-Key: ${PP_CEP_API_KEY}" http://127.0.0.1:7400/api/v1/event-types

# 스키마를 바꾸고 그 타입을 쓰는 문장까지 같은 호출에서 재배포
curl -fsS -X PUT "http://127.0.0.1:7400/api/v1/event-types/AlarmEvent?redeploy=true" \
-H "X-API-Key: ${PP_CEP_API_KEY}" -H 'Content-Type: application/json' -d @alarm-event.json
  • redeploy 의 기본값은 false 입니다.
  • redeploy=true 로 재배포하다가 하나라도 실패하면 이전 스키마로 되돌립니다.
  • 경로의 이름과 본문의 이름이 다르면 400 입니다 — 이 엔드포인트는 타입 하나를 바꾸는 것이지 개명하는 것이 아닙니다.
  • 레지스트리가 아직 초기화되지 않았으면 503 Event type registry is not initialized. 입니다. 빈 목록을 주지 않는 이유는, 그것이 「등록된 타입이 없다」로 읽히고 그러면 엔진이 모든 문장을 거부하기 때문입니다.

저장소​

PostgreSQL datalake DB 의 cep_event_type 테이블입니다.

CREATE TABLE IF NOT EXISTS cep_event_type (
name TEXT NOT NULL,
event_time_field TEXT NOT NULL,
fields_json TEXT NOT NULL,
version BIGINT NOT NULL,
updated_at_ms BIGINT NOT NULL,
updated_by TEXT,
CONSTRAINT cep_event_type_pkey PRIMARY KEY (name)
);

표는 CEP 가 기동할 때 스스로 만듭니다. 자기가 소유한 표 셋(cep_event_type · cep_statement · cep_api_key)을 자기가 마이그레이션합니다 — 전부 «있으면 그대로 두는» 형태이고 지우는 구문이 없어, 이미 값이 들어 있는 설치에도 그대로 적용됩니다.

읽기에 실패하면 빈 결과가 아니라 예외입니다. 빈 결과를 주면 멀쩡히 등록된 타입에 대해 엔진이 모든 문장을 거부하고, 그 거부가 운영자가 오타를 낸 것처럼 그 타입을 이름하게 됩니다.

표가 없으면 헬스가 이름을 대고 내려갑니다

표가 통째로 없으면 레지스트리가 그 자리에서 실패하고, 그 문장들이 미등록 타입처럼 거부됩니다 — 운영자가 오타를 찾게 됩니다.

그래서 /api/health 가 스키마를 따로 봅니다. 없는 표를 이름 대며 DEGRADED 로 보고합니다. 기동 순서 때문에 DB 가 늦은 경우는 스스로 회복합니다.

타입이 엔진의 스트림이 되는 길​

등록한 타입 하나가 백엔드의 입력 스트림 하나가 됩니다 — 스트림 이름 = 이벤트 타입 이름입니다.

타입을 엔진 타입으로 옮기는 자리는 한 곳뿐이고, 그 분기에는 기본 갈래가 없습니다 — 타입이 하나 늘면 컴파일이 깨지게 해 두었습니다. 조용히 «모르는 타입» 으로 떨어지면 컬럼 하나가 뜻 없는 값이 됩니다.

시각은 «순간» 으로 다룹니다

epoch 밀리초는 시간대가 없는 순간입니다. 시간대 없는 «벽시계» 타입에 순간을 넣으면 시간대를 고르고도 말하지 않는 셈이 되고, 다른 시각 컬럼과 비교할 때마다 세션 오프셋만큼 조용히 어긋납니다.

이벤트 클래스를 배포할 필요가 없습니다

페이로드를 JSON 으로 읽어 등록된 스키마로 투영합니다. 프로듀서의 자바 클래스가 엔진 쪽에 하나도 로드되지 않으므로, 새 이벤트 타입은 «테이블의 한 행» 입니다.

  • 타입이 틀린 값은 이름을 대고 거부합니다. 3.5 를 INT 에 넣으면 잘라내지 않고 거부합니다.
  • 없는 필드는 null 로 둡니다 — 의도된 비대칭입니다.

미등록 타입의 거부 메시지​

배포 요청 (POST /api/v1/eql/deploy, 400, EQL deploy failed: 접두):

event type(s) not registered: X, Y. A statement over an unregistered type deploys, runs
and matches nothing, so it is refused here. Registered types are: A, B

레지스트리가 비어 있으면 마지막 문장이 No event type is registered at all. 입니다.

엔진 쪽:

event type [X] is not registered. Registered types are: A, B, C
event type [X] is not registered - no event type is registered at all. Register it before deploying a statement that reads it
event type [X] has no input table in this environment after <how> - tables present: [...]

컨트롤 플레인:

the engine refused this rule and will refuse it again: <위 메시지>

검사 시점 — 둘, 둘 다 잡 제출 «전»​

시점왜 거기
배포 요청 (/api/v1/eql/deploy)미등록 타입 위의 문장은 나중에 잡히는 지점이 없으므로 여기서 이름을 대고 잡습니다
엔진 제출질의가 컴파일되기 전이지, 잡이 돈 뒤가 아닙니다
배포 게이트는 «요란하게 열립니다»

레지스트리를 읽을 수 없거나 문장을 파싱할 수 없으면 배포를 허용하되 성공 메시지에 그 사유를 덧붙입니다. 「‘배포됐다’를 읽는 운영자가 ‘이벤트 타입은 검사되지 않았다’도 같이 읽는다」는 뜻입니다.

관련 문서​