@@ -1341,6 +1341,8 @@ public long flushAndGetSequence() {
13411341 ensureNoInProgressRow ();
13421342 ensureConnected ();
13431343
1344+ long beforeFsn = cursorEngine != null ? cursorEngine .publishedFsn () : -1L ;
1345+
13441346 // Cursor SF: SF.append happens on the user thread inside
13451347 // sealAndSwapBuffer, so by the time we reach here every encoded
13461348 // batch is durable on its mmap'd segment. No processingCount to
@@ -1354,7 +1356,40 @@ public long flushAndGetSequence() {
13541356 }
13551357 cursorSendLoop .checkError ();
13561358 checkConnectionError ();
1357- return cursorEngine != null ? cursorEngine .publishedFsn () : -1L ;
1359+
1360+ long afterFsn = cursorEngine != null ? cursorEngine .publishedFsn () : -1L ;
1361+ return afterFsn > beforeFsn ? afterFsn : -1L ;
1362+ }
1363+
1364+ /**
1365+ * Flushes pending rows and blocks until the server has acknowledged
1366+ * every frame published so far (the current published-FSN watermark),
1367+ * or until {@code timeoutMillis} elapses.
1368+ * <p>
1369+ * This override uses <b>watermark semantics</b> rather than per-call
1370+ * semantics: it waits for the global {@code publishedFsn()}, not just
1371+ * the FSN returned by the flush in this call. This is necessary because
1372+ * {@link #flushAndGetSequence()} now returns {@code -1} when no data
1373+ * was published by the call, and the default {@link Sender#drain}
1374+ * implementation ({@code awaitAckedFsn(flushAndGetSequence(), timeout)})
1375+ * would short-circuit immediately on an empty flush even when prior
1376+ * publishes remain unacknowledged.
1377+ * <p>
1378+ * Close-time drain ({@link #drainOnClose()}) already uses the same
1379+ * watermark approach directly.
1380+ *
1381+ * @param timeoutMillis upper bound on the wait; {@code <= 0} returns
1382+ * the current state without blocking (the flush
1383+ * still happens before the check)
1384+ * @return {@code true} if the server has acknowledged every published
1385+ * frame on return, {@code false} on timeout
1386+ * @throws LineSenderException if the transport has latched a terminal error
1387+ */
1388+ @ Override
1389+ public boolean drain (long timeoutMillis ) {
1390+ flush ();
1391+ long targetFsn = cursorEngine != null ? cursorEngine .publishedFsn () : -1L ;
1392+ return awaitAckedFsn (targetFsn , timeoutMillis );
13581393 }
13591394
13601395 /**
0 commit comments