|
|
@@ -890,6 +890,7 @@ public class RecoverySourceHandler {
|
|
|
new MultiChunkTransfer<>(logger, threadPool.getThreadContext(), listener, maxConcurrentFileChunks, Arrays.asList(files)) {
|
|
|
|
|
|
final Deque<byte[]> buffers = new ConcurrentLinkedDeque<>();
|
|
|
+ final AtomicInteger liveBufferCount = new AtomicInteger(); // only used in assertions to verify proper recycling
|
|
|
IndexInput currentInput = null;
|
|
|
long offset = 0;
|
|
|
|
|
|
@@ -905,11 +906,15 @@ public class RecoverySourceHandler {
|
|
|
assert Transports.assertNotTransportThread("read file chunk");
|
|
|
cancellableThreads.checkForCancel();
|
|
|
final byte[] buffer = Objects.requireNonNullElseGet(buffers.pollFirst(), () -> new byte[chunkSizeInBytes]);
|
|
|
+ assert liveBufferCount.incrementAndGet() > 0;
|
|
|
final int toRead = Math.toIntExact(Math.min(md.length() - offset, buffer.length));
|
|
|
currentInput.readBytes(buffer, 0, toRead, false);
|
|
|
final boolean lastChunk = offset + toRead == md.length();
|
|
|
final FileChunk chunk = new FileChunk(md, new BytesArray(buffer, 0, toRead), offset, lastChunk,
|
|
|
- () -> buffers.addFirst(buffer));
|
|
|
+ () -> {
|
|
|
+ assert liveBufferCount.decrementAndGet() >= 0;
|
|
|
+ buffers.addFirst(buffer);
|
|
|
+ });
|
|
|
offset += toRead;
|
|
|
return chunk;
|
|
|
}
|
|
|
@@ -932,6 +937,12 @@ public class RecoverySourceHandler {
|
|
|
public void close() throws IOException {
|
|
|
IOUtils.close(currentInput, storeRef);
|
|
|
}
|
|
|
+
|
|
|
+ @Override
|
|
|
+ protected boolean assertOnSuccess() {
|
|
|
+ assert liveBufferCount.get() == 0 : "leaked [" + liveBufferCount + "] buffers";
|
|
|
+ return true;
|
|
|
+ }
|
|
|
};
|
|
|
resources.add(multiFileSender);
|
|
|
temporaryStoreRef = null; // now owned by multiFileSender, tracked in resources, so won't be leaked
|