shyjsarah commented on code in PR #720:
URL: https://github.com/apache/paimon-rust/pull/720#discussion_r3801452996


##########
crates/paimon/src/vindex/range_reader.rs:
##########
@@ -201,41 +255,74 @@ impl VindexFileReader {
     }
 
     fn fetch_exact(&self, range: Range<u64>) -> io::Result<Bytes> {
-        let mut results = 
self.fetch_range_batch(std::slice::from_ref(&range))?;
-        Ok(results.pop().expect("one requested range"))
+        let mut result = None;
+        self.fetch_range_batch(std::slice::from_ref(&range), |_, data| {
+            result = Some(data);

Review Comment:
   **[Major] The single-range path releases the response permit before the 
payload is actually copied.**
   
   `fetch_range_batch` keeps the response permit only until `consume` returns, 
but this closure moves the owned `Bytes` into `result` and returns immediately. 
`read_one` performs `copy_from_slice` afterwards (and may retain the same 
`Bytes` in the scalar cache), when the response permit has already been 
released. Consequently, cloned readers using single-range `pread` can hold more 
than the intended global `2C` responses at once; the existing bounded-response 
test blocks inside the callback and therefore does not cover this escape path.
   
   Could we either copy into the caller buffer inside the protected callback, 
or return an RAII value that binds `Bytes` to its `OwnedSemaphorePermit` until 
the copy/cache handoff finishes? A regression test with `C=1` and three clones 
doing single-range reads would make the intended bound explicit.



##########
crates/paimon/src/vindex/range_reader.rs:
##########
@@ -257,24 +344,43 @@ impl VindexFileReader {
                             .returned_bytes
                             .fetch_add(data.len() as u64, Ordering::Relaxed);
                     }
-                    Ok(data)
+                    sender
+                        .send(Ok((index, data, response_permit)))
+                        .await
+                        .map_err(|_| io::Error::other("vindex range read 
receiver closed"))
                 }
             }))
-            .await;
-            let _ = sender.send(fetched);
+            .buffer_unordered(response_limit);

Review Comment:
   **[Major] The current two-stage admission does not reliably preserve the 
cloned-reader fairness asserted by the test.**
   
   Each batch independently polls up to `2C` work items, first acquiring a 
response permit and only then entering the I/O semaphore. Ordering can be lost 
between those two queues: a clone may obtain/wait for response admission but 
not reach the I/O queue before the original batch's refill work does. I 
reproduced `cloned_reader_is_not_queued_behind_an_entire_batch` failing once in 
the full range-reader suite, while subsequent runs passed. The test hook also 
signals before `acquire_owned()` is known to be `Pending`, so it does not 
establish the ordering assumed by the assertion.
   
   Please make the test notify only after the response acquire is actually 
pending. If cross-reader fairness is a required contract, admission needs to 
preserve ordering across the response→I/O transition (for example through a 
shared scheduler/queue); otherwise the test and documentation should state the 
weaker bounded-delay guarantee rather than assert a specific start order.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to