Repository navigation
[python] Open one remote stream for concurrent cached reads - #10895
Open
Stephen0421 wants to merge 1 commit into
Open
Stephen0421 wants to merge 1 commit into
Stephen0421 wants to merge 1 commit into
Conversation
CachingInputStream.read_at is documented as thread-safe and is called concurrently by the parallel readers, but _get_remote_stream tested and assigned _stream without a lock. Concurrent cache misses each opened a remote stream while only the last one was retained, so close() left the rest open. Reads stayed correct because _io_lock still serialized the actual I/O; the cost was leaked object store connections. Guard the lazy open with a dedicated lock and re-check inside it. The lock is separate from _io_lock, which serializes seek and read on an already open stream, so opening does not block in-flight reads. With 16 threads reading disjoint cold offsets, the stream count drops from 16 to 1 and close() leaves none behind.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Purpose
CachingInputStream.read_atis documented as thread-safe, but the lazy open behind it was not:On a cold cache, several threads reading the same stream can all observe
self._stream is Noneand each open a remote stream. Only the last assignment is retained, soclose()closes one and leaks the rest.This is reachable as soon as local cache is enabled, with no further options.
local-cache.enableddefaults tofalse, so a stock table never enters this path. When the cache is on,global-indexis already in the default whitelistmeta,global-index, and_read_parallelismdefaults to 4 for remote schemes.PaimonVindexInput.pread_manythen fansranges onto one shared
CachingInputStream.Read results were never wrong — the actual I/O is still serialized by
_io_lockor executed throughpread. What leaked was object store connections.Java already guards the same lazy open in
CachingSeekableInputStream.getRemoteStream, for the same reason:The open uses its own
_open_lock._get_remote_streamruns before_io_lock, and_io_lockonly coversseek+readafter a stream already exists. Folding the open into_io_lockwould also be race-free, but that lock means the cursor is in use, and there is no stream yet while it is being opened.CachingSeekableInputStreammakes the same split: aprivate init lock, distinct from
synchronized (stream).With 16 threads reading disjoint cold offsets, the number of opened streams drops from 16 to 1, and
close()leaves none behind.Not addressed here
Two related gaps are left for separate changes, to keep this fix focused:
close()racing an in-flight open. Java additionally tracks aclosedflag and has the opening thread re-check it after publishing, closing the stream it just opened ifclose()won. PyPaimon has no such flag. The window is narrower here becausePaimonVindexInput.closeshuts its executor down withwait=True, draining in-flight reads beforeVindexReaderResources.closecloses the underlying stream. Adding the flag changes the post-close read behaviour, which deserves its own review._get_file_sizeis also unguarded. Concurrent callers can duplicate thestat, but it neither leaks connections nor returns wrong data.Tests
CachingInputStreamTest.test_concurrent_read_at_opens_one_remote_streamruns 16 threads over disjoint cold offsets and asserts exactly one remote stream is opened, that every read returns the correct bytes, and thatclose()leaves no stream open.The test fails on the unfixed code (16 streams opened) and passes with the fix.