본문으로 건너뛰기

맥락화: 시계열을 배치에 결합하기

📍 현재 위치: 3부 · 저장하고 연결하기 — 14장. 이제 히스토리언(historian)에는 수백만 건의 판독값이 쌓여 있습니다. 이 장에서는 각 판독값을 그것이 비롯된 배치(batch), 장비, 단계(phase)에 결합함으로써 모든 판독값에 의미를 부여합니다.

쉽게 말하면

가공되지 않은 히스토리언은 날짜가 떨어져 나간 영수증이 담긴 신발 상자와 같습니다. 각 쪽지에는 어느 순간엔가 BR101.Temp.PV = 37.00 °C라고 적혀 있습니다. 사실이긴 하지만 아무 말도 하지 않습니다. 맥락화(contextualization)는 각 영수증을 올바른 배치 기록의 올바른 페이지에 스테이플로 박아 넣습니다. 판독값은 레시피(recipe) CHO-MAB-001에 따라 바이오리액터(bioreactor) BR101에서 가동된 BATCH-2026-001성장(Growth) 단계에서 발생했다고요. 그 순간 여러분은 "지난주 로트의 생산 단계 동안 용존 산소는 얼마였는가?" 같은 사람의 질문을 던질 수 있게 되고, 데이터베이스가 그에 답할 수 있게 됩니다.

이 장에서 다루는 내용

13장에서 우리는 모든 센서 태그(tag)를 집어삼키는 TimescaleDB 히스토리언을 구축했습니다. 3장에서는 ISA-88/95 배치 및 장비 세계를 PostgreSQL에 모델링했습니다. 이 둘은 나란히 존재하면서도 지금까지 서로를 무시해 왔습니다. 이 장에서는 그 둘을 결혼시킵니다.

우리는 다음을 할 것입니다.

  • 단 하나의 SQL 뷰(view)로 히스토리언 스트림을 ISA-88 단계 및 ISA-95 장비 계층(equipment hierarchy)에 결합합니다.
  • 가공되지 않은 태그를 대상으로는 불가능한 배치 인식(batch-aware) 질의 — "배치 X의 생산 단계 동안의 DO" — 를 실행합니다.
  • 골든 배치(golden-batch) 오버레이의 토대가 되는, 단계별·태그별 요약을 구축합니다.
  • 그리고 단계 경계를 Postgres에서 Grafana 대시보드로 밀어 넣어, 분석가가 레시피가 그 위에 그려진 채로 추세를 볼 수 있게 합니다.

이 장의 핵심을 이루는 두 개의 뷰는 examples/platform/db/60-views.sql에 있으며 make seed로 생성됩니다. 이 뷰들은 examples/platform/db/20-historian.sql의 히스토리언 테이블을 examples/platform/db/seed/seed_cho_line.sql로 시드(seed)된 배치 모델에 결합합니다. 뒤에 나오는 구체화 뷰(materialized view)와 Grafana 스니펫은 예시입니다. 이들은 모델이 다음으로 어디로 향하는지를 보여주며 15장에서 구축됩니다. 해당하는 곳마다 그렇게 표시해 두었습니다.

우리가 결합하는 두 세계

히스토리언 테이블은 일부러 멍청하게 만들어졌습니다. 다음은 examples/platform/db/20-historian.sql에서 가져온 그 정의입니다.

CREATE TABLE ts.sensor_reading (
ts timestamptz NOT NULL,
tag text NOT NULL,
value double precision,
unit text,
quality smallint NOT NULL DEFAULT 192, -- OPC UA: 192 Good, 64 Uncertain, 0 Bad
batch_id text
);

여섯 개의 열, 그 어떤 견해도 없습니다. 전형적인 단면은 다음과 같습니다.

ts | tag | value | unit | quality | batch_id
------------------------+---------------+---------+------+---------+----------------
2026-01-13T08:00:00Z | BR101.Temp.PV | 36.9993 | degC | 192 | BATCH-2026-001
2026-01-13T08:00:00Z | BR101.DO.PV | 36.4576 | %sat | 192 | BATCH-2026-001
2026-01-13T08:00:00Z | BR101.pH.PV | 6.9922 | pH | 192 | BATCH-2026-001

이 테이블이 이미 batch_id를 가지고 있다는 점에 주목하세요. 그것이야말로 포착 계층 전체에서 가장 중요한 설계 결정입니다. 수집기(collector)는 판독값을 기록하면서 활성 배치를 각 판독값에 도장처럼 찍습니다(이 작업은 5장에서 설정했습니다). 그것이 없다면 맥락화는 "그 화요일 08:00에 BR101에서 돌아가던 게 어느 런이었지?"를 추측하는 취약한 게임이 됩니다. 그것이 있으면 결합은 정확합니다.

하지만 batch_id는 이야기의 절반에 불과합니다. 그것은 어느 런인지는 알려주지만, 그 런의 어느 단계인지는 알려주지 않습니다. 08:00은 여전히 종균 배양(seed-train) 접종이었을까요, 아니면 역가(titer)가 축적되는 생산 단계로 배양물이 넘어간 뒤였을까요? 그 답은 3장에서 구축한 관계형 세계에 있습니다.

ISA-88 배치 제어 표준은 우리에게 절차적 어휘를 제공합니다. 레시피는 작업(operation)들로 이루어지고, 각 작업은 단계들로 이루어지며, 단계는 의미 있는 가장 작은 절차적 요소입니다 [1]. ISA-95 기업 제어 표준은 물리적 측면을 제공합니다. 기업 → 사이트 → 영역 → 유닛(unit)으로 이어지므로 모든 태그를 그것이 가동된 장비에, 그리고 배치를 통해 그 로트에 묶을 수 있습니다 [2]. 두 모델은 모두 로열티 없는 기계 판독 가능한 XML 스키마(B2MML/BatchML)로 배포되며, 바로 그 덕분에 우리는 이들을 산문이 아니라 구체적인 관계형 테이블로 바꿀 수 있었습니다 [3].

examples/platform/db/seed/seed_cho_line.sql에서 유가식(fed-batch) 레시피는 네 개의 작업과 다섯 개의 단계로 쪼개집니다.

INSERT INTO s88.phase VALUES
('PH1', 'OP1', 1, 'Inoculate'),
('PH2', 'OP2', 1, 'Growth'),
('PH3', 'OP2', 2, 'Production'),
('PH4', 'OP3', 1, 'Harvest'),
('PH5', 'OP4', 1, 'Capture') ON CONFLICT DO NOTHING;

Growth 같은 레시피 단계는 추상적인 템플릿입니다. 추적선(trace)을 시간 속에 실제로 고정하는 것은 배치 단계(batch phase) — 그 단계가 하나의 특정 배치에 대해 실제로 언제 가동되었는지의 기록 — 입니다. 결합이 의존하는 윈도(window)가 바로 그것이며, 이 역시 시드 파일에 있습니다.

-- phase windows for the golden batch (drives the contextualization view)
INSERT INTO s88.batch_phase (batch_id, phase_id, unit_id, start_ts, end_ts) VALUES
('BATCH-2026-001', 'PH1', 'BR101', '2026-01-05T00:00:00Z', '2026-01-05T12:00:00Z'),
('BATCH-2026-001', 'PH2', 'BR101', '2026-01-05T12:00:00Z', '2026-01-12T00:00:00Z'),
('BATCH-2026-001', 'PH3', 'BR101', '2026-01-12T00:00:00Z', '2026-01-18T00:00:00Z'),
('BATCH-2026-001', 'PH4', 'BR101', '2026-01-18T00:00:00Z', '2026-01-19T00:00:00Z')
ON CONFLICT DO NOTHING;

이 네 행을 하나의 타임라인으로 읽어 보세요. 반나절의 접종, 그다음 일주일의 성장, 그다음 세포가 항체를 만드는 긴 생산 단계, 그다음 수확입니다. 1월 13일 08:00의 우리 판독값은 세 번째 윈도 안에 들어가므로, 그것은 진짜로 생산 단계 판독값입니다. 우리는 단지 SQL이 그것을 자동으로 알아내게 하면 됩니다.

히스토리언 태그가 왼쪽에서 오른쪽으로 흐르며 batch_id 도장을 얻고, 그다음 타임라인 위의 ISA-88 단계 윈도와 ISA-95 장비 트리에 대조된 뒤, 오른쪽에서 자신의 배치, 단계, 유닛, 레시피를 명시하는 완전히 맥락화된 행으로 나타난다.

아무 말도 못 하던 태그에서 의미 있는 기록으로: 맥락화 결합은 각 히스토리언 판독값을 그것이 들어가는 단계 윈도와, 그것이 속한 장비/레시피에 스테이플로 박아 넣는다. Original diagram by the authors, created with AI assistance.

실제 일을 해내는 결합

다음이 이 장의 핵심입니다. examples/platform/db/60-views.sql에서 가져온 뷰 전체입니다.

-- A reading with its full batch + phase context.
CREATE OR REPLACE VIEW s88.v_batch_sensor AS
SELECT r.ts, r.tag, r.value, r.unit, r.quality, r.batch_id,
b.product_id, b.recipe_id, b.unit_id,
bp.phase_id, ph.name AS phase_name
FROM ts.sensor_reading r
JOIN s88.batch b ON b.batch_id = r.batch_id
LEFT JOIN s88.batch_phase bp ON bp.batch_id = r.batch_id
AND r.ts >= bp.start_ts AND (bp.end_ts IS NULL OR r.ts < bp.end_ts)
LEFT JOIN s88.phase ph ON ph.phase_id = bp.phase_id;

짧지만 모든 절(clause)이 제 몫을 합니다.

첫 번째 JOIN s88.batchbatch_id에 대한 내부(inner) 조인입니다. 일치하는 배치가 없는 판독값은 버려집니다. 그것은 의도된 것입니다. 어떤 배치도 가동되지 않는 동안 센서가 깜빡인 것은 그 어떤 GMP 기록의 일부도 아니므로, 배치 인식 질의에 슬그머니 나타나서는 안 됩니다.

영리한 부분은 LEFT JOIN s88.batch_phase입니다. 일치 조건은 키에 대한 동등성(equality)이 아니라 **시간적 포함(temporal containment)**입니다. r.ts >= bp.start_ts AND (bp.end_ts IS NULL OR r.ts < bp.end_ts). 각 판독값에 대해 Postgres는 그 시작/끝이 판독값의 타임스탬프를 감싸는 단계 윈도를 찾습니다. 반열린 구간(시작은 >=, 끝은 <)이 뜻하는 바는, 단계 경계에 정확히 걸친 판독값은 단계에 속하며 결코 양쪽 모두에 속하지 않는다는 것입니다. 이중 계산이 없습니다. bp.end_ts IS NULL 분기는 아직 끝이 기록되지 않은 채 실시간으로 진행 중인 단계를 처리합니다. 우리는 이것을 LEFT 조인으로 유지하는데, 그래야 어떤 단계 윈도도 열리기 전에 도착한 판독값이 사라지지 않고 (단계가 NULL인 채로) 그대로 드러납니다.

그 단 하나의 뷰가 모든 하류 소비자가 질의하는 계약입니다. 대시보드, 분석 노트북, 지식 그래프(knowledge-graph) 로더가 그것입니다. 이들 중 누구도 가공되지 않은 ts.sensor_reading 테이블을 다시는 건드리지 않습니다. 이제 사람의 질문은 SQL 한 줄이 됩니다.

-- "Show me dissolved oxygen during the Production phase of the golden batch."
SELECT ts, value
FROM s88.v_batch_sensor
WHERE batch_id = 'BATCH-2026-001'
AND tag = 'BR101.DO.PV'
AND phase_name = 'Production'
ORDER BY ts;
ts | value
------------------------+---------
2026-01-12T00:00:00Z | 36.8646
2026-01-12T00:01:00Z | 35.888
2026-01-12T00:02:00Z | 36.5891
...
2026-01-17T23:59:00Z | 34.0293

이것을 맨몸의 히스토리언을 대상으로는 결코 작성할 수 없습니다. phase_name = 'Production'은 시계열 테이블이 애초에 담고 있지 않은 지식입니다. 결합이 그것을 즉석에서 만들어 냈습니다.

판독값에서 골든 배치 구성 요소로

추적선은 유용합니다. 하지만 단계별 요약은 공정 이해가 시작되는 지점입니다. examples/platform/db/60-views.sql의 두 번째 뷰는 맥락화된 판독값을 배치, 단계, 태그당 한 행으로 말아 올립니다.

-- Per-batch, per-phase, per-tag summary: the "golden batch" building block.
CREATE OR REPLACE VIEW s88.v_phase_summary AS
SELECT batch_id, phase_name, tag, unit,
count(*) AS n,
round(avg(value)::numeric, 4) AS avg_value,
round(min(value)::numeric, 4) AS min_value,
round(max(value)::numeric, 4) AS max_value
FROM s88.v_batch_sensor
WHERE phase_name IS NOT NULL
GROUP BY batch_id, phase_name, tag, unit;

골든 배치 전체에 걸쳐 온도를 질의하면 간결한 공정 지문(fingerprint)이 나옵니다.

batch_id | phase_name | tag | unit | n | avg_value | min_value | max_value
----------------+------------+---------------+------+------+-----------+-----------+----------
BATCH-2026-001 | Growth | BR101.Temp.PV | degC | 9360 | 37.0002 | 36.8724 | 37.1211
BATCH-2026-001 | Production | BR101.Temp.PV | degC | 8640 | 36.9893 | 36.3571 | 37.1218
BATCH-2026-001 | Harvest | BR101.Temp.PV | degC | 1440 | 37.0008 | 36.9132 | 37.0981

생산 단계의 그 36.36 °C 최솟값은 시뮬레이터가 유가식 추적선에 일부러 심어 놓은 7일째 이상 현상(excursion)입니다. 그리고 요약이 단계별로 잘려 있기 때문에, 그것은 14일간의 런 전체에 평균되어 보이지 않게 되는 대신 정확히 일어난 그 자리에 나타납니다.

이 단계별 축약은 편의 기능이 아니라, 배치 공정을 제대로 모니터링하기 위한 통계적 전제 조건입니다. 배치 모니터링에 관한 선구적인 다중 PCA(multiway-PCA) 연구는 정상 과거 배치들을 정렬하고 새 런을 그에 대조함으로써 기준 궤적 — "골든 배치" — 을 구축했습니다 [4]. 그리고 우리가 벽시계 시간이 아니라 ISA-88 단계로 먼저 잘라내는 이유는, 유가식 궤적이 공정 단계와 지표 변수에 정렬된 뒤에야 비로소 배치 간에 서로 들어맞기 때문입니다. 한 배치는 아직 접종 중인데 다른 배치는 이미 피드 중이라면, 두 배치의 30분 지점끼리 비교하는 것은 무의미합니다 [5]. v_phase_summary는 이 모든 것의 소박하고 질의 가능한 씨앗입니다. 모든 형제 배치가 동일한 단계 키(phase-keyed) 형태로 축약되어, 쌓을 준비가 된 상태입니다.

결합이 비싸질 때 맥락을 구체화하기

v_batch_sensor는 평범한 뷰입니다. 시간적 결합이 매 질의마다 다시 실행됩니다. 몇 개의 태그에 대한 골든 배치라면 그것은 순식간입니다. 하지만 6개 배치 캠페인, 16개 태그, 그리고 10초마다 갱신되는 대시보드에 걸쳐서라면, 동일한 포함 결합이 거듭거듭 실행됩니다.

두 가지 OSS 메커니즘이 모델을 바꾸지 않고도 이를 해결합니다.

원시 레이트(raw-rate) 롤업의 경우, TimescaleDB **연속 집계(continuous aggregate)**는 하이퍼테이블(hypertable) 위의 구체화 뷰로, 새 데이터가 도착하면 증분적으로 갱신되므로 전체 이력을 다시 계산하지 않아도 됩니다 [6]. 우리는 이미 13장에서 이들을 생성했습니다(ts.sensor_1m, ts.sensor_1h). 히스토리언은 바이오리액터의 네이티브 5초 취득값을 분당 한 행으로 다운샘플링하여 저장하므로(14일 런 동안 태그당 약 20,160행), 단계 인식 대시보드는 1분 단위 판독값을 일일이 스캔하는 대신 이미 말아둔 그 버킷(bucket)들을 단계 윈도에 결합합니다.

맥락 계층의 경우, 평범한 PostgreSQL **구체화 뷰(materialized view)**는 결합 결과를 디스크에 스냅샷으로 떠 두고 여러분이 REFRESH할 때까지 그것을 제공합니다 [7]. 요약을 구체화로 바꾸는 것은 한 단어짜리 변경입니다. 아래 블록은 예시 스니펫입니다. 저장소(repo)에 커밋되어 있지 않으며 make seed로 생성되지 않습니다. 라이브 결합이 비싸질 때 여러분이 추가할 패턴을 보여줍니다.

-- Illustrative — not in the repo; shows how you would materialize v_phase_summary.
CREATE MATERIALIZED VIEW s88.mv_phase_summary AS
SELECT * FROM s88.v_phase_summary;
-- refresh after a batch completes (or on a schedule):
REFRESH MATERIALIZED VIEW s88.mv_phase_summary;

정직한 아키텍처 노트 하나. 이 책에서 히스토리언과 관계형 모델은 동일한 PostgreSQL 인스턴스에 살고 있으므로(TimescaleDB는 별도의 데이터베이스가 아니라 Postgres 확장입니다), s88.batchts.sensor_reading은 네이티브로 결합됩니다. 실제 세계에서는 여러분의 히스토리언이 흔히 다른 서버 — 별도의 Postgres이거나, AVEVA PI 같은 상용 시스템 — 입니다. PostgreSQL은 같은 SQL인데 서버가 다른 이 경우를 **postgres_fdw**로 해결합니다. 이는 원격 테이블을 마치 로컬인 것처럼 노출하는 외부 데이터 래퍼(foreign-data wrapper)로, 하나의 뷰가 여전히 그 경계를 가로질러 결합할 수 있게 해줍니다 [8]. 우리가 여기서 단일 인스턴스 결합을 쓰는 것은 그것이 노트북에서 돌아가는 방식이기 때문입니다. FDW 패턴은 정확히 이 뷰를 두 서버에 걸쳐 늘이는 방법이며, 브리지(bridge) 장들(17–19장)은 그 아이디어를 PI와 SAP에까지 끝까지 밀고 갑니다.

추세 위에 레시피 그리기: Grafana 오버레이

맥락화된 뷰는 사람이 그것을 보는 순간 제값을 합니다. Grafana는 PostgreSQL/TimescaleDB를 네이티브로 읽습니다. 시간 열(time column)이 필요하고, 데이터를 패널 너비에 맞춰 버킷화하는 $__timeFiltertime_bucket 헬퍼를 제공합니다 [9]. 아래 두 질의는 예시입니다. 이들은 make seed가 아니라 Grafana 내부에서 실행되며, 이들을 감싸는 프로비저닝된(provisioned) 대시보드는 15장에서 구축됩니다. 추세 패널은 그저 우리 뷰입니다.

-- Illustrative Grafana panel query (runs in Grafana, not via make seed).
SELECT ts AS "time", value, tag
FROM s88.v_batch_sensor
WHERE batch_id = '$batch'
AND tag IN ($tags)
AND $__timeFilter(ts)
ORDER BY ts;

마법은 두 번째 질의에 있습니다. 이 질의는 단계 윈도를 추세 뒤에 그려지는 음영 처리된 주석(annotation) 영역으로 바꿉니다.

-- Illustrative Grafana annotation query (runs in Grafana, not via make seed).
SELECT bp.start_ts AS "time", bp.end_ts AS "timeEnd", ph.name AS text
FROM s88.batch_phase bp
JOIN s88.phase ph ON ph.phase_id = bp.phase_id
WHERE bp.batch_id = '$batch'
ORDER BY bp.start_ts;

이제 분석가는 출렁이는 선들의 벽을 읽지 않습니다. 그들은 성장생산 밴드가 아래에 칠해진 채로 용존 산소를 보고, 7일째 온도 하락이 생산 안에 눈에 띄게 자리 잡은 것을 봅니다. 맥락이 그림 위에 있는 것입니다. 런을 비교하려면, 출시된 다섯 형제 배치에 대한 v_phase_summary를 흐릿한 포락선(envelope)으로 쌓고 그 위에 새 배치를 그립니다. 그것이 바로 골든 배치 오버레이이며, 15장에서 구축됩니다.

왜 중요한가

맥락화는 데이터 수집공정 이해 사이의 경첩입니다. 맥락화되지 않으면 히스토리언은 "그 숫자가 얼마였는가?"에만 답할 수 있습니다. 그것은 어떤 조사자도, 어떤 통계학자도, 어떤 검사관도 실제로는 결코 묻지 않는 질문입니다. 맥락화되면 히스토리언은 "그 숫자가, 어느 단계 동안, 어느 배치의, 어느 장비에서 얼마였는가?"에 답합니다. 그것이야말로 중요한 모든 질문입니다.

그것은 또한 두 가지 규제 기대의 기술적 기반(substrate)입니다. 지속적 공정 검증(Continued Process Verification) — FDA 공정 검증 생애주기의 3단계 — 은 공정이 배치를 거듭하며 제어 상태에 머무른다는, 지속적이고 문서화된 보증을 요구합니다 [10]. 가공되지 않은 태그로는 CPV를 할 수 없습니다. 단계 정렬되고 배치 키가 부여된(batch-keyed) 추세로 합니다. 바로 v_phase_summary가 만들어내는 것입니다. 그리고 ICH Q10은 공정 성능 및 제품 품질 모니터링을 제약 품질 시스템의 상시 목표로 삼아, 예외 검토(review-by-exception)와 지속적 개선을 가능하게 합니다 [11]. 단일한 재현 가능 질의로 모든 배치의 생산 단계 DO를 꺼내고, 일탈한 배치만 볼 수 있는 검토자는 예외 검토를 수행하는 것입니다. 그것은 종이를 스크롤하는 것보다 훨씬 빠르고 오류가 훨씬 적습니다.

실제 현장에서는

이 패턴은 여러 이름으로 도처에 존재합니다. 상용 히스토리언은 이를 "자산 프레임워크(asset framework)" 또는 "배치 컨텍스트(batch context)"로 판매합니다(예를 들어 AVEVA PI AF는 PI 태그 위에 장비/이벤트 모델을 얹어, 포인트 이름이 아니라 자산과 이벤트로 질의할 수 있게 합니다). MES 플랫폼은 이를 전자 배치 기록(electronic batch record)이라고 부릅니다. 우리가 두 개의 SQL 뷰로 만든 것은 모든 엔지니어가 이미 아는 개방형 관계형 기본 요소로 표현된 동일한 아이디어입니다.

OSS 대 상용에 관한 정직한 셈법은 이렇습니다. 결합 자체는 오픈 소스에서 진정으로 해결되었고, 잘 해결되었습니다. PostgreSQL의 시간적 결합, TimescaleDB의 연속 집계, FDW, 그리고 Grafana가 그 메커니즘을 완전히, 그리고 라이선스 비용 없이 다룹니다. 순수 OSS가 여러분에게 건네주지 않는 것은 그 주변의 관리입니다. 공급사가 유지보수하고 검증한 자산 모델, 포인트 앤 클릭 방식의 이벤트 프레임 구성, 컨텍스트 모델 자체에 대한 변경 관리, 그리고 GAMP-5 감사가 기대하는 공급사 책임성 말입니다. 우리 뷰로는 여러분이 컨텍스트 모델을 소유합니다. 그것은 곧 여러분이 그것을 검증하고, DDL을 버전 관리하며(이는 Git에 살아 있는데, 그것은 진짜 이점입니다), 적격성 평가(qualification) 아래에서 결합 로직이 올바름을 입증하는 일도 소유한다는 뜻입니다. 그것이 이 책에서 거듭 나타나는 모습입니다. 오픈 소스는 여러분에게 깨끗하고 검사 가능한 약 80%를 가져다주고, 그것을 감싸는 검증된 시스템 래퍼는 여러분이 만들거나 사야 할 몫입니다.

미국 독자를 위한 기반 노트: NIIMBL의 SABRE 시설(NIIMBL / 델라웨어 대학교의 파일럿 규모 cGMP 공장 — cGMP는 현행 우수 제조 관리 기준(current Good Manufacturing Practice)을 뜻하며, 2024년 4월에 착공했습니다)은 맥락화가 불가피한 바로 그런 종류의 다중 공급사·센서 풍부 라인입니다. 서로 다른 스키드(skid)에서 나오는 데이터가, 누군가 그에 관해 추론하기 전에 모두 하나의 배치와 하나의 단계로 묶여야 합니다. SABRE는 데이터 표준이 아니라 시설이지만, 이 스택 전체가 봉사하도록 만들어진 물리적 무대입니다.

핵심 용어

  • 맥락화(Contextualization) — 가공되지 않은 시계열 판독값을 그것이 속한 배치, 장비, 단계, 레시피에 결합하여, 데이터가 공정 지식으로서 질의 가능해지게 하는 것.
  • 배치 단계(batch phase, batch_phase) — 특정 ISA-88 단계가 하나의 배치에 대해 실제로 언제 가동되었는지의 기록으로, start_ts/end_ts 윈도로 주어진다. 히스토리언 결합이 대조하는 그 타임라인.
  • 시간적 결합(temporal join) — 일치 조건이 키 동등성이 아니라 시간 포함 검사(ts >= start AND ts < end)인 결합. 여기서는 각 판독값을 그 단계에 배정한다.
  • 골든 배치(golden batch) — 정상 과거 배치들로부터 구축되어 새 런이 그에 대조되는 기준 궤적. v_phase_summary는 그것의 단계 키 구성 요소이다.
  • 연속 집계(continuous aggregate) — 하이퍼테이블 위의 TimescaleDB 구체화 뷰로, 새 데이터가 도착하면 증분적으로 갱신된다. 빠른 사전 롤업 요약에 사용된다.
  • 구체화 뷰(materialized view) — 결과가 디스크에 저장되어 명시적으로 REFRESH될 때까지 제공되는 PostgreSQL 뷰. 맥락화 결합을 캐시하는 데 사용된다.
  • postgres_fdw — PostgreSQL의 외부 데이터 래퍼로, 원격 서버의 테이블을 마치 로컬인 것처럼 노출하여 하나의 뷰가 서버를 가로질러 결합할 수 있게 한다.
  • 지속적 공정 검증(Continued Process Verification, CPV) — 공정 검증의 3단계로, 공정이 제어 상태에 머무른다는 지속적 보증이며, 맥락화되고 단계 정렬된 배치 데이터로 수행된다.
  • 예외 검토(review-by-exception) — 모든 값이 아니라 일탈만 검사하는 품질 검토 관행으로, 맥락화되고 예외를 표시하는 질의에 의해 가능해진다.

다음 이야기

우리는 모든 판독값에 의미를 부여했고, 시드된 골든 배치를 대상으로 한 실제 질의로 결합을 입증했습니다. 하지만 SQL 출력은 숫자의 벽이고, 공정 제어는 시각적 분야입니다. 15장 — Grafana로 시각화와 추세 보기에서 우리는 이 장의 v_batch_sensor 추세와 batch_phase 주석 질의를 가져다 프로비저닝된 코드형 대시보드(dashboards-as-code)로 바꿉니다. 레시피 단계가 추세 뒤에 칠해지고, 어제의 골든 배치가 오늘의 런 뒤에 흐릿하게 그려지는 배치 오버레이 대시보드입니다.