feat(HNT-2116): add the Redis-guarded article process step - #29
Conversation
4b7a119 to
21f1545
Compare
fca1ad9 to
90aca3b
Compare
Marker semantics: claim before, confirm afterThe discovered-article refresh window is changing from 60 minutes to 30 days: a discovered article should be extracted once and left alone, while curated articles stay fresh through the live path every 15 minutes. That change makes the marker's TTL the thing that matters, not the moment it is written. Keep writing the marker before the Zyte call, as Rename the key Why two TTLs rather than one: the long window must not apply to a failed attempt, or a single transient failure would suppress the article for 30 days; and the claim must not be dropped, or the enqueue gate turns failure into load. We considered a completion-only marker and rejected it, because
Keep the re-check inside the lock. The existing tests that pin claim-before-call, including "claims the fetch marker before the Zyte call and publish" and "keeps the fetch claim when extraction throws, so a redelivery skips without re-fetching", still hold; only their TTL assertion changes. |
|
Two corrections to the above. The attempt claim TTL is 60 minutes, not 20. Reasons, in order of weight:
Cost per persistently failing URL, for context: about 96 Zyte requests a day at 60 minutes, against about 288 at 20 minutes and about 1,440 with no claim at all. The HTTP 521 stays retryable, so no change to
So a terminal 520 or 521 must not get the long completion marker; it leaves only the hour-long claim. The implementation guide's Task 4.1 line listing 521 as a permanent error is the mistake, not the code. |
Goal
HNT-2116
Add
processArticle, the Redis-guarded step that turns a validatedcrawl-articlejob into at most one Zyte fetch and one publish to thearticlestopic. It skips a URL fetched within its refresh window, takes a per-article lock, re-checks freshness under the lock, and publishes only when the extracted content changed since the last fetch.This builds on the article extraction handler and worker helper layer, composing them with the crawl Redis keys, the
redis-stateandpubsubclients, worker config, and the domain types, and it adds thepubsubworkspace dependency to the worker. docs/ARCHITECTURE.md in #14 draws this flow.Implementation decisions
setTimestamp(fetchKey)runs inside the lock but ahead of extraction and publishing, where the tech spec writes it lastarticle:fetchafter acquiring the lockurlandextracted_atfrom the content hashcontentHashhashes only the meaningful content fields, noturlorextracted_aturlis constant per key andextracted_atchanges on every fetch, so including either would make the hash never match and republish the article every time.publishIfChangedpublishes when the hash differs, then always rewrites the hashfinallyfinallyand its rejection is caught and logged, not rethrownmessage.refresh_interval_minutes ?? config.articleFetchTtlMinutes