Skip to content

feat!: add transactional package installation foundation - #502

Merged
zeroshade merged 62 commits into
columnar-tech:mainfrom
eitsupi:feat/install-foundation
Oct 29, 2026
Merged

zeroshade merged 62 commits into
columnar-tech:mainfrom
eitsupi:feat/install-foundation

Conversation

@eitsupi

@eitsupi eitsupi commented Sep 29, 2026 •

Copy link
Copy Markdown
Member

Summary

This PR extracts the transactional package installation foundation from #497 for dbc 0.4.0. Direct install, sync, and Client.Install prepare a validated, dbc-owned driver payload before replacing the runtime registration. The previous installation remains usable until commit.

It also removes manifest-only dbc package installation, an undocumented feature with no known usage. Every installed package must contain an actual Files.driver payload; Driver.shared is no longer a fallback for a missing payload. This requirement applies even when signature verification is disabled.

Ordinary ADBC runtime manifests that reference externally managed libraries remain supported. Removing manifest-only packages does not remove external runtime registration discovery or parsing.

Transaction contract

private staging
  -> archive validation and optional verification
  -> ownership receipt
  -> unique owned-payload generation publication
  -> runtime registration replacement [commit]
  -> conservative post-commit cleanup
  • Pre-commit failures preserve the previous registration and payload. Post-commit cleanup failures do not roll back a successful installation.
  • Unix registrations use atomic file replacement. Windows restores previous raw registry values after ordinary partial failure; rollback failure preserves the candidate because registration state is uncertain.
  • Driver-scoped locks serialize cooperating mutations. Install/update writes only the primary root; lower-priority installations remain in place.
  • Cleanup retains filesystem identity, symlink/TOCTOU, cross-root reference, and external-registration borrower checks. Receipts describe owned payloads only and are deleted last.
  • InstallPackage(ctx, ...) supports cancellation during lock waits and safe pre-commit boundaries. Registration mutation completes once started, including any rollback.

Behavior changes in 0.4.0

Case Previous behavior Behavior in this PR
Package without Files.driver Could install only a manifest pointing to an external library; this was undocumented. Rejected before publication or registration changes.
Replacing an installed version Some paths uninstalled the working version before the replacement was ready. Prepare and validate the replacement first, then switch registration and clean up.
Receipt-backed dbc package uninstall No transactional ownership receipt. Remove registration, then delete validated owned files conservatively.
Normal pre-0.4 dbc package uninstall Could recursively remove the package directory. Recognize source = "dbc" and a safe same-root legacy layout; delete only the exact registered driver binary, then remove its directory if empty. Metadata may remain.
External or ambiguous registration uninstall External library bytes could be deleted in some cases. Remove registration and retain library bytes.
config.UninstallDriverShared Public API for shared-library removal. Removed; use Config.UninstallDriver or Client.Uninstall.

Legacy ownership is a compatibility assumption based on the old install layout, not a provenance proof. Unsafe paths, uncertain filesystem identity, conflicting references, or unknown/corrupt receipts prevent payload deletion. No additional heuristics reconstruct legacy ownership.

Compatibility and scope

Same-version direct install still exits before download or verification. Client.Install does not gain mandatory signature or MANIFEST-version checks. InstallDriver remains a deprecated, non-transactional API, with the same owned-payload requirement.

Native locks coordinate processes; Wasm locks are runtime-local. Wasm uninstall retains payloads because directory identity cannot be pinned safely. Post-uninstall cleanup errors identify retained paths; recovery of registration-less generations is deferred. Stable Windows registry lock identity remains follow-up work. With ADBC_DRIVER_PATH, cleanup scans other configured roots without taking their locks, so a manifest created there concurrently could reference a just-removed library; dbc never creates such cross-root references itself. Stale-generation cleanup errors after a successful install or uninstall are currently dropped; reporting them is follow-up work.

Owned payloads are stored in hidden .dbc-package-g-* directories next to the driver manifest, created with the process umask applied. On Unix, lock files are intentionally left in place after release.

Lockfile v2, Packslip, a new package format, external registration commands, aliases, and replay/repair are outside this PR. This foundation accepts prepared owned payloads so future package decoding and reproducible-artifact locking can build on the same transaction model.

Validation

CI passes on Linux, macOS, Windows, and Wasm (Node and Deno). Windows tests now use the shared registry and directory isolation from #508.

@eitsupi eitsupi changed the title feat: add transactional package installation foundation feat!: add transactional package installation foundation Sep 29, 2026
@eitsupi
eitsupi marked this pull request as draft September 30, 2026 22:42
@eitsupi

eitsupi commented Oct 1, 2026

Copy link
Copy Markdown
Member Author

Based on discussions in the chat, I refactored to remove support for manifest-only drivers, which had not been documented.

@eitsupi
eitsupi marked this pull request as ready for review October 1, 2026 01:32
@eitsupi
eitsupi requested a review from zeroshade October 1, 2026 01:32
zeroshade pushed a commit that referenced this pull request Oct 23, 2026
Fix unsafe Windows tests discovered while reviewing #502 by isolating
registry and driver directories in process-private temporary fixtures.
@eitsupi

eitsupi commented Oct 24, 2026

Copy link
Copy Markdown
Member Author

This is ready for another look. Changes since the last review:

  • Removed manifest-only package installation. Every package must now contain a Files.driver payload, while external runtime manifests remain supported and are uninstalled registration-only.
  • Merged main, including test: isolate Windows registry and driver directories #508, so the Windows tests now use the shared isolation and CI passes on all platforms.
  • Simplified the implementation: removed unused helpers, merged duplicated platform code and small platform files, shortened comments, and dropped redundant tests (about 850 fewer lines overall, with no change to the safety checks).
  • Fixed the generation directory mode to respect the umask, and documented the hidden payload directories and lock files.

The PR description is updated to match.

@zeroshade zeroshade left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks — every item from the last round is fixed, or deferred in a way we agreed on. The os.Root pinning, the digest check before deletion, and keeping external registrations registration-only all hold up against the earlier reproductions, and the legacy cleanup works on the real test-driver-1.tar.gz layout.

Non-blocking:

  • registration_namespace.go:144–167: other roots are scanned without their locks, so a CreateManifest in another root that lands between the scan and deletion can end up pointing at a deleted library. dbc doesn't create such references itself, so a note in the deferred-work list is enough.
  • install_package_test.go:72–78: this still expects 0755 after the umask change, and fails under umask 077. Please set the umask in the test or expect the umask-adjusted mode.
  • package_cleanup.go:218–228: consider reporting stale-generation cleanup errors instead of dropping them.
  • The HKCU/%AppData% lock split is only noted in a code comment; please track it in an issue.

The generation directory mode follows the process umask, which is
covered by TestInstallPackageGenerationModeHonorsUmask. The fixed 0755
expectation failed under a restrictive umask such as 077.
@eitsupi

eitsupi commented Oct 29, 2026

Copy link
Copy Markdown
Member Author

Thanks for your review.

  • umask test: removed the fixed 0755 check (4085acb); TestInstallPackageGenerationModeHonorsUmask covers this.
  • Unlocked cross-root scan: noted in the PR description's deferred list.
  • Stale-generation cleanup errors: listed as follow-up in the PR description.
  • HKCU/%AppData% lock split: tracked in Windows: HKCU mutation lock depends on %AppData% #552.

@zeroshade
zeroshade merged commit cf04861 into columnar-tech:main Oct 29, 2026
16 checks passed
@eitsupi
eitsupi deleted the feat/install-foundation branch October 29, 2026 15:39
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants