Skip to content

True minio seek - #169

Merged
asuiu merged 2 commits into
masterfrom
true-minio-seek
Jul 22, 2026
Merged

True minio seek#169
asuiu merged 2 commits into
masterfrom
true-minio-seek

Conversation

@asuiu

@asuiu asuiu commented Jul 22, 2026

Copy link
Copy Markdown
Member

Title

<type>: <short summary>

Ticket / Task

  • [Trello|Jira] link: <URL>

What & Why

"Builds X so that Y can Z" – two sentences max.

Scope

  • New feature
  • Bug fix
  • Refactor / tech-debt
  • Test / tooling only

Checklist (self-review)

See Section 5.

Risk / Impact

  • Latency critical path? ☐ Yes ☐ No
  • External API contract change? ☐ Yes ☐ No
  • Migration steps required? ☐ Yes ☐ No

Screenshots / Logs / Benchmarks

(only if relevant)

Author Self-Review Checklist

  • Single Ticket — PR addresses only one business requirement / Trello card.
  • Minimal Diff — no unrelated refactors, commented-out code, or debug prints.
  • Compiles & Tests Passpytest -q / mvn test clean locally.
  • No Dead Code — every new unit is called or covered by tests.
  • Naming & Clarity — identifiers are self-explanatory; no overloaded meanings.
  • Docs Updated — README, wiki, or docstrings updated where behaviour changed.
  • Performance Tagged — if touching hot path, attach micro-benchmarks or profiler diff.
  • Config & Secrets — no plaintext credentials; configs externalised.
  • Rollback Ready — change can be reverted with git revert without dependency hell.
  • Checklist Acknowledged — I would merge this myself if I were the reviewer.
  • LLM Review — PR has been reviewed by the approved LLM tool and suggestions addressed.
    • IDE Type Checker — PyCharm/Pylance recommendations were addressed where applicable.
    • Optional mypy — (Recommended) Ran python/scripts/run_mypy.bat locally with no critical errors

Resources:

@coderabbitai

coderabbitai Bot commented Jul 22, 2026

Copy link
Copy Markdown
📝 Walkthrough

Walkthrough

MinIO object downloads now use a lock-protected ranged reader with optional buffering, seeking, ETag conditions, and version support. MinioBucket centralizes stream creation, while versioned access delegates to the same path. Stream documentation and tests now cover readability, seeking, readinto, tail reads, and Parquet access across bucket implementations. Package metadata, formatting limits, and cache ignores were also updated.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant MinioBucket
  participant MinioObjectStream
  participant Minio
  Client->>MinioBucket: get_object_stream(object_name)
  MinioBucket->>MinioObjectStream: open configured stream
  Client->>MinioObjectStream: seek and read
  MinioObjectStream->>Minio: get_object(offset, length, If-Match)
  Minio-->>Client: requested object bytes
Loading

Suggested reviewers: amaximciuc

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Description check ⚠️ Warning The description is just the template with placeholders; it doesn't provide the required ticket, scope, or what/why details. Fill in the template with the actual title, ticket link, two-sentence what/why, scope, risk/impact, and relevant checklist items.
Docstring Coverage ⚠️ Warning Docstring coverage is 9.52% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title is concise and directly reflects the main change: adding true seek support to MinIO-backed storage.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

Warning

Billing warning: we have not been able to collect payment for this subscription for more than 72 hours. Please update the payment method or pay any pending invoices in Billing to avoid service interruption.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai
coderabbitai Bot requested a review from amaximciuc July 22, 2026 10:09

@coderabbitai coderabbitai 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.

Actionable comments posted: 2

🧹 Nitpick comments (3)
python/tests/test_minio_bucket.py (1)

34-75: 🩺 Stability & Availability | 🔵 Trivial

Run the complete test matrix from python/.

These additions depend on the configured MinIO service. Before merge, run both unit and integration suites with python/ as the working directory and confirm the suite meets the required runtime target.

As per coding guidelines: “Working directory must be python/ before running tests” and “run both unit and integration tests before task completion.”

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@python/tests/test_minio_bucket.py` around lines 34 - 75, Run the complete
test matrix from the python/ working directory, including both unit and
integration suites against the configured MinIO service, and verify the combined
suite meets the required runtime target before completing the change.

Source: Coding guidelines

python/tests/bucket_tester.py (2)

156-174: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Expose readinto in the typed stream contract.

The # type: ignore[attr-defined] is needed even though IBucket.get_object_stream() documents readinto(). Add readinto to the ObjectStream protocol/type alias in python/bucketbase/ibucket.py, then remove this suppression so static checks enforce the same API the test validates.

As per coding guidelines: “Behavioral Contracts: ensure interface implementations truly fulfill the interface contract without special handling in client code.”

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@python/tests/bucket_tester.py` around lines 156 - 174, The typed ObjectStream
contract does not expose the readinto method used by get_object_stream
consumers. Update the ObjectStream protocol/type alias in ibucket.py to declare
readinto, then remove the type: ignore[attr-defined] suppression from
test_get_object_stream_is_seekable so static typing validates the interface
directly.

Source: Coding guidelines


156-174: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Test the failure boundaries, not just the launch path.

This shared test covers successful seeks only. Add deterministic checks for invalid whence, negative resulting positions, EOF reads, and partial readinto() results so every backend is tested against the edge behavior too.

As per coding guidelines: “New code must have comprehensive unit tests covering all paths, edge cases, and error scenarios.”

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@python/tests/bucket_tester.py` around lines 156 - 174, Extend
test_get_object_stream_is_seekable with deterministic assertions for invalid
whence values and seeks producing negative positions, verifying the expected
errors. Also cover reads at EOF and readinto() when fewer bytes remain than the
destination, asserting the returned byte counts and contents while preserving
the existing successful-seek checks.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@python/bucketbase/minio_bucket.py`:
- Around line 82-108: Update the read method to catch MinIO’s
precondition-failure S3Error from get_object and translate it into one of the
documented exceptions exposed by get_object_stream/IBucket.get_object_stream,
preserving response cleanup and existing read behavior for other outcomes. Add
coverage for an object being overwritten after the captured etag so the
concurrent-modification path verifies the translated exception.
- Around line 29-41: Update MinioBucket.get_object() to download directly with
the Minio client instead of constructing _MinioRangeReader and triggering
stat_object(). Keep _MinioRangeReader usage confined to get_object_stream(),
preserving the existing seekable-stream behavior there.

---

Nitpick comments:
In `@python/tests/bucket_tester.py`:
- Around line 156-174: The typed ObjectStream contract does not expose the
readinto method used by get_object_stream consumers. Update the ObjectStream
protocol/type alias in ibucket.py to declare readinto, then remove the type:
ignore[attr-defined] suppression from test_get_object_stream_is_seekable so
static typing validates the interface directly.
- Around line 156-174: Extend test_get_object_stream_is_seekable with
deterministic assertions for invalid whence values and seeks producing negative
positions, verifying the expected errors. Also cover reads at EOF and readinto()
when fewer bytes remain than the destination, asserting the returned byte counts
and contents while preserving the existing successful-seek checks.

In `@python/tests/test_minio_bucket.py`:
- Around line 34-75: Run the complete test matrix from the python/ working
directory, including both unit and integration suites against the configured
MinIO service, and verify the combined suite meets the required runtime target
before completing the change.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 7e514c9b-9bc4-4937-b057-4f7163363e38

📥 Commits

Reviewing files that changed from the base of the PR and between 5389fb5 and 4e4f8bb.

⛔ Files ignored due to path filters (1)
  • python/uv.lock is excluded by !**/*.lock
📒 Files selected for processing (14)
  • .gitignore
  • python/bucketbase/ibucket.py
  • python/bucketbase/minio_bucket.py
  • python/bucketbase/versioned_minio_bucket.py
  • python/pyproject.toml
  • python/tests/bucket_tester.py
  • python/tests/test_append_only_fs_bucket.py
  • python/tests/test_backup_multi_bucket.py
  • python/tests/test_fs_bucket.py
  • python/tests/test_ibucket.py
  • python/tests/test_integrated_cached_immutable_bucket.py
  • python/tests/test_memory_bucket.py
  • python/tests/test_minio_bucket.py
  • python/tests/test_versioned_minio_bucket.py

Comment on lines +29 to +41
def __init__(self, minio_client: Minio, bucket_name: str, object_name: str, version_id: str | None = None) -> None:
super().__init__()
metadata = minio_client.stat_object(bucket_name, object_name, version_id=version_id)
self._minio_client = minio_client
self._bucket_name = bucket_name
self._object_name = object_name
self._version_id = version_id
if metadata.size is None:
raise IOError(f"Minio returned no size for {object_name}")
self._size = metadata.size
self._etag = metadata.etag
self._position = 0
self._lock = RLock()

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🚀 Performance & Scalability | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

set -euo pipefail

# Locate the target file and relevant symbols.
git ls-files 'python/bucketbase/minio_bucket.py'
ast-grep outline python/bucketbase/minio_bucket.py --view expanded || true

printf '\n--- search ---\n'
rg -n "def get_object|def get_object_stream|stat_object|get_object_stream\(" python/bucketbase/minio_bucket.py python/bucketbase -S

printf '\n--- file slice 1 ---\n'
sed -n '1,180p' python/bucketbase/minio_bucket.py

printf '\n--- file slice 2 ---\n'
sed -n '180,340p' python/bucketbase/minio_bucket.py

Repository: eSAMTrade/bucketbase

Length of output: 18913


🏁 Script executed:

set -euo pipefail

# Find all call sites that rely on MinioBucket reads to understand blast radius.
rg -n "\.get_object_stream\(|\.get_object\(" python -S

# Inspect the reported callers around the relevant lines.
for f in $(rg -l "get_object_stream\(|get_object\(" python -S); do
  printf '\n=== %s ===\n' "$f"
  sed -n '1,260p' "$f"
done

Repository: eSAMTrade/bucketbase

Length of output: 50377


Keep a direct get_object() fast path MinioBucket.get_object() now always routes through _MinioRangeReader, which does a stat_object() before the download. That turns the common full-read path into HEAD + GET instead of a single GET. Reserve the seekable reader for get_object_stream().

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@python/bucketbase/minio_bucket.py` around lines 29 - 41, Update
MinioBucket.get_object() to download directly with the Minio client instead of
constructing _MinioRangeReader and triggering stat_object(). Keep
_MinioRangeReader usage confined to get_object_stream(), preserving the existing
seekable-stream behavior there.

Comment on lines +82 to +108
def read(self, size: int = -1) -> bytes:
self._check_closed()
with self._lock:
remaining = max(0, self._size - self._position)
length = remaining if size is None or size < 0 else min(size, remaining)
if length == 0:
return b""

request_headers: DictType | None = {"If-Match": f'"{self._etag}"'} if self._etag else None
response = self._minio_client.get_object(
self._bucket_name,
self._object_name,
offset=self._position,
length=length,
request_headers=request_headers,
version_id=self._version_id,
)
try:
data = response.read()
finally:
response.close()
response.release_conn()

if len(data) != length:
raise IOError(f"Expected {length} bytes from {self._object_name} at offset {self._position}, but received {len(data)}")
self._position += length
return data

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

Concurrent-modification failures escape as raw, undocumented S3Error.

The If-Match header ties every ranged read() to the etag captured at stat_object() time. If the object is overwritten mid-stream, MinIO will 412 and minio.error.S3Error (PreconditionFailed) will bubble straight out of read() — uncaught here, and not one of the exceptions get_object_stream()/IBucket.get_object_stream docstring promises (only FileNotFoundError/ValueError are documented). No test exercises this path either. Worth catching it and translating to something documented (or at least covering it with a test) so callers aren't surprised by a raw S3 error leaking through a supposedly stable seek/read contract.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@python/bucketbase/minio_bucket.py` around lines 82 - 108, Update the read
method to catch MinIO’s precondition-failure S3Error from get_object and
translate it into one of the documented exceptions exposed by
get_object_stream/IBucket.get_object_stream, preserving response cleanup and
existing read behavior for other outcomes. Add coverage for an object being
overwritten after the captured etag so the concurrent-modification path verifies
the translated exception.

@asuiu
asuiu merged commit 72587cf into master Jul 22, 2026
22 checks passed
@asuiu
asuiu deleted the true-minio-seek branch July 22, 2026 10:19
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.

1 participant