fix(#310): preserve file_hash on metadata stage's resource re-fetch#312
Conversation
Closes #310. ``MetadataStage._update_resource_metadata`` re-fetches the resource from CKAN (via ``dsu.get_resource``) to pick up ``datastore_create``'s side effects before layering preview/preview_rows on top — added by ``d653129c`` two days ago. That re-fetch wholesale replaces ``context.resource`` with the on-disk CKAN copy, which still has the empty initial ``hash`` value at that point in the flow. The hash the DownloadStage computed and set on ``context.resource["hash"]`` (line 129 of ``download.py``) is silently discarded, so the ``update_resource`` call that follows persists an empty hash. The regression has been live for ~2 days but was masked because MD5 hashes also looked empty when omitted — no one was reading the value, only checking for existence. PR #309's BLAKE3-default post-merge smoke test caught it via the "CKAN persists a 64-hex digest" check. Fix is a one-liner immediately after the re-fetch: restore ``context.resource["hash"]`` from the in-flight ``context.file_hash`` when present. The DownloadStage skip path (``IGNORE_FILE_HASH`` or ``_should_skip_upload`` matched) leaves ``file_hash`` set, so this path also covers the dedup case correctly. Edge case explicitly handled: if ``context.file_hash`` is empty (a corner case — upstream bug, or some not-currently-observed code path), we leave whatever hash the re-fetch returned alone rather than clobbering a real stored hash with an empty string. Tests in new ``test_metadata_hash_persistence.py``: * Hash from ``context.file_hash`` lands on the persisted resource. * Empty ``context.file_hash`` does NOT clobber a re-fetched hash. * Current run's hash overrides a stale stored hash. 209/209 unit tests pass on dpp-test (was 206; +3). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
Fixes a regression where MetadataStage._update_resource_metadata re-fetches the CKAN resource and unintentionally discards the in-flight hash computed by DownloadStage, resulting in an empty persisted resource hash (issue #310).
Changes:
- Restore
context.resource["hash"]fromcontext.file_hashimmediately after the CKAN re-fetch (whencontext.file_hashis non-empty). - Add regression tests covering hash persistence, non-clobbering behavior when
file_hashis empty, and overriding stale stored hashes.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
ckanext/datapusher_plus/jobs/stages/metadata.py |
Restores the computed file hash after the resource re-fetch so update_resource persists the correct digest. |
tests/test_metadata_hash_persistence.py |
Adds unit-level regression coverage for hash persistence across the metadata-stage re-fetch. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
…sitive Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
|
Post-merge smoke — verifying the fix end-to-end against the integration stack. Setup
End-to-end test
Match. ✓ Specificity checkThe same dataset still has older resources from BEFORE the #312 fix landed: Empty hash on pre-fix uploads, populated hash on post-fix upload — same code path, same input. The fix is doing exactly what it claims to do. Carry-over: still relevant
|
Summary
Closes #310.
MetadataStage._update_resource_metadatare-fetches the resource from CKAN (viadsu.get_resource) to pick updatastore_create's side effects before layeringpreview/preview_rowson top — added byd653129ctwo days ago. That re-fetch wholesale replacescontext.resourcewith the on-disk CKAN copy, which still has the empty initialhashvalue at that point in the flow. The hash theDownloadStagecomputed and set oncontext.resource["hash"](line 129 ofdownload.py) is silently discarded, so theupdate_resourcecall that follows persists an empty hash.The regression has been live for ~2 days but was masked because MD5 hashes also looked empty when omitted — no one was reading the value, only checking for existence. PR #309's BLAKE3-default post-merge smoke test caught it via the "CKAN persists a 64-hex digest" check.
Fix
One-liner immediately after the re-fetch — restore
context.resource["hash"]from the in-flightcontext.file_hashwhen present.Edge case explicitly handled: if
context.file_hashis empty (corner case — upstream bug, or some not-currently-observed code path), we leave whatever hash the re-fetch returned alone rather than clobbering a real stored hash with an empty string.Test plan
dpp-test(was 206; +3).tests/test_metadata_hash_persistence.pyregression suite:context.file_hashlands on the persisted resource (the main bug).context.file_hashdoes NOT clobber a re-fetched hash (safety).integration-testjob on the head commit (0550b3b) passed against the full Prefect + Postgres + Solr + Redis + DP+-worker stack.hashfield is now populated end-to-end (9d024321107231ce794e377f48d9953583b603bc0e82d8328119cc56ba65cb68, matchingb3sumon the host). Resources uploaded to the same dataset BEFORE the merge still show empty hash — proving the fix specifically. See follow-up comment below.🤖 Generated with Claude Code