From 255751f5ceeef70813080048e2f4877f9d981281 Mon Sep 17 00:00:00 2001 From: Francisco Solla Date: Fri, 2 Oct 2026 16:51:54 -0300 Subject: [PATCH] fix(archive): teto de 15 min no download do reparo e caminhos do runbook (C246-followup) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit O canário 80/83 abortou no download do Flickr: original de ~54 MB (panorâmica 7952px) a ~260 KB/s no link direto do homeserver leva ~3,5 min, acima do teto de 2 min herdado do C231. ARCHIVE_INTEGRITY_DOWNLOAD_TIMEOUT_MS vira 15 min (exportada e pinada no unit); falha de download segue failed retentável. Runbook §C246 usa ~/archive-reports (o /srv do texto original não existe/grava) e ganha docker pull do migrator (a limpeza pós-deploy remove a tag local). --- docs/changelog/2026-10-02-c246-reparo-timeout.md | 1 + docs/ops/teqo-1313-deploy.md | 15 ++++++++++----- scripts/lib/archiveIntegrityRepair.mjs | 10 ++++++++-- tests/unit/archiveIntegrityRepair.unit.spec.ts | 9 ++++++++- 4 files changed, 27 insertions(+), 8 deletions(-) create mode 100644 docs/changelog/2026-10-02-c246-reparo-timeout.md diff --git a/docs/changelog/2026-10-02-c246-reparo-timeout.md b/docs/changelog/2026-10-02-c246-reparo-timeout.md new file mode 100644 index 00000000..02376de0 --- /dev/null +++ b/docs/changelog/2026-10-02-c246-reparo-timeout.md @@ -0,0 +1 @@ +**C246-followup — teto do download do reparo e caminhos do runbook (2026-10-02):** o primeiro uso real do `pnpm archive:integrity --apply` em produção (canário 80/83) abortou os dois reparos no `download` do Flickr: o original é uma panorâmica de 7952px com **~54 MB** e a egress direta do homeserver estava a ~260 KB/s (~3,5 min), acima do teto de 2 min herdado da ingestão C231. O teto do download do reparo vira `ARCHIVE_INTEGRITY_DOWNLOAD_TIMEOUT_MS = 15 min` (constante exportada e pinada no unit), que ainda pega conexão travada sem cortar link lento-mas-funcional; falha de download continua `failed` retentável (nunca rebaixa). O runbook §C246 passa a usar `~/archive-reports` (o `/srv/archive-reports` do texto original não existe e não é gravável pela `fsolla`) e ganha o `docker pull` do migrator antes dos comandos — a limpeza pós-deploy remove a tag local e o registry preserva a imagem. Sem migration/UI; o restante da entrega (varredura + reparo + rebaixamento) fica intocado. diff --git a/docs/ops/teqo-1313-deploy.md b/docs/ops/teqo-1313-deploy.md index 85ca01a5..38d99d56 100644 --- a/docs/ops/teqo-1313-deploy.md +++ b/docs/ops/teqo-1313-deploy.md @@ -1438,7 +1438,7 @@ docker compose --profile maintenance run --rm \ ``` Os recibos JSON ficam em `data/archive/reports/` do container — monte um volume -(`-v /srv/archive-reports:/app/data/archive`) para preservá-los. Guardas: fora de +(`-v ~/archive-reports:/app/data/archive`) para preservá-los. Guardas: fora de teste o `TEQO_ENV=staging|production` é obrigatório com o **nome exato** do banco (`teqo_staging`/`teqo_1313`); `ALLOW_REMOTE_DB` é recusado; sem as 4 `S3_*` o `--apply` recusa (o acervo não estaria acessível no alvo); `ARCHIVE_CATALOG_CONFIRM=1` @@ -1702,24 +1702,26 @@ canário primeiro: ssh homeserver cd ~/stack # 1) canário read-only nas fotos do achado (80/83) — não escreve: +# (a limpeza pós-deploy remove a tag local do migrator; o registry a preserva) +docker pull "$(docker inspect -f '{{.Config.Image}}' teqo-1313 | sed 's/teqo-1313:/teqo-1313-migrator:/')" docker compose --profile maintenance run --rm \ - -v /srv/archive-reports:/app/data/archive \ + -v ~/archive-reports:/app/data/archive \ teqo-1313-migrate pnpm archive:integrity --only 80,83 # 2) reparo do canário (exige TEQO_ENV + confirmação; as 4 S3_* e FLICKR_API_KEY): docker compose --profile maintenance run --rm \ -e TEQO_ENV=production -e ARCHIVE_INTEGRITY_CONFIRM=1 \ - -v /srv/archive-reports:/app/data/archive \ + -v ~/archive-reports:/app/data/archive \ teqo-1313-migrate pnpm archive:integrity --apply --only 80,83 # 3) conferir as duas URLs públicas respondendo 200 (antes do lote): # https://jorgesolla1313.com.br/fotos/80/midia e /fotos/83/midia # 4) varredura completa (read-only; ~6,5k downloads no bucket local; sai 1 se achar): docker compose --profile maintenance run --rm \ - -v /srv/archive-reports:/app/data/archive \ + -v ~/archive-reports:/app/data/archive \ teqo-1313-migrate pnpm archive:integrity # 5) reparo do que a varredura acusou (mesma linha com --apply): docker compose --profile maintenance run --rm \ -e TEQO_ENV=production -e ARCHIVE_INTEGRITY_CONFIRM=1 \ - -v /srv/archive-reports:/app/data/archive \ + -v ~/archive-reports:/app/data/archive \ teqo-1313-migrate pnpm archive:integrity --apply # 6) convergência: re-rodar a varredura; 0 pendências = aceite ``` @@ -1731,6 +1733,9 @@ varredura mediria o disco efêmero do container); `ALLOW_REMOTE_DB` é recusado. `--concurrency` (default 3) e `--limit`/`--only` são os canários; o recibo traz varridas/ok/ausentes/corrompidas/removidas, e no reparo recuperadas/ irrecuperáveis/falhas. A varredura **não** conserta nada: só o `--apply` escreve. +O download do original no reparo tem teto de **15 min** — um original grande +(~50 MB, ex. panorâmica 7952px) leva minutos no link lento da casa; falha de +download é `failed` retentável, nunca rebaixamento. Depois de um `--apply` que rebaixou alguma foto, buste a tag pública em um processo que a possui (o write do CLI não revalida o servidor Next): diff --git a/scripts/lib/archiveIntegrityRepair.mjs b/scripts/lib/archiveIntegrityRepair.mjs index e569bfa4..8cfc0e87 100644 --- a/scripts/lib/archiveIntegrityRepair.mjs +++ b/scripts/lib/archiveIntegrityRepair.mjs @@ -23,7 +23,13 @@ import { FLICKR_USER_AGENT, isFlickrSourceGone } from './flickrApi.mjs' /** A photo original is MBs; the guard only catches a stalled/runaway server. */ const PHOTO_DOWNLOAD_MAX_BYTES = 512 * 1024 * 1024 -const PHOTO_DOWNLOAD_TIMEOUT_MS = 2 * 60_000 +/** + * Flickr originals can pass 50 MB (a 7952px panorama was 54 MB) and the + * homeserver's direct egress runs ~260 KB/s — ~3,5 min for that file — so a + * 2 min ceiling aborted a legitimate repair (C246 ops, 2026-10-02). 15 min + * still catches a stall without capping a slow-but-working link. + */ +export const ARCHIVE_INTEGRITY_DOWNLOAD_TIMEOUT_MS = 15 * 60_000 /** Long edge of the decode proof; the same size the face index feeds the engine. */ const DECODE_MAX_EDGE = 1024 @@ -92,7 +98,7 @@ export const repairArchivePhotoFromSource = async ({ url: source.url, destinationPath: filePath, headers: { 'User-Agent': FLICKR_USER_AGENT }, - timeoutMs: PHOTO_DOWNLOAD_TIMEOUT_MS, + timeoutMs: ARCHIVE_INTEGRITY_DOWNLOAD_TIMEOUT_MS, maxBytes: PHOTO_DOWNLOAD_MAX_BYTES, }) } catch (error) { diff --git a/tests/unit/archiveIntegrityRepair.unit.spec.ts b/tests/unit/archiveIntegrityRepair.unit.spec.ts index 03ec4692..0df484b3 100644 --- a/tests/unit/archiveIntegrityRepair.unit.spec.ts +++ b/tests/unit/archiveIntegrityRepair.unit.spec.ts @@ -5,7 +5,10 @@ import { tmpdir } from 'node:os' import { basename, join } from 'node:path' import { afterAll, describe, expect, it, vi } from 'vitest' -import { repairArchivePhotoFromSource } from '../../scripts/lib/archiveIntegrityRepair.mjs' +import { + ARCHIVE_INTEGRITY_DOWNLOAD_TIMEOUT_MS, + repairArchivePhotoFromSource, +} from '../../scripts/lib/archiveIntegrityRepair.mjs' import { FlickrApiError } from '../../scripts/lib/flickrApi.mjs' import { ARCHIVE_PHOTO_JPEG_BYTES } from '../helpers/archivePhotoFixture' @@ -112,6 +115,10 @@ describe('repairArchivePhotoFromSource (C246)', () => { expect(result.sha256).toMatch(/^[0-9a-f]{64}$/) expect(callOrder).toEqual(['source', 'download', 'repair', 'inspect']) expect(basename(vi.mocked(deps.repair).mock.calls[0][1].filePath)).toBe('flickr-123.jpg') + // A >50MB Flickr original does not fit the old 2 min ceiling on a slow link. + expect(vi.mocked(deps.download).mock.calls[0][0].timeoutMs).toBe( + ARCHIVE_INTEGRITY_DOWNLOAD_TIMEOUT_MS, + ) }) it('withdraws when Flickr refuses the source (photo gone)', async () => {