Speed up queries via materialized view#1671
Conversation
Flix6x
left a comment
There was a problem hiding this comment.
Some early review comments. Nice to see the refresh logic taking shape.
Documentation build overview
13 files changed ·
|
Flix6x
left a comment
There was a problem hiding this comment.
Exciting work! Thanks for addressing my earlier comments. Just a short code review for now, so you don't have to wait for me testing. Looking forward to testing tomorrow.
nhoening
left a comment
There was a problem hiding this comment.
Also a quick review from me, before having tested myself, mostly about concepts.
I also suggest moving get_timed_belief_min_v() to timely-beliefs and add basic documentation there (not much more than the SQL command so anybody reading only that could in principle use it)
Co-authored-by: Felix Claessen <30658763+Flix6x@users.noreply.github.com> Signed-off-by: Muhammad-Moiz626 <muhammadmoiz4843@gmail.com>
…erialized-veiws # Conflicts: # documentation/changelog.rst # documentation/configuration.rst # flexmeasures/data/models/generic_assets.py # flexmeasures/ui/templates/base.html
Signed-off-by: F.N. Claessen <felix@seita.nl>
Resolved conflicts in changelog.rst, configuration.rst, time_series.py, assets.py, base.html, and views.py, keeping materialized view features alongside upstream changes. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
base.html gained ~950 lines of stale template blocks (copies of JS that lives in includes/graphs.html) and openapi-specs.json had its version reverted. Restore both to main; the materialized-view UI toggle will be re-added on top of the real chart-loading code in includes/graphs.html. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Qprz6PKnXkVE1TJ8d7PYe9
# Conflicts: # documentation/changelog.rst
…recorded refresh time - migration: frozen SQL from timely-beliefs' DDL generators (simplified view query, no CASCADE on downgrade), repointed onto main's migration head (drops the stale merge migration) - CLI: refresh-materialized-views records its run in latest_task_run via @task_with_status_report, supports --concurrent (autocommit connection), aborts with an error exit code on failure; monitorable with 'flexmeasures monitor latest-run' - config: replace FLEXMEASURES_MVIEW_REFRESH_INTERVAL with FLEXMEASURES_MVIEW_ALWAYS_INCLUDE_LIVE_TAIL (the refresh cadence now lives solely in how hosts schedule the CLI command; the recorded refresh time is the single source of truth) - queries: TimedBelief.search decides centrally whether to trust the view (it must exist at startup and have a recent enough recorded refresh) and passes a cutoff so events recorded since the last refresh are looked up in the beliefs table (live tail); new include_live_tail param threaded through Sensor/GenericAsset.search_beliefs and the chart data API endpoints - UI: 'Include latest data' toggle on asset graphs (shown only when the view is in use and the live tail is not already always included), wired to the real chart-loading code in includes/graphs.html; tooltip shows the actual last refresh time - docs: configuration.rst setting description, hosting docs section with cron example, changelog entry - tests: CLI refresh success/failure recording, mview/live search equivalence Note: requires the timely-beliefs release containing SeitaBV/timely-beliefs#204 (materialized_views module); the dependency pin should be bumped once that is released. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Qprz6PKnXkVE1TJ8d7PYe9
Signed-off-by: F.N. Claessen <claessen@seita.nl>
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
Review the following changes in direct dependencies. Learn more about Socket for GitHub.
|
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Signed-off-by: F.N. Claessen <claessen@seita.nl>
…at/materialized-veiws # Conflicts: # uv.lock
Signed-off-by: F.N. Claessen <claessen@seita.nl>
Signed-off-by: F.N. Claessen <claessen@seita.nl>
After CREATE MATERIALIZED VIEW or REFRESH MATERIALIZED VIEW, autovacuum may not get around to analyzing the view right away, leaving the Postgres planner without statistics for it. This can make queries that go through most_recent_beliefs_mview slower than the fallback path, until a manual ANALYZE fixes it. Run ANALYZE explicitly after the migration creates the view and after the refresh-materialized-views CLI command refreshes it (on the same connection/session used for the refresh, so it also works for the CONCURRENTLY branch). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01S4jYQMv87WJt1Y1SB7gi1w
…wn and correctness fixes 4.0.1 pushes the caller's event-window bounds down into the materialized view subquery (Postgres does not propagate range predicates across the equijoin, so windowed queries used to scan the entire view), and disables the view when belief-time filters or non-group-constant custom filter criteria could silently drop events. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01S4jYQMv87WJt1Y1SB7gi1w
…imate Benchmarks (see PR #1671) measured view + index creation at roughly 1 minute per 15 million timed_belief rows, scaling linearly. The changelog warning now states that rate, and the migration logs an estimate for the host's own table size (via pg_class.reltuples, to avoid paying for an exact count) before it starts. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01S4jYQMv87WJt1Y1SB7gi1w
…visible FlexMeasures' logging configuration does not surface the alembic logger, so the estimate added in f725653 never reached the terminal (verified against a full production-scale restore, where the whole migration took ~1 minute for 118.6M rows - the stated 1-minute-per-15M-rows rate is a conservative upper bound on idle NVMe hardware). Print the message as well, and phrase the estimate as an upper bound. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01S4jYQMv87WJt1Y1SB7gi1w
Description
Our data model with multiple beliefs per event time (multiple horizons as well as sources) makes queries computationally expensive. Most-used are queries where we look only for the most recent beliefs about each event, when given any moment in time as max belief time.
Pre-computation with Postgres' materialized views can make queries much faster. This PR will enable hosts to use this performance enhancement.
Performance (benchmarked)
Benchmarked with
timely-beliefs>=4.0.1(required by this PR — 4.0.1 pushes event-window filters down into the mview subquery and closes two correctness holes; without it, windowed queries scan the entire view and can even get slower). Setup: one bulk sensor with 10 belief revisions per event, on a deliberately de-clustered heap (per-sensor rows scattered across pages, as they are in practice when many sensors' data interleaves on arrival). Timings are min-of-4 after warm-up, viaTimedBelief.search(most_recent_beliefs_only=True).Query time vs. table size
365-day event window — first line: without mview (fallback), second line: with mview (ms):
xychart-beta title "365d window query time (ms) vs timed_belief rows" x-axis ["4M", "8M", "16M", "32M"] y-axis "ms" 0 --> 1100 line [1048, 786, 677, 967] line [658, 582, 559, 613]30-day event window — same series order (ms):
xychart-beta title "30d window query time (ms) vs timed_belief rows" x-axis ["4M", "8M", "16M", "32M"] y-axis "ms" 0 --> 200 line [107, 119, 135, 170] line [122, 112, 151, 160]Takeaways:
BeliefsDataFrameconstruction, so the SQL-side gain is larger than these end-to-end factors suggest.(event_start, sensor_id, source_id) INCLUDE (belief_horizon)covering index already bounds the fallback's work tightly at small windows.One-time migration cost and refresh cost
First line: view + index creation (the migration); second line: non-concurrent
REFRESH(including the built-inANALYZE). Values at 64M and 128M are linear extrapolations (marked*):xychart-beta title "Creation and refresh time (s) vs timed_belief rows" x-axis ["4.6M", "8.6M", "16.6M", "32.6M", "64M*", "128M*"] y-axis "seconds" 0 --> 550 line [10, 25, 40, 132, 260, 520] line [8, 12, 25, 25, 50, 100]pg_class.reltuples) when it starts.--concurrent.ANALYZEon the view afterwards; without fresh statistics the planner picks bad plans and the view can lose to the fallback....
Further Improvements
Related Items
Closes 108
...
Sign-off