pjfanning opened a new pull request, #1930:
URL: https://github.com/apache/pekko-connectors/pull/1930

   `DiskBuffer` writes each multipart chunk to a temp file and deletes it 
through a countdown:
   
   ```scala
   val deleteCounter = new AtomicInteger(maxMaterializations)
   val src = FileIO.fromPath(path.toPath, 65536).mapMaterializedValue { f =>
     if (deleteCounter.decrementAndGet() <= 0) f.onComplete(_ => path.delete())
     NotUsed
   }
   ```
   
   `maxMaterializations` is `(maxRetriesPerChunk + 1) * 2` — every possible 
upload attempt times two materializations per attempt. So the file is deleted 
only when *every* retry has been used. A chunk that uploads successfully first 
time materializes twice out of a budget of eight, the counter never reaches 
zero, and the file stays on disk until the JVM exits via the `deleteOnExit` 
registered at line 68.
   
   For a long-running service uploading through the disk buffer that means 
every 5MB+ chunk it has ever sent is still on disk, plus a permanent entry in 
the JVM-global `DeleteOnExitHook` set, which is never pruned.
   
   ### Changes
   
   * `Chunk` gains `dispose()`, a no-op by default; `DiskChunk` carries its 
`File` and deletes it.
   * `chunkAndRequest` calls `dispose()` in the branch of the `RetryFlow` 
decision function that returns `None`. That is the point at which the chunk is 
definitively finished with: the request has been sent and the result is not 
going to be retried. The retry branch leaves the file alone, since a retry 
re-materializes the source.
   * `DiskBuffer` deletes its temp file in `postStop` when it never emitted a 
chunk, so a failure or cancellation before emission cleans up immediately 
instead of at JVM exit.
   
   The existing countdown stays as-is. It now covers the one case disposal does 
not: retries exhausted, where `RetryFlow` emits the final failure without 
asking the decision function again — and that is exactly when the counter 
reaches zero.
   
   `deleteOnExit` is deliberately kept. It is the only remaining net for a 
chunk that is emitted and then abandoned mid-flight, and dropping it would 
trade a small unbounded registration for leaked multi-megabyte files on 
cancelled uploads. With eager disposal the files themselves no longer 
accumulate during a run.
   
   `MemoryChunk` inherits the no-op, so the memory buffer path and 
`chunkAndRequestWithContext` (memory-only) are unaffected. `buffer = "memory"` 
is the default, so this only affects users who opt into `DiskBufferType` or 
`MemoryAndDiskBufferType`.
   
   ### Testing
   
   Two cases in `DiskBufferSpec`: disposal releases the file after a 
materialization count well below the retry budget, and a failure before 
emission leaves nothing behind. The existing "after N materializations" test 
still passes unchanged.
   
   `s3/test` passes — 169 tests, 20 suites. `MinioS3IntegrationSpec` aborts in 
my environment because Docker is not available, not from this change. 
`checkCodeStyle` is clean.
   
   🤖 Generated with [Claude Code](https://claude.com/claude-code)


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


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to