david-streamlio opened a new pull request, #81:
URL: https://github.com/apache/pulsar-connectors/pull/81

   Fixes #80
   
   ### Motivation
   
   `FileListingThread` records a file as offered **before** checking that the 
work queue accepted it:
   
   ```java
   for (File f : listing) {
       if (alreadyOffered.add(f)) {
           workQueue.offer(f);      // return value ignored
       }
   }
   ```
   
   Ignoring `offer()`'s result is safe only while `workQueue` is unbounded, 
which it is today. The moment it is bounded, `offer()` returns `false` when the 
queue is full — the file is already in `alreadyOffered`, and the prune step 
only forgets files that have **left the disk**. The file is never enqueued, 
never retried, and never processed.
   
   This is not theoretical: #28 ("Prevent OOM in FileSource by bounding 
internal queues") does exactly that. It is green and conflicts with nothing, 
because it touches different files — so the loss would have merged silently. 
Worse, the failure triggers precisely under the backlog #28 exists to survive.
   
   Reproduced against current master with a bounded `workQueue` of capacity 1 
and 3 files on disk:
   
   ```
   WORKQUEUE_SIZE=1
   FILES_ON_DISK=3
   AFTER_DRAIN_REOFFERED=0     <-- the other 2 files are never re-offered
   ```
   
   The assumption is mine, introduced in #41. This PR removes it so #28 can 
land safely.
   
   ### Modifications
   
   Track a file only once the queue has accepted it:
   
   ```java
   for (File f : listing) {
       if (!alreadyOffered.contains(f) && workQueue.offer(f)) {
           alreadyOffered.add(f);
       }
   }
   ```
   
   A rejected file is simply retried on the next listing pass — the lister 
slows to the consumer's rate rather than discarding work, which is the 
back-pressure behavior a bounded queue is meant to produce. The `keepFile=true` 
branch re-checks `workQueue.contains(f)` each pass and is unaffected.
   
   ### Tests
   
   `FileListingThreadBoundedQueueTest` bounds the queue itself, so it holds 
regardless of what `FileSource` constructs today and guards the invariant ahead 
of #28:
   
   - `rejectedFilesAreRetriedOnceTheQueueDrains` — 3 files, queue capacity 1; 
every file must reach the queue as it drains.
   - `filesAreNotOfferedTwiceWhileTheyRemainOnDisk` — the exactly-once property 
from #41 must survive the new retry path.
   
   ### Verifying this change
   
   The regression test genuinely catches the bug. Reverting `FileListingThread` 
to master's version:
   
   ```
   rejectedFilesAreRetriedOnceTheQueueDrains FAILED
       org.awaitility.core.ConditionTimeoutException: Condition ... was not 
fulfilled within 30 seconds.
   filesAreNotOfferedTwiceWhileTheyRemainOnDisk PASSED
   ```
   
   The second test passes either way, as it should — it pins the property the 
bug never broke.
   
   With the fix applied, the full `:file:test` suite is green (`BUILD 
SUCCESSFUL in 1m 58s`), including the previously flaky 
`ProcessedFileThreadTest`.
   
   ### Suggested merge order
   
   1. this PR
   2. #28 — rebase; its bounded queues then cannot drop files


-- 
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