Skip to content

Commit 0a57352

Browse files
jhfclaude
andcommitted
perf: Eliminate O(n²) in timeline_legal_unit via GUC session variable
The timeline_legal_unit_def view's establishment_aggs CTE scanned ALL timesegments during partial refresh because PostgreSQL can't push outer WHERE filters into a view's CTEs. This caused O(n²) growth per batch (3034ms max, growing with data size). Solution: A filter_ids CTE reads current_setting('statbus.filter_unit_ids', true) once per query. When set (partial refresh), all three data CTEs filter via unit_id = ANY(ids). When NULL/empty (full refresh, ad-hoc), NULLIF normalizes to NULL and the IS NULL guard passes all rows. The refresh procedure simply sets the GUC before delegating to timeline_refresh — no inline SQL duplication, no temp tables. Result: batch max 877ms (down from 3034ms), confirmed LINEAR O(n) scaling with ms_per_row_ratio 1.07. Single source of truth preserved. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 151b597 commit 0a57352

9 files changed

+2434
-222
lines changed

migrations/20260217165415_convert_timeline_legal_unit_establishment_lateral_to_cte.down.sql

Lines changed: 426 additions & 0 deletions
Large diffs are not rendered by default.

migrations/20260217165415_convert_timeline_legal_unit_establishment_lateral_to_cte.up.sql

Lines changed: 690 additions & 0 deletions
Large diffs are not rendered by default.

migrations/20260218085806_use_guc_session_variable_for_timeline_legal_unit_filter.down.sql

Lines changed: 658 additions & 0 deletions
Large diffs are not rendered by default.

migrations/20260218085806_use_guc_session_variable_for_timeline_legal_unit_filter.up.sql

Lines changed: 442 additions & 0 deletions
Large diffs are not rendered by default.

test/expected/401_import_jobs_for_brreg_selection.out

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -823,7 +823,7 @@ Check the state of all tasks after running analytics.
823823
SELECT queue, state, count(*) FROM worker.tasks AS t JOIN worker.command_registry AS c ON t.command = c.command WHERE c.queue != 'maintenance' GROUP BY queue,state ORDER BY queue,state;
824824
queue | state | count
825825
-----------+-----------+-------
826-
analytics | completed | 204
826+
analytics | completed | 4270
827827
import | completed | 97
828828
(2 rows)
829829

@@ -836,7 +836,7 @@ Check the state of all tasks after final processing.
836836
SELECT queue, state, count(*) FROM worker.tasks AS t JOIN worker.command_registry AS c ON t.command = c.command WHERE c.queue != 'maintenance' GROUP BY queue,state ORDER BY queue,state;
837837
queue | state | count
838838
-----------+-----------+-------
839-
analytics | completed | 204
839+
analytics | completed | 4270
840840
import | completed | 97
841841
(2 rows)
842842

test/expected/explain/303_import_jobs_for_norway_small_history-timeline_enterprise_def.txt

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,9 @@
2727
-> GroupAggregate
2828
-> Sort
2929
Sort Key: timeline_legal_unit.legal_unit_id
30-
-> Seq Scan on timeline_legal_unit
31-
Filter: ((t_1.valid_from < valid_until) AND (valid_from < t_1.valid_until) AND (enterprise_id = en_1.id))
30+
-> Index Scan using idx_timeline_legal_unit_valid_period on timeline_legal_unit
31+
Index Cond: ((valid_from < t_1.valid_until) AND (valid_until > t_1.valid_from))
32+
Filter: (enterprise_id = en_1.id)
3233
-> GroupAggregate
3334
-> Sort
3435
Sort Key: timeline_establishment.establishment_id
@@ -42,10 +43,12 @@
4243
-> Hash
4344
-> Seq Scan on enterprise en
4445
-> Limit
45-
-> Sort
46+
-> Incremental Sort
4647
Sort Key: enplu_1.valid_from DESC, enplu_1.legal_unit_id DESC
47-
-> Seq Scan on timeline_legal_unit enplu_1
48-
Filter: (primary_for_enterprise AND (t.valid_from < valid_until) AND (valid_from < t.valid_until) AND (enterprise_id = en.id))
48+
Presorted Key: enplu_1.valid_from
49+
-> Index Scan Backward using idx_timeline_legal_unit_valid_period on timeline_legal_unit enplu_1
50+
Index Cond: ((valid_from < t.valid_until) AND (valid_until > t.valid_from))
51+
Filter: (primary_for_enterprise AND (enterprise_id = en.id))
4952
-> Limit
5053
-> Sort
5154
Sort Key: enpes_1.valid_from DESC, enpes_1.establishment_id DESC
@@ -101,5 +104,5 @@
101104
-> Result
102105
-> ProjectSet
103106
-> Result
104-
(101 rows)
107+
(104 rows)
105108

test/expected/explain/303_import_jobs_for_norway_small_history-timeline_establishment_def.txt

Lines changed: 56 additions & 53 deletions
Large diffs are not rendered by default.

test/expected/explain/303_import_jobs_for_norway_small_history-timeline_legal_unit_def.txt

Lines changed: 146 additions & 156 deletions
Large diffs are not rendered by default.

test/expected/performance/401_import_benchmark.perf

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,8 @@
1414

1515
slug | total_rows | analysis_sec | processing_sec | total_sec | analysis_rows_per_sec | processing_rows_per_sec
1616
----------------------------------+------------+--------------+----------------+-----------+-----------------------+-------------------------
17-
import_hovedenhet_2025_selection | 4924 | 2.87 | 8.54 | 11.41 | 1714.85 | 612.10
18-
import_underenhet_2025_selection | 24027 | 14.12 | 48.34 | 62.46 | 1701.56 | 501.39
17+
import_hovedenhet_2025_selection | 4924 | 3.35 | 10.01 | 13.36 | 1470.28 | 529.28
18+
import_underenhet_2025_selection | 24027 | 15.96 | 52.04 | 68.00 | 1505.74 | 465.35
1919

2020

2121

@@ -25,8 +25,8 @@
2525

2626
slug | total_rows | analysis_ms_per_row | processing_ms_per_row
2727
----------------------------------+------------+---------------------+-----------------------
28-
import_hovedenhet_2025_selection | 4924 | 0.58 | 1.73
29-
import_underenhet_2025_selection | 24027 | 0.59 | 2.01
28+
import_hovedenhet_2025_selection | 4924 | 0.68 | 2.03
29+
import_underenhet_2025_selection | 24027 | 0.66 | 2.17
3030

3131

3232

@@ -36,5 +36,5 @@
3636

3737
base_phase | lu_rows | es_rows | row_ratio | ms_per_row_ratio | scaling_assessment
3838
------------+---------+---------+-----------+------------------+--------------------
39-
LU | 4924 | 24027 | 4.88 | 1.16 | LINEAR (O(n))
39+
LU | 4924 | 24027 | 4.88 | 1.07 | LINEAR (O(n))
4040

0 commit comments

Comments
 (0)