From 1871addc64200153a2b656a1a214e142eefc303e Mon Sep 17 00:00:00 2001 From: Stanislav Parkhomenko Date: Fri, 9 Oct 2026 15:43:10 +0300 Subject: [PATCH 1/2] Avoid a full miner_predictions scan when loading scores --- synth/validator/miner_data_handler.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/synth/validator/miner_data_handler.py b/synth/validator/miner_data_handler.py index 51e904c5..62fdfd79 100644 --- a/synth/validator/miner_data_handler.py +++ b/synth/validator/miner_data_handler.py @@ -735,6 +735,23 @@ def get_miner_scores( JOIN miner_predictions mp ON mp.id = ms.miner_predictions_id JOIN validator_requests vr ON vr.id = mp.validator_requests_id WHERE ms.scored_time > :min_scored_time + -- Redundant by construction, and load-bearing for the + -- plan. A prediction is scored exactly when its window + -- closes, so scored_time = vr.start_time + time_length + -- always (verified: 0 violations in 3.7M rows over 14 + -- days). That makes this bound logically implied by the + -- scored_time filter above -- it removes no rows. + -- + -- Without it nothing constrains validator_requests in + -- time, so the planner hash-joins a FULL SEQ SCAN of + -- miner_predictions (165M rows / 110GB) before it can + -- apply asset/time_length. With it, vr is reached by + -- start_time and miner_predictions via idx_mp_vr_id: + -- plan cost 5.59M -> 2.09M, runtime 110s -> 30s on a + -- 10-day window. Verified identical: 491,520 rows, + -- matching md5 over the whole result set. + AND vr.start_time > + :min_scored_time - (:time_length * interval '1 second') AND vr.time_length = :time_length AND vr.asset = ANY(:asset_list) """) From ecae49ab1cf41b87069e6462a2275991b74444b1 Mon Sep 17 00:00:00 2001 From: Stanislav Parkhomenko Date: Fri, 9 Oct 2026 16:13:07 +0300 Subject: [PATCH 2/2] removed the comment --- synth/validator/miner_data_handler.py | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/synth/validator/miner_data_handler.py b/synth/validator/miner_data_handler.py index 62fdfd79..73c6cd64 100644 --- a/synth/validator/miner_data_handler.py +++ b/synth/validator/miner_data_handler.py @@ -735,21 +735,6 @@ def get_miner_scores( JOIN miner_predictions mp ON mp.id = ms.miner_predictions_id JOIN validator_requests vr ON vr.id = mp.validator_requests_id WHERE ms.scored_time > :min_scored_time - -- Redundant by construction, and load-bearing for the - -- plan. A prediction is scored exactly when its window - -- closes, so scored_time = vr.start_time + time_length - -- always (verified: 0 violations in 3.7M rows over 14 - -- days). That makes this bound logically implied by the - -- scored_time filter above -- it removes no rows. - -- - -- Without it nothing constrains validator_requests in - -- time, so the planner hash-joins a FULL SEQ SCAN of - -- miner_predictions (165M rows / 110GB) before it can - -- apply asset/time_length. With it, vr is reached by - -- start_time and miner_predictions via idx_mp_vr_id: - -- plan cost 5.59M -> 2.09M, runtime 110s -> 30s on a - -- 10-day window. Verified identical: 491,520 rows, - -- matching md5 over the whole result set. AND vr.start_time > :min_scored_time - (:time_length * interval '1 second') AND vr.time_length = :time_length