fix(mpc): skip Core DP shadows that cannot finish in time - #1509
Merged
Merged
Conversation
On the home box (Raspberry Pi 4, Energyplan primary, 26 Sep to 3 Oct), the Core DP shadow ran out of its 10 s limit on 213 of 228 replans with a car plugged in. Each try cost about 10 s of CPU and compared nothing. Battery-only shadows finished all 826 times, in 2 s at most. After a shadow runs out of time, Core now skips shadows with at least as much DP work per slot (battery SoC and power levels, times EV SoC levels and charger steps) for an hour, then tries one again. The horizon shrinks through the day, so a later try may fit. Each skip is recorded in dp_shadow with status "skipped", the reason and the time of the next try, and logged, so the plan view and /api/mpc/diagnose tell a skip from a missing comparison. Smaller shadows still run. A cancelled shadow does not count as slow. The active plan, dispatch, validation and the Energyplan request do not change. Replayed over the same records, the rule leaves 20 timeouts instead of 213 and keeps 3 of the 5 EV comparisons that finished, plus every battery-only one. Signed-off-by: Fredrik Ahlgren <fredrik@sourceful-labs.com> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A Pi can boot with a wrong time and step it back later. The skip deadline came from the old clock, so a step back stretched the skip by the size of the step. Treat a deadline more than an hour ahead as expired. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Fredrik Ahlgren <fredrik@sourceful-labs.com>
frahlg
marked this pull request as ready for review
October 3, 2026 16:17
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem and result
Owner request: stop the Core DP shadow from wasting CPU where it cannot finish.
After Core publishes an Energyplan plan, it runs Core DP in the background, with a 10 s limit, to compare the two plans. Stored diagnostics from the home box (Raspberry Pi 4, v0.138 and v0.139 betas, 26 Sep to 3 Oct) show:
fleet_milp_rustvalue_curve_rustSo almost every replan with a car spent 10 s of CPU, and the heat and power that go with it, on a comparison that never came. Replans seldom overlap (10 of 1 057 started within 10 s of the one before), so the cost is CPU, not a late plan.
With this PR, after a shadow runs out of time, Core skips shadows with at least as much DP work per slot for an hour, then tries one again. DP work per slot is battery SoC levels × battery power levels, times EV SoC levels × charger steps when a car is plugged in. On the home box an EV shadow has about 30 times the work per slot of a battery-only one, so battery-only shadows keep running while EV shadows wait.
Core records each skip instead of leaving a gap:
dp_shadow.solver.statusisskipped, andfallback_reasonreadsskipped: a shadow no larger than this one ran out of time; next try after <UTC time>. The live plan,/api/mpc/diagnoseand the stored diagnostic all carry it. A missing comparison still has nodp_shadowblock.fallback_reasonin the tooltip, as it does for a timeout today. No UI change.WARN mpc: Core DP shadow ran out of time; skipping shadows this largeper timeout, and oneINFO mpc: Core DP shadow skippedper skip, both with the decision ID.Replayed over the same records, the rule leaves 20 timeouts instead of 213. It keeps 3 of the 5 EV comparisons that finished and all 826 battery-only ones. Shadow CPU over the week drops from about 2 900 s to about 930 s; EV shadows alone drop from about 2 200 s to 230 s.
Why this rule:
Scope and safety
Verification
go/internal/mpc/core_dp_shadow_test.go:TestCoreDPShadowSkipsAfterTimeout: after a timeout, the next shadow of the same size recordsskipped, with the reason and the next try, in the live plan,Diagnose()and the stored diagnostic.TestCoreDPShadowSkipKeepsSmallerShadows: a battery-only shadow runs during an EV skip.TestCoreDPShadowRetriesAfterAnHour: skipped at 59 minutes, compared at 60.TestCoreDPShadowCancellationDoesNotSkip: a cancelled shadow causes no skip.TestCoreDPShadowSkipIgnoresClockStepBack: after the clock steps back three hours, the shadow runs. It fails without the one-hour bound.TestNativeEnergyplanSkipsEVShadowAfterTimeout: the same through realReplancalls, with the bundled Energyplan worker and a car from the loadpoint probe.SkipsAfterTimeoutandRetriesAfterAnHourfail: the shadow runs again. With the skip check disabled, those two and the native test fail. Marking on cancellation makesCancellationDoesNotSkipfail.go test -race -count=20 ./internal/mpc -run 'TestCoreDPShadow|TestNativeEnergyplanSkipsEVShadowAfterTimeout|TestNativeEnergyplanDownsideAndAsyncShadow', withFTW_NATIVE_SOLVERset to the darwin-arm64 worker: pass.go test -race ./internal/mpc: pass.make verifyon the final commit: pass (68 Go packages, native worker check, vet and build).Checklist
go/internal/mpc. fix(web): pause plan, heating, settings, and card polls when hidden #1177 touchesweb/plan.js; this PR leaves it alone.patch), or the change is exempt.🤖 Generated with Claude Code