mirror of
https://github.com/whyour/qinglong.git
synced 2026-09-22 02:27:44 +08:00
feat(ql3): add cluster log retention authority
This commit is contained in:
@@ -4474,6 +4474,162 @@ export const runAttempts = ql3Schema.table(
|
||||
index('ql3_run_attempts_lease_idx')
|
||||
.on(table.leaseExpiresAtMs, table.id)
|
||||
.where(sql`${table.leaseExpiresAtMs} is not null`),
|
||||
index('ql3_run_log_retention_candidate_idx')
|
||||
.on(table.finishedAtMs, table.id)
|
||||
.where(
|
||||
sql`${table.executorType} = 'remote_worker' and ${table.logArtifactId} is not null and ${table.status} in ('succeeded', 'failed', 'cancelled', 'timed_out')`,
|
||||
),
|
||||
],
|
||||
);
|
||||
|
||||
export const runAttemptLogRetentionControls = ql3Schema.table(
|
||||
'run_attempt_log_retention_controls',
|
||||
{
|
||||
attemptId: varchar('attempt_id', { length: 36 }).primaryKey(),
|
||||
projectId: varchar('project_id', { length: 128 }).notNull(),
|
||||
runId: varchar('run_id', { length: 36 }).notNull(),
|
||||
logArtifactId: varchar('log_artifact_id', { length: 36 }).notNull(),
|
||||
executorType: varchar('executor_type', { length: 32 }).notNull(),
|
||||
finishedAtMs: bigint('finished_at_ms', { mode: 'number' }).notNull(),
|
||||
eligibleAtMs: bigint('eligible_at_ms', { mode: 'number' }).notNull(),
|
||||
state: varchar('state', { length: 16 }).notNull(),
|
||||
claimOwner: varchar('claim_owner', { length: 128 }),
|
||||
claimToken: varchar('claim_token', { length: 64 }),
|
||||
claimVersion: integer('claim_version').default(1).notNull(),
|
||||
claimExpiresAtMs: bigint('claim_expires_at_ms', { mode: 'number' }),
|
||||
nextClaimAtMs: bigint('next_claim_at_ms', { mode: 'number' }),
|
||||
failureCount: integer('failure_count').default(0).notNull(),
|
||||
lastFailureCode: varchar('last_failure_code', { length: 64 }),
|
||||
createdAtMs: bigint('created_at_ms', { mode: 'number' }).notNull(),
|
||||
updatedAtMs: bigint('updated_at_ms', { mode: 'number' }).notNull(),
|
||||
},
|
||||
(table) => [
|
||||
uniqueIndex('ql3_run_log_retention_control_artifact_key').on(
|
||||
table.logArtifactId,
|
||||
),
|
||||
check(
|
||||
'ql3_run_log_retention_control_identity_check',
|
||||
sql`char_length(${table.projectId}) between 1 and 128 and char_length(${table.runId}) between 1 and 36 and char_length(${table.attemptId}) between 1 and 36 and ${table.logArtifactId} ~ '^wlog-[a-f0-9]{30}$' and ${table.executorType} = 'remote_worker'`,
|
||||
),
|
||||
check(
|
||||
'ql3_run_log_retention_control_time_check',
|
||||
sql`${table.finishedAtMs} >= 0 and ${table.eligibleAtMs} >= ${table.finishedAtMs} and ${table.createdAtMs} >= 0 and ${table.updatedAtMs} >= ${table.createdAtMs}`,
|
||||
),
|
||||
check(
|
||||
'ql3_run_log_retention_control_state_check',
|
||||
sql`${table.state} in ('claimed', 'retry', 'manual')`,
|
||||
),
|
||||
check(
|
||||
'ql3_run_log_retention_control_claim_owner_check',
|
||||
sql`${table.claimOwner} is null or ${table.claimOwner} ~ '^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$'`,
|
||||
),
|
||||
check(
|
||||
'ql3_run_log_retention_control_claim_token_check',
|
||||
sql`${table.claimToken} is null or ${table.claimToken} ~ '^[A-Za-z0-9][A-Za-z0-9._:-]{15,63}$'`,
|
||||
),
|
||||
check(
|
||||
'ql3_run_log_retention_control_claim_version_check',
|
||||
sql`${table.claimVersion} between 1 and 2147483647`,
|
||||
),
|
||||
check(
|
||||
'ql3_run_log_retention_control_claim_expiry_check',
|
||||
sql`${table.claimExpiresAtMs} is null or ${table.claimExpiresAtMs} >= 0`,
|
||||
),
|
||||
check(
|
||||
'ql3_run_log_retention_control_next_claim_check',
|
||||
sql`${table.nextClaimAtMs} is null or ${table.nextClaimAtMs} >= 0`,
|
||||
),
|
||||
check(
|
||||
'ql3_run_log_retention_control_failure_count_check',
|
||||
sql`${table.failureCount} between 0 and 2147483647`,
|
||||
),
|
||||
check(
|
||||
'ql3_run_log_retention_control_failure_code_check',
|
||||
sql`${table.lastFailureCode} is null or ${table.lastFailureCode} in ('artifact_unavailable', 'artifact_integrity_mismatch', 'retirement_record_unavailable')`,
|
||||
),
|
||||
check(
|
||||
'ql3_run_log_retention_control_state_shape_check',
|
||||
sql`(${table.state} = 'claimed' and ${table.claimOwner} is not null and ${table.claimToken} is not null and ${table.claimExpiresAtMs} is not null and ${table.nextClaimAtMs} is null) or (${table.state} = 'retry' and ${table.claimOwner} is null and ${table.claimToken} is null and ${table.claimExpiresAtMs} is null and ${table.nextClaimAtMs} is not null and ${table.lastFailureCode} is not null) or (${table.state} = 'manual' and ${table.claimOwner} is null and ${table.claimToken} is null and ${table.claimExpiresAtMs} is null and ${table.nextClaimAtMs} is null and ${table.lastFailureCode} is not null)`,
|
||||
),
|
||||
foreignKey({
|
||||
name: 'ql3_run_log_retention_control_attempt_fk',
|
||||
columns: [table.attemptId],
|
||||
foreignColumns: [runAttempts.id],
|
||||
}).onDelete('cascade'),
|
||||
foreignKey({
|
||||
name: 'ql3_run_log_retention_control_run_fk',
|
||||
columns: [table.runId],
|
||||
foreignColumns: [runs.id],
|
||||
}).onDelete('cascade'),
|
||||
index('ql3_run_log_retention_retry_idx')
|
||||
.on(table.nextClaimAtMs, table.finishedAtMs, table.attemptId)
|
||||
.where(sql`${table.state} = 'retry'`),
|
||||
index('ql3_run_log_retention_claim_expiry_idx')
|
||||
.on(table.claimExpiresAtMs, table.finishedAtMs, table.attemptId)
|
||||
.where(sql`${table.state} = 'claimed'`),
|
||||
],
|
||||
);
|
||||
|
||||
export const runAttemptLogArtifactTombstones = ql3Schema.table(
|
||||
'run_attempt_log_artifact_tombstones',
|
||||
{
|
||||
logArtifactId: varchar('log_artifact_id', { length: 36 }).primaryKey(),
|
||||
projectId: varchar('project_id', { length: 128 }).notNull(),
|
||||
runId: varchar('run_id', { length: 36 }).notNull(),
|
||||
attemptId: varchar('attempt_id', { length: 36 }).notNull(),
|
||||
executorType: varchar('executor_type', { length: 32 }).notNull(),
|
||||
finishedAtMs: bigint('finished_at_ms', { mode: 'number' }).notNull(),
|
||||
eligibleAtMs: bigint('eligible_at_ms', { mode: 'number' }).notNull(),
|
||||
retiredAtMs: bigint('retired_at_ms', { mode: 'number' }).notNull(),
|
||||
disposition: varchar('disposition', { length: 16 }).notNull(),
|
||||
byteLength: bigint('byte_length', { mode: 'number' }).notNull(),
|
||||
truncated: varchar('truncated', { length: 16 }).notNull(),
|
||||
maximumBytes: bigint('maximum_bytes', { mode: 'number' }),
|
||||
truncationObservedAtMs: bigint('truncation_observed_at_ms', {
|
||||
mode: 'number',
|
||||
}),
|
||||
recordDigest: char('record_digest', { length: 64 }).notNull(),
|
||||
},
|
||||
(table) => [
|
||||
uniqueIndex('ql3_run_log_tombstone_attempt_key').on(table.attemptId),
|
||||
check(
|
||||
'ql3_run_log_tombstone_identity_check',
|
||||
sql`char_length(${table.projectId}) between 1 and 128 and char_length(${table.runId}) between 1 and 36 and char_length(${table.attemptId}) between 1 and 36 and ${table.logArtifactId} ~ '^wlog-[a-f0-9]{30}$' and ${table.executorType} = 'remote_worker'`,
|
||||
),
|
||||
check(
|
||||
'ql3_run_log_tombstone_time_check',
|
||||
sql`${table.finishedAtMs} >= 0 and ${table.eligibleAtMs} >= ${table.finishedAtMs} and ${table.retiredAtMs} >= ${table.eligibleAtMs}`,
|
||||
),
|
||||
check(
|
||||
'ql3_run_log_tombstone_disposition_check',
|
||||
sql`${table.disposition} in ('deleted', 'already_absent') and (${table.disposition} <> 'already_absent' or ${table.byteLength} = 0)`,
|
||||
),
|
||||
check(
|
||||
'ql3_run_log_tombstone_size_check',
|
||||
sql`${table.byteLength} between 0 and 1073741824`,
|
||||
),
|
||||
check(
|
||||
'ql3_run_log_tombstone_truncation_check',
|
||||
sql`(${table.truncated} = 'unknown' and ${table.maximumBytes} is null and ${table.truncationObservedAtMs} is null) or (${table.truncated} in ('true', 'false') and ${table.maximumBytes} >= 1 and ${table.truncationObservedAtMs} >= 0)`,
|
||||
),
|
||||
check(
|
||||
'ql3_run_log_tombstone_digest_check',
|
||||
sql`${table.recordDigest} ~ '^[a-f0-9]{64}$'`,
|
||||
),
|
||||
foreignKey({
|
||||
name: 'ql3_run_log_tombstone_attempt_fk',
|
||||
columns: [table.attemptId],
|
||||
foreignColumns: [runAttempts.id],
|
||||
}).onDelete('cascade'),
|
||||
foreignKey({
|
||||
name: 'ql3_run_log_tombstone_run_fk',
|
||||
columns: [table.runId],
|
||||
foreignColumns: [runs.id],
|
||||
}).onDelete('cascade'),
|
||||
index('ql3_run_log_tombstone_retired_idx').on(
|
||||
table.retiredAtMs,
|
||||
table.attemptId,
|
||||
),
|
||||
],
|
||||
);
|
||||
|
||||
@@ -5700,6 +5856,8 @@ export const ql3PostgresTables = [
|
||||
toolExecutionResultRekeyHeads,
|
||||
toolResultKeyRetirementReceipts,
|
||||
runAttempts,
|
||||
runAttemptLogRetentionControls,
|
||||
runAttemptLogArtifactTombstones,
|
||||
workerSessions,
|
||||
runDispatchLeases,
|
||||
workerCredentials,
|
||||
|
||||
@@ -15,12 +15,13 @@ export interface PostgresSchemaContractFunction {
|
||||
export interface PostgresSchemaContract {
|
||||
readonly schema: 'ql3';
|
||||
readonly contractName: 'control-core';
|
||||
readonly contractVersion: 53;
|
||||
readonly migrationId: 'pg-0054-approval-management-boundary';
|
||||
readonly contractVersion: 54;
|
||||
readonly migrationId: 'pg-0055-run-attempt-log-retention';
|
||||
readonly minimumServerMajor: 16;
|
||||
readonly maximumServerMajor: 18;
|
||||
readonly capabilities: Readonly<{
|
||||
run_core: 1;
|
||||
run_attempt_log_retention: 1;
|
||||
run_dispatch_lease: 1;
|
||||
run_retry_policy: 1;
|
||||
project_policy: 1;
|
||||
@@ -100,8 +101,8 @@ export const postgresqlControlSchemaContract: PostgresSchemaContract =
|
||||
Object.freeze({
|
||||
schema: 'ql3',
|
||||
contractName: 'control-core',
|
||||
contractVersion: 53,
|
||||
migrationId: 'pg-0054-approval-management-boundary',
|
||||
contractVersion: 54,
|
||||
migrationId: 'pg-0055-run-attempt-log-retention',
|
||||
minimumServerMajor: 16,
|
||||
maximumServerMajor: 18,
|
||||
capabilities: Object.freeze({
|
||||
@@ -141,6 +142,7 @@ export const postgresqlControlSchemaContract: PostgresSchemaContract =
|
||||
project_policy: 1,
|
||||
project_tool_definition_snapshot: 1,
|
||||
run_core: 1,
|
||||
run_attempt_log_retention: 1,
|
||||
run_dispatch_lease: 1,
|
||||
run_retry_policy: 1,
|
||||
security_audit: 1,
|
||||
@@ -1166,6 +1168,41 @@ export const postgresqlControlSchemaContract: PostgresSchemaContract =
|
||||
'error_code',
|
||||
'error_summary',
|
||||
]),
|
||||
table('run_attempt_log_retention_controls', [
|
||||
'attempt_id',
|
||||
'project_id',
|
||||
'run_id',
|
||||
'log_artifact_id',
|
||||
'executor_type',
|
||||
'finished_at_ms',
|
||||
'eligible_at_ms',
|
||||
'state',
|
||||
'claim_owner',
|
||||
'claim_token',
|
||||
'claim_version',
|
||||
'claim_expires_at_ms',
|
||||
'next_claim_at_ms',
|
||||
'failure_count',
|
||||
'last_failure_code',
|
||||
'created_at_ms',
|
||||
'updated_at_ms',
|
||||
]),
|
||||
table('run_attempt_log_artifact_tombstones', [
|
||||
'log_artifact_id',
|
||||
'project_id',
|
||||
'run_id',
|
||||
'attempt_id',
|
||||
'executor_type',
|
||||
'finished_at_ms',
|
||||
'eligible_at_ms',
|
||||
'retired_at_ms',
|
||||
'disposition',
|
||||
'byte_length',
|
||||
'truncated',
|
||||
'maximum_bytes',
|
||||
'truncation_observed_at_ms',
|
||||
'record_digest',
|
||||
]),
|
||||
table('worker_sessions', [
|
||||
'worker_id',
|
||||
'session_id',
|
||||
@@ -1594,6 +1631,14 @@ export const postgresqlControlSchemaContract: PostgresSchemaContract =
|
||||
'ql3_run_attempts_dispatch_candidates_idx',
|
||||
'ql3_run_attempts_recovery_idx',
|
||||
'ql3_run_attempts_lease_idx',
|
||||
'run_attempt_log_retention_controls_pkey',
|
||||
'ql3_run_log_retention_control_artifact_key',
|
||||
'ql3_run_log_retention_retry_idx',
|
||||
'ql3_run_log_retention_claim_expiry_idx',
|
||||
'run_attempt_log_artifact_tombstones_pkey',
|
||||
'ql3_run_log_tombstone_attempt_key',
|
||||
'ql3_run_log_tombstone_retired_idx',
|
||||
'ql3_run_log_retention_candidate_idx',
|
||||
'worker_sessions_pkey',
|
||||
'ql3_worker_sessions_available_idx',
|
||||
'run_dispatch_leases_pkey',
|
||||
@@ -1913,6 +1958,23 @@ export const postgresqlControlSchemaContract: PostgresSchemaContract =
|
||||
'ql3_run_attempts_created_at_check',
|
||||
'ql3_run_attempts_started_at_check',
|
||||
'ql3_run_attempts_finished_at_check',
|
||||
'ql3_run_log_retention_control_identity_check',
|
||||
'ql3_run_log_retention_control_time_check',
|
||||
'ql3_run_log_retention_control_state_check',
|
||||
'ql3_run_log_retention_control_claim_owner_check',
|
||||
'ql3_run_log_retention_control_claim_token_check',
|
||||
'ql3_run_log_retention_control_claim_version_check',
|
||||
'ql3_run_log_retention_control_claim_expiry_check',
|
||||
'ql3_run_log_retention_control_next_claim_check',
|
||||
'ql3_run_log_retention_control_failure_count_check',
|
||||
'ql3_run_log_retention_control_failure_code_check',
|
||||
'ql3_run_log_retention_control_state_shape_check',
|
||||
'ql3_run_log_tombstone_identity_check',
|
||||
'ql3_run_log_tombstone_time_check',
|
||||
'ql3_run_log_tombstone_disposition_check',
|
||||
'ql3_run_log_tombstone_size_check',
|
||||
'ql3_run_log_tombstone_truncation_check',
|
||||
'ql3_run_log_tombstone_digest_check',
|
||||
'ql3_worker_sessions_worker_id_check',
|
||||
'ql3_worker_sessions_session_id_check',
|
||||
'ql3_worker_sessions_generation_check',
|
||||
@@ -2211,6 +2273,10 @@ export const postgresqlControlSchemaContract: PostgresSchemaContract =
|
||||
'ql3_result_retirement_catalog_fk',
|
||||
'ql3_run_attempts_run_fk',
|
||||
'ql3_run_attempts_step_run_fk',
|
||||
'ql3_run_log_retention_control_attempt_fk',
|
||||
'ql3_run_log_retention_control_run_fk',
|
||||
'ql3_run_log_tombstone_attempt_fk',
|
||||
'ql3_run_log_tombstone_run_fk',
|
||||
'ql3_run_dispatch_leases_attempt_fk',
|
||||
'ql3_run_dispatch_leases_run_fk',
|
||||
'ql3_run_dispatch_leases_worker_fk',
|
||||
|
||||
@@ -539,6 +539,18 @@ const REQUIRED_RUNTIME_PRIVILEGES = Object.freeze({
|
||||
update: true,
|
||||
delete: false,
|
||||
}),
|
||||
run_attempt_log_retention_controls: Object.freeze({
|
||||
select: true,
|
||||
insert: true,
|
||||
update: true,
|
||||
delete: true,
|
||||
}),
|
||||
run_attempt_log_artifact_tombstones: Object.freeze({
|
||||
select: true,
|
||||
insert: true,
|
||||
update: false,
|
||||
delete: false,
|
||||
}),
|
||||
worker_sessions: Object.freeze({
|
||||
select: true,
|
||||
insert: true,
|
||||
@@ -1034,6 +1046,18 @@ const REQUIRED_ADMIN_PRIVILEGES = Object.freeze({
|
||||
update: false,
|
||||
delete: false,
|
||||
}),
|
||||
run_attempt_log_retention_controls: Object.freeze({
|
||||
select: false,
|
||||
insert: false,
|
||||
update: false,
|
||||
delete: false,
|
||||
}),
|
||||
run_attempt_log_artifact_tombstones: Object.freeze({
|
||||
select: false,
|
||||
insert: false,
|
||||
update: false,
|
||||
delete: false,
|
||||
}),
|
||||
worker_sessions: Object.freeze({
|
||||
select: false,
|
||||
insert: false,
|
||||
|
||||
Reference in New Issue
Block a user