Add history-based state changes and recompute pipeline

This commit is contained in:
2026-02-22 19:52:07 +03:00
parent c84102d2f1
commit ec54d3249e
22 changed files with 4973 additions and 32 deletions

View File

@@ -0,0 +1,104 @@
DROP TABLE IF EXISTS history_admin_audit;
DROP TABLE IF EXISTS history_recompute_jobs;
DROP TABLE IF EXISTS component_state_snapshots;
DROP TABLE IF EXISTS component_change_events;
DROP TABLE IF EXISTS asset_state_snapshots;
DROP TABLE IF EXISTS asset_change_events;
SET @sql = (
SELECT IF(
EXISTS(
SELECT 1 FROM information_schema.statistics
WHERE table_schema = DATABASE()
AND table_name = 'timeline_events'
AND index_name = 'idx_timeline_subject_deleted_time'
),
'ALTER TABLE timeline_events DROP INDEX idx_timeline_subject_deleted_time',
'SELECT 1'
)
);
PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt;
SET @sql = (
SELECT IF(
EXISTS(
SELECT 1 FROM information_schema.statistics
WHERE table_schema = DATABASE()
AND table_name = 'timeline_events'
AND index_name = 'idx_timeline_logical_event'
),
'ALTER TABLE timeline_events DROP INDEX idx_timeline_logical_event',
'SELECT 1'
)
);
PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt;
SET @sql = (
SELECT IF(
EXISTS(
SELECT 1 FROM information_schema.statistics
WHERE table_schema = DATABASE()
AND table_name = 'timeline_events'
AND index_name = 'idx_timeline_correlation'
),
'ALTER TABLE timeline_events DROP INDEX idx_timeline_correlation',
'SELECT 1'
)
);
PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt;
SET @sql = (
SELECT IF(
EXISTS(
SELECT 1 FROM information_schema.columns
WHERE table_schema = DATABASE()
AND table_name = 'timeline_events'
AND column_name = 'logical_entity_type'
),
'ALTER TABLE timeline_events DROP COLUMN logical_entity_type',
'SELECT 1'
)
);
PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt;
SET @sql = (
SELECT IF(
EXISTS(
SELECT 1 FROM information_schema.columns
WHERE table_schema = DATABASE()
AND table_name = 'timeline_events'
AND column_name = 'logical_event_id'
),
'ALTER TABLE timeline_events DROP COLUMN logical_event_id',
'SELECT 1'
)
);
PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt;
SET @sql = (
SELECT IF(
EXISTS(
SELECT 1 FROM information_schema.columns
WHERE table_schema = DATABASE()
AND table_name = 'timeline_events'
AND column_name = 'correlation_id'
),
'ALTER TABLE timeline_events DROP COLUMN correlation_id',
'SELECT 1'
)
);
PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt;
SET @sql = (
SELECT IF(
EXISTS(
SELECT 1 FROM information_schema.columns
WHERE table_schema = DATABASE()
AND table_name = 'timeline_events'
AND column_name = 'is_deleted'
),
'ALTER TABLE timeline_events DROP COLUMN is_deleted',
'SELECT 1'
)
);
PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt;

View File

@@ -0,0 +1,159 @@
CREATE TABLE IF NOT EXISTS component_change_events (
id VARCHAR(16) PRIMARY KEY,
part_id VARCHAR(16) NOT NULL,
version BIGINT NOT NULL,
effective_at TIMESTAMP NOT NULL,
recorded_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
source_type VARCHAR(32) NOT NULL,
source_ref VARCHAR(255) NULL,
actor_type VARCHAR(32) NOT NULL,
actor_id VARCHAR(255) NULL,
change_type VARCHAR(64) NOT NULL,
correlation_id VARCHAR(64) NULL,
idempotency_key VARCHAR(255) NULL,
patch_json JSON NOT NULL,
before_hash CHAR(64) NULL,
after_hash CHAR(64) NOT NULL,
is_deleted BOOLEAN NOT NULL DEFAULT FALSE,
deleted_at TIMESTAMP NULL,
deleted_by VARCHAR(255) NULL,
deleted_reason VARCHAR(255) NULL,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT fk_component_change_events_part
FOREIGN KEY (part_id) REFERENCES parts(id)
ON DELETE CASCADE ON UPDATE CASCADE,
UNIQUE KEY uniq_component_change_events_part_version (part_id, version),
INDEX idx_component_change_events_part_deleted_version (part_id, is_deleted, version),
INDEX idx_component_change_events_part_effective (part_id, effective_at, id),
INDEX idx_component_change_events_correlation (correlation_id),
INDEX idx_component_change_events_source_idem (part_id, source_type, idempotency_key)
);
CREATE TABLE IF NOT EXISTS component_state_snapshots (
id VARCHAR(16) PRIMARY KEY,
part_id VARCHAR(16) NOT NULL,
event_id VARCHAR(16) NOT NULL,
version BIGINT NOT NULL,
state_json JSON NOT NULL,
state_hash CHAR(64) NOT NULL,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT fk_component_state_snapshots_part
FOREIGN KEY (part_id) REFERENCES parts(id)
ON DELETE CASCADE ON UPDATE CASCADE,
CONSTRAINT fk_component_state_snapshots_event
FOREIGN KEY (event_id) REFERENCES component_change_events(id)
ON DELETE CASCADE ON UPDATE CASCADE,
UNIQUE KEY uniq_component_state_snapshots_event (event_id),
UNIQUE KEY uniq_component_state_snapshots_part_version (part_id, version),
INDEX idx_component_state_snapshots_part_version (part_id, version)
);
CREATE TABLE IF NOT EXISTS asset_change_events (
id VARCHAR(16) PRIMARY KEY,
machine_id VARCHAR(16) NOT NULL,
version BIGINT NOT NULL,
effective_at TIMESTAMP NOT NULL,
recorded_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
source_type VARCHAR(32) NOT NULL,
source_ref VARCHAR(255) NULL,
actor_type VARCHAR(32) NOT NULL,
actor_id VARCHAR(255) NULL,
change_type VARCHAR(64) NOT NULL,
correlation_id VARCHAR(64) NULL,
idempotency_key VARCHAR(255) NULL,
patch_json JSON NOT NULL,
before_hash CHAR(64) NULL,
after_hash CHAR(64) NOT NULL,
is_deleted BOOLEAN NOT NULL DEFAULT FALSE,
deleted_at TIMESTAMP NULL,
deleted_by VARCHAR(255) NULL,
deleted_reason VARCHAR(255) NULL,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT fk_asset_change_events_machine
FOREIGN KEY (machine_id) REFERENCES machines(id)
ON DELETE CASCADE ON UPDATE CASCADE,
UNIQUE KEY uniq_asset_change_events_machine_version (machine_id, version),
INDEX idx_asset_change_events_machine_deleted_version (machine_id, is_deleted, version),
INDEX idx_asset_change_events_machine_effective (machine_id, effective_at, id),
INDEX idx_asset_change_events_correlation (correlation_id),
INDEX idx_asset_change_events_source_idem (machine_id, source_type, idempotency_key)
);
CREATE TABLE IF NOT EXISTS asset_state_snapshots (
id VARCHAR(16) PRIMARY KEY,
machine_id VARCHAR(16) NOT NULL,
event_id VARCHAR(16) NOT NULL,
version BIGINT NOT NULL,
state_json JSON NOT NULL,
state_hash CHAR(64) NOT NULL,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT fk_asset_state_snapshots_machine
FOREIGN KEY (machine_id) REFERENCES machines(id)
ON DELETE CASCADE ON UPDATE CASCADE,
CONSTRAINT fk_asset_state_snapshots_event
FOREIGN KEY (event_id) REFERENCES asset_change_events(id)
ON DELETE CASCADE ON UPDATE CASCADE,
UNIQUE KEY uniq_asset_state_snapshots_event (event_id),
UNIQUE KEY uniq_asset_state_snapshots_machine_version (machine_id, version),
INDEX idx_asset_state_snapshots_machine_version (machine_id, version)
);
CREATE TABLE IF NOT EXISTS history_recompute_jobs (
id VARCHAR(32) PRIMARY KEY,
job_type VARCHAR(32) NOT NULL,
entity_type VARCHAR(16) NOT NULL,
entity_id VARCHAR(16) NOT NULL,
status VARCHAR(16) NOT NULL,
requested_by VARCHAR(255) NULL,
payload JSON NOT NULL,
result JSON NULL,
error TEXT NULL,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
started_at TIMESTAMP NULL,
finished_at TIMESTAMP NULL,
locked_at TIMESTAMP NULL,
worker_id VARCHAR(64) NULL,
attempt_count INT NOT NULL DEFAULT 0,
INDEX idx_history_recompute_jobs_status_created (status, created_at),
INDEX idx_history_recompute_jobs_entity_created (entity_type, entity_id, created_at)
);
CREATE TABLE IF NOT EXISTS history_admin_audit (
id VARCHAR(16) PRIMARY KEY,
entity_type VARCHAR(16) NOT NULL,
entity_id VARCHAR(16) NOT NULL,
operation_type VARCHAR(32) NOT NULL,
requested_by VARCHAR(255) NULL,
requested_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
target_version BIGINT NULL,
target_snapshot_id VARCHAR(16) NULL,
deleted_future_events_count INT NOT NULL DEFAULT 0,
deleted_future_snapshots_count INT NOT NULL DEFAULT 0,
job_id VARCHAR(32) NOT NULL,
details JSON NULL,
INDEX idx_history_admin_audit_entity_requested (entity_type, entity_id, requested_at),
INDEX idx_history_admin_audit_job (job_id)
);
ALTER TABLE timeline_events
ADD COLUMN logical_entity_type VARCHAR(16) NULL;
ALTER TABLE timeline_events
ADD COLUMN logical_event_id VARCHAR(16) NULL;
ALTER TABLE timeline_events
ADD COLUMN correlation_id VARCHAR(64) NULL;
ALTER TABLE timeline_events
ADD COLUMN is_deleted BOOLEAN NOT NULL DEFAULT FALSE;
CREATE INDEX idx_timeline_subject_deleted_time ON timeline_events(subject_type, subject_id, is_deleted, event_time, id);
CREATE INDEX idx_timeline_logical_event ON timeline_events(logical_entity_type, logical_event_id);
CREATE INDEX idx_timeline_correlation ON timeline_events(correlation_id);
INSERT IGNORE INTO id_sequences (entity_type, next_value) VALUES
('component_change_event', 1),
('component_snapshot', 1),
('asset_change_event', 1),
('asset_snapshot', 1),
('history_admin_audit', 1);