internetarchive / internetarchive/openlibrary
Cover archival to archive.org appears stalled since `covers_0014` - 2.24M covers (15%) have no bulk backup
- Dominant language
- Python
- Stars
- 6.7k
- Forks
- 2k
- Avg merge
- 2d 19h
- Merged PRs (30d)
- 138
Description
### Problem
Bulk cover archives on archive.org (`covers_0000`–`covers_0014`) are missing two full historical ID ranges (`covers_0006`, `covers_0007`) and stop entirely at ID 14,999,999. Based on the official covers metadata dump, this currently means **2,242,201 covers (≈15% of all covers in the system) have zero backup copy anywhere** - not in original resolution, and for one of the two historical gaps, not in any resolution at all.
The uncovered tail is actively growing at roughly **16,000–17,000 covers/month** with nothing catching it up - every cover added since the last `covers_0014` upload exists only as a live, single copy on OL's own infrastructure.
#### What currently exists on archive.org
Sizes are exact `.tar`/`.zip` byte sums pulled from each item's `/metadata/` endpoint (methodology at the bottom).
| Range (original tier) | Size |
|---|---|
| covers_0000 | 40.8 GB |
| covers_0001 | 36.1 GB |
| covers_0002 | 34.0 GB |
| covers_0003 | 18.9 GB |
| covers_0004 | 5.9 GB |
| covers_0005 | 390.3 GB |
| **covers_0006** | **missing** |
| **covers_0007** | **missing** |
| covers_0008 | 300.1 GB |
| covers_0009 | 476.6 GB |
| covers_0010 | 332.3 GB |
| covers_0011 | 315.0 GB |
| covers_0012 | 322.1 GB |
| covers_0013 | 197.8 GB |
| covers_0014 | 141.1 GB |
Derived-size tiers (`l_covers_*` / `m_covers_*` / `s_covers_*`) total **383.5 GB / 145.8 GB / 23.8 GB** across all present ranges. Notably:
- `l_covers_0006`, `m_covers_0006`, `s_covers_0006` **do exist** (22.4 / 8.7 / 1.8 GB) even though original-resolution `covers_0006` does not - range 6 is partially recoverable at reduced resolution.
- **Nothing exists for range 7 in any tier.** Unrecoverable from bulk data by any means other than the live API.
Grand total currently downloadable in bulk, all tiers: **3,164.2 GB**.
#### What the covers metadata dump says about the actual gap
Using [`ol_dump_covers_metadata_latest.txt.gz`](https://openlibrary.org/data/ol_dump_covers_metadata_latest.txt.gz) (`id, width, height, created`), a full scan of all 14,920,917 records shows:
| | |
|---|---|
| Total records | 14,920,917 |
| ID range | 1 – 15,242,455 |
| IDs inside the 6,000,000–7,999,999 gap | **1,999,844** (99.99% of the theoretical range - dense, fully-used ID space) |
| IDs above 14,999,999 (past the last existing item) | **242,357**, `created` 2025-05-05 → 2026-07-31 |
| **Total covers with no bulk backup** | **2,242,201** |
#### Cost of recovering this via the public API instead
Respecting the documented Covers API rate limit (3 req/s with identified `User-Agent`):
| Scope | Requests | Time at 3 req/s |
|---|---|---|
| Single size (e.g. L only) | 2,242,201 | ~207 hours (~8.6 days), continuous |
| All four sizes | 8,968,804 | ~830 hours (~34.6 days), continuous |
This is what any good-faith third-party mirror is currently forced into, purely because the bulk path stopped - the same job historically produced a bulk item for every other 1M-ID range.
### Reproducing the bug
1. Go to https://archive.org/details/covers_0006 or `covers_0007` → item does not exist (404).
2. Go to https://archive.org/details/covers_0014 → item exists but is unusually small (141.1 GB vs. 300–480 GB for neighboring ranges), suggesting the range wasn't fully populated before archival stopped.
3. Download `ol_dump_covers_metadata_latest.txt.gz` and check for `id` values above 14,999,999 → 242,357 present, with `created` dates through 2026-07-31.
* Expected behavior: a `covers_0015`+ item (or a rolling equivalent) should exist covering IDs added after `covers_0014`, the same way `covers_0008`–`covers_0014` were each produced in turn.
* Actual behavior: no bulk item exists past `covers_0014`, and the gap has grown continuously for over a year.
### Context
- Environment: prod (archive.org bulk items + openlibrary.org data dumps)
- Related issues:
- #476 - original report of the `covers_0006`/`covers_0007`/`l_covers_0007` gap (2017), still unresolved 9 years later.
- #8278 (closed via #9296) - proposed a monthly cover archival cron; **still labeled `State: Blocked`** at time of closing, and the issue text describes the recipe as a manual stopgap "until there's a fully automatic cron." Related to #6822 and #7691.
- #5402 - a separate, unrelated cron (main data dumps) went silent the same way ("Data Dumps not auto-generating" → "Run manually for now"), suggesting this may be a recurring pattern in the `olsystem`/`cron.d` scheduling setup rather than something specific to covers.
### Breakdown
Implementation Details (for maintainers)
`openlibrary/coverstore/archive.py` (current `master`) defines:
```python
ITEM_SIZE = 1_000_000
BATCH_SIZE = 10_000
BATCH_SIZES = ("", "s", "m", "l")
```
This matches the range/tier structure observed on archive.org exactly, confirming the archiver is designed to write into these same public `covers_NNNN` / `s_covers_NNNN` items - there is no separate, hidden archive being missed.
Critically, `Batch.get_pending()` only finds batches by globbing zip files already present in `config.data_root/items/covers_*` **on the coverstore server's local disk**. If the job that zips and uploads pending batches isn't running on schedule, new covers simply accumulate as raw files locally and never reach any bulk item - consistent with the steadily growing, entirely unbacked-up tail shown above.
Our best guess is this is an operational gap (cron not running / needs a manual catch-up run per the #8278 recipe), not missing functionality. Happy to be corrected if there's a reason new items aren't being cut.
#### Requirements Checklist
* [ ] Confirm whether `archive.archive()` + `Batch.process_pending(upload=True, finalize=True)` is currently scheduled/running on `ol-covers0`
* [ ] If stalled, re-run the #8278 catch-up recipe to flush the local backlog into new archive.org items (`covers_0015`+, and `covers_0006`/`covers_0007` if source files still exist)
* [ ] If a fully automatic cron isn't feasible soon, consider a temporary elevated Covers API rate limit for a one-off, coordinated community backfill (fixed IP + identified contact) so volunteers can close the gap without weeks of continuous polling
#### Related files
* `openlibrary/coverstore/archive.py`
* `olsystem/etc/cron.d/*` (cron scheduling, per #5402 precedent)
#### Stakeholders
* @mekarpeles (lead on both `Module: Cover Service` and `Module: Data dumps`, author of #8278)
#### Volunteer offer
I'm willing to help offset the bandwidth cost of any newly-published items: once `covers_0006`/`covers_0007`/a new tail item exist as torrents, I'll download and reseed them as a super-seeder until the swarm reaches a healthy ratio (targeting 3–5x), so this doesn't just add load without giving something back.
#### Methodology / reproducibility
- Bulk item sizes: `curl -s https://archive.org/metadata/ | jq '[.files[] | select(.name | endswith(".tar")) | (.size|tonumber)] | add'`
- Gap/tail analysis: full streaming scan of `ol_dump_covers_metadata_latest.txt.gz`, classifying each `id` against the known covered ranges (0–5,999,999 and 8,000,000–14,999,999) and the two known-missing sub-ranges.
Happy to share the exact scripts if useful for verification.
Contributor guide
Research direction
Start by reading openlibrary/coverstore/archive.py, especially Batch.get_pending(), then inspect olsystem/etc/cron.d/* for the schedule that should run the archiver on ol-covers0. Compare the current schedule and local pending batches with the #8278 catch-up recipe. Done means the archival job is confirmed or restored and the missing ranges or new tail items are published to archive.org.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- python
- Domain
- data-engineering, devops, infrastructure
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 35/100