Skip to content

upload/download transfers files within a part sequentially — add file-level parallelism (file_transfer_concurrency) #1454

Description

@minguyen9988

Problem

UploadPath and DownloadPath in pkg/storage/general.go transfer the files of a part strictly sequentially:

  • DownloadPath (pkg/storage/general.go:699) walks the remote prefix and downloads one file at a time inside bd.Walk(...).
  • UploadPath (pkg/storage/general.go:766) iterates for _, filename := range files and calls PutFile one file at a time.

upload_concurrency / download_concurrency parallelize across parts/tables, but each part's files still move one-by-one. For wide MergeTree parts (dozens to hundreds of small/medium column files) against high-latency object storage (S3/GCS), per-file round-trip latency dominates and a single part transfers far below the available bandwidth. On multi-TiB backups with many wide tables this leaves most of the network idle.

Proposal

Add a file_transfer_concurrency option (general section, env FILE_TRANSFER_CONCURRENCY) and parallel variants UploadPathParallel / DownloadPathParallel (plus a manifest-aware DownloadPathParallelWithManifest) that transfer the files of a single part with N concurrent workers using errgroup:

  • Call sites in pkg/backup/upload.go and pkg/backup/download.go pick the parallel variant when file_transfer_concurrency > 1, otherwise the existing sequential code path runs unchanged.
  • Bandwidth throttling still honors upload_max_bytes_per_second / download_max_bytes_per_second as an aggregate cap: one shared bwlimit.Limiter is created per path-transfer and shared by all workers, so parallelism does not multiply the configured max speed.
  • Default file_transfer_concurrency: 1 preserves current behavior exactly (opt-in).

We run this in a production fork backing up multi-TiB ClickHouse clusters; file-level parallelism is the single biggest wall-clock win we measured for wide tables (parts with many files) on object storage.

I have a PR ready to submit.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions