release orchestrator pivot, architecture and planning
This commit is contained in:
343
docs/modules/release-orchestrator/data-model/entities.md
Normal file
343
docs/modules/release-orchestrator/data-model/entities.md
Normal file
@@ -0,0 +1,343 @@
|
||||
# Entity Definitions
|
||||
|
||||
This document describes the core entities in the Release Orchestrator data model.
|
||||
|
||||
## Entity Relationship Overview
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────────────────┐
|
||||
│ ENTITY RELATIONSHIPS │
|
||||
│ │
|
||||
│ ┌──────────┐ ┌──────────────┐ ┌────────────┐ │
|
||||
│ │ Tenant │───────│ Environment │───────│ Target │ │
|
||||
│ └──────────┘ └──────────────┘ └────────────┘ │
|
||||
│ │ │ │ │
|
||||
│ │ │ │ │
|
||||
│ ▼ ▼ ▼ │
|
||||
│ ┌──────────┐ ┌──────────────┐ ┌────────────┐ │
|
||||
│ │ Component│ │ Approval │ │ Agent │ │
|
||||
│ └──────────┘ │ Policy │ └────────────┘ │
|
||||
│ │ └──────────────┘ │ │
|
||||
│ │ │ │ │
|
||||
│ ▼ │ ▼ │
|
||||
│ ┌──────────┐ │ ┌─────────────┐ │
|
||||
│ │ Version │ │ │ Deployment │ │
|
||||
│ │ Map │ │ │ Task │ │
|
||||
│ └──────────┘ │ └─────────────┘ │
|
||||
│ │ │ │ │
|
||||
│ │ │ │ │
|
||||
│ ▼ │ ▼ │
|
||||
│ ┌─────────────────────────┼─────────────────────────────┐ │
|
||||
│ │ │ │ │
|
||||
│ │ ┌──────────┐ ┌─────▼─────┐ ┌─────────────┐ │ │
|
||||
│ │ │ Release │─────│ Promotion │─────│ Deployment │ │ │
|
||||
│ │ └──────────┘ └───────────┘ │ Job │ │ │
|
||||
│ │ │ │ └─────────────┘ │ │
|
||||
│ │ │ │ │ │ │
|
||||
│ │ │ ▼ │ │ │
|
||||
│ │ │ ┌───────────┐ │ │ │
|
||||
│ │ │ │ Approval │ │ │ │
|
||||
│ │ │ └───────────┘ │ │ │
|
||||
│ │ │ │ │ │ │
|
||||
│ │ │ ▼ ▼ │ │
|
||||
│ │ │ ┌───────────┐ ┌───────────┐ │ │
|
||||
│ │ │ │ Decision │ │ Generated │ │ │
|
||||
│ │ │ │ Record │ │ Artifacts │ │ │
|
||||
│ │ │ └───────────┘ └───────────┘ │ │
|
||||
│ │ │ │ │ │ │
|
||||
│ │ │ └────────┬────────┘ │ │
|
||||
│ │ │ │ │ │
|
||||
│ │ │ ▼ │ │
|
||||
│ │ │ ┌───────────┐ │ │
|
||||
│ │ └───────────────────►│ Evidence │◄────────────┘ │
|
||||
│ │ │ Packet │ │
|
||||
│ │ └───────────┘ │
|
||||
│ │ │ │
|
||||
│ │ ▼ │
|
||||
│ │ ┌───────────┐ │
|
||||
│ │ │ Version │ │
|
||||
│ │ │ Sticker │ │
|
||||
│ │ └───────────┘ │
|
||||
│ │ │
|
||||
│ └─────────────────────────────────────────────────────────────────────────┘
|
||||
└─────────────────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
## Core Entities
|
||||
|
||||
### Environment
|
||||
|
||||
Represents a deployment target environment (dev, staging, production).
|
||||
|
||||
| Field | Type | Description |
|
||||
|-------|------|-------------|
|
||||
| `id` | UUID | Primary key |
|
||||
| `tenant_id` | UUID | Tenant reference |
|
||||
| `name` | string | Unique name (e.g., "prod") |
|
||||
| `display_name` | string | Display name (e.g., "Production") |
|
||||
| `order_index` | integer | Promotion order |
|
||||
| `config` | JSONB | Environment configuration |
|
||||
| `freeze_windows` | JSONB | Active freeze windows |
|
||||
| `required_approvals` | integer | Approvals needed for promotion |
|
||||
| `require_sod` | boolean | Require separation of duties |
|
||||
| `created_at` | timestamp | Creation time |
|
||||
|
||||
### Target
|
||||
|
||||
Represents a deployment target (host, service).
|
||||
|
||||
| Field | Type | Description |
|
||||
|-------|------|-------------|
|
||||
| `id` | UUID | Primary key |
|
||||
| `tenant_id` | UUID | Tenant reference |
|
||||
| `environment_id` | UUID | Environment reference |
|
||||
| `name` | string | Target name |
|
||||
| `target_type` | string | Type (docker_host, compose_host, etc.) |
|
||||
| `connection` | JSONB | Connection configuration |
|
||||
| `labels` | JSONB | Target labels |
|
||||
| `health_status` | string | Current health status |
|
||||
| `current_digest` | string | Currently deployed digest |
|
||||
|
||||
### Agent
|
||||
|
||||
Represents a deployment agent.
|
||||
|
||||
| Field | Type | Description |
|
||||
|-------|------|-------------|
|
||||
| `id` | UUID | Primary key |
|
||||
| `tenant_id` | UUID | Tenant reference |
|
||||
| `name` | string | Agent name |
|
||||
| `version` | string | Agent version |
|
||||
| `capabilities` | JSONB | Agent capabilities |
|
||||
| `status` | string | online/offline/degraded |
|
||||
| `last_heartbeat` | timestamp | Last heartbeat time |
|
||||
|
||||
### Component
|
||||
|
||||
Represents a deployable component (maps to an image repository).
|
||||
|
||||
| Field | Type | Description |
|
||||
|-------|------|-------------|
|
||||
| `id` | UUID | Primary key |
|
||||
| `tenant_id` | UUID | Tenant reference |
|
||||
| `name` | string | Component name |
|
||||
| `display_name` | string | Display name |
|
||||
| `image_repository` | string | Image repository URL |
|
||||
| `versioning_strategy` | JSONB | How versions are determined |
|
||||
| `default_channel` | string | Default version channel |
|
||||
|
||||
### Version Map
|
||||
|
||||
Maps image tags to digests and semantic versions.
|
||||
|
||||
| Field | Type | Description |
|
||||
|-------|------|-------------|
|
||||
| `id` | UUID | Primary key |
|
||||
| `component_id` | UUID | Component reference |
|
||||
| `tag` | string | Image tag |
|
||||
| `digest` | string | Image digest (sha256:...) |
|
||||
| `semver` | string | Semantic version |
|
||||
| `channel` | string | Version channel (stable, beta) |
|
||||
|
||||
### Release
|
||||
|
||||
A versioned bundle of component digests.
|
||||
|
||||
| Field | Type | Description |
|
||||
|-------|------|-------------|
|
||||
| `id` | UUID | Primary key |
|
||||
| `tenant_id` | UUID | Tenant reference |
|
||||
| `name` | string | Release name |
|
||||
| `display_name` | string | Display name |
|
||||
| `components` | JSONB | Component/digest mappings |
|
||||
| `source_ref` | JSONB | Source code reference |
|
||||
| `status` | string | draft/ready/deployed/deprecated |
|
||||
| `created_by` | UUID | Creator user reference |
|
||||
|
||||
### Promotion
|
||||
|
||||
A request to promote a release to an environment.
|
||||
|
||||
| Field | Type | Description |
|
||||
|-------|------|-------------|
|
||||
| `id` | UUID | Primary key |
|
||||
| `tenant_id` | UUID | Tenant reference |
|
||||
| `release_id` | UUID | Release reference |
|
||||
| `source_environment_id` | UUID | Source environment (nullable) |
|
||||
| `target_environment_id` | UUID | Target environment |
|
||||
| `status` | string | Promotion status |
|
||||
| `decision_record` | JSONB | Gate evaluation results |
|
||||
| `workflow_run_id` | UUID | Associated workflow run |
|
||||
| `requested_by` | UUID | Requesting user |
|
||||
| `requested_at` | timestamp | Request time |
|
||||
|
||||
### Approval
|
||||
|
||||
An approval or rejection of a promotion.
|
||||
|
||||
| Field | Type | Description |
|
||||
|-------|------|-------------|
|
||||
| `id` | UUID | Primary key |
|
||||
| `promotion_id` | UUID | Promotion reference |
|
||||
| `approver_id` | UUID | Approving user |
|
||||
| `action` | string | approved/rejected |
|
||||
| `comment` | string | Approval comment |
|
||||
| `approved_at` | timestamp | Approval time |
|
||||
|
||||
### Deployment Job
|
||||
|
||||
A deployment execution job.
|
||||
|
||||
| Field | Type | Description |
|
||||
|-------|------|-------------|
|
||||
| `id` | UUID | Primary key |
|
||||
| `promotion_id` | UUID | Promotion reference |
|
||||
| `release_id` | UUID | Release reference |
|
||||
| `environment_id` | UUID | Environment reference |
|
||||
| `status` | string | Job status |
|
||||
| `strategy` | string | Deployment strategy |
|
||||
| `artifacts` | JSONB | Generated artifacts |
|
||||
| `rollback_of` | UUID | If rollback, original job |
|
||||
|
||||
### Deployment Task
|
||||
|
||||
A task to deploy to a single target.
|
||||
|
||||
| Field | Type | Description |
|
||||
|-------|------|-------------|
|
||||
| `id` | UUID | Primary key |
|
||||
| `job_id` | UUID | Job reference |
|
||||
| `target_id` | UUID | Target reference |
|
||||
| `digest` | string | Digest to deploy |
|
||||
| `status` | string | Task status |
|
||||
| `agent_id` | UUID | Assigned agent |
|
||||
| `logs` | text | Execution logs |
|
||||
| `previous_digest` | string | Previous digest (for rollback) |
|
||||
|
||||
### Evidence Packet
|
||||
|
||||
Immutable audit evidence for a promotion/deployment.
|
||||
|
||||
| Field | Type | Description |
|
||||
|-------|------|-------------|
|
||||
| `id` | UUID | Primary key |
|
||||
| `promotion_id` | UUID | Promotion reference |
|
||||
| `packet_type` | string | Type of evidence |
|
||||
| `content` | JSONB | Evidence content |
|
||||
| `content_hash` | string | SHA-256 of content |
|
||||
| `signature` | string | Cryptographic signature |
|
||||
| `signer_key_ref` | string | Signing key reference |
|
||||
| `created_at` | timestamp | Creation time (no update) |
|
||||
|
||||
### Version Sticker
|
||||
|
||||
Version marker placed on deployment targets.
|
||||
|
||||
| Field | Type | Description |
|
||||
|-------|------|-------------|
|
||||
| `id` | UUID | Primary key |
|
||||
| `target_id` | UUID | Target reference |
|
||||
| `release_id` | UUID | Release reference |
|
||||
| `promotion_id` | UUID | Promotion reference |
|
||||
| `sticker_content` | JSONB | Sticker JSON content |
|
||||
| `content_hash` | string | Content hash |
|
||||
| `written_at` | timestamp | Write time |
|
||||
| `drift_detected` | boolean | Drift detection flag |
|
||||
|
||||
## Workflow Entities
|
||||
|
||||
### Workflow Template
|
||||
|
||||
A reusable workflow definition.
|
||||
|
||||
| Field | Type | Description |
|
||||
|-------|------|-------------|
|
||||
| `id` | UUID | Primary key |
|
||||
| `tenant_id` | UUID | Tenant reference (null for builtin) |
|
||||
| `name` | string | Template name |
|
||||
| `version` | integer | Template version |
|
||||
| `nodes` | JSONB | Step nodes |
|
||||
| `edges` | JSONB | Step edges |
|
||||
| `inputs` | JSONB | Input definitions |
|
||||
| `outputs` | JSONB | Output definitions |
|
||||
| `is_builtin` | boolean | Is built-in template |
|
||||
|
||||
### Workflow Run
|
||||
|
||||
An execution of a workflow template.
|
||||
|
||||
| Field | Type | Description |
|
||||
|-------|------|-------------|
|
||||
| `id` | UUID | Primary key |
|
||||
| `template_id` | UUID | Template reference |
|
||||
| `template_version` | integer | Template version at execution |
|
||||
| `status` | string | Run status |
|
||||
| `context` | JSONB | Execution context |
|
||||
| `inputs` | JSONB | Input values |
|
||||
| `outputs` | JSONB | Output values |
|
||||
| `started_at` | timestamp | Start time |
|
||||
| `completed_at` | timestamp | Completion time |
|
||||
|
||||
### Step Run
|
||||
|
||||
Execution of a single step within a workflow run.
|
||||
|
||||
| Field | Type | Description |
|
||||
|-------|------|-------------|
|
||||
| `id` | UUID | Primary key |
|
||||
| `workflow_run_id` | UUID | Workflow run reference |
|
||||
| `node_id` | string | Node ID from template |
|
||||
| `status` | string | Step status |
|
||||
| `inputs` | JSONB | Resolved inputs |
|
||||
| `outputs` | JSONB | Produced outputs |
|
||||
| `logs` | text | Execution logs |
|
||||
| `attempt_number` | integer | Retry attempt number |
|
||||
|
||||
## Plugin Entities
|
||||
|
||||
### Plugin
|
||||
|
||||
A registered plugin.
|
||||
|
||||
| Field | Type | Description |
|
||||
|-------|------|-------------|
|
||||
| `id` | UUID | Primary key |
|
||||
| `plugin_id` | string | Unique plugin identifier |
|
||||
| `version` | string | Plugin version |
|
||||
| `vendor` | string | Plugin vendor |
|
||||
| `manifest` | JSONB | Plugin manifest |
|
||||
| `status` | string | Plugin status |
|
||||
| `entrypoint` | string | Plugin entrypoint path |
|
||||
|
||||
### Plugin Instance
|
||||
|
||||
A tenant-specific plugin configuration.
|
||||
|
||||
| Field | Type | Description |
|
||||
|-------|------|-------------|
|
||||
| `id` | UUID | Primary key |
|
||||
| `plugin_id` | UUID | Plugin reference |
|
||||
| `tenant_id` | UUID | Tenant reference |
|
||||
| `config` | JSONB | Tenant configuration |
|
||||
| `enabled` | boolean | Is enabled for tenant |
|
||||
|
||||
## Integration Entities
|
||||
|
||||
### Integration
|
||||
|
||||
A configured external integration.
|
||||
|
||||
| Field | Type | Description |
|
||||
|-------|------|-------------|
|
||||
| `id` | UUID | Primary key |
|
||||
| `tenant_id` | UUID | Tenant reference |
|
||||
| `type_id` | string | Integration type |
|
||||
| `name` | string | Integration name |
|
||||
| `config` | JSONB | Integration configuration |
|
||||
| `credential_ref` | string | Vault credential reference |
|
||||
| `health_status` | string | Connection health |
|
||||
|
||||
## References
|
||||
|
||||
- [Database Schema](schema.md)
|
||||
- [Module Overview](../modules/overview.md)
|
||||
631
docs/modules/release-orchestrator/data-model/schema.md
Normal file
631
docs/modules/release-orchestrator/data-model/schema.md
Normal file
@@ -0,0 +1,631 @@
|
||||
# Database Schema (PostgreSQL)
|
||||
|
||||
This document specifies the complete PostgreSQL schema for the Release Orchestrator.
|
||||
|
||||
## Schema Organization
|
||||
|
||||
All release orchestration tables reside in the `release` schema:
|
||||
|
||||
```sql
|
||||
CREATE SCHEMA IF NOT EXISTS release;
|
||||
SET search_path TO release, public;
|
||||
```
|
||||
|
||||
## Core Tables
|
||||
|
||||
### Tenant and Authority Extensions
|
||||
|
||||
```sql
|
||||
-- Extended: Add release-related permissions
|
||||
ALTER TABLE permissions ADD COLUMN IF NOT EXISTS
|
||||
resource_type VARCHAR(50) CHECK (resource_type IN (
|
||||
'environment', 'release', 'promotion', 'target', 'workflow', 'plugin'
|
||||
));
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Integration Hub
|
||||
|
||||
```sql
|
||||
CREATE TABLE integration_types (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
name VARCHAR(100) NOT NULL UNIQUE,
|
||||
category VARCHAR(50) NOT NULL CHECK (category IN (
|
||||
'scm', 'ci', 'registry', 'vault', 'target', 'router'
|
||||
)),
|
||||
plugin_id UUID REFERENCES plugins(id),
|
||||
config_schema JSONB NOT NULL,
|
||||
secrets_schema JSONB NOT NULL,
|
||||
is_builtin BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE TABLE integrations (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
tenant_id UUID NOT NULL REFERENCES tenants(id) ON DELETE CASCADE,
|
||||
integration_type_id UUID NOT NULL REFERENCES integration_types(id),
|
||||
name VARCHAR(255) NOT NULL,
|
||||
config JSONB NOT NULL,
|
||||
credential_ref VARCHAR(500), -- Vault path or encrypted ref
|
||||
status VARCHAR(50) NOT NULL DEFAULT 'unknown' CHECK (status IN (
|
||||
'healthy', 'degraded', 'unhealthy', 'unknown'
|
||||
)),
|
||||
last_health_check TIMESTAMPTZ,
|
||||
last_health_message TEXT,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
created_by UUID REFERENCES users(id),
|
||||
UNIQUE (tenant_id, name)
|
||||
);
|
||||
|
||||
CREATE INDEX idx_integrations_tenant ON integrations(tenant_id);
|
||||
CREATE INDEX idx_integrations_type ON integrations(integration_type_id);
|
||||
|
||||
CREATE TABLE connection_profiles (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
tenant_id UUID NOT NULL REFERENCES tenants(id) ON DELETE CASCADE,
|
||||
user_id UUID NOT NULL REFERENCES users(id),
|
||||
integration_type_id UUID NOT NULL REFERENCES integration_types(id),
|
||||
name VARCHAR(255) NOT NULL,
|
||||
config_defaults JSONB NOT NULL,
|
||||
is_default BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
UNIQUE (tenant_id, user_id, integration_type_id, name)
|
||||
);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Environment & Inventory
|
||||
|
||||
```sql
|
||||
CREATE TABLE environments (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
tenant_id UUID NOT NULL REFERENCES tenants(id) ON DELETE CASCADE,
|
||||
name VARCHAR(100) NOT NULL,
|
||||
display_name VARCHAR(255) NOT NULL,
|
||||
order_index INTEGER NOT NULL,
|
||||
config JSONB NOT NULL DEFAULT '{}',
|
||||
freeze_windows JSONB NOT NULL DEFAULT '[]',
|
||||
required_approvals INTEGER NOT NULL DEFAULT 0,
|
||||
require_sod BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
auto_promote_from UUID REFERENCES environments(id),
|
||||
promotion_policy VARCHAR(255),
|
||||
deployment_timeout INTEGER NOT NULL DEFAULT 600,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
UNIQUE (tenant_id, name)
|
||||
);
|
||||
|
||||
CREATE INDEX idx_environments_tenant ON environments(tenant_id);
|
||||
CREATE INDEX idx_environments_order ON environments(tenant_id, order_index);
|
||||
|
||||
CREATE TABLE target_groups (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
tenant_id UUID NOT NULL REFERENCES tenants(id) ON DELETE CASCADE,
|
||||
environment_id UUID NOT NULL REFERENCES environments(id) ON DELETE CASCADE,
|
||||
name VARCHAR(255) NOT NULL,
|
||||
labels JSONB NOT NULL DEFAULT '{}',
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
UNIQUE (tenant_id, environment_id, name)
|
||||
);
|
||||
|
||||
CREATE TABLE targets (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
tenant_id UUID NOT NULL REFERENCES tenants(id) ON DELETE CASCADE,
|
||||
environment_id UUID NOT NULL REFERENCES environments(id) ON DELETE CASCADE,
|
||||
target_group_id UUID REFERENCES target_groups(id),
|
||||
name VARCHAR(255) NOT NULL,
|
||||
target_type VARCHAR(100) NOT NULL,
|
||||
connection JSONB NOT NULL,
|
||||
capabilities JSONB NOT NULL DEFAULT '[]',
|
||||
labels JSONB NOT NULL DEFAULT '{}',
|
||||
deployment_directory VARCHAR(500),
|
||||
health_status VARCHAR(50) NOT NULL DEFAULT 'unknown' CHECK (health_status IN (
|
||||
'healthy', 'degraded', 'unhealthy', 'unknown'
|
||||
)),
|
||||
last_health_check TIMESTAMPTZ,
|
||||
current_digest VARCHAR(100),
|
||||
agent_id UUID REFERENCES agents(id),
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
UNIQUE (tenant_id, environment_id, name)
|
||||
);
|
||||
|
||||
CREATE INDEX idx_targets_tenant_env ON targets(tenant_id, environment_id);
|
||||
CREATE INDEX idx_targets_type ON targets(target_type);
|
||||
CREATE INDEX idx_targets_labels ON targets USING GIN (labels);
|
||||
|
||||
CREATE TABLE agents (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
tenant_id UUID NOT NULL REFERENCES tenants(id) ON DELETE CASCADE,
|
||||
name VARCHAR(255) NOT NULL,
|
||||
version VARCHAR(50) NOT NULL,
|
||||
capabilities JSONB NOT NULL DEFAULT '[]',
|
||||
labels JSONB NOT NULL DEFAULT '{}',
|
||||
status VARCHAR(50) NOT NULL DEFAULT 'offline' CHECK (status IN (
|
||||
'online', 'offline', 'degraded'
|
||||
)),
|
||||
last_heartbeat TIMESTAMPTZ,
|
||||
resource_usage JSONB,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
UNIQUE (tenant_id, name)
|
||||
);
|
||||
|
||||
CREATE INDEX idx_agents_tenant ON agents(tenant_id);
|
||||
CREATE INDEX idx_agents_status ON agents(status);
|
||||
CREATE INDEX idx_agents_capabilities ON agents USING GIN (capabilities);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Release Management
|
||||
|
||||
```sql
|
||||
CREATE TABLE components (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
tenant_id UUID NOT NULL REFERENCES tenants(id) ON DELETE CASCADE,
|
||||
name VARCHAR(255) NOT NULL,
|
||||
display_name VARCHAR(255) NOT NULL,
|
||||
image_repository VARCHAR(500) NOT NULL,
|
||||
registry_integration_id UUID REFERENCES integrations(id),
|
||||
versioning_strategy JSONB NOT NULL DEFAULT '{"type": "semver"}',
|
||||
deployment_template VARCHAR(255),
|
||||
default_channel VARCHAR(50) NOT NULL DEFAULT 'stable',
|
||||
metadata JSONB NOT NULL DEFAULT '{}',
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
UNIQUE (tenant_id, name)
|
||||
);
|
||||
|
||||
CREATE INDEX idx_components_tenant ON components(tenant_id);
|
||||
|
||||
CREATE TABLE version_maps (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
tenant_id UUID NOT NULL REFERENCES tenants(id) ON DELETE CASCADE,
|
||||
component_id UUID NOT NULL REFERENCES components(id) ON DELETE CASCADE,
|
||||
tag VARCHAR(255) NOT NULL,
|
||||
digest VARCHAR(100) NOT NULL,
|
||||
semver VARCHAR(50),
|
||||
channel VARCHAR(50) NOT NULL DEFAULT 'stable',
|
||||
prerelease BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
build_metadata VARCHAR(255),
|
||||
resolved_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
source VARCHAR(50) NOT NULL DEFAULT 'auto' CHECK (source IN ('auto', 'manual')),
|
||||
UNIQUE (tenant_id, component_id, digest)
|
||||
);
|
||||
|
||||
CREATE INDEX idx_version_maps_component ON version_maps(component_id);
|
||||
CREATE INDEX idx_version_maps_digest ON version_maps(digest);
|
||||
CREATE INDEX idx_version_maps_semver ON version_maps(semver);
|
||||
|
||||
CREATE TABLE releases (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
tenant_id UUID NOT NULL REFERENCES tenants(id) ON DELETE CASCADE,
|
||||
name VARCHAR(255) NOT NULL,
|
||||
display_name VARCHAR(255) NOT NULL,
|
||||
components JSONB NOT NULL, -- [{componentId, digest, semver, tag, role}]
|
||||
source_ref JSONB, -- {scmIntegrationId, commitSha, ciIntegrationId, buildId}
|
||||
status VARCHAR(50) NOT NULL DEFAULT 'draft' CHECK (status IN (
|
||||
'draft', 'ready', 'promoting', 'deployed', 'deprecated', 'archived'
|
||||
)),
|
||||
metadata JSONB NOT NULL DEFAULT '{}',
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
created_by UUID REFERENCES users(id),
|
||||
UNIQUE (tenant_id, name)
|
||||
);
|
||||
|
||||
CREATE INDEX idx_releases_tenant ON releases(tenant_id);
|
||||
CREATE INDEX idx_releases_status ON releases(status);
|
||||
CREATE INDEX idx_releases_created ON releases(created_at DESC);
|
||||
|
||||
CREATE TABLE release_environment_state (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
tenant_id UUID NOT NULL REFERENCES tenants(id) ON DELETE CASCADE,
|
||||
environment_id UUID NOT NULL REFERENCES environments(id) ON DELETE CASCADE,
|
||||
release_id UUID NOT NULL REFERENCES releases(id),
|
||||
status VARCHAR(50) NOT NULL CHECK (status IN (
|
||||
'deployed', 'deploying', 'failed', 'rolling_back', 'rolled_back'
|
||||
)),
|
||||
deployed_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
deployed_by UUID REFERENCES users(id),
|
||||
promotion_id UUID, -- will reference promotions
|
||||
evidence_ref VARCHAR(255),
|
||||
UNIQUE (tenant_id, environment_id)
|
||||
);
|
||||
|
||||
CREATE INDEX idx_release_env_state_env ON release_environment_state(environment_id);
|
||||
CREATE INDEX idx_release_env_state_release ON release_environment_state(release_id);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Workflow Engine
|
||||
|
||||
```sql
|
||||
CREATE TABLE workflow_templates (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
tenant_id UUID REFERENCES tenants(id) ON DELETE CASCADE, -- NULL for builtin
|
||||
name VARCHAR(255) NOT NULL,
|
||||
display_name VARCHAR(255) NOT NULL,
|
||||
description TEXT,
|
||||
version INTEGER NOT NULL DEFAULT 1,
|
||||
nodes JSONB NOT NULL,
|
||||
edges JSONB NOT NULL,
|
||||
inputs JSONB NOT NULL DEFAULT '[]',
|
||||
outputs JSONB NOT NULL DEFAULT '[]',
|
||||
is_builtin BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
tags JSONB NOT NULL DEFAULT '[]',
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
created_by UUID REFERENCES users(id),
|
||||
UNIQUE (tenant_id, name, version)
|
||||
);
|
||||
|
||||
CREATE INDEX idx_workflow_templates_tenant ON workflow_templates(tenant_id);
|
||||
CREATE INDEX idx_workflow_templates_builtin ON workflow_templates(is_builtin);
|
||||
|
||||
CREATE TABLE workflow_runs (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
tenant_id UUID NOT NULL REFERENCES tenants(id) ON DELETE CASCADE,
|
||||
template_id UUID NOT NULL REFERENCES workflow_templates(id),
|
||||
template_version INTEGER NOT NULL,
|
||||
status VARCHAR(50) NOT NULL DEFAULT 'created' CHECK (status IN (
|
||||
'created', 'running', 'paused', 'succeeded', 'failed', 'cancelled'
|
||||
)),
|
||||
context JSONB NOT NULL, -- inputs, variables, release info
|
||||
outputs JSONB,
|
||||
started_at TIMESTAMPTZ,
|
||||
completed_at TIMESTAMPTZ,
|
||||
error_message TEXT,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
triggered_by UUID REFERENCES users(id)
|
||||
);
|
||||
|
||||
CREATE INDEX idx_workflow_runs_tenant ON workflow_runs(tenant_id);
|
||||
CREATE INDEX idx_workflow_runs_status ON workflow_runs(status);
|
||||
CREATE INDEX idx_workflow_runs_template ON workflow_runs(template_id);
|
||||
|
||||
CREATE TABLE step_runs (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
workflow_run_id UUID NOT NULL REFERENCES workflow_runs(id) ON DELETE CASCADE,
|
||||
node_id VARCHAR(100) NOT NULL,
|
||||
step_type VARCHAR(100) NOT NULL,
|
||||
status VARCHAR(50) NOT NULL DEFAULT 'pending' CHECK (status IN (
|
||||
'pending', 'running', 'succeeded', 'failed', 'skipped', 'retrying', 'cancelled'
|
||||
)),
|
||||
inputs JSONB NOT NULL,
|
||||
config JSONB NOT NULL,
|
||||
outputs JSONB,
|
||||
started_at TIMESTAMPTZ,
|
||||
completed_at TIMESTAMPTZ,
|
||||
attempt_number INTEGER NOT NULL DEFAULT 1,
|
||||
error_message TEXT,
|
||||
error_type VARCHAR(100),
|
||||
logs TEXT,
|
||||
artifacts JSONB NOT NULL DEFAULT '[]',
|
||||
t_hlc BIGINT, -- Hybrid Logical Clock for ordering (optional)
|
||||
ts_wall TIMESTAMPTZ, -- Wall-clock timestamp for debugging (optional)
|
||||
UNIQUE (workflow_run_id, node_id, attempt_number)
|
||||
);
|
||||
|
||||
CREATE INDEX idx_step_runs_workflow ON step_runs(workflow_run_id);
|
||||
CREATE INDEX idx_step_runs_status ON step_runs(status);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Promotion & Approval
|
||||
|
||||
```sql
|
||||
CREATE TABLE promotions (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
tenant_id UUID NOT NULL REFERENCES tenants(id) ON DELETE CASCADE,
|
||||
release_id UUID NOT NULL REFERENCES releases(id),
|
||||
source_environment_id UUID REFERENCES environments(id),
|
||||
target_environment_id UUID NOT NULL REFERENCES environments(id),
|
||||
status VARCHAR(50) NOT NULL DEFAULT 'pending_approval' CHECK (status IN (
|
||||
'pending_approval', 'pending_gate', 'approved', 'rejected',
|
||||
'deploying', 'deployed', 'failed', 'cancelled', 'rolled_back'
|
||||
)),
|
||||
decision_record JSONB,
|
||||
workflow_run_id UUID REFERENCES workflow_runs(id),
|
||||
requested_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
requested_by UUID NOT NULL REFERENCES users(id),
|
||||
request_reason TEXT,
|
||||
decided_at TIMESTAMPTZ,
|
||||
started_at TIMESTAMPTZ,
|
||||
completed_at TIMESTAMPTZ,
|
||||
evidence_packet_id UUID,
|
||||
t_hlc BIGINT, -- Hybrid Logical Clock for ordering (optional)
|
||||
ts_wall TIMESTAMPTZ -- Wall-clock timestamp for debugging (optional)
|
||||
);
|
||||
|
||||
CREATE INDEX idx_promotions_tenant ON promotions(tenant_id);
|
||||
CREATE INDEX idx_promotions_release ON promotions(release_id);
|
||||
CREATE INDEX idx_promotions_status ON promotions(status);
|
||||
CREATE INDEX idx_promotions_target_env ON promotions(target_environment_id);
|
||||
|
||||
-- Add FK to release_environment_state
|
||||
ALTER TABLE release_environment_state
|
||||
ADD CONSTRAINT fk_release_env_state_promotion
|
||||
FOREIGN KEY (promotion_id) REFERENCES promotions(id);
|
||||
|
||||
CREATE TABLE approvals (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
tenant_id UUID NOT NULL REFERENCES tenants(id) ON DELETE CASCADE,
|
||||
promotion_id UUID NOT NULL REFERENCES promotions(id) ON DELETE CASCADE,
|
||||
approver_id UUID NOT NULL REFERENCES users(id),
|
||||
action VARCHAR(50) NOT NULL CHECK (action IN ('approved', 'rejected')),
|
||||
comment TEXT,
|
||||
approved_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
approver_role VARCHAR(255),
|
||||
approver_groups JSONB NOT NULL DEFAULT '[]'
|
||||
);
|
||||
|
||||
CREATE INDEX idx_approvals_promotion ON approvals(promotion_id);
|
||||
CREATE INDEX idx_approvals_approver ON approvals(approver_id);
|
||||
|
||||
CREATE TABLE approval_policies (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
tenant_id UUID NOT NULL REFERENCES tenants(id) ON DELETE CASCADE,
|
||||
environment_id UUID NOT NULL REFERENCES environments(id) ON DELETE CASCADE,
|
||||
required_count INTEGER NOT NULL DEFAULT 1,
|
||||
required_roles JSONB NOT NULL DEFAULT '[]',
|
||||
required_groups JSONB NOT NULL DEFAULT '[]',
|
||||
require_sod BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
allow_self_approval BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
expiration_minutes INTEGER NOT NULL DEFAULT 1440,
|
||||
UNIQUE (tenant_id, environment_id)
|
||||
);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Deployment
|
||||
|
||||
```sql
|
||||
CREATE TABLE deployment_jobs (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
tenant_id UUID NOT NULL REFERENCES tenants(id) ON DELETE CASCADE,
|
||||
promotion_id UUID NOT NULL REFERENCES promotions(id),
|
||||
release_id UUID NOT NULL REFERENCES releases(id),
|
||||
environment_id UUID NOT NULL REFERENCES environments(id),
|
||||
status VARCHAR(50) NOT NULL DEFAULT 'pending' CHECK (status IN (
|
||||
'pending', 'running', 'succeeded', 'failed', 'cancelled', 'rolling_back', 'rolled_back'
|
||||
)),
|
||||
strategy VARCHAR(50) NOT NULL DEFAULT 'all-at-once',
|
||||
started_at TIMESTAMPTZ,
|
||||
completed_at TIMESTAMPTZ,
|
||||
artifacts JSONB NOT NULL DEFAULT '[]',
|
||||
rollback_of UUID REFERENCES deployment_jobs(id),
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
t_hlc BIGINT, -- Hybrid Logical Clock for ordering (optional)
|
||||
ts_wall TIMESTAMPTZ -- Wall-clock timestamp for debugging (optional)
|
||||
);
|
||||
|
||||
CREATE INDEX idx_deployment_jobs_promotion ON deployment_jobs(promotion_id);
|
||||
CREATE INDEX idx_deployment_jobs_status ON deployment_jobs(status);
|
||||
|
||||
CREATE TABLE deployment_tasks (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
job_id UUID NOT NULL REFERENCES deployment_jobs(id) ON DELETE CASCADE,
|
||||
target_id UUID NOT NULL REFERENCES targets(id),
|
||||
digest VARCHAR(100) NOT NULL,
|
||||
status VARCHAR(50) NOT NULL DEFAULT 'pending' CHECK (status IN (
|
||||
'pending', 'running', 'succeeded', 'failed', 'cancelled', 'skipped'
|
||||
)),
|
||||
agent_id UUID REFERENCES agents(id),
|
||||
started_at TIMESTAMPTZ,
|
||||
completed_at TIMESTAMPTZ,
|
||||
exit_code INTEGER,
|
||||
logs TEXT,
|
||||
previous_digest VARCHAR(100),
|
||||
sticker_written BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE INDEX idx_deployment_tasks_job ON deployment_tasks(job_id);
|
||||
CREATE INDEX idx_deployment_tasks_target ON deployment_tasks(target_id);
|
||||
CREATE INDEX idx_deployment_tasks_status ON deployment_tasks(status);
|
||||
|
||||
CREATE TABLE generated_artifacts (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
tenant_id UUID NOT NULL REFERENCES tenants(id) ON DELETE CASCADE,
|
||||
deployment_job_id UUID REFERENCES deployment_jobs(id) ON DELETE CASCADE,
|
||||
artifact_type VARCHAR(50) NOT NULL CHECK (artifact_type IN (
|
||||
'compose_lock', 'script', 'sticker', 'evidence', 'config'
|
||||
)),
|
||||
name VARCHAR(255) NOT NULL,
|
||||
content_hash VARCHAR(100) NOT NULL,
|
||||
content BYTEA, -- for small artifacts
|
||||
storage_ref VARCHAR(500), -- for large artifacts (S3, etc.)
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE INDEX idx_generated_artifacts_job ON generated_artifacts(deployment_job_id);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Progressive Delivery
|
||||
|
||||
```sql
|
||||
CREATE TABLE ab_releases (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
tenant_id UUID NOT NULL REFERENCES tenants(id) ON DELETE CASCADE,
|
||||
environment_id UUID NOT NULL REFERENCES environments(id),
|
||||
name VARCHAR(255) NOT NULL,
|
||||
variations JSONB NOT NULL, -- [{name, releaseId, targetGroupId, trafficPercentage}]
|
||||
active_variation VARCHAR(50) NOT NULL DEFAULT 'A',
|
||||
traffic_split JSONB NOT NULL,
|
||||
rollout_strategy JSONB NOT NULL,
|
||||
status VARCHAR(50) NOT NULL DEFAULT 'created' CHECK (status IN (
|
||||
'created', 'deploying', 'running', 'promoting', 'completed', 'rolled_back'
|
||||
)),
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
completed_at TIMESTAMPTZ,
|
||||
created_by UUID REFERENCES users(id)
|
||||
);
|
||||
|
||||
CREATE INDEX idx_ab_releases_tenant_env ON ab_releases(tenant_id, environment_id);
|
||||
CREATE INDEX idx_ab_releases_status ON ab_releases(status);
|
||||
|
||||
CREATE TABLE canary_stages (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
ab_release_id UUID NOT NULL REFERENCES ab_releases(id) ON DELETE CASCADE,
|
||||
stage_number INTEGER NOT NULL,
|
||||
traffic_percentage INTEGER NOT NULL,
|
||||
status VARCHAR(50) NOT NULL DEFAULT 'pending' CHECK (status IN (
|
||||
'pending', 'running', 'succeeded', 'failed', 'skipped'
|
||||
)),
|
||||
health_threshold DECIMAL(5,2),
|
||||
duration_seconds INTEGER,
|
||||
require_approval BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
started_at TIMESTAMPTZ,
|
||||
completed_at TIMESTAMPTZ,
|
||||
health_result JSONB,
|
||||
UNIQUE (ab_release_id, stage_number)
|
||||
);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Release Evidence
|
||||
|
||||
```sql
|
||||
CREATE TABLE evidence_packets (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
tenant_id UUID NOT NULL REFERENCES tenants(id) ON DELETE CASCADE,
|
||||
promotion_id UUID NOT NULL REFERENCES promotions(id),
|
||||
packet_type VARCHAR(50) NOT NULL CHECK (packet_type IN (
|
||||
'release_decision', 'deployment', 'rollback', 'ab_promotion'
|
||||
)),
|
||||
content JSONB NOT NULL,
|
||||
content_hash VARCHAR(100) NOT NULL,
|
||||
signature TEXT,
|
||||
signer_key_ref VARCHAR(255),
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
-- Note: No UPDATE or DELETE allowed (append-only)
|
||||
);
|
||||
|
||||
CREATE INDEX idx_evidence_packets_promotion ON evidence_packets(promotion_id);
|
||||
CREATE INDEX idx_evidence_packets_created ON evidence_packets(created_at DESC);
|
||||
|
||||
-- Append-only enforcement via trigger
|
||||
CREATE OR REPLACE FUNCTION prevent_evidence_modification()
|
||||
RETURNS TRIGGER AS $$
|
||||
BEGIN
|
||||
RAISE EXCEPTION 'Evidence packets are immutable and cannot be modified or deleted';
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;
|
||||
|
||||
CREATE TRIGGER evidence_packets_immutable
|
||||
BEFORE UPDATE OR DELETE ON evidence_packets
|
||||
FOR EACH ROW EXECUTE FUNCTION prevent_evidence_modification();
|
||||
|
||||
CREATE TABLE version_stickers (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
tenant_id UUID NOT NULL REFERENCES tenants(id) ON DELETE CASCADE,
|
||||
target_id UUID NOT NULL REFERENCES targets(id),
|
||||
deployment_job_id UUID REFERENCES deployment_jobs(id),
|
||||
release_id UUID NOT NULL REFERENCES releases(id),
|
||||
digest VARCHAR(100) NOT NULL,
|
||||
sticker_content JSONB NOT NULL,
|
||||
written_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
verified_at TIMESTAMPTZ,
|
||||
verification_status VARCHAR(50) CHECK (verification_status IN ('valid', 'mismatch', 'missing'))
|
||||
);
|
||||
|
||||
CREATE INDEX idx_version_stickers_target ON version_stickers(target_id);
|
||||
CREATE INDEX idx_version_stickers_release ON version_stickers(release_id);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Plugin Infrastructure
|
||||
|
||||
```sql
|
||||
CREATE TABLE plugins (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
name VARCHAR(255) NOT NULL UNIQUE,
|
||||
display_name VARCHAR(255) NOT NULL,
|
||||
version VARCHAR(50) NOT NULL,
|
||||
description TEXT,
|
||||
manifest JSONB NOT NULL,
|
||||
status VARCHAR(50) NOT NULL DEFAULT 'inactive' CHECK (status IN (
|
||||
'active', 'inactive', 'error'
|
||||
)),
|
||||
error_message TEXT,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE TABLE plugin_instances (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
tenant_id UUID NOT NULL REFERENCES tenants(id) ON DELETE CASCADE,
|
||||
plugin_id UUID NOT NULL REFERENCES plugins(id),
|
||||
config JSONB NOT NULL DEFAULT '{}',
|
||||
enabled BOOLEAN NOT NULL DEFAULT TRUE,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
UNIQUE (tenant_id, plugin_id)
|
||||
);
|
||||
|
||||
CREATE INDEX idx_plugin_instances_tenant ON plugin_instances(tenant_id);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
---
|
||||
|
||||
## Hybrid Logical Clock (HLC) for Distributed Ordering
|
||||
|
||||
**Optional Enhancement**: For strict distributed ordering and multi-region support, the following tables include optional `t_hlc` (Hybrid Logical Clock timestamp) and `ts_wall` (wall-clock timestamp) columns:
|
||||
|
||||
- `promotions` — Promotion state transitions
|
||||
- `deployment_jobs` — Deployment task ordering
|
||||
- `step_runs` — Workflow step execution ordering
|
||||
|
||||
**When to use HLC**:
|
||||
- Multi-region deployments requiring strict causal ordering
|
||||
- Deterministic replay across distributed systems
|
||||
- Timeline event ordering in audit logs
|
||||
|
||||
**HLC Schema**:
|
||||
```sql
|
||||
t_hlc BIGINT -- HLC timestamp (monotonic, skew-tolerant)
|
||||
ts_wall TIMESTAMPTZ -- Wall-clock timestamp (informational)
|
||||
```
|
||||
|
||||
**Usage**:
|
||||
- `t_hlc` is generated by `IHybridLogicalClock.Tick()` on state transitions
|
||||
- `ts_wall` is populated by `TimeProvider.GetUtcNow()` for debugging
|
||||
- Index on `t_hlc` for ordering queries: `CREATE INDEX idx_promotions_hlc ON promotions(t_hlc);`
|
||||
|
||||
**Reference**: See [Implementation Guide](../implementation-guide.md#hybrid-logical-clock-hlc-for-distributed-ordering) for HLC usage patterns.
|
||||
|
||||
---
|
||||
|
||||
## Row-Level Security (Multi-Tenancy)
|
||||
|
||||
All tables with `tenant_id` should have RLS enabled:
|
||||
|
||||
```sql
|
||||
-- Enable RLS on all release tables
|
||||
ALTER TABLE integrations ENABLE ROW LEVEL SECURITY;
|
||||
ALTER TABLE environments ENABLE ROW LEVEL SECURITY;
|
||||
ALTER TABLE targets ENABLE ROW LEVEL SECURITY;
|
||||
ALTER TABLE releases ENABLE ROW LEVEL SECURITY;
|
||||
ALTER TABLE promotions ENABLE ROW LEVEL SECURITY;
|
||||
-- ... etc.
|
||||
|
||||
-- Example policy
|
||||
CREATE POLICY tenant_isolation ON integrations
|
||||
FOR ALL
|
||||
USING (tenant_id = current_setting('app.tenant_id')::UUID);
|
||||
```
|
||||
Reference in New Issue
Block a user