Pipeline robustness: stop silent data loss, cache poisoning, and hangs - #6
Open
LukasWallrich wants to merge 3 commits into
Open
Pipeline robustness: stop silent data loss, cache poisoning, and hangs#6LukasWallrich wants to merge 3 commits into
LukasWallrich wants to merge 3 commits into
Conversation
refresh_data.py: - Skip an original (retry next run) when a replication's citations fail to fetch, instead of substituting [] and committing under-counted co-citations; surface via meta.json fetch_errors/originals_errored and partial_run. - oc_entity_ids: only cache on a confirmed 200; never cache a non-200 or a JSON-parse failure as an empty id set (was defeating the OMID-conflation check for 30 days). - fetch_oc_citations: a 200 whose body fails to parse now returns None (transient) instead of caching [] as zero citations. - Extract a stable citing key (omid: token, else DOI token) from the OpenCitations composite string at read time, so co-citation matching is robust and existing cache files stay valid. - 429 handling: exponential backoff over several attempts honoring Retry-After. - Timeline: add a deduped with_any per-year bucket so aggregate co-citation yearly totals no longer double-count a work co-citing two outcomes. - Cache TTL now measured from an embedded fetched_at timestamp, not file mtime (git checkout resets mtime in CI); old bare-list caches refetch once. - load_flora fetches via the session with timeout + retry, then read_csv. compute_omc.py: - Only cache negative venue results on a genuine 200-with-no-results; transient errors (network/429/5xx/parse) retry next run. Added backoff retries. - Atomic cache write (temp file + os.replace). - Similarity sanity check on the fuzzy top hit; record a miss rather than attaching a wrong venue. - Add the per-run MAX_RUNTIME_SECONDS budget the docstring promised. compute_author_overlap.py: - Port classifyOutcome from assets/app.js and add an "other" bucket so the by_outcome breakdown reconciles with n_total (was 2208 vs 2298). refresh_flora.py: - Fetch with timeout + retry/backoff so a one-off blip doesn't fail the job. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Address codex review of the workflow PR: a widespread OpenAlex outage previously produced an incompletely enriched CSV with a fresh timestamp. If >50% of new lookups (min 10) fail transiently, exit nonzero before writing the CSV so the workflow fails instead of committing stale-looking-fresh data. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…count, retry coverage, Retry-After dates refresh_data.py: - Mid-study time-budget exhaustion in the replication loop now marks the current original incomplete (RUN_STATS.originals_errored) and stops the outer loop, so it is deferred to the next run instead of being published from only the replications fetched so far — closing the same silent under-counting the skip-and-flag change was meant to prevent. - build_panel n_cit now uses only+with_any for deduped timeline rows (with the legacy summed-bucket fallback), so a work co-citing replications of two outcomes is no longer double-counted in the event-study citation series. - fetch_oc_citations now retries network exceptions and 5xx responses under the same exponential-backoff budget as 429 (previously bailed on the first one). - retry_after_seconds parses HTTP-date Retry-After headers (parsedate_to_datetime) in addition to delta-seconds, clamped to 120s. compute_omc.py: - lookup_venue retries 5xx responses (not just network/429) within the attempt budget before giving up as transient. - Shared retry_after_seconds helper parses HTTP-date headers too, clamped to 120s. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Contributor
Author
|
Codex review (gpt, read-only): REQUEST_CHANGES — four findings, all addressed in 95072ff:
Codex verified the rest cleanly: the wrapped cache-format migration is internally consistent (old bare-list files go stale without crashing), citing keys are normalized at every compare/store site, the transient-failure abort correctly excludes cached genuine and weak-match misses, and the Python outcome classifier matches the JS branch ordering. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Part of a repo audit. Python data-pipeline changes only (no workflow/R/frontend edits).
refresh_data.pymeta.json(fetch_errors,fetch_error_dois,originals_errored) withpartial_runset.omid:token, DOI fallback) from OpenCitations' compositecitingstring instead of comparing the whole space-joined identifier list; applied at read time so existing caches stay valid (verified 41/41 rows on a real cache file).Retry-After(was: one 8s retry then skip).fetched_attimestamp stored inside each cache file — file mtimes are reset by every CI checkout, so the 30-day TTL was effectively never expiring. Old-format files are treated as stale and upgrade on first refetch.pd.read_csv(URL)with no timeout, which could hang the 350-minute job).compute_omc.pyos.replace); a crash can no longer truncate the committed venue cache.results[0]unconditionally, risking confidently wrong impact factors).MAX_RUNTIME_SECONDS, mirrors refresh_data.py).compute_author_overlap.py— outcome breakdown now uses the frontend'sclassifyOutcomemapping plus an "other" bucket; it previously dropped 90 known-overlap rows with variant labels (verified:by_outcomenow sums to 2298 ==n_total, was 2208).refresh_flora.py— download gets a timeout + 3-attempt backoff.All scripts compile; key helpers (citing-key extraction against a real cache file, TTL round-trip, name matching, outcome classification) unit-tested; the author-overlap script was run end-to-end against real data. The full OC/OpenAlex pipelines were not run (live APIs, hours).
🤖 Generated with Claude Code