From cac75bcbcf1b9309fff86790e1cbdb6ea2de22b8 Mon Sep 17 00:00:00 2001 From: AaronPlave Date: Fri, 29 May 2026 10:01:09 -0700 Subject: [PATCH 1/2] Add anchor_id index to activity_directive --- .../Aerie/35_activity_directive_anchor_index/down.sql | 3 +++ .../Aerie/35_activity_directive_anchor_index/up.sql | 5 +++++ .../tables/merlin/activity_directive/activity_directive.sql | 4 ++++ 3 files changed, 12 insertions(+) create mode 100644 deployment/hasura/migrations/Aerie/35_activity_directive_anchor_index/down.sql create mode 100644 deployment/hasura/migrations/Aerie/35_activity_directive_anchor_index/up.sql diff --git a/deployment/hasura/migrations/Aerie/35_activity_directive_anchor_index/down.sql b/deployment/hasura/migrations/Aerie/35_activity_directive_anchor_index/down.sql new file mode 100644 index 0000000000..ced1e5ec79 --- /dev/null +++ b/deployment/hasura/migrations/Aerie/35_activity_directive_anchor_index/down.sql @@ -0,0 +1,3 @@ +drop index if exists merlin.activity_directive_anchor_id_index; + +call migrations.mark_migration_rolled_back(35); diff --git a/deployment/hasura/migrations/Aerie/35_activity_directive_anchor_index/up.sql b/deployment/hasura/migrations/Aerie/35_activity_directive_anchor_index/up.sql new file mode 100644 index 0000000000..cd68390359 --- /dev/null +++ b/deployment/hasura/migrations/Aerie/35_activity_directive_anchor_index/up.sql @@ -0,0 +1,5 @@ +create index activity_directive_anchor_id_index + on merlin.activity_directive (anchor_id, plan_id) + where anchor_id is not null; + +call migrations.mark_migration_applied(35); diff --git a/deployment/postgres-init-db/sql/tables/merlin/activity_directive/activity_directive.sql b/deployment/postgres-init-db/sql/tables/merlin/activity_directive/activity_directive.sql index 5ddafbb641..bba559f46e 100644 --- a/deployment/postgres-init-db/sql/tables/merlin/activity_directive/activity_directive.sql +++ b/deployment/postgres-init-db/sql/tables/merlin/activity_directive/activity_directive.sql @@ -54,6 +54,10 @@ create table merlin.activity_directive ( create index activity_directive_plan_id_index on merlin.activity_directive (plan_id); +create index activity_directive_anchor_id_index + on merlin.activity_directive (anchor_id, plan_id) + where anchor_id is not null; + comment on table merlin.activity_directive is e'' 'A single activity_directive within a plan.'; From 9122b4b956c1a3f12dcd53b06bb1ae100a000577 Mon Sep 17 00:00:00 2001 From: AaronPlave Date: Tue, 2 Jun 2026 10:26:47 -0700 Subject: [PATCH 2/2] Speed up anchor-dependency lookups during bulk activity_directive inserts --- .../down.sql | 20 ++++++++++++++++ .../35_activity_directive_anchor_index/up.sql | 23 +++++++++++++++++-- .../activity_directive/activity_directive.sql | 4 ++-- .../anchor_validation_status.sql | 8 +++---- 4 files changed, 47 insertions(+), 8 deletions(-) diff --git a/deployment/hasura/migrations/Aerie/35_activity_directive_anchor_index/down.sql b/deployment/hasura/migrations/Aerie/35_activity_directive_anchor_index/down.sql index ced1e5ec79..8efd172ad6 100644 --- a/deployment/hasura/migrations/Aerie/35_activity_directive_anchor_index/down.sql +++ b/deployment/hasura/migrations/Aerie/35_activity_directive_anchor_index/down.sql @@ -1,3 +1,23 @@ +create or replace function merlin.get_dependent_activities(_activity_id int, _plan_id int) + returns table(activity_id int, total_offset interval) + stable + language plpgsql as $$ +begin + return query + with recursive d_activities(activity_id, anchor_id, anchored_to_start, start_offset, total_offset) as ( + select ad.id, ad.anchor_id, ad.anchored_to_start, ad.start_offset, ad.start_offset + from merlin.activity_directive ad + where (ad.anchor_id, ad.plan_id) = (_activity_id, _plan_id) + union + select ad.id, ad.anchor_id, ad.anchored_to_start, ad.start_offset, da.total_offset + ad.start_offset + from merlin.activity_directive ad, d_activities da + where (ad.anchor_id, ad.plan_id) = (da.activity_id, _plan_id) + and ad.anchored_to_start + ) select da.activity_id, da.total_offset + from d_activities da; +end; +$$; + drop index if exists merlin.activity_directive_anchor_id_index; call migrations.mark_migration_rolled_back(35); diff --git a/deployment/hasura/migrations/Aerie/35_activity_directive_anchor_index/up.sql b/deployment/hasura/migrations/Aerie/35_activity_directive_anchor_index/up.sql index cd68390359..d28a3dcd41 100644 --- a/deployment/hasura/migrations/Aerie/35_activity_directive_anchor_index/up.sql +++ b/deployment/hasura/migrations/Aerie/35_activity_directive_anchor_index/up.sql @@ -1,5 +1,24 @@ +-- Used by merlin.get_dependent_activities (called per row from validate_anchors_insert_trigger). create index activity_directive_anchor_id_index - on merlin.activity_directive (anchor_id, plan_id) - where anchor_id is not null; + on merlin.activity_directive (anchor_id, plan_id); + +-- `language sql` (not plpgsql) so the planner can inline this into the calling +-- query and pick the index above. The plpgsql wrapper was opaque to the planner. +create or replace function merlin.get_dependent_activities(_activity_id int, _plan_id int) + returns table(activity_id int, total_offset interval) + stable + language sql as $$ + with recursive d_activities(activity_id, anchor_id, anchored_to_start, start_offset, total_offset) as ( + select ad.id, ad.anchor_id, ad.anchored_to_start, ad.start_offset, ad.start_offset + from merlin.activity_directive ad + where (ad.anchor_id, ad.plan_id) = (_activity_id, _plan_id) + union + select ad.id, ad.anchor_id, ad.anchored_to_start, ad.start_offset, da.total_offset + ad.start_offset + from merlin.activity_directive ad, d_activities da + where (ad.anchor_id, ad.plan_id) = (da.activity_id, _plan_id) + and ad.anchored_to_start + ) select da.activity_id, da.total_offset + from d_activities da; +$$; call migrations.mark_migration_applied(35); diff --git a/deployment/postgres-init-db/sql/tables/merlin/activity_directive/activity_directive.sql b/deployment/postgres-init-db/sql/tables/merlin/activity_directive/activity_directive.sql index bba559f46e..cb31084efd 100644 --- a/deployment/postgres-init-db/sql/tables/merlin/activity_directive/activity_directive.sql +++ b/deployment/postgres-init-db/sql/tables/merlin/activity_directive/activity_directive.sql @@ -54,9 +54,9 @@ create table merlin.activity_directive ( create index activity_directive_plan_id_index on merlin.activity_directive (plan_id); +-- Used by merlin.get_dependent_activities (called per row from validate_anchors_insert_trigger). create index activity_directive_anchor_id_index - on merlin.activity_directive (anchor_id, plan_id) - where anchor_id is not null; + on merlin.activity_directive (anchor_id, plan_id); comment on table merlin.activity_directive is e'' diff --git a/deployment/postgres-init-db/sql/tables/merlin/activity_directive/anchor_validation_status.sql b/deployment/postgres-init-db/sql/tables/merlin/activity_directive/anchor_validation_status.sql index 5484b89736..55e441c840 100644 --- a/deployment/postgres-init-db/sql/tables/merlin/activity_directive/anchor_validation_status.sql +++ b/deployment/postgres-init-db/sql/tables/merlin/activity_directive/anchor_validation_status.sql @@ -24,12 +24,13 @@ comment on column merlin.anchor_validation_status.plan_id is e'' comment on column merlin.anchor_validation_status.reason_invalid is e'' 'If null, the anchor is valid. If not null, this contains a reason why the anchor is invalid.'; +-- `language sql` (not plpgsql) so the planner can inline this into the calling +-- query and pick the (anchor_id, plan_id) index. The plpgsql wrapper was opaque +-- to the planner and chose a worse plan. create function merlin.get_dependent_activities(_activity_id int, _plan_id int) returns table(activity_id int, total_offset interval) stable - language plpgsql as $$ -begin - return query + language sql as $$ with recursive d_activities(activity_id, anchor_id, anchored_to_start, start_offset, total_offset) as ( select ad.id, ad.anchor_id, ad.anchored_to_start, ad.start_offset, ad.start_offset from merlin.activity_directive ad @@ -41,7 +42,6 @@ begin and ad.anchored_to_start -- stop at next end-time anchor ) select da.activity_id, da.total_offset from d_activities da; -end; $$; comment on function merlin.get_dependent_activities(_activity_id int, _plan_id int) is e''