Repository navigation
Description
Summary
Decide, from measurements, whether PyAthena's S3 filesystem (S3FileSystem and AioS3FileSystem) has an advantage over s3fs that justifies offering it as an s3fs alternative, and whether it should then be developed and published as a standalone package that PyAthena depends on.
Background
The filesystem is built on the synchronous boto3 client and thread pools; s3fs is built on aiobotocore. The two differ in three ways that matter to users:
- Dependencies. s3fs 2026.9.0 requires
aiobotocore>=2.19,<4and pinsfsspecto its own version (>=2026.9.0,<2026.9.1). aiobotocore 3.10.0 requiresbotocore>=1.43.115,<1.43.118, while the latest boto3 is 1.43.118. PyAthena requires onlyboto3>=1.43.31,botocore>=1.43.31,fsspec>=2026.9.0, andtenacity. The filesystem will not depend on aiobotocore (Separate an S3 core from the fsspec adapter in pyathena.filesystem #1053). - Concurrency model. One PyAthena file reads its ranges and uploads its parts in parallel through an
S3Executorsized bymax_workers; the aio filesystem runs the blocking boto3 calls in its own thread pools (Size the asyncio filesystem's parallel requests by max_workers instead of the default executor #1159). s3fs runs every request as a coroutine on fsspec's IO loop, so batch operations over many objects (cat,get,put,rm,copy) run withbatch_sizeconcurrency. The synchronousS3FileSysteminherits fsspec's sequential loops for these batch operations (copyandgetinpyathena/filesystem/s3.pyfall through toAbstractFileSystem), andAioS3FileSystemgets its batch concurrency fromAsyncFileSystemwith threads. - Design. Typed models (
S3Object,S3Core,S3Path,S3PathPairing), per-operation request parameters, error translation, and the write session (Give multipart writes a single owner of their parts and an explicit state #1211) are PyAthena's own; the s3fs API surface is kept for migration (Improve s3fs / fsspec API parity for the built-in S3FileSystem #722).
The filesystem suite of the benchmark project measures one-object reads, writes, and copies through the two PyAthena filesystems only. The 4.2.0 run found the aio paths at the sync throughput for writes and copies, but nothing in the history compares either with s3fs, and the suite has no scenario with many objects, where s3fs's coroutine concurrency should matter most.
Plan
- Add s3fs to the filesystem suite as a third API, in its own benchmark environment because aiobotocore pins botocore: Add s3fs as an API of the filesystem benchmark suite #1316.
- Add many-object scenarios to the suite: batch reads, writes, deletes, listing, and metadata fan-out over hundreds of small objects: Add many-object scenarios to the filesystem benchmark suite #1317.
- Measure the extended suite on one
c8gn.2xlargeSpot host and record a history entry with the comparison and a recommendation: Measure the filesystem suite against s3fs on c8gn.2xlarge #1318. - Decide from the results. Expected outcomes, to be confirmed or refuted by step 3:
- One large object: PyAthena's parallel range reads should match or beat s3fs's single-stream
open().read(); writes and server-side copies should be close, since both are multipart. - Many small objects: s3fs is likely ahead for the synchronous filesystem, which runs its batch operations sequentially, and may be ahead of the aio filesystem at high concurrency, where threads and the connection pool cap PyAthena. If so, parallel batch operations for
S3FileSystemare the first improvement to file. - Process cost: peak RSS, thread count, and CPU time per transferred byte for each implementation.
- One large object: PyAthena's parallel range reads should match or beat s3fs's single-stream
- Standalone package, only if step 4 finds an advantage worth offering to users without Athena. The design question is filed separately after the measurement; the inventory below is what it has to resolve.
Coupling inventory for step 5
pyathena.filesystem is 10,971 lines in 11 modules with 374 commits since 2022-09. Its imports from the rest of PyAthena:
| Import | Used by | Note |
|---|---|---|
pyathena.util.override, RetryConfig, retry_api_call, stop_retries_on, unstoppable_retries |
s3_core.py, s3.py, s3_async.py, s3_executor.py, s3_writer.py, reader.py, s3_path.py, s3_object.py |
Retry policy and stop scopes shared with the cursors; would move with the package or be duplicated |
pyathena.aio.util.create_in_thread |
s3_async.py |
Cancellation-safe construction (#1208) |
pyathena.connection.Connection |
s3.py, s3_async.py |
The connection= constructor argument and the shared S3 client (#1011); would become a PyAthena-side factory |
pyathena.user_agent_extra |
s3.py |
User agent of the S3 client |
PyAthena modules that import the filesystem: the pandas, Polars, Arrow, and S3FS result sets, pyathena.aio.s3fs.cursor, and the pyathena.pandas / pyathena.polars packages, which register the s3 and s3a protocols. A standalone package would register through fsspec's fsspec.specs entry point instead, and pyathena.filesystem would stay as a compatibility re-export for 4.x.
fsspec ships no benchmark (pytest-benchmark is a test dependency of fsspec without a test that uses it), and s3fs none either, so the comparison has to come from the suite here. fsspec does ship the conformance tests fsspec.tests.abstract (copy, get, put, mv, open, pipe), which s3fs runs in s3fs/tests/derived; running them against PyAthena's filesystem is a precondition of step 5, since an s3fs alternative is judged by that contract.
A uv workspace member in this repository (as benchmarks/ is) published as its own distribution would keep the AWS test infrastructure, CI, and docs shared; a separate repository is a later option if the release cadence diverges.
Decision criteria
- A measured throughput, memory, or CPU advantage in at least one common scenario, or parity with the dependency advantage above, supports the alternative.
- Parity everywhere with higher process cost means the filesystem stays an internal part of PyAthena, and step 5 is not filed.
Activity
laughingman7743 commentedon Oct 24, 2026
Results of step 3 (#1318), recorded in the history entry (PR #1321): one
c8gn.2xlargeSpot host, commit7b20b499, s3fs 2026.9.0 with aiobotocore 3.9.2, five trials per case, no failures. The suite used 8 MiB blocks for every API; because that understates s3fs's sequential uploads and copies, a supplementary run repeated s3fs's single-object cases with its default of 50 MiB.Against the expected outcomes
open().read(), which stayed at 75 to 93 MiB/s.put_file()ran at 1.5 to 3.0 times.get_file()was 2.2 to 3.7 times faster with 8 MiB chunks, and 5 to 9 times with 50 MiB from 256 MiB up, because PyAthena'sget_file()reads one block at a time.S3FileSystem: confirmed.cat/get/put/pipe/copyran one object at a time, 7 to 59 times slower than s3fs.AioS3FileSystemat high concurrency: mostly refuted. At batch size 8 it matched s3fs; at 40 it was 1.4 to 2.5 times faster forcat/get/put/pipe, while s3fs was 1.20 times faster forcopyand 1.45 times forinfo().rmandfindran at the same rate with every API.rm, whose harness preparation adds 32 threads to every API; PyAthena 19 to 22 with 8 workers and up to 54 with 40.max_concurrency40 peaked at 0.9 to 2.1 GiB, against 0.3 GiB.Against the decision criteria
The first criterion is met: PyAthena has a measured throughput advantage in common scenarios (single-object reads, writes, puts, and copies with either API, and
AioS3FileSystembatch transfers at high concurrency), with the dependency advantage described above.Two gaps would meet a user migrating from s3fs, and are filed as sub-issues of this issue:
S3FileSystemrun one object at a time;get_file()reads one block at a time.Proposed next steps: fix these two, re-measure the affected cases (
syncobjects cases,getcases), and then file the design issue for step 5, including the fsspecfsspec.tests.abstractconformance tests as its precondition.The
copyandinfo()differences ofAioS3FileSystemat batch size 40 (0.84 and 0.69 times s3fs) are small and unexplained; they can be looked at during the re-measurement rather than filed now.