mirror of
https://gh-proxy.org/https://github.com/klinsmaya/multica.git
synced 2026-11-09 09:56:47 +08:00
* perf(db): mint UUIDv7 primary keys for the append-heavy tables
Random v4 primary keys scatter every insert across the whole primary-key
B-tree, so each row dirties a different page. On the tables that grow
continuously — the task queue, messages, comments, activity and audit rows —
that is pure write amplification. UUIDv7 puts a big-endian millisecond
timestamp in the leading 48 bits, so consecutive inserts share a page.
The 12 tables converted here are the append-only hot paths:
agent_task_queue, activity_log, inbox_item, comment, issue, chat_session,
chat_message, autopilot_run, webhook_delivery, task_token,
channel_inbound_audit, sys_cron_executions
Ids come from the new pkg/dbid, which returns a pgtype.UUID ready to assign to
a sqlc params struct. Upsert-dominated tables (task_usage, agent_runtime, ...)
are deliberately left alone: v7 only buys locality for rows that are actually
inserted. Lease and claim tokens keep gen_random_uuid() — they must stay
unpredictable and carry no timestamp.
No migration, and no backfill. Every converted INSERT binds its id as
COALESCE(sqlc.narg('id')::uuid, gen_random_uuid()), so the column default stays
reachable: a caller that passes no id inserts exactly as it does today. Tables
will hold a mix of v4 and v7 ids permanently, which the uuid type and every
parser in the repo already accept.
Verification: - pkg/dbid: version/variant, uniqueness, and a non-decreasing timestamp
prefix; a shape test asserting every converted query keeps the COALESCE
fallback; and a source scan asserting every production params literal mints
an id, so a call site added later cannot silently fall back to v4.
- internal/handler: round-trips issue, comment, activity_log, inbox_item,
chat_session, chat_message, agent_task_queue, task_token and
channel_inbound_audit against Postgres and asserts the stored id is v7,
plus one case proving an unset id still inserts (as v4).
- internal/scheduler: covers the one hand-written INSERT, asserting the
execution id is v7 while lease_token stays v4.
Co-authored-by: multica-agent <[email protected]>
* fix(db): address UUIDv7 review feedback
Co-authored-by: multica-agent <[email protected]>
* fix(db): cover Telegram audit UUIDv7
Co-authored-by: multica-agent <[email protected]>
---------
Co-authored-by: Sol-Boy <[email protected]>
Co-authored-by: multica-agent <[email protected]>
61 lines
2.2 KiB
SQL
61 lines
2.2 KiB
SQL
-- name: ListActivitiesForIssue :many
|
|
-- The NEWEST $2 activities for an issue, returned in chronological order.
|
|
--
|
|
-- The cap has to bite at the OLD end, not the new one. The inner query takes
|
|
-- the window with the keyset ordering (created_at DESC, id DESC), which
|
|
-- idx_activity_log_issue_keyset (migration 068) satisfies without a sort step —
|
|
-- it is not an index-only scan, since the index does not cover the columns
|
|
-- SELECT * needs, so the heap is still read for the rows in the window. The
|
|
-- outer query re-sorts ascending so every caller keeps the chronological
|
|
-- contract it already had.
|
|
--
|
|
-- Capping with ORDER BY created_at ASC instead discards the newest rows, which
|
|
-- made a busy issue's timeline appear to stop at some point in the past with no
|
|
-- indication anything was missing. Activity is machine-paced (description
|
|
-- autosave, every agent run, status/assignee changes), so this was reachable in
|
|
-- normal use, not only on pathological issues (MUL-5492).
|
|
SELECT * FROM (
|
|
SELECT * FROM activity_log
|
|
WHERE issue_id = $1
|
|
ORDER BY created_at DESC, id DESC
|
|
LIMIT $2
|
|
) AS recent
|
|
ORDER BY created_at ASC, id ASC;
|
|
|
|
-- name: GetActivity :one
|
|
SELECT * FROM activity_log
|
|
WHERE id = $1;
|
|
|
|
-- name: CreateActivity :one
|
|
INSERT INTO activity_log (
|
|
workspace_id, issue_id, actor_type, actor_id, action, details, id
|
|
) VALUES ($1, $2, $3, $4, $5, $6, COALESCE(sqlc.narg('id')::uuid, gen_random_uuid()))
|
|
RETURNING *;
|
|
|
|
-- name: HasSquadLeaderNoActionEvaluationForTask :one
|
|
SELECT EXISTS (
|
|
SELECT 1
|
|
FROM activity_log
|
|
WHERE issue_id = @issue_id
|
|
AND actor_type = 'agent'
|
|
AND actor_id = @agent_id
|
|
AND action = 'squad_leader_evaluated'
|
|
AND details->>'outcome' = 'no_action'
|
|
AND details->>'task_id' = @task_id::text
|
|
) AS exists;
|
|
|
|
-- name: CountAssigneeChangesByActor :many
|
|
-- Count how many times a user assigned each target via assignee_changed activities.
|
|
SELECT
|
|
details->>'to_type' as assignee_type,
|
|
details->>'to_id' as assignee_id,
|
|
COUNT(*)::bigint as frequency
|
|
FROM activity_log
|
|
WHERE workspace_id = $1
|
|
AND actor_id = $2
|
|
AND actor_type = 'member'
|
|
AND action = 'assignee_changed'
|
|
AND details->>'to_type' IS NOT NULL
|
|
AND details->>'to_id' IS NOT NULL
|
|
GROUP BY details->>'to_type', details->>'to_id';
|