Skip to content

Fix/148 resolution sets one commit#248

Open
Oskaril-data wants to merge 6 commits into
forecastingresearch:mainfrom
Oskaril-data:fix/148-resolution-sets-one-commit
Open

Fix/148 resolution sets one commit#248
Oskaril-data wants to merge 6 commits into
forecastingresearch:mainfrom
Oskaril-data:fix/148-resolution-sets-one-commit

Conversation

@Oskaril-data

@Oskaril-data Oskaril-data commented Jun 30, 2026

Copy link
Copy Markdown
Collaborator

src/orchestration/_io.py — upload_resolution_set now only uploads to the bucket (no git push). Added push_all_resolution_sets() that gathers all resolution sets from the bucket and pushes them in one commit.
src/orchestration/func_push_resolution_sets/ — new Cloud Run job (func-push-resolution-sets) that runs push_all_resolution_sets() as a single task.
src/nightly_update_workflow/manager/main.py — runs the push job after resolve finishes, before leaderboards.
src/nightly_update_workflow/worker/main.py — registers the new job.
Makefile — adds the push-resolution-sets target and wires it into resolve.
src/helpers/git.py — clone_and_push_files skips empty commits and returns whether it pushed (so no false "pushed" log when nothing changed).

Summary by CodeRabbit

  • New Features

    • Added a new workflow step to push resolution sets after forecasts are resolved, before downstream updates run.
    • Introduced a job that collects all available resolution sets and publishes them in a single commit, helping avoid push conflicts.
    • Added deployment support for the new job so it can run in the cloud like existing workflow tasks.
  • Bug Fixes

    • Skips creating empty commits when there are no file changes, while still allowing remote mirrors to catch up.
    • Improves error reporting for push failures.

New single-task Cloud Run job `func-push-resolution-sets` gathers all
resolution sets from the bucket and pushes them in one commit, fixing the race
condition from parallel resolve tasks pushing independently.

Still needs end-to-end testing in a GCP dev project.

Refs forecastingresearch#148
clone_and_push_files now returns whether it pushed, and push_all_resolution_sets only logs the push count when a commit was actually made. Avoids a misleading 'Pushed N resolution sets' log on nights where the empty-commit skip applied.
@Oskaril-data Oskaril-data reopened this Jun 30, 2026
@Oskaril-data Oskaril-data requested a review from houtanb June 30, 2026 10:14
…sets-one-commit

# Conflicts:
#	src/orchestration/_io.py
@Oskaril-data Oskaril-data removed the request for review from houtanb June 30, 2026 10:52
Upstream moved orchestration jobs to the shared orchestration_upload.mk staging macro and made utils a pip dependency (fri-utils). Update the resolution-set push job's Makefile to match instead of copying the removed utils submodule by hand.
@Oskaril-data Oskaril-data marked this pull request as ready for review June 30, 2026 12:56
@Oskaril-data Oskaril-data requested a review from houtanb June 30, 2026 12:57
@houtanb houtanb requested a review from nikbpetrov July 6, 2026 14:28
Comment thread src/helpers/git.py Outdated
shutil.copy(source, full_destination_path, follow_symlinks=False)
repo.index.add([destination])

# Skip empty commits: nothing to push if the staged files match HEAD.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what happens if a mirror fails but git succeeds? mirror would never get the update on same-day rerun?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah you're right about that. My plan for the fix: only make a commit when something actually changed, but always push to both origin and the mirror anyway. Git only sends what's missing, so if a remote is already up to date the push just does nothing, and if the mirror fell behind it catches up on the next run. Would that work?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you're actually right about git, but then why would you even download from git then? git will indeed only push changes so even if older files changed somehow, those changes would be pushed correctly

no idea about mirrors, probably @houtanb can better advise; mirrors is written as a generic one so not sure what that could capture in theory, and also not sure if that's how HF works either

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The diff check and the push do different jobs: git push only compares commit pointers, not file contents.

Two scenarios:
A: content unchanged. We clone origin, so HEAD == origin's HEAD. Diff has no changes => index.commit() is skipped => no new commit. origin.push() sees the remote already has the same commit HEAD => transfers nothing. But if yesterday's mirror push failed, the mirror is behind => the mirror push sends it the missing commits and it catches up. That's the fix for the original concern.

B: content changed. Diff has changes => new commit with index.commit() => both pushes send it, as expected.

Why not just always commit and let push sort it out? GitPython's index.commit() creates a commit even with an identical tree and origin.push() pushes it as the commit is new. Without the diff check, the dataset repo would gain an empty commit every night.

Comment thread src/orchestration/_io.py
Comment thread src/orchestration/func_push_resolution_sets/requirements.txt Outdated
@nikbpetrov

Copy link
Copy Markdown
Collaborator

Apart from these minor notes, LGTM

Push origin and mirrors on every run so a mirror that fell behind on an earlier
partial failure catches up, while still skipping empty commits. Check push
results with `raise_if_error()`, which GitPython does not raise on by default,
so a rejected push now fails the job instead of being reported as success.
@coderabbitai

coderabbitai Bot commented Jul 9, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

This PR adds a dedicated push-resolution-sets pipeline step. clone_and_push_files now returns a boolean indicating whether a commit was made, always pushing origin/mirrors. A new push_all_resolution_sets() function collects and pushes all GCS resolution sets in one commit via a new Cloud Run job, wired into the nightly workflow and Makefile.

Changes

Push Resolution Sets Feature

Layer / File(s) Summary
Git push helper returns commit status
src/helpers/git.py
clone_and_push_files detects existing changes, conditionally commits, always pushes to origin and mirrors with explicit error raising, and returns a bool; clone_commit_and_push docstring updated.
push_all_resolution_sets orchestration function
src/orchestration/_io.py
upload_resolution_set docstring clarified as GCS-only; new push_all_resolution_sets() lists, downloads, and pushes all resolution set files to git in a single commit.
New func_push_resolution_sets Cloud Run job
src/orchestration/func_push_resolution_sets/main.py, .../Makefile, .../requirements.txt
New Cloud Run job entrypoint invoking push_all_resolution_sets, deployment Makefile with all/deploy/clean targets, and pinned dependencies.
Nightly workflow and Makefile wiring
src/nightly_update_workflow/worker/main.py, src/nightly_update_workflow/manager/main.py, Makefile
Worker adds push_resolution_sets job group and dispatch entry; manager blocks on resolve_forecasts then runs and blocks on push_resolution_sets; root Makefile adds push-resolution-sets target wired into resolve.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Manager as Nightly Manager
  participant Worker
  participant CloudRunJob as func-push-resolution-sets
  participant IO as orchestration._io
  participant Git as git helper

  Manager->>Worker: call_worker(resolve_forecasts)
  Worker-->>Manager: block_and_check_job_result
  Manager->>Worker: call_worker(push_resolution_sets)
  Worker->>CloudRunJob: run func-push-resolution-sets
  CloudRunJob->>IO: push_all_resolution_sets()
  IO->>IO: list & download GCS resolution sets
  IO->>Git: clone_and_push_files(files, message)
  Git->>Git: detect changes, commit if needed
  Git->>Git: push to origin and mirrors
  Git-->>IO: return has_changes (bool)
  IO-->>CloudRunJob: log pushed count
  CloudRunJob-->>Worker: job complete
  Worker-->>Manager: block_and_check_job_result
Loading
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title is related to the main change: batching resolution sets into a single commit, even though the phrasing is terse.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
src/orchestration/_io.py (1)

264-308: 🚀 Performance & Scalability | 🔵 Trivial

Full resync of all historical resolution sets on every run.

push_all_resolution_sets() lists, downloads, and restages every resolution-set file under the prefix on each invocation, not just newly uploaded ones. This is presumably intentional (self-healing if a prior push/mirror failed), but as the corpus grows this means unbounded, ever-increasing GCS I/O and git index work per nightly run for what is typically a single new file.

Worth keeping an eye on job duration/cost over time; if it becomes a bottleneck, consider only fetching files newer than the last successful push (e.g. tracked via a marker/manifest) while still falling back to a full resync periodically for recovery.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/orchestration/_io.py` around lines 264 - 308,
`push_all_resolution_sets()` currently re-downloads and re-commits every
resolution set on each run, which will keep increasing GCS and git work as the
dataset grows. Update this flow to avoid a full resync on every invocation by
tracking the last successful push (for example with a marker or manifest) and
only fetching/restaging newer files, while preserving a periodic or fallback
full resync for recovery. Use the existing `push_all_resolution_sets`,
`gcp.storage.list_with_prefix`, and `git.clone_and_push_files` flow as the place
to implement the incremental behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@src/orchestration/_io.py`:
- Around line 264-308: `push_all_resolution_sets()` currently re-downloads and
re-commits every resolution set on each run, which will keep increasing GCS and
git work as the dataset grows. Update this flow to avoid a full resync on every
invocation by tracking the last successful push (for example with a marker or
manifest) and only fetching/restaging newer files, while preserving a periodic
or fallback full resync for recovery. Use the existing
`push_all_resolution_sets`, `gcp.storage.list_with_prefix`, and
`git.clone_and_push_files` flow as the place to implement the incremental
behavior.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: e273aa85-329e-48ac-b0bb-5d16bd97c11d

📥 Commits

Reviewing files that changed from the base of the PR and between 86a399d and cc51475.

📒 Files selected for processing (8)
  • Makefile
  • src/helpers/git.py
  • src/nightly_update_workflow/manager/main.py
  • src/nightly_update_workflow/worker/main.py
  • src/orchestration/_io.py
  • src/orchestration/func_push_resolution_sets/Makefile
  • src/orchestration/func_push_resolution_sets/main.py
  • src/orchestration/func_push_resolution_sets/requirements.txt

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants