Skip to content

Add DB-driven R2 sync function#96

Open
swalkinshaw wants to merge 5 commits intomainfrom
incremental-sync/db-driven-r2-sync
Open

Add DB-driven R2 sync function#96
swalkinshaw wants to merge 5 commits intomainfrom
incremental-sync/db-driven-r2-sync

Conversation

@swalkinshaw
Copy link
Copy Markdown
Member

Part of #52

Summary

Add deploy.Sync() — a new DB-driven R2 sync function that replaces the filesystem-based approach. This is additive (no existing code removed) and prepares for the Phase 3 cutover.

  • deploy.Sync(): Queries packages where content_hash != deployed_hash, serializes Composer p2 JSON via composer.PackageFiles(), uploads to R2 in parallel, deletes p2 files for deactivated packages, conditionally uploads packages.json (ETag check), and stamps deployed_hash
  • composer.PackageFiles(): New abstraction that encapsulates the tagged/dev file split — callers get []PackageFile{Key, Data} without knowing about ~dev naming conventions
  • composer.ObjectKeysForPackage(): Returns all possible Object keys for a package (used for deletion)
  • Package.ComposerMeta(): Centralizes the nullable DB field → PackageMeta mapping that was duplicated across sync, HTTP handler, and builder
  • GetDirtyPackages() / GetDeactivatedDeployedPackages(): DB queries extracted from deploy into packages layer
  • withRetry(): Shared retry helper with exponential backoff, replacing duplicated loops in putObjectWithRetry and deleteObjectWithRetry
  • R2Config.Concurrency: Upload concurrency is now configurable (default 50) instead of hardcoded
  • Integration test: Full cycle against gofakes3 — upload all, idempotent re-sync, deactivation triggers deletion

Test plan

  • go test ./internal/packages/ — unit tests pass
  • go test ./internal/deploy/ — unit tests pass
  • go test -tags=integration -run TestDBDrivenSync ./internal/integration/ — full sync cycle passes
  • Existing TestR2Sync integration test still passes (old path untouched)

swalkinshaw and others added 5 commits April 4, 2026 10:50
New deploy.Sync() replaces the filesystem-based SyncToR2 approach:
- Queries packages where content_hash != deployed_hash
- Serializes Composer p2 JSON from DB via composer.SerializePackage()
- Uploads tagged + dev files to R2 in parallel (50 goroutines)
- Deletes p2 files for deactivated packages (fixes existing gap where
  closures detected by check-status left orphaned files on R2)
- Conditionally uploads packages.json (skips if ETag matches)
- Stamps deployed_hash after successful upload

Also adds deleteObjectWithRetry and headObject helpers to r2.go.

Integration test (db_sync_test.go) validates full cycle against gofakes3:
first sync uploads all, second sync is idempotent, deactivation triggers
deletion.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Deduplicates the retry loop from putObjectWithRetry and
deleteObjectWithRetry into a single withRetry function with
exponential backoff and context cancellation.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add Concurrency field to R2Config (default 50) instead of hardcoding
the errgroup limit in SyncToR2 and Sync. Follows the same pattern as
DiscoveryConfig.Concurrency.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add Package.ComposerMeta() to centralize the nullable-field-to-PackageMeta
mapping that was duplicated across sync.go, composer.go, and builder.go.

Move dirty package and deactivated package queries into the packages
package as GetDirtyPackages() and GetDeactivatedDeployedPackages(),
keeping DB access out of the deploy layer.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
PackageFiles encapsulates the tagged/dev file split — callers get back
a slice of {Key, Data} without needing to know about ~dev naming, the
themes-have-no-dev rule, or trunk-only plugin handling.

R2KeysForPackage returns all possible R2 keys for deletion without
hardcoding suffix lists at call sites.

Simplifies deploy.Sync to a loop over PackageFiles for uploads and
R2KeysForPackage for deletions.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@swalkinshaw swalkinshaw force-pushed the incremental-sync/db-driven-r2-sync branch from aa52a97 to 20b5884 Compare April 4, 2026 20:04
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.

1 participant