feat(report): P1b 연속집계 history_1min — 1초 버퍼 60초 롤업(장기 무손실)
온라인 히스토리안 2계층: history_1s(최근 14일, 고해상) + history_1min(장기, 60초).
- history_1min 연속집계(timescaledb.continuous): time_bucket('1 min') + last(value)/last(controller_id).
refresh 정책(start 3h, end 10min, 5분마다) → 집계 lag ≪ 보존윈도(14일)이라 1초 raw evict 전 materialize.
- Hc900FastHistoryService.EnsureSchemaAsync에 cagg 생성+정책 멱등 추가. SQL 파일 동기화.
검증: 2계층 값 일치(1s last == 1min cagg), 정책 활성, 무손실 불변식 충족.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -20,3 +20,16 @@ SELECT add_compression_policy('hc900.history_1s', INTERVAL '6 hours', if_not_exi
|
|||||||
SELECT add_retention_policy('hc900.history_1s', INTERVAL '14 days', if_not_exists => TRUE);
|
SELECT add_retention_policy('hc900.history_1s', INTERVAL '14 days', if_not_exists => TRUE);
|
||||||
|
|
||||||
CREATE INDEX IF NOT EXISTS ix_h1s_tag ON hc900.history_1s (tagname, recorded_at DESC);
|
CREATE INDEX IF NOT EXISTS ix_h1s_tag ON hc900.history_1s (tagname, recorded_at DESC);
|
||||||
|
|
||||||
|
-- P1b: 연속집계 history_1min — 1초 버퍼 evict 전 60초 롤업(장기 무손실, 2계층)
|
||||||
|
CREATE MATERIALIZED VIEW IF NOT EXISTS hc900.history_1min
|
||||||
|
WITH (timescaledb.continuous) AS
|
||||||
|
SELECT time_bucket('1 minute', recorded_at) AS bucket, tagname,
|
||||||
|
last(value, recorded_at) AS value, last(controller_id, recorded_at) AS controller_id
|
||||||
|
FROM hc900.history_1s GROUP BY bucket, tagname
|
||||||
|
WITH NO DATA;
|
||||||
|
|
||||||
|
-- 집계 lag(≤3h) ≪ 보존 윈도(14일) → raw 삭제 전 materialize 보장
|
||||||
|
SELECT add_continuous_aggregate_policy('hc900.history_1min',
|
||||||
|
start_offset => INTERVAL '3 hours', end_offset => INTERVAL '10 minutes',
|
||||||
|
schedule_interval => INTERVAL '5 minutes', if_not_exists => TRUE);
|
||||||
|
|||||||
@@ -90,6 +90,15 @@ WHERE tagname = ANY(@tags)";
|
|||||||
"SELECT add_compression_policy('hc900.history_1s', INTERVAL '6 hours', if_not_exists => TRUE)",
|
"SELECT add_compression_policy('hc900.history_1s', INTERVAL '6 hours', if_not_exists => TRUE)",
|
||||||
$"SELECT add_retention_policy('hc900.history_1s', INTERVAL '{_retentionDays} days', if_not_exists => TRUE)",
|
$"SELECT add_retention_policy('hc900.history_1s', INTERVAL '{_retentionDays} days', if_not_exists => TRUE)",
|
||||||
"CREATE INDEX IF NOT EXISTS ix_h1s_tag ON hc900.history_1s (tagname, recorded_at DESC)",
|
"CREATE INDEX IF NOT EXISTS ix_h1s_tag ON hc900.history_1s (tagname, recorded_at DESC)",
|
||||||
|
// P1b: 연속집계 — 1초 버퍼 evict 전 60초 롤업(장기 무손실)
|
||||||
|
@"CREATE MATERIALIZED VIEW IF NOT EXISTS hc900.history_1min
|
||||||
|
WITH (timescaledb.continuous) AS
|
||||||
|
SELECT time_bucket('1 minute', recorded_at) AS bucket, tagname,
|
||||||
|
last(value, recorded_at) AS value, last(controller_id, recorded_at) AS controller_id
|
||||||
|
FROM hc900.history_1s GROUP BY bucket, tagname WITH NO DATA",
|
||||||
|
@"SELECT add_continuous_aggregate_policy('hc900.history_1min',
|
||||||
|
start_offset => INTERVAL '3 hours', end_offset => INTERVAL '10 minutes',
|
||||||
|
schedule_interval => INTERVAL '5 minutes', if_not_exists => TRUE)",
|
||||||
};
|
};
|
||||||
foreach (var s in stmts)
|
foreach (var s in stmts)
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user