Skip to content

feat(py-client): implement "many" api for batch requests#546

Draft
matt-codecov wants to merge 1 commit into
mainfrom
matt/py-client-many
Draft

feat(py-client): implement "many" api for batch requests#546
matt-codecov wants to merge 1 commit into
mainfrom
matt/py-client-many

Conversation

@matt-codecov

@matt-codecov matt-codecov commented Jul 8, 2026

Copy link
Copy Markdown
Contributor

#277 / #478 implemented the many API in the Rust client which uses Objectstore's batch endpoint. This PR ports it over to Python.

Closes FS-330

@linear-code

linear-code Bot commented Jul 8, 2026

Copy link
Copy Markdown

FS-361

FS-330

@codecov

codecov Bot commented Jul 8, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 86.55462% with 48 lines in your changes missing coverage. Please review.
✅ Project coverage is 87.22%. Comparing base (82f8bdf) to head (9ac7967).

Files with missing lines Patch % Lines
clients/python/src/objectstore_client/many.py 86.49% 47 Missing ⚠️
clients/python/src/objectstore_client/client.py 88.88% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #546      +/-   ##
==========================================
- Coverage   87.22%   87.22%   -0.01%     
==========================================
  Files          91       92       +1     
  Lines       14487    14843     +356     
==========================================
+ Hits        12637    12947     +310     
- Misses       1850     1896      +46     
Components Coverage Δ
Rust Backend 92.09% <ø> (ø)
Rust Client 79.89% <ø> (ø)
Python Client 88.00% <86.55%> (-0.87%) ⬇️

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@jan-auer

jan-auer commented Jul 9, 2026

Copy link
Copy Markdown
Member

Please see also the now closed #419. It's gone out of sync with main, so it's better to carry on here.

The prior PR focused on streaming as much as possible and had gone through a round of feedback with the Python SDK maintainers for the public API. Also, there's configuration for concurrency that uses an optional thread pool executor. I hope there are some parts we could leverage from that.

@matt-codecov

Copy link
Copy Markdown
Contributor Author

bugbot run
@sentry review

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 2 potential issues.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Want reviews to match your repository better? Bugbot Learning can learn team-specific rules from PR activity. A team admin can enable Learning in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 9ac7967. Configure here.

try:
return self._send_batch(ops)
except Exception as exc:
return [_error_result_for(op, exc) for op in ops]

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Batch parse error fails chunk

Medium Severity

When correlating a successful batch HTTP response, an exception while handling one multipart part (for example NotImplementedError from _maybe_decompress on a GET with non-zstd compression, or header parsing in Metadata.from_headers) aborts _send_batch. _run_batch then assigns that same exception to every operation in the chunk, so successful server-side ops are reported as failed. Individual many paths return one error per op instead.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 9ac7967. Configure here.

**not** guaranteed to be in the order the operations were enqueued.
"""
individual_concurrency = self._max_individual_concurrency or DEFAULT_INDIVIDUAL_CONCURRENCY
batch_concurrency = self._max_batch_concurrency or DEFAULT_BATCH_CONCURRENCY

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Zero concurrency ignores setting

Low Severity

ManyBuilder.send() resolves concurrency limits with self._max_individual_concurrency or DEFAULT_INDIVIDUAL_CONCURRENCY (and the batch equivalent). A caller who sets either limit to 0 has that value treated as unset, so defaults (5 and 3) apply instead of a minimum of one worker, unlike the Rust client’s .max(1) clamp.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 9ac7967. Configure here.

Comment on lines +525 to +527
if key is None:
if is_error:
key = "<unknown>"

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Bug: A successful keyless insert is incorrectly reported as an ErrorResult if the server returns a success status but omits the x-sn-batch-operation-key header.
Severity: MEDIUM

Suggested Fix

Make the client code more robust to server contract violations. For a successful response (2xx status) that is missing the x-sn-batch-operation-key header, consider returning a PutResult with a sentinel or None key instead of an ErrorResult. This would correctly represent the operation's success while indicating the key is unknown, preventing a successful write from being reported as a failure.

Prompt for AI Agent
Review the code at the location below. A potential bug has been identified by an AI
agent. Verify if this is a real issue. If it is, propose a fix; if not, explain why it's
not valid.

Location: clients/python/src/objectstore_client/many.py#L525-L527

Potential issue: The client-side parsing logic for batch operations assumes that a
successful response for a keyless insert will always contain the
`x-sn-batch-operation-key` header with the server-generated key. If the server returns a
success status (e.g., 2xx) but omits this header due to a bug, proxy issue, or future
change, the client code incorrectly interprets this as a failure. Instead of returning a
`PutResult`, it constructs and returns an `ErrorResult` with a "missing header" message.
This misrepresents a successful data insertion as an error to the caller, creating a
discrepancy between the client's state and the actual state on the server.

Did we get this right? 👍 / 👎 to inform future reviews.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants