Repository navigation
Read Commons category members from the links replica, and batch file lookups - #628
Conversation
create_app() bootstraps the maintainers through get_mw_userid() before any responses mock is active, so every app a test created called the live Commons API. The new import tests create many apps; on PR #623 CI the Commons API started returning non-JSON partway through the run and every later fixture setup failed with JSONDecodeError. An autouse fixture now replaces montage.rdb.get_mw_userid with an offline lookup: Slaporte keeps their real id (the devtest session cookie carries it), everyone else gets a stable crc32-based id. With network access blocked, master gives 5 passed / 9 errors; this branch gives 29 passed, 1 xfailed. Refs #620 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Since 2026-09-08 the Commons links tables (categorylinks, linktarget, ...) live on their own database cluster (x4); the copies on the main Commons replica are no longer written. get_files() joined categorylinks with file/filerevision in one query on the main replica, so category imports silently missed every file added to a category after that date: on montage-dev, Images from Wiki Loves Monuments 2026 in Russia gave 4,989 of 15,678 files (the stale copy has links up to 2026-09-08, none after). get_files() now reads the category's file names from the links replica (categorylinks + linktarget + page, which exists on both clusters), then looks up file/filerevision/actor/filetypes on the main replica in chunks of 500 names and joins in Python. Output keys and order are unchanged. The links host can be overridden with MONTAGE_COMMONS_LINKS_DB_HOST. On the links replica the category has 15,678 members (checked from the Toolforge bastion). The new unit tests fail on the old single query. See https://wikitech.wikimedia.org/wiki/News/2026_Commons_links_tables_database_split and https://phabricator.wikimedia.org/T398709 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
File-list ("selected") imports, filename-only CSV imports and the public
/utils/file endpoint looked up every file with its own query and its own
database connection. On montage-beta a 1000+ name list was killed by
gunicorn's 30 s worker timeout (rounds 19 and 21, 2026-10-01).
They now share labs.get_files_info_by_names(), which queries the main
replica in chunks of 500 names. Results keep the input order and every
missing name still gets its warning.
Also fixed on this path:
- load_name_list (local source) unpacked the file info dict into
(edict, warnings) and could never succeed;
- a file-list import with any missing name added its warning as a set
{'import issues', msg} instead of a dict, which can't be serialised (500).
Removes get_files_legacy() and its xfail parity test: it read the dropped
image/oldimage tables and the stale categorylinks copy, and its docstring
asked for removal once image/oldimage were dropped (after 28 May 2026).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A 'selected' (file-list) import stores its whole file list in round_sources.params, a TEXT column (64 KB on MySQL). With batched lookups a 1000+ name list no longer times out, but its names can pass 64 KB, which MySQL strict mode rejects. params now uses LongJSONEncodedDict: MEDIUMTEXT (16 MB) on MySQL, Text elsewhere. Migration: tools/migrate_round_sources_params.sql, revert: tools/revert_round_sources_params.sql. The schema check ignores column types, so the migration can run before or after the deploy. The class, the column change and the SQL files are identical to the ones in the background import work (#621), so the two merge cleanly. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…nd migration From the PR check of fix/commons-lookups: - A CSV import's warning was a plain string; the frontend shows one value per warning and displayed only its last character. It is now a dict like the other warnings. This became reachable here, because the local filename-only CSV import used to crash and now succeeds. - deployment.md: MONTAGE_COMMONS_LINKS_DB_HOST in the env-var table, and tools/migrate_round_sources_params.sql in both upgrade paths, with a note that large file lists keep failing until it has run. - Tests: binary (case-sensitive) order like the old ORDER BY, duplicates and space/underscore pairs through load_by_filename, CSV warnings are dicts. Refs #625 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
get_files opened a new pymysql connection for the links replica and one per 500-name chunk on the main replica, and get_files_info_by_names one per chunk; none was ever closed. A whole lookup now uses one connection per replica host (commonswiki_connection, a context manager that yields a fetchall function and closes the connection in a finally). Public signatures and output are unchanged; fetchall_from_commonswiki keeps working and now closes its connection too. Tests fake the connection (labs._connect) instead of fetchall_from_commonswiki, and assert 2 connections for a multi-chunk get_files (links + main), 1 for get_files_info_by_names, all closed, also when a query fails. Part of #625. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
test_selected_import_with_missing_name_reports_warning and test_csv_import_warnings_are_dicts read data['warnings'] from the /admin/round/<id>/import response. With the background import worker (#621) devtest imports are queued: the response carries data['job'] and the warnings are on the job endpoint. A small helper, _import_result, returns the response data unchanged when there is no job, and otherwise runs the queue (run_import_jobs, imported lazily from conftest), asserts the job succeeded and returns the job's data. On this branch the worker path is never taken. Part of #625. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Tested on montage-dev in synchronous import mode (the way production runs), on 2026-10-02, on an integration branch with #623, #626, #628, #630 and #631:
Activating the 21.5k-file round afterwards got the webservice OOMKilled. That is activation, not changed by this PR; it is fixed in #632, with which the same round activated in 4.5 s. Written with Claude (Claude Code) on behalf of @lgelauff. |
COMMONS_DB_HOST was the legacy alias commonswiki.labsdb, fixed in the code, next to the new COMMONS_LINKS_DB_HOST, which is written in the current scheme and can be set with MONTAGE_COMMONS_LINKS_DB_HOST. The alias points to s4.analytics.db.svc.wikimedia.cloud, so the default becomes commonswiki.analytics.db.svc.wikimedia.cloud (same replica), and MONTAGE_COMMONS_DB_HOST can override it, e.g. when Wikimedia moves tables again, as it did with the links tables (#625). Tests check both defaults and both overrides in a fresh interpreter, since the hosts are read at import. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Added a commit (8508612): both wikireplica hosts are now written the same way, and both can be overridden. Tested on montage-dev in synchronous mode after this commit: Images from Wiki Loves Monuments 2026 in Russia imported 15,676 files in 13.6 s through both replicas, and the round (15,656 tasks) activated in 5.2 s with #632. Tests: 31 passed, 1 xfailed; two new tests check both defaults and both overrides. Written with Claude (Claude Code) on behalf of @lgelauff. |
|
Check before merging: old vs new category query on last year's production imports I took every category import on production since 2025-10-04 (284 imports, 175 distinct categories) and ran, for each category, both the old query (master: one join on the main Commons replica) and the new one (this PR: members from the links replica, files from the main replica). Since 2026-09-08 the main replica's copy of categorylinks is no longer updated, so the old query sees category membership as of that date; files added to a category after it were left out of the comparison. Results: Duplicates: 0 in every category, in both queries. |
Fixes #625. Fixes #627 (file-list and filename-only CSV imports time out).
Based on
masterand code-independent of #623. Deploy together with or after #623 (batched inserts): without it a ~15k-file category import still writes one row per file and can pass gunicorn's 30 s.Why
Two problems with how Montage reads files from Commons:
categorylinks,linktarget, …) live on their own database cluster (x4), and the copies on the main Commons replica are no longer written (Wikitech, T398709).get_files()joinedcategorylinkswithfile/filerevisionin one query on the main replica, so it silently missed every file added to a category after that date. Images from Wiki Loves Monuments 2026 in Russia imported 4,989 of 15,678 files. The stale copy has links up to 2026-09-08 and none after; the links replica has all 15,678./utils/filelooked up each file with its own query and its own connection. On montage-beta a 1000+ name list was killed by gunicorn's 30 s worker timeout. This is not caused by the x4 move; it is a separate bug on the same code path.What changes
labs.get_files()reads the category's file names from the Commonslinksreplica (categorylinks⋈linktarget⋈page;pageexists on both clusters). It then looks upfile/filerevision/actor/filetypeson the main replica, 500 names per query, and joins in Python. Output keys and order are unchanged. The host can be overridden withMONTAGE_COMMONS_LINKS_DB_HOST./utils/fileshare the batchedlabs.get_files_info_by_names(). Results keep the input order, and every missing name still gets its warning.get_files()call now uses one connection per replica host (links + main), andget_files_info_by_names()one, instead of one connection per 500-name chunk. Connections are closed when the lookup ends, also on errors; before, they were never closed and were dropped by garbage collection.fetchall_from_commonswiki()keeps its signature and also closes its connection now.{'import issues', msg}.(edict, warnings).{'import issues': ...}) like the other warnings. This was pre-existing, but became reachable here because the filename-only CSV import used to crash before it got that far.round_sources.params→ MEDIUMTEXT on MySQL (LongJSONEncodedDict). A 1000+ name list is over TEXT's 64 KB. Migration:tools/migrate_round_sources_params.sql; revert:tools/revert_round_sources_params.sql.get_files_legacy()and its xfail parity test are removed. They read the droppedimage/oldimagetables and the stalecategorylinks; the docstring asked for their removal after 28 May 2026.deployment.mdlistsMONTAGE_COMMONS_LINKS_DB_HOSTand the migration.Tests
pytest montage/tests/gives 29 passed, 1 xfailed (the new live test, Toolforge only). External HTTP and the replica connections are mocked.get_files, 1 forget_files_info_by_names), all closed, also when a query fails;Verified
commonswiki_p) and counts 15,678 members for the category above.Deploy note
tools/migrate_round_sources_params.sql. The deploy works either way round, but file lists over 64 KB keep failing until it has run. No other schema change.Written with Claude (Claude Code) on behalf of @lgelauff.
🤖 Generated with Claude Code