perf: index agent paths so the merged listing and owner counts skip other resources

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Vo7HXMdgqXJhR7p8YyVgjB
This commit is contained in:
hugocasa
2026-10-09 19:36:22 +02:00
co-authored by Claude Opus 5.5
parent 837358e962
commit 5e674cdfb9
4 changed files with 14 additions and 2 deletions
@@ -0,0 +1 @@
DROP INDEX IF EXISTS idx_resource_agent_owner_prefix;
@@ -0,0 +1,7 @@
-- Backs the agent branch of /runnables/list and /runnables/counts, which reach the
-- `ai_agent` rows of `resource` by workspace and byte-ordered path prefix. Partial, so
-- neither walks the workspace's other resources (state, cache, ...).
-- Created CONCURRENTLY via the OVERRIDDEN_MIGRATIONS rewrite in windmill-api/src/db.rs.
CREATE INDEX IF NOT EXISTS idx_resource_agent_owner_prefix
ON resource (workspace_id, path text_pattern_ops)
WHERE resource_type = 'ai_agent';
+3
View File
@@ -111,6 +111,9 @@ lazy_static::lazy_static! {
(20260909163047, include_str!(
"../../migrations/20260909163047_workspace_delete_cascade_indexes.up.sql"
).replace("CREATE INDEX", "CREATE INDEX CONCURRENTLY").replace("DROP INDEX", "DROP INDEX CONCURRENTLY")),
(20261009173546, include_str!(
"../../migrations/20261009173546_agent_owner_prefix_index.up.sql"
).replace("CREATE INDEX", "CREATE INDEX CONCURRENTLY")),
].into_iter().collect();
}
+3 -2
View File
@@ -16,7 +16,8 @@
//! Efficiency: each kind is a UNION ALL branch ordered by an index on
//! `(workspace_id, archived, <sort key>)` (created_at / edited_at, or the lowered
//! summary-or-path expression for name orders); Postgres merges the ordered
//! branches and stops at the page limit. Pagination is keyset — a
//! branches and stops at the page limit. Agents, which are few, are instead
//! reached through the partial `idx_resource_agent_owner_prefix` and sorted whole. Pagination is keyset — a
//! `(sort_key, path, kind, tiebreak)` cursor, where `tiebreak` (a script's hash,
//! 0 for flow/app) is a stable final key that keeps the order total even when rows
//! tie on (sort_key, path, kind) — so deep pages don't re-scan. Visibility is
@@ -858,7 +859,7 @@ struct OwnerCount {
/// `owner/<rest>` is >= `owner || '/'` and, since '/' (0x2F) is immediately
/// followed by '0' (0x30), < `owner || '0'`. `~>=~` / `~<~` are the
/// text_pattern_ops operators, which is what lets `idx_<kind>_owner_prefix`
/// answer the count with an index-only scan — the default opclass sorts by the
/// (`idx_resource_agent_owner_prefix` for agents) answer the count with an index-only scan — the default opclass sorts by the
/// database collation and cannot serve a byte-prefix range.
fn owner_prefix_range(alias: &str) -> String {
format!("{alias}.path ~>=~ (o.owner || '/') AND {alias}.path ~<~ (o.owner || '0')")