Skip to content

Commit db10384

Browse files
committed
Test deferred close caller fallbacks
Add deterministic gray-box coverage for BackgroundDrainer and the cursor WebSocket send loop. The tests stall SegmentManager quiescence, verify callers return without releasing the slot lock, and confirm deferred cleanup releases the slot after quiescence resumes. Add narrow test-only seams for engine injection and I/O thread joining so the tests avoid a live server and wait for the complete exit path.
1 parent c6f8a33 commit db10384

3 files changed

Lines changed: 372 additions & 2 deletions

File tree

core/src/main/java/io/questdb/client/cutlass/qwp/client/sf/cursor/BackgroundDrainer.java

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,7 @@ public final class BackgroundDrainer implements Runnable {
108108
private final String slotPath;
109109
/** Latest known {@code engine.ackedFsn()}; published for visibility. */
110110
private volatile long ackedFsn = -1L;
111+
private CursorSendEngine engineForTesting;
111112
private volatile String lastErrorMessage;
112113
/**
113114
* Optional observer for durable-ack-unavailable transients and the
@@ -530,8 +531,10 @@ public void run() {
530531
// holds it, the engine constructor throws and we exit silently
531532
// (no .failed sentinel — contention is expected, not an error).
532533
try {
533-
engine = new CursorSendEngine(slotPath, segmentSizeBytes,
534-
sfMaxTotalBytes, CursorSendEngine.DEFAULT_APPEND_DEADLINE_NANOS);
534+
engine = engineForTesting != null
535+
? engineForTesting
536+
: new CursorSendEngine(slotPath, segmentSizeBytes,
537+
sfMaxTotalBytes, CursorSendEngine.DEFAULT_APPEND_DEADLINE_NANOS);
535538
} catch (IllegalStateException t) {
536539
String msg = t.getMessage();
537540
if (msg != null && msg.contains("already in use")) {
@@ -725,6 +728,15 @@ public void run() {
725728
}
726729
}
727730

731+
/**
732+
* Replaces the engine created by {@link #run()} so tests can drive the
733+
* production teardown path with a controlled manager.
734+
*/
735+
@TestOnly
736+
public void setEngineForTesting(CursorSendEngine engineForTesting) {
737+
this.engineForTesting = engineForTesting;
738+
}
739+
728740
/**
729741
* Plug an observer for durable-ack-related events. {@code null} clears
730742
* any previously installed listener. See {@link BackgroundDrainerListener}

core/src/main/java/io/questdb/client/cutlass/qwp/client/sf/cursor/CursorWebSocketSendLoop.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -842,6 +842,15 @@ public boolean delegateEngineClose() {
842842
return shutdownLatch.getCount() != 0L;
843843
}
844844

845+
/**
846+
* Returns the I/O thread so lifecycle tests can wait for its exit path,
847+
* which continues beyond the shutdown-latch countdown.
848+
*/
849+
@TestOnly
850+
public Thread getIoThreadForTesting() {
851+
return ioThread;
852+
}
853+
845854
/**
846855
* Typed server-rejection payload of the latched terminal error, or
847856
* {@code null} when the loop latched a wire-level failure (or nothing).

0 commit comments

Comments
 (0)