Skip to content

A download abandoned before its body starts does not close the store's handle promptly #1604

Open

Description

@peteski22

Problem

GET /api/v1/files/{file_id}/content streams the bytes. Before it answers, FileService.content reads the first chunk eagerly through _primed in services/files/_service.py, so a blob that is gone or unreadable fails while the failure can still be reported.

Priming starts the store's own generator, which is then suspended inside the adapter's async with on an open file handle. The close is owned by the finally in _primed's inner generator, and that finally only runs once something starts iterating it. If the request is abandoned between the service returning and the response beginning to send, the inner generator is never entered, closing it runs no finally, and the store's handle is not closed there.

It is not a permanent leak. The handle closes when the suspended generator is collected and the event loop's async-generator finalizer runs it, or at loop shutdown. That is the delay the finally exists to avoid, and the comment beside it claims the case is covered. Under sustained abandoned downloads the handles accumulate for as long as collection lags.

Fix

Give the close an owner that does not depend on the body iterator starting. Either:

  • prime lazily, moving the first read inside the try so the caller's first __anext__ is what raises; or
  • have the response path close the underlying stream on teardown whether or not iteration began.

An idempotent aclose makes either safe.

Done when

  • The store's handle is closed when the body iterator is never started.
  • A test in tests/unit/test_files_streaming.py covers the never-started consumer. The existing tests all start the generator.

Metadata

Metadata

Assignees

No one assigned

    Labels

    area/backendBackend service implementationtype/tech-debtMaintenance and cleanup

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions