Page MenuHomePhabricator

DYK Archive Backfill — Fetch Raw Archive Pages for all wikis
Open, Needs TriagePublic

Description

Summary

Run a one-time Spark backfill that fetches the raw Parsoid HTML of every Did You Know (DYK) archive page across the 25 in-scope Wikipedias and lands it in an Iceberg table on HDFS.

This task is fetch-only. It does not parse DYK facts, resolve linked pages, or enrich topics — those are separate downstream tasks. Its single job is to make the historical archive HTML available for the parser to run against.

Background

DYK facts rotate off each wiki's Main Page into archive pages. Per the design, ongoing ingestion comes from the HTML content-change event stream, but there are no historical entries for archive pages in the event table. We therefore seed the archive by calling the MediaWiki core REST API once per archive page.

Scope
  • Discover archive page titles for the 25 in-scope wikis (Action API).
  • Fetch each page via GET /w/rest.php/v1/page/{title}/with_html.
  • Write one row per archive page to ebysans.dyk_archive_pages_html_content (Iceberg, partitioned by wiki_id), including revision metadata and a dated flag indicating whether the wiki exposes per-entry date headings.
In-scope wikis (25)

enwiki, dewiki, frwiki, svwiki, fiwiki, bgwiki, nlwiki, hewiki, ruwiki, srwiki, idwiki, ukwiki, viwiki, zhwiki, arwiki, ptwiki, kowiki, dawiki, nowiki, huwiki, cswiki, elwiki, trwiki, thwiki, fawiki.

Excluded from the list according to the audit:

jawiki (cannot find),
itwiki (cannot find),
eswiki (no active DYK),
plwiki (their links don't match their article, worry that link title will point to a different article),
rowiki (archive not maintained),
cawiki (cannot find),
hiwiki (cannot find),

Implementation

Standalone spark-submit job (structured to later drop into the
research/dags/content_html_dag.py DAG pattern):

  1. Discover: per-wiki strategy — allpages by prefix (most wikis), categorymembers (arwiki), or single page (nlwiki, srwiki, cswiki, elwiki, fawiki). Namespace resolved dynamically.
  2. Fetch: /with_html per page — descriptive User-Agent with metadata wrapper, 404 recorded as a row-level error rather than failing the job.
  3. Write: Iceberg dynamic partition overwrite by wiki_id — re-running a wiki replaces its partition, so the job is idempotent.

Each wiki has an ArchiveStructure that determines how pages are discovered:

StructureDescriptionWikis
MONTHLY_SUBPAGESallpages API with monthly prefixenwiki, dewiki, zhwiki, ruwiki, ukwiki
YEARLY_SUBPAGESallpages API with yearly prefixfrwiki, svwiki, hewiki, ptwiki, viwiki, kowiki, dawiki, huwiki
DATE_SUBPAGESallpages API with date-named prefixidwiki, thwiki
CATEGORY_MEMBERScategorymembers API (archive pages in a category, not subpages)arwiki
NUMBERED_ARCHIVESfixed list (Arkisto10–14)fiwiki
MULTI_PAGEfixed list of era pagestrwiki
SINGLE_PAGEsingle fixed page titlenowiki, elwiki, nlwiki, cswiki, bgwiki, srwiki, fawiki

Notable non-obvious configs:

  • arwiki: uses the CATEGORY_MEMBERS structure — archive pages are members of تصنيف:قائمة_مشروع_هل_تعلم and discovered via the categorymembers API, not allpages.
  • ruwiki: namespace 4 is Проект: (not Википедия:); archive root is Проект:Знаете_ли_вы/Архив_рубрики.
  • ukwiki: namespace 4 is Вікіпедія:; the page name within that namespace contains an internal colon: Проєкт:Чи_ви_знаєте/Архів_рубрики.
  • elwiki: archive is in the Portal namespace (Πύλη:, ns=100), not Wikipedia: (ns=4). Single page.
  • bgwiki: audit URL had a #Архив anchor — this is navigation only; the full page Уикипедия:Последни_попълнения is fetched.

Rate limiting: 5 partitions × 2 req/s per executor = 10 req/s global (WMF API convention).

Output table — ebysans.dyk_archive_pages_html_content

Iceberg table, PARTITIONED BY (wiki_id). 11 columns:

fieldtypecomment
wiki_idstringwiki database code, e.g. enwiki (partition key)
hoststringwiki host used for fetching, e.g. en.wikipedia.org
archive_pathstringtitle/path of the archive page fetched (not a linked-article title)
content_bodystringParsoid HTML of the archive page
rev_idbigintlatest revision id of the archive page at fetch time
rev_dttimestamplatest timestamp of that revision
datedbooleanwhether the wiki archive exposes per-entry date headings (false for nowiki, elwiki)
errorstringerror message; null on success
http_statusintHTTP status of the fetch
source_urlstringREST API URL fetched
fetched_attimestampwhen this row was fetched (UTC)
Partitioned by `wiki_id`. 


## Acceptance criteria

1. `wmf_content.dyk_archive_pages_html_content` exists and is queryable in the Iceberg catalog.
2. All 25 wikis have at least one row in the table.
3. Overall fetch error rate < 5%.
5. Job is idempotent: re-running overwrites existing partitions without duplicating rows.

Event Timeline

wmf_content.dyk_archive_pages_html_content

Since this is a one off backfill, I'm not so sure we should promote this to a permanent data product table in the wmf_content database. Can we just do this in a temp place (your database maybe?) and then delete it when we are done?

One day I hope we have a backfilled wmf_content html table T426347: Backfill commonswiki and enwiki HTML for latest HTML when non-existent in event.mediawiki_page_html_content_change_v1 that will also contain the archive pages.

Snwachukwu renamed this task from Backfill DYK Archive HTML for latest HTML in event.mediawiki_page_html_content_change_v1 to DYK Archive Backfill — Fetch Raw Archive Pages for all wikis.Thu, Jul 23, 5:17 AM

FWIW, I updated the rowiki archive page - there was a single DYK missing.

Can we just do this in a temp place (your database maybe?) and then delete it when we are done?

I have updated the ticket with a temporary database. Thank you @Ottomata

Total archive pages to fetch is 10334. By wiki we have:

 "enwiki": 272,
"dewiki": 269,
"frwiki": 24,
"svwiki": 23,
"fiwiki": 17,
"bgwiki": 2,
"nlwiki": 366,
"hewiki": 276,
"ruwiki": 265,
"srwiki": 1,
"idwiki": 13,
"ukwiki": 86,
"viwiki": 2721,
"zhwiki": 5751,
"arwiki": 111,
"ptwiki": 24,
"kowiki": 24,
"dawiki": 20,
"nowiki": 1,
"huwiki": 5,
"cswiki": 18,
"elwiki": 1,
"trwiki": 4,
"thwiki": 26,
"fawiki": 14