quiver-tools 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (33) hide show
  1. quiver_tools-0.1.0/.gitignore +19 -0
  2. quiver_tools-0.1.0/LICENSE +201 -0
  3. quiver_tools-0.1.0/NOTICE +13 -0
  4. quiver_tools-0.1.0/PKG-INFO +127 -0
  5. quiver_tools-0.1.0/README.md +100 -0
  6. quiver_tools-0.1.0/bench/README.md +97 -0
  7. quiver_tools-0.1.0/bench/qvm2/README.md +13 -0
  8. quiver_tools-0.1.0/hatch_build.py +82 -0
  9. quiver_tools-0.1.0/pyproject.toml +62 -0
  10. quiver_tools-0.1.0/quiver/__init__.py +0 -0
  11. quiver_tools-0.1.0/quiver/cli.py +287 -0
  12. quiver_tools-0.1.0/quiver/compiler/gen_qvm2.py +39 -0
  13. quiver_tools-0.1.0/quiver/compiler/gen_templates.py +64 -0
  14. quiver_tools-0.1.0/quiver/exec/Makefile +39 -0
  15. quiver_tools-0.1.0/quiver/exec/blocks.py +3196 -0
  16. quiver_tools-0.1.0/quiver/exec/blocks_cli.py +10 -0
  17. quiver_tools-0.1.0/quiver/exec/build-deps.sh +77 -0
  18. quiver_tools-0.1.0/quiver/exec/bvm.c +2550 -0
  19. quiver_tools-0.1.0/quiver/exec/qvm2.c +1476 -0
  20. quiver_tools-0.1.0/quiver/exec/qvm2_gen.h +11 -0
  21. quiver_tools-0.1.0/quiver/nock/__init__.py +2 -0
  22. quiver_tools-0.1.0/quiver/nock/nockidx.py +244 -0
  23. quiver_tools-0.1.0/quiver/nock/zserve.py +304 -0
  24. quiver_tools-0.1.0/quiver/opcodes.py +14 -0
  25. quiver_tools-0.1.0/quiver/pupyarrow/__init__.py +5 -0
  26. quiver_tools-0.1.0/quiver/pupyarrow/fb.py +263 -0
  27. quiver_tools-0.1.0/quiver/pupyarrow/file_reader.py +518 -0
  28. quiver_tools-0.1.0/quiver/pupyarrow/reader.py +1307 -0
  29. quiver_tools-0.1.0/quiver/pupyarrow/writer.py +244 -0
  30. quiver_tools-0.1.0/quiver/qplan2.py +318 -0
  31. quiver_tools-0.1.0/tests/conftest.py +17 -0
  32. quiver_tools-0.1.0/tests/test_blocks.py +397 -0
  33. quiver_tools-0.1.0/tests/test_cli.py +341 -0
@@ -0,0 +1,19 @@
1
+ __pycache__/
2
+ *.pyc
3
+ quiver/exec/quiver-exec
4
+ quiver/exec/bvm
5
+ quiver/exec/ipc_gen.h
6
+ *.wal
7
+ *.wal.done
8
+ dist/
9
+ *.egg-info/
10
+
11
+ # chat logs / scratch
12
+ .claude/
13
+
14
+ # large data archives (never commit)
15
+ *.tar.zstd
16
+ *.tar.zst
17
+
18
+ # static deps for bvm, built by quiver/exec/build-deps.sh
19
+ quiver/exec/deps/
@@ -0,0 +1,201 @@
1
+ Apache License
2
+ Version 2.0, January 2004
3
+ http://www.apache.org/licenses/
4
+
5
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6
+
7
+ 1. Definitions.
8
+
9
+ "License" shall mean the terms and conditions for use, reproduction,
10
+ and distribution as defined by Sections 1 through 9 of this document.
11
+
12
+ "Licensor" shall mean the copyright owner or entity authorized by
13
+ the copyright owner that is granting the License.
14
+
15
+ "Legal Entity" shall mean the union of the acting entity and all
16
+ other entities that control, are controlled by, or are under common
17
+ control with that entity. For the purposes of this definition,
18
+ "control" means (i) the power, direct or indirect, to cause the
19
+ direction or management of such entity, whether by contract or
20
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
21
+ outstanding shares, or (iii) beneficial ownership of such entity.
22
+
23
+ "You" (or "Your") shall mean an individual or Legal Entity
24
+ exercising permissions granted by this License.
25
+
26
+ "Source" form shall mean the preferred form for making modifications,
27
+ including but not limited to software source code, documentation
28
+ source, and configuration files.
29
+
30
+ "Object" form shall mean any form resulting from mechanical
31
+ transformation or translation of a Source form, including but
32
+ not limited to compiled object code, generated documentation,
33
+ and conversions to other media types.
34
+
35
+ "Work" shall mean the work of authorship, whether in Source or
36
+ Object form, made available under the License, as indicated by a
37
+ copyright notice that is included in or attached to the work
38
+ (an example is provided in the Appendix below).
39
+
40
+ "Derivative Works" shall mean any work, whether in Source or Object
41
+ form, that is based on (or derived from) the Work and for which the
42
+ editorial revisions, annotations, elaborations, or other modifications
43
+ represent, as a whole, an original work of authorship. For the purposes
44
+ of this License, Derivative Works shall not include works that remain
45
+ separable from, or merely link (or bind by name) to the interfaces of,
46
+ the Work and Derivative Works thereof.
47
+
48
+ "Contribution" shall mean any work of authorship, including
49
+ the original version of the Work and any modifications or additions
50
+ to that Work or Derivative Works thereof, that is intentionally
51
+ submitted to Licensor for inclusion in the Work by the copyright owner
52
+ or by an individual or Legal Entity authorized to submit on behalf of
53
+ the copyright owner. For the purposes of this definition, "submitted"
54
+ means any form of electronic, verbal, or written communication sent
55
+ to the Licensor or its representatives, including but not limited to
56
+ communication on electronic mailing lists, source code control systems,
57
+ and issue tracking systems that are managed by, or on behalf of, the
58
+ Licensor for the purpose of discussing and improving the Work, but
59
+ excluding communication that is conspicuously marked or otherwise
60
+ designated in writing by the copyright owner as "Not a Contribution."
61
+
62
+ "Contributor" shall mean Licensor and any individual or Legal Entity
63
+ on behalf of whom a Contribution has been received by Licensor and
64
+ subsequently incorporated within the Work.
65
+
66
+ 2. Grant of Copyright License. Subject to the terms and conditions of
67
+ this License, each Contributor hereby grants to You a perpetual,
68
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69
+ copyright license to reproduce, prepare Derivative Works of,
70
+ publicly display, publicly perform, sublicense, and distribute the
71
+ Work and such Derivative Works in Source or Object form.
72
+
73
+ 3. Grant of Patent License. Subject to the terms and conditions of
74
+ this License, each Contributor hereby grants to You a perpetual,
75
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76
+ (except as stated in this section) patent license to make, have made,
77
+ use, offer to sell, sell, import, and otherwise transfer the Work,
78
+ where such license applies only to those patent claims licensable
79
+ by such Contributor that are necessarily infringed by their
80
+ Contribution(s) alone or by combination of their Contribution(s)
81
+ with the Work to which such Contribution(s) was submitted. If You
82
+ institute patent litigation against any entity (including a
83
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
84
+ or a Contribution incorporated within the Work constitutes direct
85
+ or contributory patent infringement, then any patent licenses
86
+ granted to You under this License for that Work shall terminate
87
+ as of the date such litigation is filed.
88
+
89
+ 4. Redistribution. You may reproduce and distribute copies of the
90
+ Work or Derivative Works thereof in any medium, with or without
91
+ modifications, and in Source or Object form, provided that You
92
+ meet the following conditions:
93
+
94
+ (a) You must give any other recipients of the Work or
95
+ Derivative Works a copy of this License; and
96
+
97
+ (b) You must cause any modified files to carry prominent notices
98
+ stating that You changed the files; and
99
+
100
+ (c) You must retain, in the Source form of any Derivative Works
101
+ that You distribute, all copyright, patent, trademark, and
102
+ attribution notices from the Source form of the Work,
103
+ excluding those notices that do not pertain to any part of
104
+ the Derivative Works; and
105
+
106
+ (d) If the Work includes a "NOTICE" text file as part of its
107
+ distribution, then any Derivative Works that You distribute must
108
+ include a readable copy of the attribution notices contained
109
+ within such NOTICE file, excluding those notices that do not
110
+ pertain to any part of the Derivative Works, in at least one
111
+ of the following places: within a NOTICE text file distributed
112
+ as part of the Derivative Works; within the Source form or
113
+ documentation, if provided along with the Derivative Works; or,
114
+ within a display generated by the Derivative Works, if and
115
+ wherever such third-party notices normally appear. The contents
116
+ of the NOTICE file are for informational purposes only and
117
+ do not modify the License. You may add Your own attribution
118
+ notices within Derivative Works that You distribute, alongside
119
+ or as an addendum to the NOTICE text from the Work, provided
120
+ that such additional attribution notices cannot be construed
121
+ as modifying the License.
122
+
123
+ You may add Your own copyright statement to Your modifications and
124
+ may provide additional or different license terms and conditions
125
+ for use, reproduction, or distribution of Your modifications, or
126
+ for any such Derivative Works as a whole, provided Your use,
127
+ reproduction, and distribution of the Work otherwise complies with
128
+ the conditions stated in this License.
129
+
130
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
131
+ any Contribution intentionally submitted for inclusion in the Work
132
+ by You to the Licensor shall be under the terms and conditions of
133
+ this License, without any additional terms or conditions.
134
+ Notwithstanding the above, nothing herein shall supersede or modify
135
+ the terms of any separate license agreement you may have executed
136
+ with Licensor regarding such Contributions.
137
+
138
+ 6. Trademarks. This License does not grant permission to use the trade
139
+ names, trademarks, service marks, or product names of the Licensor,
140
+ except as required for reasonable and customary use in describing the
141
+ origin of the Work and reproducing the content of the NOTICE file.
142
+
143
+ 7. Disclaimer of Warranty. Unless required by applicable law or
144
+ agreed to in writing, Licensor provides the Work (and each
145
+ Contributor provides its Contributions) on an "AS IS" BASIS,
146
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147
+ implied, including, without limitation, any warranties or conditions
148
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149
+ PARTICULAR PURPOSE. You are solely responsible for determining the
150
+ appropriateness of using or redistributing the Work and assume any
151
+ risks associated with Your exercise of permissions under this License.
152
+
153
+ 8. Limitation of Liability. In no event and under no legal theory,
154
+ whether in tort (including negligence), contract, or otherwise,
155
+ unless required by applicable law (such as deliberate and grossly
156
+ negligent acts) or agreed to in writing, shall any Contributor be
157
+ liable to You for damages, including any direct, indirect, special,
158
+ incidental, or consequential damages of any character arising as a
159
+ result of this License or out of the use or inability to use the
160
+ Work (including but not limited to damages for loss of goodwill,
161
+ work stoppage, computer failure or malfunction, or any and all
162
+ other commercial damages or losses), even if such Contributor
163
+ has been advised of the possibility of such damages.
164
+
165
+ 9. Accepting Warranty or Additional Liability. While redistributing
166
+ the Work or Derivative Works thereof, You may choose to offer,
167
+ and charge a fee for, acceptance of support, warranty, indemnity,
168
+ or other liability obligations and/or rights consistent with this
169
+ License. However, in accepting such obligations, You may act only
170
+ on Your own behalf and on Your sole responsibility, not on behalf
171
+ of any other Contributor, and only if You agree to indemnify,
172
+ defend, and hold each Contributor harmless for any liability
173
+ incurred by, or claims asserted against, such Contributor by reason
174
+ of your accepting any such warranty or additional liability.
175
+
176
+ END OF TERMS AND CONDITIONS
177
+
178
+ APPENDIX: How to apply the Apache License to your work.
179
+
180
+ To apply the Apache License to your work, attach the following
181
+ boilerplate notice, with the fields enclosed by brackets "[]"
182
+ replaced with your own identifying information. (Don't include
183
+ the brackets!) The text should be enclosed in the appropriate
184
+ comment syntax for the file format. We also recommend that a
185
+ file or class name and description of purpose be included on the
186
+ same "printed page" as the copyright notice for easier
187
+ identification within third-party archives.
188
+
189
+ Copyright [yyyy] [name of copyright owner]
190
+
191
+ Licensed under the Apache License, Version 2.0 (the "License");
192
+ you may not use this file except in compliance with the License.
193
+ You may obtain a copy of the License at
194
+
195
+ http://www.apache.org/licenses/LICENSE-2.0
196
+
197
+ Unless required by applicable law or agreed to in writing, software
198
+ distributed under the License is distributed on an "AS IS" BASIS,
199
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200
+ See the License for the specific language governing permissions and
201
+ limitations under the License.
@@ -0,0 +1,13 @@
1
+ quiver
2
+ Copyright (c) 2026 quiver contributors
3
+
4
+ This product includes software developed at Hume AI (wsds/pupyarrow,
5
+ ducl/pwalk2 worker model).
6
+
7
+ pupyarrow/fb.py contains a builder algorithm ported from
8
+ google/flatbuffers (https://github.com/google/flatbuffers),
9
+ licensed under the Apache License, Version 2.0.
10
+
11
+ quiver/exec/md5.c and md5.h are Alexander Peslyak's (Solar Designer)
12
+ OpenSSL-compatible MD5 implementation, placed in the public domain:
13
+ https://openwall.info/wiki/people/solar/software/public-domain-source-code/md5
@@ -0,0 +1,127 @@
1
+ Metadata-Version: 2.5
2
+ Name: quiver-tools
3
+ Version: 0.1.0
4
+ Summary: Set-at-a-time Unix file tools on io_uring, Polars, and Arrow; nock self-indexing archives
5
+ Project-URL: Homepage, https://github.com/jpc/quiver
6
+ Project-URL: Source, https://github.com/jpc/quiver
7
+ License-Expression: Apache-2.0
8
+ License-File: LICENSE
9
+ License-File: NOTICE
10
+ Classifier: Operating System :: POSIX :: Linux
11
+ Classifier: Programming Language :: C
12
+ Classifier: Programming Language :: Python :: 3
13
+ Classifier: Topic :: System :: Archiving
14
+ Classifier: Topic :: System :: Filesystems
15
+ Requires-Python: >=3.10
16
+ Requires-Dist: blake3>=0.4
17
+ Requires-Dist: numpy>=1.26
18
+ Requires-Dist: polars>=1.0
19
+ Requires-Dist: zstandard>=0.22
20
+ Provides-Extra: dev
21
+ Requires-Dist: flatbuffers; extra == 'dev'
22
+ Requires-Dist: pyarrow; extra == 'dev'
23
+ Requires-Dist: pytest; extra == 'dev'
24
+ Provides-Extra: s3
25
+ Requires-Dist: boto3; extra == 's3'
26
+ Description-Content-Type: text/markdown
27
+
28
+ # quiver & nock
29
+
30
+ **quiver** — Unix file tooling rebuilt set-at-a-time: `scan`, `cp`, `rm`,
31
+ `du`, `sync`, `pack`. One relational planner (Polars), swappable
32
+ execution engines (io_uring / sync), Arrow record batches as the only
33
+ interchange.
34
+
35
+ **nock** — the index format: an Arrow IPC table of members parked in the
36
+ trailing slack of a host container (tar, raw, or Arrow IPC), making any
37
+ shard listable in one read and extractable by predicate.
38
+
39
+ Built for high-latency parallel filesystems (WEKA-class) and object
40
+ stores, where round-trips dominate and batching wins. On a warm local
41
+ fs, coreutils will beat it — that's expected and honest.
42
+
43
+ ## Install
44
+
45
+ ```bash
46
+ pip install quiver-tools # Linux x86_64 / aarch64 wheels, glibc >= 2.28; import name is `quiver`
47
+ ```
48
+
49
+ The wheel ships the `bvm` executor prebuilt (static zstd, libsodium, liburing, BLAKE3).
50
+ io_uring paths need a reasonably recent kernel; everything falls back to sync I/O otherwise.
51
+
52
+ From a checkout, build the executor once (pinned dependency sources, ~1-2 minutes), then
53
+ install editable; `pip install -e` leaves the in-tree `bvm` alone, `make` owns it:
54
+
55
+ ```bash
56
+ quiver/exec/build-deps.sh && make -C quiver/exec bvm # or: make -C quiver/exec bvm DEPS=/prefix/with/static/libs
57
+ pip install -e ".[dev]"
58
+ ```
59
+
60
+ ## The one idea
61
+
62
+ ```
63
+ producers planner executors
64
+ ┌──────────────┐ ┌───────────────────────┐ ┌──────────────────┐
65
+ │ fs scan (C, │ │ Polars: join, delta, │ │ fs exec (C, │
66
+ │ io_uring) │──▶│ cum_sum layout, epoch │──▶│ io_uring chains) │
67
+ │ nock footers │ │ assignment, header │ │ s3 exec (boto3) │
68
+ │ S3 listing │ │ packing — all exprs │ │ ssh remote exec │
69
+ └──────────────┘ └───────────────────────┘ └──────────────────┘
70
+ ```
71
+
72
+ Every tool is a query over stat frames that emits command frames.
73
+ `sync` is the general tool; `cp` is sync into an empty destination,
74
+ `rm` is sync from an empty source. tar headers are generated as Polars
75
+ expressions (byte-compatible with GNU tar, PAX included). Archives
76
+ carry their own index; retrofitting existing WebDataset tar shards or
77
+ Arrow shards touches zero payload bytes.
78
+
79
+ ## Highlights
80
+
81
+ - **Zero-dependency runtime**: polars + numpy. pyarrow and flatbuffers
82
+ are test-suite oracles only — `pupyarrow/` (vendored fork of
83
+ [wsds](https://github.com/HumeAI/wsds)'s reader) gained a writer and
84
+ a hand-rolled flatbuffers subset (`fb.py`, 12.6× faster metadata
85
+ walks than generated accessors).
86
+ - **One C binary** (`quiver-exec`): io_uring scanner (pwalk2 worker
87
+ model) + executor (linked SQE chains, refcount forest scheduler,
88
+ direct descriptors) + hashing (part-parallel MD5 composite ETags,
89
+ slice-by-8 CRC-64/NVME with GF(2) combine).
90
+ - **Predicate pushdown into the scanner**: path prefixes prune whole
91
+ subtrees (no getdents), basename globs skip statx for known-regular
92
+ misses.
93
+ - **Pipelined execution**: `quiver.stream` runs the executor while the
94
+ scan is still going; monotone plans overlap, pipeline breakers
95
+ (rmdir, joins, sorts) drain as a tail.
96
+ - **WAL / resume**: the command frame *is* the write-ahead log; crash
97
+ anywhere, rerun the WAL — completed rows skip, failed rows retry.
98
+ - **ssh mode**: `ssh host quiver-exec` driven verbatim; changed
99
+ payloads ride inside the protocol as inline chunk chains; `verify`
100
+ sends only u64 hashes across the wire.
101
+ - **S3 sync**: content-addressed via listed ETags vs locally computed
102
+ composite ETags; multipart, CRC64 end-to-end, moto-tested.
103
+
104
+ ## Quickstart
105
+
106
+ ```sh
107
+ apt install liburing-dev libssl-dev # build deps
108
+ make -C quiver/exec # builds quiver-exec + templates
109
+ PYTHONPATH=. python3 -m quiver.tests.run_all # 18 groups
110
+ PYTHONPATH=. python3 -m quiver.cli du /some/tree
111
+ PYTHONPATH=. python3 -m quiver.cli pack --tar /some/tree out.tar
112
+ python3 -c "from quiver import nock; print(nock.read_index('out.tar'))"
113
+ ```
114
+
115
+ ## Status
116
+
117
+ Prototype consolidated through P0–P2 of [docs/PLAN.md](docs/PLAN.md);
118
+ 18 integration test groups (GNU tar interop, oracle scan parity,
119
+ crash-resume, live-sshd remote sync, moto S3). Not yet handled:
120
+ symlinks, hardlink preservation in cp, sparse files, xattrs — see the
121
+ plan before pointing it at trees it didn't create.
122
+
123
+ ## Credits
124
+
125
+ `pupyarrow/` originates from HumeAI/wsds. `pupyarrow/fb.py` ports the
126
+ builder algorithm from google/flatbuffers (Apache-2.0) — see NOTICE.
127
+ The scanner's worker model follows HumeAI/ducl's pwalk2.
@@ -0,0 +1,100 @@
1
+ # quiver & nock
2
+
3
+ **quiver** — Unix file tooling rebuilt set-at-a-time: `scan`, `cp`, `rm`,
4
+ `du`, `sync`, `pack`. One relational planner (Polars), swappable
5
+ execution engines (io_uring / sync), Arrow record batches as the only
6
+ interchange.
7
+
8
+ **nock** — the index format: an Arrow IPC table of members parked in the
9
+ trailing slack of a host container (tar, raw, or Arrow IPC), making any
10
+ shard listable in one read and extractable by predicate.
11
+
12
+ Built for high-latency parallel filesystems (WEKA-class) and object
13
+ stores, where round-trips dominate and batching wins. On a warm local
14
+ fs, coreutils will beat it — that's expected and honest.
15
+
16
+ ## Install
17
+
18
+ ```bash
19
+ pip install quiver-tools # Linux x86_64 / aarch64 wheels, glibc >= 2.28; import name is `quiver`
20
+ ```
21
+
22
+ The wheel ships the `bvm` executor prebuilt (static zstd, libsodium, liburing, BLAKE3).
23
+ io_uring paths need a reasonably recent kernel; everything falls back to sync I/O otherwise.
24
+
25
+ From a checkout, build the executor once (pinned dependency sources, ~1-2 minutes), then
26
+ install editable; `pip install -e` leaves the in-tree `bvm` alone, `make` owns it:
27
+
28
+ ```bash
29
+ quiver/exec/build-deps.sh && make -C quiver/exec bvm # or: make -C quiver/exec bvm DEPS=/prefix/with/static/libs
30
+ pip install -e ".[dev]"
31
+ ```
32
+
33
+ ## The one idea
34
+
35
+ ```
36
+ producers planner executors
37
+ ┌──────────────┐ ┌───────────────────────┐ ┌──────────────────┐
38
+ │ fs scan (C, │ │ Polars: join, delta, │ │ fs exec (C, │
39
+ │ io_uring) │──▶│ cum_sum layout, epoch │──▶│ io_uring chains) │
40
+ │ nock footers │ │ assignment, header │ │ s3 exec (boto3) │
41
+ │ S3 listing │ │ packing — all exprs │ │ ssh remote exec │
42
+ └──────────────┘ └───────────────────────┘ └──────────────────┘
43
+ ```
44
+
45
+ Every tool is a query over stat frames that emits command frames.
46
+ `sync` is the general tool; `cp` is sync into an empty destination,
47
+ `rm` is sync from an empty source. tar headers are generated as Polars
48
+ expressions (byte-compatible with GNU tar, PAX included). Archives
49
+ carry their own index; retrofitting existing WebDataset tar shards or
50
+ Arrow shards touches zero payload bytes.
51
+
52
+ ## Highlights
53
+
54
+ - **Zero-dependency runtime**: polars + numpy. pyarrow and flatbuffers
55
+ are test-suite oracles only — `pupyarrow/` (vendored fork of
56
+ [wsds](https://github.com/HumeAI/wsds)'s reader) gained a writer and
57
+ a hand-rolled flatbuffers subset (`fb.py`, 12.6× faster metadata
58
+ walks than generated accessors).
59
+ - **One C binary** (`quiver-exec`): io_uring scanner (pwalk2 worker
60
+ model) + executor (linked SQE chains, refcount forest scheduler,
61
+ direct descriptors) + hashing (part-parallel MD5 composite ETags,
62
+ slice-by-8 CRC-64/NVME with GF(2) combine).
63
+ - **Predicate pushdown into the scanner**: path prefixes prune whole
64
+ subtrees (no getdents), basename globs skip statx for known-regular
65
+ misses.
66
+ - **Pipelined execution**: `quiver.stream` runs the executor while the
67
+ scan is still going; monotone plans overlap, pipeline breakers
68
+ (rmdir, joins, sorts) drain as a tail.
69
+ - **WAL / resume**: the command frame *is* the write-ahead log; crash
70
+ anywhere, rerun the WAL — completed rows skip, failed rows retry.
71
+ - **ssh mode**: `ssh host quiver-exec` driven verbatim; changed
72
+ payloads ride inside the protocol as inline chunk chains; `verify`
73
+ sends only u64 hashes across the wire.
74
+ - **S3 sync**: content-addressed via listed ETags vs locally computed
75
+ composite ETags; multipart, CRC64 end-to-end, moto-tested.
76
+
77
+ ## Quickstart
78
+
79
+ ```sh
80
+ apt install liburing-dev libssl-dev # build deps
81
+ make -C quiver/exec # builds quiver-exec + templates
82
+ PYTHONPATH=. python3 -m quiver.tests.run_all # 18 groups
83
+ PYTHONPATH=. python3 -m quiver.cli du /some/tree
84
+ PYTHONPATH=. python3 -m quiver.cli pack --tar /some/tree out.tar
85
+ python3 -c "from quiver import nock; print(nock.read_index('out.tar'))"
86
+ ```
87
+
88
+ ## Status
89
+
90
+ Prototype consolidated through P0–P2 of [docs/PLAN.md](docs/PLAN.md);
91
+ 18 integration test groups (GNU tar interop, oracle scan parity,
92
+ crash-resume, live-sshd remote sync, moto S3). Not yet handled:
93
+ symlinks, hardlink preservation in cp, sparse files, xattrs — see the
94
+ plan before pointing it at trees it didn't create.
95
+
96
+ ## Credits
97
+
98
+ `pupyarrow/` originates from HumeAI/wsds. `pupyarrow/fb.py` ports the
99
+ builder algorithm from google/flatbuffers (Apache-2.0) — see NOTICE.
100
+ The scanner's worker model follows HumeAI/ducl's pwalk2.
@@ -0,0 +1,97 @@
1
+ # quiver benchmark suite
2
+
3
+ Every performance knob quiver exposes has a **filesystem-dependent** optimum. The numbers
4
+ below from one cluster are not portable — on WEKA, splitting output across 32 sink files is
5
+ worth **3.9x**; on local ext4 the same change is worth **1.0x**. So measure your own storage:
6
+
7
+ ```sh
8
+ make
9
+ ./run_bench.py --out results/mysite --dir /path/on/the/fs/under/test
10
+ cat results/mysite/report.md
11
+ ```
12
+
13
+ The report ends with **recommended settings computed from your run** (`--shards`,
14
+ `--frame-mb`, `-j`), so it is directly actionable.
15
+
16
+ ## What it measures, and why quiver cares
17
+
18
+ | bench | measures | knob it informs |
19
+ |---|---|---|
20
+ | `fsbw` | write bandwidth vs threads, chunk size, **sink count**, buffered/O_DIRECT | `--shards`, `--frame-mb`, `-j` |
21
+ | `read` | read bandwidth vs threads; one file vs many | restore/verify; sharded nocksets |
22
+ | `dirspread` | the same create/stat/unlink work over 1..N **directories** | work shuffling, `rm` strategy |
23
+ | `scan` | `readdir`+`d_type` vs `lstat` vs raw `getdents64` | whether enumerate paths may stat |
24
+ | `cdc` | content-defined chunking rate + delta size for a localized edit | backup/rsync delta path |
25
+ | `hashes` | BLAKE3 / BLAKE2b / SHA-256 / SipHash | chunk-id + digest algorithm |
26
+ | `crypto` | AES-GCM / XChaCha20-Poly1305 / keyed BLAKE2b | encryption headroom |
27
+ | `cfr` | stored-frame write shapes: buffered copy vs `writev` vs `copy_file_range` | incompressible pack path |
28
+ | `rw` | read+write concurrently vs each alone, **in both I/O modes** | is the job near the limit? |
29
+
30
+ **Read `rw` before comparing any job against a ceiling.** The mode decides the answer. On WEKA,
31
+ 32 threads / 8 MB / 16 sinks:
32
+
33
+ | mode | read-only | write-only | concurrent (each) | combined |
34
+ |---|---|---|---|---|
35
+ | O_DIRECT | 4.78 | 5.14 | 2.63 | 5.26 |
36
+ | buffered + 64 MB paced writeback | 4.52 | 3.24 | 2.04 | 4.08 |
37
+
38
+ quiver writes buffered with paced writeback, so 2.04 GB/s/direction is its ceiling — and it
39
+ reaches 2.20 read / 1.78 write, i.e. 97.5% of the combined 4.08. An earlier version of this
40
+ bench measured O_DIRECT only; comparing the job against that 2.93 figure invented a 37% gap
41
+ that does not exist. O_DIRECT is *faster* for this bench's sequential per-thread writes and
42
+ *2.3x slower* for quiver's offset-reserved shared-sink writes (see `BVM_SINK_DIRECT`).
43
+ | `framecap` | compression ratio vs (zstd level x frame cap) on YOUR corpora | `--frame-cap-mb` |
44
+ | `numa` | fully-loaded compression vs CPU/memory placement | whether to pin workers |
45
+ | `multinode` | aggregate bandwidth with N nodes writing at once | how many nodes to use |
46
+ | `quiver` | end-to-end pack of a real tree, incl. quiver's own queue-occupancy verdict | everything |
47
+
48
+ Multi-node needs hostnames:
49
+
50
+ ```sh
51
+ ./run_bench.py --only multinode --nodes n1,n2,n3,n4 --node-counts 1 2 4 \
52
+ --srun-base "srun -p gpu -G 8 -N 1 -n 1 --mem=0 --time=30"
53
+ ```
54
+
55
+ HTML reports (self-contained, no external assets):
56
+
57
+ ```sh
58
+ ./report_html.py results/siteA results/siteB --index results/index.html
59
+ ```
60
+
61
+ `--tree /some/real/data` makes the end-to-end pack use your data instead of synthetic files;
62
+ that is the most representative single number.
63
+
64
+ ## Reading the results
65
+
66
+ Three traps this suite is built to avoid — all of them cost us real debugging time:
67
+
68
+ 1. **Buffered writes measure DRAM, not storage.** A buffered run "achieved" 20.9 GB/s here;
69
+ the same work with `O_DIRECT` was 6.4 GB/s, and the buffered `fsync` then stalled 17s.
70
+ Every headline number in the report is `O_DIRECT` (or durability-inclusive).
71
+ 2. **Data generation must not be the bottleneck.** `fsbw` fills one buffer with xorshift128+
72
+ *once* and reuses it hundreds of times; it prints the amortized cost (<0.001% of runtime).
73
+ 3. **Concurrent writers to one file may not scale.** This is the single biggest effect we
74
+ found, and it is invisible unless you test it explicitly.
75
+
76
+ `quiver`'s own counters also self-report a bottleneck verdict using queue occupancy
77
+ (Little's law): a bounded queue persistently *full* means its consumer is the constraint,
78
+ persistently *empty* means its producer is. The report includes that verdict verbatim.
79
+
80
+ ## Cluster use
81
+
82
+ Pass `--srun` to run each measurement on an exclusive node — otherwise co-tenants skew
83
+ results (we measured a ~2x error from a busy node):
84
+
85
+ ```sh
86
+ ./run_bench.py --out results/mysite --dir /mnt/fs/scratch \
87
+ --srun "srun -p gpu -G 8 -N 1 -n 1 --mem=0 --time=30"
88
+ ```
89
+
90
+ Multi-node aggregate bandwidth (does the fabric scale?) is a separate question; run the
91
+ suite on N nodes simultaneously against the same filesystem and sum the `fsbw` numbers.
92
+
93
+ ## Re-rendering / diffing
94
+
95
+ `results.json` is the machine-readable record; `./run_bench.py --report results/mysite`
96
+ re-renders the markdown from it. Diff two `results.json` files to compare sites, kernels,
97
+ client versions, or before/after a tuning change.
@@ -0,0 +1,13 @@
1
+ # qvm2 micro benchmarks / milestone gates
2
+
3
+ Each `drive_m*.py` is a milestone gate (see the header of `quiver/exec/qvm2.c`);
4
+ every one packs real bytes and self-verifies. `bench_scan.py <root>` runs the
5
+ three-leg scan A/B (bvm cold, qvm2 I_SCAN, bvm warm — same process, back to back;
6
+ only same-run numbers are comparable).
7
+
8
+ Build first: `make -C quiver/exec bvm qvm2` (override ZSTD/URING/BLAKE3 to your
9
+ static-lib prefixes). Gates create their trees in the CWD — run from a scratch dir:
10
+
11
+ python bench/qvm2/drive_m6.py # cross-engine pack/unpack gate
12
+ python bench/qvm2/drive_m5.py <tree> # I_SCAN leaf vs os.walk truth
13
+ python bench/qvm2/bench_scan.py <tree> # the A/B
@@ -0,0 +1,82 @@
1
+ """Hatchling build hook -- compiles the bvm executor into the wheel.
2
+
3
+ bvm (quiver/exec/bvm.c) is a plain C program linked statically against zstd, libsodium,
4
+ liburing and BLAKE3 (see quiver/exec/build-deps.sh and the Makefile), so the wheel's only
5
+ runtime requirement is glibc. Linux only: elsewhere the wheel is pure Python with no executor.
6
+
7
+ Environment:
8
+ QUIVER_SKIP_BVM=1 skip the build; pure py3-none-any wheel
9
+ QUIVER_WHEEL_PLAT=TAG platform tag to claim, e.g. manylinux_2_28_x86_64. release.yml builds
10
+ inside that manylinux container; the default, linux_<machine>, is the
11
+ honest tag for a wheel built on an arbitrary host.
12
+ DEPS=<prefix> passed through to make (static deps prefix; default quiver/exec/deps)
13
+
14
+ A manylinux claim is checked against the binary's GLIBC_* symbol versions: a bvm that needs a
15
+ newer glibc than the tag promises fails the build instead of failing on users' machines.
16
+ """
17
+
18
+ import os
19
+ import platform
20
+ import re
21
+ import subprocess
22
+ import sys
23
+
24
+ from hatchling.builders.hooks.plugin.interface import BuildHookInterface
25
+
26
+
27
+ def _max_glibc(binary: str):
28
+ """Highest GLIBC_x.y version the binary references, via objdump -T; None if unknown."""
29
+ try:
30
+ out = subprocess.run(["objdump", "-T", binary], capture_output=True, text=True,
31
+ check=True).stdout
32
+ except (OSError, subprocess.CalledProcessError):
33
+ return None
34
+ versions = {tuple(int(x) for x in v.split(".")) for v in re.findall(r"GLIBC_(\d+\.\d+)", out)}
35
+ return max(versions) if versions else None
36
+
37
+
38
+ class CustomBuildHook(BuildHookInterface):
39
+ def initialize(self, version, build_data):
40
+ if platform.system() != "Linux" or os.environ.get("QUIVER_SKIP_BVM"):
41
+ return
42
+
43
+ exec_dir = os.path.join(self.root, "quiver", "exec")
44
+ binary = os.path.join(exec_dir, "bvm")
45
+ if version == "editable":
46
+ # `pip install -e .`: the package runs from the checkout, so default_bvm() finds
47
+ # whatever ./bvm is there. Don't rebuild it behind the developer's back (it may be
48
+ # the binary running jobs depend on); just say so if it is missing.
49
+ if not os.path.isfile(binary):
50
+ print(f"hatch_build: no {binary}; run quiver/exec/build-deps.sh && make -C "
51
+ f"quiver/exec bvm", file=sys.stderr)
52
+ return
53
+ # -B: never ship a stale bvm left over from an earlier/other build. PYTHON: the
54
+ # ipc_gen.h generator runs with the build environment's interpreter (it has numpy).
55
+ env = dict(os.environ, PYTHON=sys.executable)
56
+ subprocess.check_call(["make", "-C", exec_dir, "-B", "bvm"], env=env)
57
+ if not os.path.isfile(binary):
58
+ raise SystemExit(f"hatch_build: make did not produce {binary}")
59
+
60
+ build_data.setdefault("force_include", {})[binary] = "quiver/exec/bvm"
61
+ build_data["pure_python"] = False
62
+ build_data["infer_tag"] = True
63
+
64
+ machine = platform.machine()
65
+ plat = os.environ.get("QUIVER_WHEEL_PLAT") or f"linux_{machine}"
66
+ m = re.fullmatch(r"manylinux_(\d+)_(\d+)_(\w+)", plat)
67
+ if m:
68
+ if m[3] != machine:
69
+ raise SystemExit(f"hatch_build: QUIVER_WHEEL_PLAT={plat} but this host is {machine}")
70
+ claimed = (int(m[1]), int(m[2]))
71
+ needed = _max_glibc(binary)
72
+ if needed is None:
73
+ print("hatch_build: objdump unavailable; cannot verify the manylinux claim",
74
+ file=sys.stderr)
75
+ elif needed > claimed:
76
+ raise SystemExit(
77
+ f"hatch_build: bvm needs GLIBC_{needed[0]}.{needed[1]} but the wheel would "
78
+ f"claim {plat}. Build inside the quay.io/pypa/{plat} container (release.yml) "
79
+ f"or unset QUIVER_WHEEL_PLAT for a plain linux_{machine} wheel.")
80
+ else:
81
+ print(f"hatch_build: bvm needs GLIBC_{needed[0]}.{needed[1]} <= {plat}: ok")
82
+ build_data["tag"] = f"py3-none-{plat}"