import { type DB, type Kysely, sql } from "@umt/db"; // 日志性能测试的数据量:约 30,000 条引擎运行(多种工作流 / 状态 / 业务对象,约 28 天) // + 定时任务运行 + 智能体运行 + 审计。分布参照部署库(大部分是 RSS / 站点快照的成功运行,失败约 1%)。 export const VOLUME_RUNS = 30_000; export async function seedLogVolume(db: Kysely, engineSchema: string, runs = VOLUME_RUNS): Promise { const wr = sql.raw(`"${engineSchema}".workflow_runs`); await sql`insert into site (key, name, adapter, base_url) select 'vs' || g, '站点 ' || g, 'nexusphp', 'https://s' || g || '.example' from generate_series(1, 20) g`.execute( db, ); await sql`insert into feed_source (name, url_display) select case when g = 7 then 'my feeds 汇总' else 'RSS 源 ' || g end, 'https://f' || g || '.example' from generate_series(1, 30) g`.execute(db); await sql`insert into media_entity (kind, title) select 'movie', '电影 ' || g from generate_series(1, 500) g`.execute( db, ); await sql`insert into subscription (media_kind, media_entity_id, title_query) select 'movie', e.id, null from (select id from media_entity order by title limit 200) e`.execute(db); const inst = await sql<{ id: string }>`insert into downloader_instance (name, base_url) values ('qb-volume', 'http://qb') returning id`.execute(db); await sql`insert into download_task (instance_id, torrent_hash, name) select ${inst.rows[0]!.id}::uuid, md5('vol' || g) || left(md5('h' || g), 8), '下载 ' || g || ' 2160p' from generate_series(1, 2000) g`.execute(db); await sql`insert into agent_run (trigger, goal, status, created_at, started_at, finished_at) select (array['event', 'schedule', 'user'])[1 + g % 3], '调查 ' || g, (array['succeeded', 'succeeded', 'succeeded', 'failed', 'budget_exceeded', 'cancelled'])[1 + g % 6], now() - g * interval '20 minutes', now() - g * interval '20 minutes', now() - g * interval '20 minutes' + interval '1 minute' from generate_series(1, 1500) g`.execute(db); await sql`insert into scheduled_job (id, name, board_group, kind, enabled, default_enabled, schedule, default_schedule) values ('vol.feeds', 'RSS 抓取(量)', 'collect', 'task', true, true, '{"type":"interval","everyMs":60000}'::jsonb, '{"type":"interval","everyMs":60000}'::jsonb), ('trash.purge', '清理回收站', 'maintain', 'task', true, true, '{"type":"interval","everyMs":3600000}'::jsonb, '{"type":"interval","everyMs":3600000}'::jsonb), ('vol.check', '巡检(量)', 'agent', 'agent_check', true, true, '{"type":"interval","everyMs":86400000}'::jsonb, '{"type":"interval","everyMs":86400000}'::jsonb) on conflict (id) do nothing`.execute(db); await sql`insert into scheduled_job_run (job_id, trigger, status, error, started_at, finished_at, duration_ms) select (array['vol.feeds', 'trash.purge', 'vol.check'])[1 + g % 3], 'schedule', (case when g % 50 = 0 then 'failed' when g % 7 = 0 then 'skipped' else 'ok' end), (case when g % 50 = 0 then '站点超时 ' || g end), now() - g * interval '37 minutes', now() - g * interval '37 minutes', 500 from generate_series(1, 1100) g`.execute(db); await sql`insert into audit_event (at, actor_type, actor_id, action, target_type, target_id, payload) select now() - g * interval '53 minutes', 'user', 'admin', (array['admin.login_success', 'settings.update', 'admin.login_failed', 'download.cancel'])[1 + g % 4], 'settings', 'k' || g, jsonb_build_object('ip', '10.1.0.' || (g % 200)) from generate_series(1, 800) g`.execute(db); // 引擎运行:按 g % 100 分桶决定工作流与业务对象,g 越大越早(每条间隔 80 秒) await sql`with feeds as (select array_agg(id::text order by name) a from feed_source), sites as (select array_agg(id::text order by key) a from site where key like 'vs%'), dls as (select array_agg(id::text order by name) a from download_task where instance_id = ${inst.rows[0]!.id}::uuid), agents as (select array_agg(id::text order by goal) a from agent_run where goal like '调查 %'), subs as (select array_agg(id::text order by id) a from subscription), src as ( select g, g % 100 as b, now() - g * interval '80 seconds' as at, (case when g <= 5 then 'running' when g % 97 = 0 then 'failed' when g % 193 = 0 then 'canceled' else 'completed' end) as st from generate_series(1, ${runs}) g) insert into ${wr} (namespace_id, id, workflow_name, version, status, config, context, input, error, attempts, available_at, started_at, finished_at, created_at, updated_at) select 'default', 'vol-' || lpad(g::text, 6, '0'), (case when b < 40 then 'ingestFeed' when b < 55 then 'snapshotSite' when b < 65 then 'refreshSiteAccount' when b < 70 then 'submitDownload' when b < 75 then 'verifyAndImport' when b < 80 then 'runAgent' when b < 88 then 'searchSubscription' when b < 93 then 'purgeTrash' when b < 98 then 'scanOrganizeSource' else 'evaluateAcquisition' end), 'v1', st, '{}'::jsonb, null, jsonb_build_object('input', (case when b < 40 then jsonb_build_object('feedSourceId', feeds.a[1 + g % 30]) when b < 55 then jsonb_build_object('siteId', sites.a[1 + g % 20]) when b < 65 then '{}'::jsonb when b < 75 then jsonb_build_object('downloadTaskId', dls.a[1 + g % 2000]) when b < 80 then jsonb_build_object('agentRunId', agents.a[1 + g % 1500]) when b < 88 then jsonb_build_object('subscriptionId', subs.a[1 + g % 200]) when b < 98 then '{}'::jsonb else jsonb_build_object('acquisitionPlanId', md5('plan' || g)::uuid::text) end)), (case when st = 'failed' then jsonb_build_object('message', '失败原因 ' || g) end), 1, at, at, (case when st = 'running' then null else at + interval '3 seconds' end), at, at from src, feeds, sites, dls, agents, subs`.execute(db); await sql`analyze ${wr}`.execute(db); await sql`analyze site, feed_source, media_entity, subscription, download_task, agent_run, scheduled_job, scheduled_job_run, audit_event`.execute(db); }