mirror of
https://github.com/whyour/qinglong.git
synced 2026-09-21 01:32:44 +08:00
228 lines
8.0 KiB
TypeScript
228 lines
8.0 KiB
TypeScript
import { createHash } from 'crypto';
|
|
import { DataTypes, Op } from 'sequelize';
|
|
import {
|
|
APPROVED_ACTION_EXECUTION_STATUSES,
|
|
DEFAULT_APPROVED_ACTION_MAX_ATTEMPTS,
|
|
MAX_APPROVED_ACTION_ATTEMPTS,
|
|
} from '../runtime/domain/approvedActionDispatchExecution';
|
|
import { PROJECT_TABLE } from './0017-project-policy';
|
|
import { APPROVED_ACTION_DISPATCH_TABLE } from './0020-approval-requests';
|
|
import type { Migration } from './types';
|
|
|
|
export const APPROVED_ACTION_DISPATCH_EXECUTION_TABLE =
|
|
'ApprovedActionDispatchExecutions';
|
|
export const APPROVED_ACTION_DISPATCH_EXECUTION_DUE_INDEX =
|
|
'approved_action_execution_due_idx';
|
|
export const APPROVED_ACTION_DISPATCH_EXECUTION_PROJECT_INDEX =
|
|
'approved_action_execution_project_idx';
|
|
export const APPROVED_ACTION_DISPATCH_EXECUTION_LEASE_INDEX =
|
|
'approved_action_execution_lease_idx';
|
|
|
|
const columns = [
|
|
'dispatch_id',
|
|
'project_id',
|
|
'status',
|
|
'version',
|
|
'attempt_count',
|
|
'max_attempts',
|
|
'eligible_at_ms',
|
|
'next_attempt_at_ms',
|
|
'lease_owner',
|
|
'lease_token',
|
|
'lease_expires_at_ms',
|
|
'started_at_ms',
|
|
'result_mutation_id',
|
|
'last_result_code',
|
|
'completed_at_ms',
|
|
'created_at_ms',
|
|
'updated_at_ms',
|
|
];
|
|
|
|
const manifest = {
|
|
table: APPROVED_ACTION_DISPATCH_EXECUTION_TABLE,
|
|
columns,
|
|
indexes: [
|
|
`${APPROVED_ACTION_DISPATCH_EXECUTION_DUE_INDEX}(eligible_at_ms,dispatch_id)`,
|
|
`${APPROVED_ACTION_DISPATCH_EXECUTION_PROJECT_INDEX}(project_id,status,created_at_ms,dispatch_id)`,
|
|
`${APPROVED_ACTION_DISPATCH_EXECUTION_LEASE_INDEX}(status,lease_expires_at_ms,dispatch_id)`,
|
|
],
|
|
constraints: [
|
|
'approved_action_execution_status_check',
|
|
'approved_action_execution_version_check',
|
|
'approved_action_execution_attempt_count_check',
|
|
'approved_action_execution_max_attempts_check',
|
|
'approved_action_execution_attempt_budget_check',
|
|
'approved_action_execution_lease_tuple_check',
|
|
'approved_action_execution_created_at_check',
|
|
'approved_action_execution_updated_at_check',
|
|
],
|
|
baselineMaxAttempts: DEFAULT_APPROVED_ACTION_MAX_ATTEMPTS,
|
|
};
|
|
|
|
export const approvedActionDispatchExecutionManifest = manifest;
|
|
|
|
export const approvedActionDispatchExecutionMigration: Migration = {
|
|
id: '0021-approved-action-dispatch-executions',
|
|
checksum: createHash('sha256').update(JSON.stringify(manifest)).digest('hex'),
|
|
async up({ queryInterface, transaction }) {
|
|
await queryInterface.createTable(
|
|
APPROVED_ACTION_DISPATCH_EXECUTION_TABLE,
|
|
{
|
|
dispatch_id: {
|
|
type: DataTypes.STRING(64),
|
|
allowNull: false,
|
|
primaryKey: true,
|
|
references: { model: APPROVED_ACTION_DISPATCH_TABLE, key: 'id' },
|
|
onDelete: 'CASCADE',
|
|
onUpdate: 'CASCADE',
|
|
},
|
|
project_id: {
|
|
type: DataTypes.STRING(128),
|
|
allowNull: false,
|
|
references: { model: PROJECT_TABLE, key: 'id' },
|
|
onDelete: 'CASCADE',
|
|
onUpdate: 'CASCADE',
|
|
},
|
|
status: { type: DataTypes.STRING(16), allowNull: false },
|
|
version: { type: DataTypes.INTEGER, allowNull: false },
|
|
attempt_count: { type: DataTypes.INTEGER, allowNull: false },
|
|
max_attempts: { type: DataTypes.INTEGER, allowNull: false },
|
|
eligible_at_ms: { type: DataTypes.BIGINT, allowNull: true },
|
|
next_attempt_at_ms: { type: DataTypes.BIGINT, allowNull: true },
|
|
lease_owner: { type: DataTypes.STRING(128), allowNull: true },
|
|
lease_token: { type: DataTypes.STRING(128), allowNull: true },
|
|
lease_expires_at_ms: { type: DataTypes.BIGINT, allowNull: true },
|
|
started_at_ms: { type: DataTypes.BIGINT, allowNull: true },
|
|
result_mutation_id: { type: DataTypes.STRING(64), allowNull: true },
|
|
last_result_code: { type: DataTypes.STRING(64), allowNull: true },
|
|
completed_at_ms: { type: DataTypes.BIGINT, allowNull: true },
|
|
created_at_ms: { type: DataTypes.BIGINT, allowNull: false },
|
|
updated_at_ms: { type: DataTypes.BIGINT, allowNull: false },
|
|
},
|
|
{ transaction },
|
|
);
|
|
await queryInterface.addConstraint(
|
|
APPROVED_ACTION_DISPATCH_EXECUTION_TABLE,
|
|
{
|
|
fields: ['status'],
|
|
type: 'check',
|
|
where: { status: { [Op.in]: APPROVED_ACTION_EXECUTION_STATUSES } },
|
|
name: 'approved_action_execution_status_check',
|
|
transaction,
|
|
},
|
|
);
|
|
for (const [field, minimum, maximum, name] of [
|
|
['version', 0, 2_147_483_647, 'approved_action_execution_version_check'],
|
|
[
|
|
'attempt_count',
|
|
0,
|
|
MAX_APPROVED_ACTION_ATTEMPTS,
|
|
'approved_action_execution_attempt_count_check',
|
|
],
|
|
[
|
|
'max_attempts',
|
|
1,
|
|
MAX_APPROVED_ACTION_ATTEMPTS,
|
|
'approved_action_execution_max_attempts_check',
|
|
],
|
|
] as const) {
|
|
await queryInterface.addConstraint(
|
|
APPROVED_ACTION_DISPATCH_EXECUTION_TABLE,
|
|
{
|
|
fields: [field],
|
|
type: 'check',
|
|
where: { [field]: { [Op.between]: [minimum, maximum] } },
|
|
name,
|
|
transaction,
|
|
},
|
|
);
|
|
}
|
|
await queryInterface.addConstraint(
|
|
APPROVED_ACTION_DISPATCH_EXECUTION_TABLE,
|
|
{
|
|
fields: ['attempt_count', 'max_attempts'],
|
|
type: 'check',
|
|
where: { attempt_count: { [Op.lte]: { [Op.col]: 'max_attempts' } } },
|
|
name: 'approved_action_execution_attempt_budget_check',
|
|
transaction,
|
|
},
|
|
);
|
|
await queryInterface.addConstraint(
|
|
APPROVED_ACTION_DISPATCH_EXECUTION_TABLE,
|
|
{
|
|
fields: ['lease_owner', 'lease_token', 'lease_expires_at_ms'],
|
|
type: 'check',
|
|
where: {
|
|
[Op.or]: [
|
|
{
|
|
lease_owner: null,
|
|
lease_token: null,
|
|
lease_expires_at_ms: null,
|
|
},
|
|
{
|
|
lease_owner: { [Op.not]: null },
|
|
lease_token: { [Op.not]: null },
|
|
lease_expires_at_ms: { [Op.not]: null },
|
|
},
|
|
],
|
|
},
|
|
name: 'approved_action_execution_lease_tuple_check',
|
|
transaction,
|
|
},
|
|
);
|
|
await queryInterface.addConstraint(
|
|
APPROVED_ACTION_DISPATCH_EXECUTION_TABLE,
|
|
{
|
|
fields: ['created_at_ms'],
|
|
type: 'check',
|
|
where: { created_at_ms: { [Op.gte]: 0 } },
|
|
name: 'approved_action_execution_created_at_check',
|
|
transaction,
|
|
},
|
|
);
|
|
await queryInterface.addConstraint(
|
|
APPROVED_ACTION_DISPATCH_EXECUTION_TABLE,
|
|
{
|
|
fields: ['updated_at_ms', 'created_at_ms'],
|
|
type: 'check',
|
|
where: { updated_at_ms: { [Op.gte]: { [Op.col]: 'created_at_ms' } } },
|
|
name: 'approved_action_execution_updated_at_check',
|
|
transaction,
|
|
},
|
|
);
|
|
await queryInterface.addIndex(
|
|
APPROVED_ACTION_DISPATCH_EXECUTION_TABLE,
|
|
['eligible_at_ms', 'dispatch_id'],
|
|
{
|
|
name: APPROVED_ACTION_DISPATCH_EXECUTION_DUE_INDEX,
|
|
transaction,
|
|
},
|
|
);
|
|
await queryInterface.addIndex(APPROVED_ACTION_DISPATCH_EXECUTION_TABLE, {
|
|
fields: ['project_id', 'status', 'created_at_ms', 'dispatch_id'],
|
|
name: APPROVED_ACTION_DISPATCH_EXECUTION_PROJECT_INDEX,
|
|
transaction,
|
|
});
|
|
await queryInterface.addIndex(APPROVED_ACTION_DISPATCH_EXECUTION_TABLE, {
|
|
fields: ['status', 'lease_expires_at_ms', 'dispatch_id'],
|
|
name: APPROVED_ACTION_DISPATCH_EXECUTION_LEASE_INDEX,
|
|
transaction,
|
|
});
|
|
await queryInterface.sequelize.query(
|
|
`INSERT INTO "${APPROVED_ACTION_DISPATCH_EXECUTION_TABLE}"
|
|
(dispatch_id, project_id, status, version, attempt_count, max_attempts,
|
|
eligible_at_ms, next_attempt_at_ms, lease_owner, lease_token,
|
|
lease_expires_at_ms, started_at_ms, result_mutation_id,
|
|
last_result_code, completed_at_ms, created_at_ms, updated_at_ms)
|
|
SELECT id, project_id, 'pending', 0, 0, :maxAttempts,
|
|
created_at_ms, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
|
|
created_at_ms, created_at_ms
|
|
FROM "${APPROVED_ACTION_DISPATCH_TABLE}"`,
|
|
{
|
|
replacements: { maxAttempts: DEFAULT_APPROVED_ACTION_MAX_ATTEMPTS },
|
|
transaction,
|
|
},
|
|
);
|
|
},
|
|
};
|