Skip to content

fix(cloud): emit fixed-size multipart upload parts for R2 compatibility#1161

Open
alleneubank wants to merge 4 commits into
EnterpriseDB:masterfrom
0xsend:bb/fix-r2-multipart-uniform-parts
Open

fix(cloud): emit fixed-size multipart upload parts for R2 compatibility#1161
alleneubank wants to merge 4 commits into
EnterpriseDB:masterfrom
0xsend:bb/fix-r2-multipart-uniform-parts

Conversation

@alleneubank

Copy link
Copy Markdown

Summary

  • Refactor CloudTarUploader to fill each disk-backed temp file to exactly chunk_size bytes before uploading, so every non-trailing multipart part is uniform in size
  • Cloudflare R2 requires all non-trailing parts to have identical sizes; the previous implementation flushed whenever the buffer exceeded chunk_size, producing variable-sized parts
  • The trailing part (uploaded via flush/close) may be smaller, which is allowed by the multipart upload spec
  • Backward compatible with AWS S3 and other providers

Changes

  • barman/cloud.py: Extract _write_data() (fills fixed-size part buffers) and _upload_part_buffer() (uploads a single part) from the existing write()/flush() methods
  • tests/test_cloud.py: Add two new tests verifying partial-buffer behavior and fixed-size part emission; fix existing throttle test to set up buffer state correctly

Test plan

  • test_write_uses_disk_buffer_for_partial_data — partial writes stay in buffer, no upload triggered
  • test_write_emits_fixed_size_parts_and_retains_tail — 10 bytes at chunk_size=4 produces two 4-byte parts + 2-byte tail
  • Existing TestCloudTarUploader tests continue to pass

Fixes: #954

Cloudflare R2 requires all non-trailing parts in a multipart upload to
have identical sizes. The previous CloudTarUploader flushed its buffer
whenever it exceeded chunk_size, producing variable-sized parts because
compressed output varies per chunk.

Refactor CloudTarUploader to fill each disk-backed temp file to exactly
chunk_size bytes before uploading, so every non-trailing multipart part
is uniform. The trailing part (uploaded via flush/close) may be smaller.

This is backward compatible with AWS S3 and other providers that accept
uniform parts.

Fixes: EnterpriseDB#954
@alleneubank alleneubank requested a review from a team as a code owner February 21, 2026 16:13
Multi-stage build producing a minimal image with barman-cloud tools
(backup, wal-archive, restore, wal-restore) and snappy compression
support. Uses --no-deps install to avoid building psycopg2 from source,
substituting psycopg2-binary for container use.

Image: ghcr.io/0xsend/barman:3.17.0-r2fix
…fety

- Move chunk_size validation to __init__ (fail fast on bad config)
- Add is_final flag to _upload_part_buffer to enforce uniform part sizes
  for non-trailing parts (R2 requirement)
- Preserve buffer and part counter on upload failure for retry callers
- Add tests for validation, final-part semantics, and failure recovery
@martinmarques

Copy link
Copy Markdown
Contributor

Hello, we had an initial look at the PR, but couldn't get to it due to conflicting priorities. We want to have a closer look at the proposal. We'll get back to you here after the next release is over.

GureevLeonid added a commit to maestra-io/barman that referenced this pull request May 6, 2026
Standalone barman-cloud CLI image (python:3.13-slim + snappy + lz4 +
boto3). Built with our patched cloud.py, pushed to public ECR
public.ecr.aws/g5f9s8a4/barman on main + tags via the existing
Teleport-Workload-Identity → AWS-STS pattern.

Why this fork exists: PR EnterpriseDB#1161 (R2-compatible multipart parts) is open
since 2026-02 against EDB/barman with no upstream review traction; we
need it shipped to fix daily backups on omicron's R2 buckets for any
postgres relation file >5 MiB. Retire when 3.19+ merges the fix.

See MAESTRA_FORK.md for branch layout and consumption notes.
@linwalth

linwalth commented Jun 8, 2026

Copy link
Copy Markdown

is this still on the table?

CloudNativePG's in-tree barmanObjectStore runs barman-cloud-backup inside
the postgres operand image, so the R2 fix ships as an overlay image:
ghcr.io/0xsend/cnpg-postgresql:18-r2 built from the upstream operand with
the forked barman force-reinstalled over stock 3.17.0.
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.

Backups to Cloudflare R2 (S3 Compatible) storage fail

3 participants