Skip to content

Use connection pool for faster download#287

Draft
hagenw wants to merge 5 commits into
mainfrom
connection-pooling
Draft

Use connection pool for faster download#287
hagenw wants to merge 5 commits into
mainfrom
connection-pooling

Conversation

@hagenw

@hagenw hagenw commented Feb 23, 2026

Copy link
Copy Markdown
Member

Addresses audeering/audb#546

This uses the proposal from audeering/audb#546 3b and uses pooled connections to avoid having to establish a HTTPS connection for every single file that it will download. Currently, max connection is set to 10 and can be changed in the config file (maybe we should increase default to 100?).

I did a benchmark on a dataset with ~1,500,000 files and the remaining time after 5 minutes into the download with 6 workers is reduced from 175:40:24 hours to 167:05:28 hours. I will provide another benchmark with 48 workers at a later stage. A first test with 48 workers and max connection of 100 did not show any improvement. In both cases the time was ~155 hours.

It also adds the get_config() method to audbackend.backend.Artifactory to be in line with audbackend.backend.Minio. The config file is still stored at a different place, but this will be handled in a different pull request (#288).

image image image

@sourcery-ai

sourcery-ai Bot commented Feb 23, 2026

Copy link
Copy Markdown
Contributor

Reviewer's Guide

Introduces configurable HTTP connection pooling for Artifactory and Minio backends (with sensible defaults, validation, and tests) to speed up bulk parallel downloads while keeping behavior robust for invalid or missing config.

Sequence diagram for pooled connection setup and file download (Artifactory backend)

sequenceDiagram
    actor Worker
    participant BackendArtifactory as Artifactory
    participant Config as ArtifactoryConfigFile
    participant Requests as RequestsSession
    participant Adapter as HTTPAdapter
    participant Server as ArtifactoryServer

    Worker->>BackendArtifactory: create_backend(host, repository)
    activate BackendArtifactory
    BackendArtifactory->>Config: get_config(host)
    alt config_file_exists_and_section_present
        Config-->>BackendArtifactory: {pool_connections, pool_maxsize, max_retries}
    else missing_or_invalid_config
        Config-->>BackendArtifactory: {}
    end

    BackendArtifactory->>BackendArtifactory: _parse_int(pool_connections)
    BackendArtifactory->>BackendArtifactory: _parse_int(pool_maxsize)
    BackendArtifactory->>BackendArtifactory: _parse_int(max_retries)

    BackendArtifactory->>Requests: Session()
    Requests-->>BackendArtifactory: session
    BackendArtifactory->>Adapter: HTTPAdapter(pool_connections, pool_maxsize, max_retries)
    Adapter-->>BackendArtifactory: adapter
    BackendArtifactory->>Requests: mount("https://", adapter)
    BackendArtifactory->>Requests: mount("http://", adapter)

    BackendArtifactory-->>Worker: backend_ready_with_pooled_session
    deactivate BackendArtifactory

    par multiple_workers_download_in_parallel
        Worker->>BackendArtifactory: download_file(path)
        BackendArtifactory->>Requests: GET path (reuses pool connection)
        Requests->>Server: HTTPS request
        Server-->>Requests: response
        Requests-->>BackendArtifactory: response
        BackendArtifactory-->>Worker: file_content
    and
        Worker->>BackendArtifactory: download_file(other_path)
        BackendArtifactory->>Requests: GET other_path (reuses pool connection)
        Requests->>Server: HTTPS request
        Server-->>Requests: response
        Requests-->>BackendArtifactory: response
        BackendArtifactory-->>Worker: file_content
    end
Loading

Class diagram for updated Artifactory and Minio backends with connection pooling

classDiagram
    class Base {
    }

    class Artifactory {
        - str host
        - str repository
        - requests.Session _session
        - repo _repo
        + get_authentication(host: str) str str
        + get_config(host: str) dict
        - _open() None
        - _checksum(path: str, checksum: str, label: str) str
        - _upload_file(path: str, file: str, label: str) None
        - _remove_file(path: str, label: str) None
    }

    class Minio {
        - str host
        - str bucket
        - minio.Minio _client
        + __init__(host: str, bucket: str, secure: bool, kwargs) None
        + get_config(host: str) dict
        - _upload_file(path: str, file: str, label: str) None
        - _remove_file(path: str, label: str) None
    }

    class HTTPAdapter {
        + HTTPAdapter(pool_connections: int, pool_maxsize: int, max_retries: int)
    }

    class PoolManager {
        + PoolManager(timeout, num_pools: int, maxsize: int, block: bool)
    }

    class _parse_int_artifactory {
        + _parse_int(value: str|int, name: str, default: int) int
    }

    class _parse_int_minio {
        + _parse_int(value: str|int, name: str, default: int) int
    }

    class _parse_bool {
        + _parse_bool(value: str|bool, name: str, default: bool) bool
    }

    Base <|-- Artifactory
    Base <|-- Minio

    Artifactory --> HTTPAdapter : uses
    Artifactory --> _parse_int_artifactory : uses

    Minio --> PoolManager : uses
    Minio --> _parse_int_minio : uses
    Minio --> _parse_bool : uses
Loading

Architecture diagram for backends using HTTP connection pools

flowchart LR
    App["Application<br>(audb client, download workers)"] -->|uses| BackendArtifactory["Artifactory backend"]
    App -->|uses| BackendMinio["Minio backend"]

    BackendArtifactory -->|config via get_config| ArtifactoryConfig["Artifactory config file<br>~/.config/audbackend/artifactory.cfg"]
    BackendMinio -->|config via get_config| MinioConfig["Minio config file<br>~/.config/audbackend/minio.cfg"]

    BackendArtifactory -->|creates| RequestsSession["requests.Session<br>with HTTPAdapter(pool_connections, pool_maxsize, max_retries)"]
    BackendMinio -->|creates| PoolManager["urllib3.PoolManager<br>(timeout, num_pools, maxsize, block)"]

    RequestsSession -->|reused HTTP connections| ArtifactoryService["Artifactory server"]
    PoolManager -->|reused HTTP connections| MinioService["MinIO server"]
Loading

File-Level Changes

Change Details Files
Add configurable connection pooling to the Artifactory backend session setup, driven by a new backend config file and validated parsing helpers.
  • Introduce Artifactory.get_config() to read per-host pool settings from an INI-style config file located at ~/.config/audbackend/artifactory.cfg or ARTIFACTORY_POOL_CONFIG_FILE.
  • Extend Artifactory._open() to build a requests.Session with an HTTPAdapter configured via parsed pool_connections, pool_maxsize, and max_retries, mounting it for both http and https.
  • Add a shared _parse_int() helper in artifactory backend to safely parse integer settings, emitting warnings and falling back to defaults on invalid values.
  • Document available Artifactory connection pool settings and recommended tuning for bulk downloads in the class docstring and get_config() docstring.
audbackend/core/backend/artifactory.py
Extend Minio backend to support configurable connection pooling alongside existing timeout configuration, with parsing helpers and documentation.
  • Update Minio class docstring and get_config() documentation to describe new connection pool options (num_pools, pool_maxsize, pool_block) and their behavior for bulk downloads.
  • Modify Minio.init to construct a urllib3.PoolManager with timeout plus num_pools, maxsize, and block derived from backend config, unless a custom http_client is provided.
  • Introduce _parse_bool() to robustly parse boolean config values and warn/fallback on invalid inputs.
  • Introduce a Minio-specific _parse_int() helper mirroring the Artifactory behavior for integer pool settings.
audbackend/core/backend/minio.py
Add tests to verify pool configuration parsing, defaults, overrides, and invalid-value handling for Artifactory and Minio backends.
  • Extend hide_credentials fixture to clear ARTIFACTORY_POOL_CONFIG_FILE and add tests for Artifactory.get_config() behavior with missing, empty, mismatched, and valid host sections.
  • Add Artifactory backend tests that assert default pool settings when config is empty, usage of custom pool settings from config, and warnings + fallback defaults when pool-related config values are invalid, mocking network access.
  • Add Minio backend tests to verify default pool configuration when no pool values are set, proper application of custom num_pools/pool_maxsize/pool_block from config, correct parsing of pool_block false-ish values, and warnings + fallback defaults for invalid pool entries.
tests/test_backend_artifactory.py
tests/test_backend_minio.py

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

sourcery-ai[bot]

This comment was marked as resolved.

Comment thread audbackend/core/backend/artifactory.py Outdated
@codecov

codecov Bot commented Feb 23, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 100.0%. Comparing base (52ffc48) to head (a84ee86).

Additional details and impacted files
Files with missing lines Coverage Δ
audbackend/core/backend/artifactory.py 100.0% <100.0%> (ø)
audbackend/core/backend/minio.py 100.0% <100.0%> (ø)
audbackend/core/utils.py 100.0% <100.0%> (ø)
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

hagenw and others added 2 commits February 23, 2026 15:51
Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com>
@hagenw

hagenw commented Apr 3, 2026

Copy link
Copy Markdown
Member Author

This has not shown a big boost in performance, so I will set it to draft for now.

@hagenw hagenw marked this pull request as draft April 3, 2026 08:51
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