Repository navigation
Upload raw files above 300 MiB to R2 through its S3 API, and register UCL's house price per square metre (#409, #408 item 6) - #426
juaristi22 wants to merge 2 commits into
Conversation
… UCL's house price per square metre (#409, #408 item 6) wrangler r2 object put refuses objects above 300 MiB. chronicle/r2_s3.py signs requests to R2's S3-compatible API (Signature Version 4, region auto) and sends such files as multipart uploads in 100 MiB parts. A failed part is retried on its own, and the upload is aborted if a part still fails. Each part is freed, and the reference cycle httpx leaves around its request collected, before the next is read, so memory holds one part. Credentials come from R2_ACCESS_KEY_ID and R2_SECRET_ACCESS_KEY, or else from the CLOUDFLARE_API_TOKEN Wrangler uses. publish-raw, inventory-artifacts and publish-derived hash files a chunk at a time, and fetch-artifact streams a download to a .partial file. UCL's House Price Per Square Metre in England and Wales, 1995-2023 (UK Data Service ReShare 856542, CC BY 4.0) is registered raw-only, with both bundles (3.2 and 4.4 GB) in R2 and git-ignored here. No build parses it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Review of e120b65. I read Should
Nit
Checked and fine
CI at e120b65: Chronicle bundle and Chronicle surface were still running at 15:34 UTC. The upload path looks sound. Item 1 is a time cost, not a correctness problem, and could go in this PR or a follow-up. 🤖 Generated with Claude Code |
… MiB boundary Review on #426 (Vahid, round 1): - The S3 path now sends HEAD for the key first. The key carries the file's sha256, so an object of the file's size there already holds these bytes, and the file is not sent again. After a failed upload, such as a completion that committed but lost its response, the same check counts the upload once the object is there, and otherwise reports the upload's own error. - A test puts sparse files of exactly 300 MiB and one byte more through _upload_r2_object: the first goes to Wrangler, the second to the S3 path. - The manifest and doc note ReShare's temporal coverage, to 23 October 2023, beside the readme's and abstract's 31 October 2023. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Thanks, Vahid. All three are in 555c440.
The four test files give 114 passed. |
This implements option (a) of #409 and uses it for item 6 of #408. Files above Wrangler's 300 MiB limit now go to R2 through its S3-compatible API. UCL's house price per square metre deposit, both bundles, is registered raw-only and is in R2. Max agreed to keeping both bundles in full, not bundle 1 alone.
Large files to R2 (#409, option a)
chronicle/r2_s3.pysigns requests to R2's S3 API with AWS Signature Version 4 (regionauto). It uploads a file as a multipart upload in 100 MiB parts:_upload_r2_objectsends a file aboveWRANGLER_PUT_LIMIT_BYTES(300 MiB) through that path, and everything else throughwrangler r2 object putas before.publish-raw,fetch-artifactandpublish-derivedall upload through it._upload_r2_object_in_partsfirst sendsHEADfor it and skips the upload when the object has the file's size. After a failed upload, such as a completion that committed but lost its response, the same check counts the upload once the object is there, and otherwise the upload's own error is reported.R2_ACCESS_KEY_IDandR2_SECRET_ACCESS_KEYare used when both are set.CLOUDFLARE_API_TOKENthat Wrangler already uses: Cloudflare's R2 token docs give its S3 key ID as the token's ID, read from the token verify endpoint, and its secret as the SHA-256 of its value.CLOUDFLARE_ACCOUNT_ID, or else theaccount_idinwrangler.toml. No new secret is needed.publish-raw,inventory-artifactsandpublish-derivedhash with the existing_hash_stream.fetch-artifactstreams a download to a.partialfile and renames it only when it is complete, so a failed download leaves an earlier copy in place.put_filenow frees each part and collects before it reads the next:verify-rawis unchanged. It already streams objects back throughwrangler r2 object get --pipe, and Wrangler's remote get pipes the API response to stdout without buffering it. It read both bundles back in full and matched their sha256s: 3.2 GB, and 4.4 GB, which is past 4 GiB. Wrangler peaked at 543 MiB resident andverify-rawitself at 170 MB, so Wrangler'sgetdoes handle files this size (Vahid's question on Raw files over 300 MiB: how Chronicle should hold UCL's house price per square metre file (chronicle#408, item 6) #409). The first read of bundle 2 was cut when the machine running it went to sleep.verify-rawreported that asfetch_error, not as a mismatch, and a rerun on that file passed.UCL's house price per square metre (#408, item 6)
ucl-house-price-per-square-metre-856542(db/data/ucl/house_price_per_square_metre_856542/) registers UK Data Service ReShare 856542, House Price Per Square Metre in England and Wales, 1995-2023, by Bin Chi, Adam Dennett, Thomas Oléron-Evans and Robin Morphet (UCL). It was published on 22 August 2025 under CC BY 4.0 (DOI 10.5255/UKDA-SN-856542). It parses nothing.856542_bundle_1.zip: 3,214,161,126 bytes, sha256e397cdac…34b296. It holds the linked filetranall_link_02122023.zip, with UCL's readme and licence note. The readme describes the linked file as 21,438,854 transactions in England and Wales from 1 January 1995 to 31 October 2023, with 107 fields.856542_bundle_2.zip: 4,426,805,029 bytes, sha25661cbf6a7…02da97. It holds the files UCL linked from:epc20_id.zip);LRPPD_02122023.zip);NSPL_NOV_2023_UK.zip).raw/uk/ucl/.publish-rawuploaded both through the new path, in 31 and 43 parts, in 2 h 30 min on this connection. R2's multipart ETags,"7eb31fba…44b1df-31"and"d04abcef…798c87-43", equal the MD5 of the parts' MD5s computed from the pinned bytes, andverify-rawmatched both sha256s on a full read.r2_only_package_artifactsskips. A test checks that a checkout without the zips stays valid.For the reviewers
uclneeds a fourth exception besideisc,iciandkff, astpafrom UK land value inputs for chronicle#408, on main (re-lands #410) #415 would.ucljoins the UK publishers inR2_COUNTRY_PUBLISHERS, so the keys sit underraw/uk/, asisc's do.Tests
db/data:tests/test_chronicle_r2_s3.py(20):<Error>inside a 200, and an empty file;HEADfor a present, missing or refused key, and the credential paths.tests/test_chronicle_artifacts.py(55) andtests/test_chronicle_verify_raw.py(37):Path.read_bytespatched to fail, and a failed download keeping the earlier copy;NoSuchUpload, and sparse files of exactly 300 MiB and one byte more.tests/test_source_ucl_house_price_per_square_metre_856542.py(2).test_chronicle_boundaries,test_chronicle_geography_names,test_etl_eurostat,test_issue_266_geography_names,test_issue_343_uk_high_value_housing,test_issue_356_local_energy_bus,test_issue_376_one_label_per_value,test_issue_412_uk_area_inputsandtest_source_uk_408_facts.ruff formatandruff check(0.14.10) are clean on the edited files.🤖 Generated with Claude Code