Repository navigation
Conversation
POST /admin/round/<id>/import now validates the request, records a queued import_jobs row and returns at once; a Toolforge continuous job (python -m montage.import_worker, Procfile "import-worker", one replica) claims it with a compare-and-set UPDATE, fetches the files with no transaction open, and runs the existing import code in one transaction whose last statement marks the job succeeded (fenced on a claim token). Failures roll back and are recorded as failed jobs with a sanitised reason and an import_failed audit entry. - ImportJob model + LongJSONEncodedDict (MEDIUMTEXT on MySQL); tools/migrate_import_jobs.sql / revert_import_jobs.sql - import_state_for(): admin payloads (/admin, /admin/campaigns/all, /admin/campaign/<id>, /admin/round/<id>) carry import_state - activate_round refuses while any job is queued/running or the latest failed (non-locking pre-check, round lock, locking re-check) - GET /admin/round/<id>/imports and /admin/round/<id>/import/<job_id> - stuck-job rule: running jobs failed at worker startup and after 60 min - MONTAGE_IMPORT_MODE=sync keeps the synchronous import as a rollback switch (default sync outside devtest until the worker runs) - 400 instead of 500 for missing import keys; the selected-import warning is a dict again (was a set literal) - make_engine(config) extracted from create_app for the worker Refs #621, #618 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…g tests - conftest.run_import_jobs(app): run queued jobs in-process (import_worker.run_once) while external APIs are mocked - test_web_basic: drain after each import (imports are queued now); test_category_import_batches_inserts reads counts from the job - test_import_entries: runs in import_mode 'sync' (asserts on the synchronous response; same run_import code path as the worker) - new test_import_jobs.py: enqueue without external calls, worker success/failure/rollback, CAS claim, fenced commit, startup and 60-min stuck-job sweeps, loop survives DB errors, activation gate, import_state in admin (not juror) payloads, 400 on missing keys, sync rollback mode, schema check, MEDIUMTEXT columns Refs #621 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- RoundNew: after a queued import, close the form at once with "Import started; ... Refresh the page" (no waiting, no polling); the old result dialog stays for the sync rollback mode - RoundImportStatus (new, in RoundInfo): queued since / running since / finished with counts and warnings / failed with the reason and the "cancel this round and create it again" hint; one details fetch on load, no polling - RoundInfo: Activate disabled with the reason while the import blocks activation (state read from the campaign payload, so from first render) - RoundView header and CoordinatorCampaignCard: "importing…" / "import failed" badge - adminService.getImportJobs/getImportJob; utils.getImportBadgeKey, formatUtcDateTime (server dates are naive UTC) - new strings in en.json + qqq.json only Refs #622, #621 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- deploy.sh: check the schema inside the new image with a one-off schema-check job (tools/check_schema.py) and abort before restarting if a migration is missing; restart the import-worker continuous job after the webservice (or print the command to create it) - deployment.md: MONTAGE_IMPORT_MODE; tools/migrate_import_jobs.sql; new step 7b (create the import-worker job, exactly one replica); worker logs; "Imports stay queued" troubleshooting with the rollback SQL; missing import_jobs table at startup The Toolforge behaviour the script relies on (envvars in job pods, jobs run --wait exit status and working directory, jobs restart pulling :latest, ~/replica.my.cnf in a job pod) is still to be verified on montage-beta. Refs #621 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
With db_echo the worker logged the SQL of every 5-second poll; it now only logs its own lines (seen on the hague dev setup). Refs #621 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…rt-621 Brings in fdfa606 (offline Wikimedia user-id lookups in the test suite), so the Phase 1 tests run without the live Commons API. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
check_no_active_import took the round row lock and a FOR UPDATE read on import_jobs, then the same request fetched and imported everything. On MariaDB that read's gap lock is held for the whole import, so imports into other new rounds wait or deadlock (1205/1213) and get a 500. Sync mode (the rollback switch, and the default) now only does the plain active-job pre-check, which restores the pre-#621 lock footprint. The residual race (an enqueue from a worker-mode process during a mode flip) is documented in the docstring. Refs #621 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A dead wikireplica connection or a stalling CSV/API host blocked the single import worker for ever, so every later import stayed queued. - requests_get/requests_post: default timeout (15 s connect, 600 s per read), overridable with timeout= - labs.fetchall_from_commonswiki: connect_timeout 30 s, read_timeout 45 min (one query, under the worker's 60-min cap), write_timeout 60 s; MONTAGE_LABS_CONNECT_TIMEOUT / MONTAGE_LABS_READ_TIMEOUT override - worker: readable reasons for fetch timeouts and wikireplica errors; a failing rollback no longer skips marking the job failed - check_schema closes its session and disposes its engine (the worker kept an idle connection open for its lifetime) - deployment.md: "hung worker" recovery note Refs #621 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
RoundImportStatus fetched job details only on mount; a campaign reload reuses the component, so a job that had just finished or failed showed no warnings or reason until a full page reload. Watch job id + status (immediate) instead. Refs #622 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
From the PR check's mutation run (M3, M7, M8, M10 survived): - main() runs the startup sweep before the first loop iteration - run_loop_iteration runs the 60-min sweep - a failure after a sweep is fenced (keeps the sweep's error, one audit) - claim CAS goes through claim_next_job with a racing writer between its SELECT and UPDATE (replaces the hand-written UPDATE test) Also: sync mode takes no lock before the fetch; default HTTP and wikireplica timeouts; a timed-out fetch fails its job readably and the next job runs; a failing rollback still marks the job failed; the sync failure test checks entries/round_entries/round_sources roll back; every run_import_jobs call in test_web_basic asserts its outcome. Refs #621 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The import worker runs as a Toolforge job, where HOME is / and not the tool's home, so ~/replica.my.cnf did not exist and pymysql connected without a password: on montage-dev every worker category import failed with 1045 "Access denied ... (using password: NO)". replica_credentials() now uses TOOL_REPLICA_USER/TOOL_REPLICA_PASSWORD when Toolforge sets them, else replica.my.cnf in TOOL_DATA_DIR, else ~ (the old behaviour), and raises a clear error if none exists. Checked in a montage-dev job pod: HOME=/, TOOL_DATA_DIR set, replica.my.cnf present there, TOOL_REPLICA_* set. Refs #621 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This was referenced Oct 2, 2026
Collaborator
Author
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.
Phase 1 of #621 (backend) and #622 (frontend). Builds on #623 (batched inserts); the base branch is
fix/large-import-timeout-618, so this diff shows only the background-import work.What changes
With
MONTAGE_IMPORT_MODE=worker, importing entries into a round no longer happens inside the HTTP request:POST /admin/round/<id>/importvalidates the request, records animport_jobsrow, and returns at once with the job.montage/import_worker.pyruns as a Toolforge continuous job, with exactly one replica. It claims the oldest queued job with a compare-and-set UPDATE and fetches the files with no transaction open. It then runs the whole import (entries, round entries, round source, autodisqualify, audit log) in one transaction. The UPDATE that marks the jobsucceededis fenced and comes last, so "finished" only appears once the data is committed. A failure is recorded separately, with a readable reason.running(a restart or deploy interrupted them), and it fails jobs running longer than 60 minutes. Fetches now have timeouts: HTTP is (15 s, 600 s); the Commons database is 30 s to connect and 45 min per query. A hung fetch therefore fails its job and the worker moves on.MONTAGE_IMPORT_MODEdefaults tosyncoutside dev and devtest. Insyncmode the import runs in the request as before (no locks are held across the fetch) and asucceededjob row is recorded. Deploying this code changes nothing until worker mode is switched on.import_jobstable:tools/migrate_import_jobs.sql, withtools/revert_import_jobs.sqlto undo it.tools/deploy.shruns a schema pre-flight before restarting the webservice, then restarts the worker job.deployment.mdcovers the worker job, the migration, rollback, and a hung worker.Tests
pytest montage/tests/gives 73 passed, 1 xfailed, also with network access blocked.test_import_jobs.pycovers:FOR UPDATE). Those need MariaDB.npx prettier --check src/,npx eslint src/andnpm run buildall pass.Not verified yet
~/replica.my.cnfreach the job pod;toolforge jobs run --wait;Known limitations (Phase 2)
Refs #621, #622, #618
Written with Claude (Claude Code) on behalf of @lgelauff.
🤖 Generated with Claude Code