Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 45 additions & 1 deletion docs/filesystem.md
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,49 @@ fs.cp("s3://YOUR_S3_BUCKET/src", "s3://YOUR_S3_BUCKET/dst")
fs.rm("s3://YOUR_S3_BUCKET/path/to/", recursive=True)
```

`cat`, `cat_ranges`, `get`, `put`, `pipe` with a dict of values, `cp`, and `mv` transfer
their objects in parallel, `batch_size` objects at a time:

```python
fs.get("s3://YOUR_S3_BUCKET/path/to/", "local/dir/", recursive=True, batch_size=16)
```

`batch_size` is an argument of these methods, of `rm`, and of `S3FileSystem`. By default,
and with `-1`, it is the filesystem's `max_workers`. The transfers of a batch run in
threads of their own. With several transfers at once, each object sends at most
`max(1, max_workers // n)` range reads, parts, or part copies at once, `n` being the
smaller of `batch_size` and the number of objects, so that a batch keeps about
`max_workers` requests in the request pool and as many parts in memory; a smaller
`max_workers` given to the method is kept. A single object keeps the filesystem's
`max_workers`. `cat`, `cat_ranges`, and a small `pipe` send their single request from the
transfer's thread, so a `batch_size` larger than `max_workers` sends that many requests
at once.

When a transfer fails, no further transfer starts, the retries of the running ones stop,
and the error is raised once they have ended. A running multipart upload or copy sends
no further part and is aborted instead of being completed, and a running download reads
and writes no further block. A request already sent, such as the PutObject of a small
object, runs to its end; a multipart copy that has been completed still copies its
annotations. With `cache_type="background"`, a block that fsspec's cache prefetches in
its own thread may still be read after the stop. The transfers that ended before the failure stay done, and their number is
not bounded by `batch_size`. `mv` deletes the sources only once every copy has
succeeded. `on_error` of `cat`, `cat_ranges`, and `cp` keeps its fsspec meaning; with
`on_error="omit"` or `"return"` of `cat`, `"return"` of `cat_ranges`, or `"ignore"` of
`cp` for a missing source, those errors do not stop the batch.

Pairs whose result would depend on the order of the transfers raise `ValueError` before

@laughingman7743 laughingman7743 Nov 7, 2026 •

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Self-review round 2 (claims, compatibility, operations) — base 7fcbcc64b02f9a5efe16ec04815b4143d1a2c615, head 24d9872ff4026dc67e6d9bd63fed6080fe11b120.

Claims checked against the code: cat_file and the small pipe_file send their request from the task thread (so batch_size > max_workers raises total concurrency — documented); mv deletes only after map returns; a committed multipart copy still copies its annotations under unstoppable_retries(); the conflict rules match S3PathPairing._check (self-copy kept, prefix-only skips excluded, same existing file by st_dev/st_ino, file-like by identity); rm(max_workers=) warns with stacklevel=2.

Existing callers: positional arguments of S3FileSystem.__init__ unchanged (batch_size is keyword-only after *args); AioS3FileSystem keeps its own batch_size and does not pass it to the internal filesystem; AioS3FileSystem._mv still uses _move_pairs, whose return is unchanged; no rm(max_workers=) call in pyathena/ or benchmarks/.

Operational: a stopped stop_retries_on() scope now also makes S3 reads and writes raise CancelledError before their next request (release note in the PR body). The design comment said retry_api_call and wait_with_retry_stop would reuse the new test; they keep their own snapshot of the events, which is equivalent, so that sentence is not implemented and is not claimed in the PR.

Evidence: local AWS filesystem suite 1549 passed (implementation of 24d9872f); AWS CI not run yet (Draft). CLEAN.

anything is transferred: two sources of `get`, `put`, `cp`, or `pipe` with the same
destination, including two paths of one existing local file or one file-like object,
and a `cp` destination that is another source. Local paths that do not exist yet are
compared as strings, after `os.path.normcase`, so on a case-insensitive filesystem other
than Windows, such as the default one of macOS, give `get` destinations that differ by
more than case. A `cp` of an object onto itself, such as with
`MetadataDirective="REPLACE"`, is no conflict. `get` with `outfile` writes the objects
into it one after another, in order.

`rm` deletes up to 1,000 objects per DeleteObjects request, `batch_size` requests at a
time. Its `max_workers` argument is a deprecated alias of `batch_size`.

Writes with `pipe`/`pipe_file` issue a single PutObject request for data up to the
block size (5 MiB by default); larger data is uploaded as a parallel multipart upload
through the buffered file path. Inside an
Expand Down Expand Up @@ -128,7 +171,8 @@ cancels the range reads that have not started, and the running ones make no furt
attempt. The interrupt or the error is raised once they have ended, and their data is
released. A second interrupt stops that wait, and the reads still finish. `rm` and
`clear_multipart_uploads` send every request also after one fails; an interrupt cancels
those that have not started and is raised once the running ones have ended. Closing
those that have not started and is raised once the running ones have ended. An interrupt of
a batch operation, such as `get` or `cp`, does the same as a failed transfer. Closing
the file waits for its running requests; an interrupt of that wait stops it, and the
requests still finish. An interrupt while a
request is being submitted can let one more request than `max_workers` run at once
Expand Down
Loading
Loading