more audit work
This commit is contained in:
@@ -2,8 +2,6 @@
|
||||
-- Consolidated from migrations 001-012b (pre_1.0 archived)
|
||||
-- Creates the complete scheduler schema for jobs, triggers, workers, runs, and policies
|
||||
|
||||
BEGIN;
|
||||
|
||||
-- ============================================================================
|
||||
-- SECTION 1: Schema Creation
|
||||
-- ============================================================================
|
||||
@@ -102,14 +100,14 @@ CREATE TABLE IF NOT EXISTS scheduler.jobs (
|
||||
UNIQUE(tenant_id, idempotency_key)
|
||||
);
|
||||
|
||||
CREATE INDEX idx_jobs_tenant_status ON scheduler.jobs(tenant_id, status);
|
||||
CREATE INDEX idx_jobs_tenant_type ON scheduler.jobs(tenant_id, job_type);
|
||||
CREATE INDEX idx_jobs_scheduled ON scheduler.jobs(tenant_id, status, not_before, priority DESC, created_at)
|
||||
CREATE INDEX IF NOT EXISTS idx_jobs_tenant_status ON scheduler.jobs(tenant_id, status);
|
||||
CREATE INDEX IF NOT EXISTS idx_jobs_tenant_type ON scheduler.jobs(tenant_id, job_type);
|
||||
CREATE INDEX IF NOT EXISTS idx_jobs_scheduled ON scheduler.jobs(tenant_id, status, not_before, priority DESC, created_at)
|
||||
WHERE status = 'scheduled';
|
||||
CREATE INDEX idx_jobs_leased ON scheduler.jobs(tenant_id, status, lease_until)
|
||||
CREATE INDEX IF NOT EXISTS idx_jobs_leased ON scheduler.jobs(tenant_id, status, lease_until)
|
||||
WHERE status = 'leased';
|
||||
CREATE INDEX idx_jobs_project ON scheduler.jobs(tenant_id, project_id);
|
||||
CREATE INDEX idx_jobs_correlation ON scheduler.jobs(correlation_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_jobs_project ON scheduler.jobs(tenant_id, project_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_jobs_correlation ON scheduler.jobs(correlation_id);
|
||||
|
||||
-- Triggers table (cron-based job triggers)
|
||||
CREATE TABLE IF NOT EXISTS scheduler.triggers (
|
||||
@@ -134,9 +132,9 @@ CREATE TABLE IF NOT EXISTS scheduler.triggers (
|
||||
UNIQUE(tenant_id, name)
|
||||
);
|
||||
|
||||
CREATE INDEX idx_triggers_tenant_id ON scheduler.triggers(tenant_id);
|
||||
CREATE INDEX idx_triggers_next_fire ON scheduler.triggers(enabled, next_fire_at) WHERE enabled = TRUE;
|
||||
CREATE INDEX idx_triggers_job_type ON scheduler.triggers(tenant_id, job_type);
|
||||
CREATE INDEX IF NOT EXISTS idx_triggers_tenant_id ON scheduler.triggers(tenant_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_triggers_next_fire ON scheduler.triggers(enabled, next_fire_at) WHERE enabled = TRUE;
|
||||
CREATE INDEX IF NOT EXISTS idx_triggers_job_type ON scheduler.triggers(tenant_id, job_type);
|
||||
|
||||
CREATE TRIGGER trg_triggers_updated_at
|
||||
BEFORE UPDATE ON scheduler.triggers
|
||||
@@ -157,9 +155,9 @@ CREATE TABLE IF NOT EXISTS scheduler.workers (
|
||||
metadata JSONB NOT NULL DEFAULT '{}'
|
||||
);
|
||||
|
||||
CREATE INDEX idx_workers_status ON scheduler.workers(status);
|
||||
CREATE INDEX idx_workers_heartbeat ON scheduler.workers(last_heartbeat_at);
|
||||
CREATE INDEX idx_workers_tenant ON scheduler.workers(tenant_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_workers_status ON scheduler.workers(status);
|
||||
CREATE INDEX IF NOT EXISTS idx_workers_heartbeat ON scheduler.workers(last_heartbeat_at);
|
||||
CREATE INDEX IF NOT EXISTS idx_workers_tenant ON scheduler.workers(tenant_id);
|
||||
|
||||
COMMENT ON TABLE scheduler.workers IS 'Global worker registry. Not RLS-protected - workers serve all tenants.';
|
||||
|
||||
@@ -173,8 +171,8 @@ CREATE TABLE IF NOT EXISTS scheduler.locks (
|
||||
metadata JSONB NOT NULL DEFAULT '{}'
|
||||
);
|
||||
|
||||
CREATE INDEX idx_locks_tenant ON scheduler.locks(tenant_id);
|
||||
CREATE INDEX idx_locks_expires ON scheduler.locks(expires_at);
|
||||
CREATE INDEX IF NOT EXISTS idx_locks_tenant ON scheduler.locks(tenant_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_locks_expires ON scheduler.locks(expires_at);
|
||||
|
||||
-- Job history
|
||||
CREATE TABLE IF NOT EXISTS scheduler.job_history (
|
||||
@@ -195,10 +193,10 @@ CREATE TABLE IF NOT EXISTS scheduler.job_history (
|
||||
archived_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE INDEX idx_job_history_tenant ON scheduler.job_history(tenant_id);
|
||||
CREATE INDEX idx_job_history_job_id ON scheduler.job_history(job_id);
|
||||
CREATE INDEX idx_job_history_type ON scheduler.job_history(tenant_id, job_type);
|
||||
CREATE INDEX idx_job_history_completed ON scheduler.job_history(tenant_id, completed_at);
|
||||
CREATE INDEX IF NOT EXISTS idx_job_history_tenant ON scheduler.job_history(tenant_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_job_history_job_id ON scheduler.job_history(job_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_job_history_type ON scheduler.job_history(tenant_id, job_type);
|
||||
CREATE INDEX IF NOT EXISTS idx_job_history_completed ON scheduler.job_history(tenant_id, completed_at);
|
||||
|
||||
-- Metrics table
|
||||
CREATE TABLE IF NOT EXISTS scheduler.metrics (
|
||||
@@ -219,7 +217,7 @@ CREATE TABLE IF NOT EXISTS scheduler.metrics (
|
||||
UNIQUE(tenant_id, job_type, period_start)
|
||||
);
|
||||
|
||||
CREATE INDEX idx_metrics_tenant_period ON scheduler.metrics(tenant_id, period_start);
|
||||
CREATE INDEX IF NOT EXISTS idx_metrics_tenant_period ON scheduler.metrics(tenant_id, period_start);
|
||||
|
||||
-- ============================================================================
|
||||
-- SECTION 5: Schedules and Runs
|
||||
@@ -593,4 +591,3 @@ BEGIN
|
||||
END
|
||||
$$;
|
||||
|
||||
COMMIT;
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Dapper;
|
||||
using Npgsql;
|
||||
@@ -21,10 +22,11 @@ public sealed class GraphJobRepository : IGraphJobRepository
|
||||
(id, tenant_id, type, status, payload, created_at, updated_at, correlation_id)
|
||||
VALUES (@Id, @TenantId, @Type, @Status, @Payload, @CreatedAt, @UpdatedAt, @CorrelationId);";
|
||||
|
||||
var jobId = ParseJobId(job.Id, nameof(job.Id));
|
||||
await using var conn = await _dataSource.OpenConnectionAsync(job.TenantId, cancellationToken).ConfigureAwait(false);
|
||||
await conn.ExecuteAsync(sql, new
|
||||
{
|
||||
job.Id,
|
||||
Id = jobId,
|
||||
job.TenantId,
|
||||
Type = (short)GraphJobQueryType.Build,
|
||||
Status = (short)job.Status,
|
||||
@@ -41,10 +43,11 @@ public sealed class GraphJobRepository : IGraphJobRepository
|
||||
(id, tenant_id, type, status, payload, created_at, updated_at, correlation_id)
|
||||
VALUES (@Id, @TenantId, @Type, @Status, @Payload, @CreatedAt, @UpdatedAt, @CorrelationId);";
|
||||
|
||||
var jobId = ParseJobId(job.Id, nameof(job.Id));
|
||||
await using var conn = await _dataSource.OpenConnectionAsync(job.TenantId, cancellationToken).ConfigureAwait(false);
|
||||
await conn.ExecuteAsync(sql, new
|
||||
{
|
||||
job.Id,
|
||||
Id = jobId,
|
||||
job.TenantId,
|
||||
Type = (short)GraphJobQueryType.Overlay,
|
||||
Status = (short)job.Status,
|
||||
@@ -59,7 +62,8 @@ public sealed class GraphJobRepository : IGraphJobRepository
|
||||
{
|
||||
const string sql = "SELECT payload FROM scheduler.graph_jobs WHERE tenant_id=@TenantId AND id=@Id AND type=@Type LIMIT 1";
|
||||
await using var conn = await _dataSource.OpenConnectionAsync(tenantId, cancellationToken).ConfigureAwait(false);
|
||||
var payload = await conn.ExecuteScalarAsync<string?>(sql, new { TenantId = tenantId, Id = jobId, Type = (short)GraphJobQueryType.Build });
|
||||
var parsedId = ParseJobId(jobId, nameof(jobId));
|
||||
var payload = await conn.ExecuteScalarAsync<string?>(sql, new { TenantId = tenantId, Id = parsedId, Type = (short)GraphJobQueryType.Build });
|
||||
return payload is null ? null : CanonicalJsonSerializer.Deserialize<GraphBuildJob>(payload);
|
||||
}
|
||||
|
||||
@@ -67,7 +71,8 @@ public sealed class GraphJobRepository : IGraphJobRepository
|
||||
{
|
||||
const string sql = "SELECT payload FROM scheduler.graph_jobs WHERE tenant_id=@TenantId AND id=@Id AND type=@Type LIMIT 1";
|
||||
await using var conn = await _dataSource.OpenConnectionAsync(tenantId, cancellationToken).ConfigureAwait(false);
|
||||
var payload = await conn.ExecuteScalarAsync<string?>(sql, new { TenantId = tenantId, Id = jobId, Type = (short)GraphJobQueryType.Overlay });
|
||||
var parsedId = ParseJobId(jobId, nameof(jobId));
|
||||
var payload = await conn.ExecuteScalarAsync<string?>(sql, new { TenantId = tenantId, Id = parsedId, Type = (short)GraphJobQueryType.Overlay });
|
||||
return payload is null ? null : CanonicalJsonSerializer.Deserialize<GraphOverlayJob>(payload);
|
||||
}
|
||||
|
||||
@@ -173,11 +178,12 @@ public sealed class GraphJobRepository : IGraphJobRepository
|
||||
SET status=@NewStatus, payload=@Payload, updated_at=NOW()
|
||||
WHERE tenant_id=@TenantId AND id=@Id AND status=@ExpectedStatus AND type=@Type";
|
||||
|
||||
var jobId = ParseJobId(job.Id, nameof(job.Id));
|
||||
await using var conn = await _dataSource.OpenConnectionAsync(job.TenantId, cancellationToken).ConfigureAwait(false);
|
||||
var rows = await conn.ExecuteAsync(sql, new
|
||||
{
|
||||
job.TenantId,
|
||||
job.Id,
|
||||
Id = jobId,
|
||||
ExpectedStatus = (short)expectedStatus,
|
||||
NewStatus = (short)job.Status,
|
||||
Type = (short)GraphJobQueryType.Build,
|
||||
@@ -192,11 +198,12 @@ public sealed class GraphJobRepository : IGraphJobRepository
|
||||
SET status=@NewStatus, payload=@Payload, updated_at=NOW()
|
||||
WHERE tenant_id=@TenantId AND id=@Id AND status=@ExpectedStatus AND type=@Type";
|
||||
|
||||
var jobId = ParseJobId(job.Id, nameof(job.Id));
|
||||
await using var conn = await _dataSource.OpenConnectionAsync(job.TenantId, cancellationToken).ConfigureAwait(false);
|
||||
var rows = await conn.ExecuteAsync(sql, new
|
||||
{
|
||||
job.TenantId,
|
||||
job.Id,
|
||||
Id = jobId,
|
||||
ExpectedStatus = (short)expectedStatus,
|
||||
NewStatus = (short)job.Status,
|
||||
Type = (short)GraphJobQueryType.Overlay,
|
||||
@@ -204,6 +211,16 @@ public sealed class GraphJobRepository : IGraphJobRepository
|
||||
});
|
||||
return rows == 1;
|
||||
}
|
||||
|
||||
private static Guid ParseJobId(string jobId, string paramName)
|
||||
{
|
||||
if (Guid.TryParse(jobId, out var parsed))
|
||||
{
|
||||
return parsed;
|
||||
}
|
||||
|
||||
throw new ArgumentException("Graph job id must be a UUID.", paramName);
|
||||
}
|
||||
}
|
||||
|
||||
internal enum GraphJobQueryType : short
|
||||
|
||||
@@ -36,4 +36,8 @@
|
||||
<EmbeddedResource Include="Migrations\**\*.sql" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Remove="Migrations\_archived\**\*.sql" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
Reference in New Issue
Block a user