Skip to content

perf(cloudimg): sniff before full download and reuse the non-Range probe response #137

Description

@CMGS

Two efficiency gaps in the cloud-image pull path, both surfaced in the 2026-07-16 whole-repo audit. Deferred here rather than refactored blind because they touch the tuned parallel-download hot path and warrant a before/after benchmark per our perf-is-a-product-requirement rule.

1. Magic-byte sniff runs after the whole download

pullwithDownload downloads and hashes the entire body (up to maxDownloadBytes = 20 GiB) before sniffImageSource(f) at pull.go checks the first 8 bytes. A misconfigured URL serving a multi-GB non-image payload pays the full transfer + write + hash before rejection. The OCI import path (importQcow2Reader via utils.PeekReader) peeks first — cloudimg should too.

Constraint: the content-addressed digest requires hashing the full file anyway, so the sniff can move early but the hash cannot. The win is bounded to the reject-early case (operator misconfiguration), not the happy path.

2. Non-Range servers pay two full request cycles

probeRangeSupport issues GET Range: bytes=0-0. A server that ignores Range replies 200 with the full body; the code closes that response unread (killing connection reuse per net/http) and downloadSerial then issues a fresh GET. Every pull from a non-Range source is two full request/response cycles.

Options: probe with HEAD + Accept-Ranges, or thread the already-open 200 response into downloadSerial instead of discarding it.

Acceptance

  • A non-image URL is rejected after reading only the header bytes, not the full payload (add a test asserting bytes read).
  • A non-Range source performs one GET, not two (assert request count via a test server).
  • Range-capable parallel download path unchanged; before/after wall-clock + bytes-read on 1/8/16-way fan-out recorded on a compatible host.
  • No regression in TestDownloadToFileFallsBackWhenRangeUnsupported and the digest-verification tests.

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

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions