Fix analysis-zip corruption on shared-FS (k8s/NFS) deployments (#857) - #863
Merged
Conversation
…ct, verified download, receipt re-check, NFS-safe lock probe (#857) Four defects, each with a red-first regression spec: 1. extract_archive (worker + Analysis) used the Zip::File.open block form with ::Zip.sort_entries = true: the implicit close calls commit, which REWRITES the archive in place (temp + rename) for any unsorted zip. Web-background rewrote the live seed_zip at analysis start while workers downloaded it; workers rewrote the shared analysis.zip after every extract. Now uses read-only Zip::File.new (same rationale as seed_zip_error) and drops the global sort_entries. 2. analysis.zip downloads wrote straight to the final shared path unvalidated; truncated bytes were extracted and escalated to terminal CorruptAnalysisZip failures. Now: download to pid-suffixed temp, validate via Analysis.seed_zip_error, atomic rename. Transient corruption retries; 3 consecutive validation failures escalate to the existing terminal CorruptAnalysisZip cleanup (keeps #841 fail-fast for corrupt-at-rest zips). 3. A worker that raced past the outer receipt check re-downloaded and re-extracted even when the receipt appeared before it acquired the lock (N-worker serial re-download herd observed as 6 downloads of one analysis zip in 3s). Now re-checks the receipt while holding the lock. 4. lock_abandoned? probed with LOCK_EX on a read-only fd: legal on local filesystems, Errno::EBADF on NFSv4 (flock emulated as write byte-range lock), crashing every waiter on k8s/NFS - and the deployed EBADF->abandoned runtime patch turned that into live-lock deletion + concurrent re-init on a new inode. Probe now uses LOCK_SH (grantable on read-only fds everywhere, still blocked by a live LOCK_EX) and treats an indeterminate probe as held, never abandoned. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…luster testing Temporary mapping so the k8s cluster can pull the issue-857 fix candidate as nrel/openstudio-server:179-flock; remove once merged into 179. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
keepalive_timeout 0 (copy-paste from 2016 stock sample, not a tuning choice) forced a new TCP conn per asset/API request; gzip off served HTML/JSON uncompressed. Matches NatLabRockies/openstudio-server-helm c3c91ea ConfigMap override so that helm-side fork can be dropped once images rebuild. Refs #864 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
brianlball
force-pushed
the
fix/zip-read-only-extract
branch
from
July 30, 2026 11:51
22ba379 to
ee89e42
Compare
Contributor
Author
This was referenced Jul 30, 2026
Closed
overwrite was declared but never checked - existing files were always skipped, so a worker killed mid-extract left stale measure.xml files the next worker kept (BCLMeasure "could not be read as XML data"). Red-first regression specs for both copies; overwrite=false skip path pinned too. Refs #858 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Contributor
Author
|
Added b1caeb0: honor |
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.
Summary
Fixes the analysis-zip corruption behind #857 at its actual sources. Cluster diagnostics (mount options, cross-pod flock test, pod logs) showed flock exclusion works on the k8s/NFS deployment — the corruption chain was:
lock_abandoned?probedLOCK_EXon a read-only fd — legal on local filesystems,Errno::EBADFon NFSv4 (flock is emulated as a write byte-range lock). Every waiter crashed; the deployed EBADF→abandoned runtime override then deleted live locks, letting a second worker re-create the lock on a new inode and initialize the same analysis_dir concurrently.extract_archive(worker +Analysis) used theZip::File.openblock form with::Zip.sort_entries = true: the implicit close calls commit, rewriting the archive in place for any unsorted zip — web-background rewrote the live seed zip at analysis start while workers downloaded it; workers rewrote the sharedanalysis.zipafter every extract.CorruptAnalysisZipfailures.Changes
lock_abandoned?: probe withLOCK_SH(grantable on read-only fds everywhere, still blocked by a liveLOCK_EX); indeterminate probe = held, never abandoned.extract_archive(both copies): read-onlyZip::File.new, no globalsort_entries— archive is byte-identical after extraction.Analysis.seed_zip_errorvalidation → atomic rename. Transient corruption retries; 3 consecutive validation failures escalate to the existing terminalCorruptAnalysisZipcleanup (Enhance InitializeAnalysis resilience for corrupt seed.zip files #841 fail-fast preserved).Tests
All regression specs written red-first against the unfixed code (md5-identical archive after extract, EBADF probe crash, truncated-transfer retry, receipt re-check skip); existing #841 corrupt-zip spec updated to the new validation seam. 36 examples green across the three spec files.
Deploy notes
nrel/openstudio-server:179-flockimages for cluster testing (temporary mapping, remove on merge).worker.runtimeOverridesEBADF patch and the lock-janitor should be disabled when this deploys — fix 1 makes them unnecessary, and the EBADF→abandoned override is itself the live-lock deleter.🤖 Generated with Claude Code