Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
a0dd202
test(qwp): pin slot retention until worker quiescence
bluestreak01 Jul 10, 2026
8f0dd45
fix(qwp): make engine close a worker-quiescence barrier before releas…
bluestreak01 Jul 10, 2026
d976fd5
fix(qwp): preserve slot ownership after incomplete close
bluestreak01 Jul 10, 2026
d3b6147
test(qwp): pin slot retention on the owned-manager close path
bluestreak01 Jul 10, 2026
7cf9612
perf(qwp): wake quiescence waiters only when one is parked
bluestreak01 Jul 10, 2026
930175f
fix(qwp): transfer slot cleanup to the worker's exit path and recover…
bluestreak01 Jul 10, 2026
eea7f89
test(qwp): pin slot retention when worker-exit handoff registration f…
bluestreak01 Jul 10, 2026
940e130
fix(qwp): publish close completion only after confirmed slot flock re…
bluestreak01 Jul 10, 2026
7b9924c
fix(qwp): retain startup-retired recoverers so a late flock release r…
bluestreak01 Jul 10, 2026
d990470
fix(qwp): give capacity-starved borrows a final retired-slot probe be…
bluestreak01 Jul 10, 2026
7df62d5
test(qwp): close the remaining engine/manager shutdown coverage gaps
bluestreak01 Jul 10, 2026
79e0847
Make final-probe regression deterministic
bluestreak01 Jul 11, 2026
dd0c547
docs(qwp): fix load-bearing concurrency comments that contradict the …
bluestreak01 Jul 11, 2026
b89384f
fix(qwp): recover SF slots after deferred cleanup
bluestreak01 Jul 11, 2026
6b373cb
Merge remote-tracking branch 'origin/fix/qwp-worker-quiescence' into …
bluestreak01 Jul 11, 2026
a086575
fix(qwp): harden cleanup and recovery lifecycle
bluestreak01 Jul 11, 2026
cad1bb8
fix(qwp): make close-time SF cleanup crash-safe and bound flock-relea…
bluestreak01 Jul 11, 2026
a531164
fix(qwp): harden SF crash recovery and cleanup
bluestreak01 Jul 12, 2026
b8d965d
Make socket traffic shutdown idempotent
bluestreak01 Jul 12, 2026
79eb13c
fix(client): serialize QuestDB.close() through shutdown completion
bluestreak01 Jul 13, 2026
e8c2dcd
fix(qwp): cancel in-flight connect during close, with a bounded backstop
bluestreak01 Jul 13, 2026
d130f2c
fix(client): fail closed in SF recovery and add crash-safe boundary m…
bluestreak01 Jul 13, 2026
c448e34
fix(client): make SF recovery segment sort O(N log N) unconditionally
bluestreak01 Jul 13, 2026
4b6ba13
Fix client durability and creation shutdown
bluestreak01 Jul 14, 2026
21345b0
Harden QWP client recovery and shutdown
bluestreak01 Jul 14, 2026
94a0ae6
Isolate flock retry ordering test
bluestreak01 Jul 14, 2026
d5673b3
Continue direct pool startup recovery
bluestreak01 Jul 14, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 34 additions & 0 deletions core/src/main/c/share/files.c
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,13 @@ JNIEXPORT jint JNICALL Java_io_questdb_client_std_Files_openRW0
return (jint) fd;
}

JNIEXPORT jint JNICALL Java_io_questdb_client_std_Files_openRWExclusive0
(JNIEnv *e, jclass cl, jlong lpszName) {
int fd;
RESTARTABLE(open((const char *) (uintptr_t) lpszName, O_CREAT | O_EXCL | O_RDWR, 0644), fd);
return (jint) fd;
}

JNIEXPORT jint JNICALL Java_io_questdb_client_std_Files_openAppend0
(JNIEnv *e, jclass cl, jlong lpszName) {
int fd;
Expand Down Expand Up @@ -124,6 +131,21 @@ JNIEXPORT jint JNICALL Java_io_questdb_client_std_Files_fsync
return res;
}

JNIEXPORT jint JNICALL Java_io_questdb_client_std_Files_fsyncDir0
(JNIEnv *e, jclass cl, jlong lpszName) {
int fd;
RESTARTABLE(open((const char *) (uintptr_t) lpszName, O_RDONLY), fd);
if (fd < 0) {
return -1;
}
int res;
RESTARTABLE(fsync(fd), res);
int saved_errno = errno;
close(fd);
errno = saved_errno;
return res;
}

JNIEXPORT jboolean JNICALL Java_io_questdb_client_std_Files_truncate
(JNIEnv *e, jclass cl, jint fd, jlong size) {
int res;
Expand Down Expand Up @@ -236,6 +258,18 @@ JNIEXPORT jint JNICALL Java_io_questdb_client_std_Files_lock
return flock((int) fd, LOCK_EX | LOCK_NB);
}

JNIEXPORT jint JNICALL Java_io_questdb_client_cutlass_qwp_client_sf_cursor_SlotLock_release0
(JNIEnv *e, jclass cl, jint fd) {
if (flock((int) fd, LOCK_UN) != 0) {
return -1;
}
/* Unlock success confirms that the slot is reusable. close() is one-shot:
* POSIX leaves descriptor state unspecified on EINTR, so retrying its
* numeric value could close an unrelated descriptor after reuse. */
(void) close((int) fd);
return 0;
}

JNIEXPORT jint JNICALL Java_io_questdb_client_std_Files_mkdir0
(JNIEnv *e, jclass cl, jlong lpszPath, jint mode) {
return mkdir((const char *) (uintptr_t) lpszPath, (mode_t) mode);
Expand Down
6 changes: 6 additions & 0 deletions core/src/main/c/share/net.c
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,12 @@ JNIEXPORT jint JNICALL Java_io_questdb_client_network_Net_send
return com_questdb_network_Net_EOTHERDISCONNECT;
}

JNIEXPORT jint JNICALL Java_io_questdb_client_network_Net_shutdown
(JNIEnv *e, jclass cl, jint fd) {
const int result = shutdown((int) fd, SHUT_RDWR);
return result == -1 && errno == ENOTCONN ? 0 : result;
}

JNIEXPORT jint JNICALL Java_io_questdb_client_network_Net_recv
(JNIEnv *e, jclass cl, jint fd, jlong ptr, jint len) {
ssize_t n;
Expand Down
8 changes: 8 additions & 0 deletions core/src/main/c/share/net.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

42 changes: 42 additions & 0 deletions core/src/main/c/windows/files.c
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,15 @@ JNIEXPORT jint JNICALL Java_io_questdb_client_std_Files_openRW0
FILE_ATTRIBUTE_NORMAL);
}

JNIEXPORT jint JNICALL Java_io_questdb_client_std_Files_openRWExclusive0
(JNIEnv *e, jclass cl, jlong lpszName) {
return open_file((const char *) (uintptr_t) lpszName,
GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
CREATE_NEW,
FILE_ATTRIBUTE_NORMAL);
}

JNIEXPORT jint JNICALL Java_io_questdb_client_std_Files_openAppend0
(JNIEnv *e, jclass cl, jlong lpszName) {
jint fd = open_file((const char *) (uintptr_t) lpszName,
Expand Down Expand Up @@ -215,6 +224,25 @@ JNIEXPORT jint JNICALL Java_io_questdb_client_std_Files_fsync
return 0;
}

JNIEXPORT jint JNICALL Java_io_questdb_client_std_Files_fsyncDir0
(JNIEnv *e, jclass cl, jlong lpszName) {
jint fd = open_file((const char *) (uintptr_t) lpszName,
GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
OPEN_EXISTING,
FILE_FLAG_BACKUP_SEMANTICS);
if (fd < 0) {
return -1;
}
if (!FlushFileBuffers(FD_TO_HANDLE(fd))) {
SaveLastError();
CloseHandle(FD_TO_HANDLE(fd));
return -1;
}
CloseHandle(FD_TO_HANDLE(fd));
return 0;
}

JNIEXPORT jboolean JNICALL Java_io_questdb_client_std_Files_truncate
(JNIEnv *e, jclass cl, jint fd, jlong size) {
FILE_END_OF_FILE_INFO eof;
Expand Down Expand Up @@ -331,6 +359,20 @@ JNIEXPORT jint JNICALL Java_io_questdb_client_std_Files_lock
return 0;
}

JNIEXPORT jint JNICALL Java_io_questdb_client_cutlass_qwp_client_sf_cursor_SlotLock_release0
(JNIEnv *e, jclass cl, jint fd) {
OVERLAPPED ov;
memset(&ov, 0, sizeof(ov));
if (!UnlockFileEx(FD_TO_HANDLE(fd), 0, MAXDWORD, MAXDWORD, &ov)) {
SaveLastError();
return -1;
}
/* Unlock success confirms that the slot is reusable. Match POSIX by
* closing once without making handle cleanup part of that signal. */
(void) CloseHandle(FD_TO_HANDLE(fd));
return 0;
}

JNIEXPORT jint JNICALL Java_io_questdb_client_std_Files_mkdir0
(JNIEnv *e, jclass cl, jlong lpszPath, jint mode) {
(void) mode;
Expand Down
14 changes: 14 additions & 0 deletions core/src/main/c/windows/net.c
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,20 @@ JNIEXPORT jint JNICALL Java_io_questdb_client_network_Net_configureNonBlocking
return res;
}

JNIEXPORT jint JNICALL Java_io_questdb_client_network_Net_shutdown
(JNIEnv *e, jclass cl, jint fd) {
const int result = shutdown((SOCKET) fd, SD_BOTH);
if (result == SOCKET_ERROR) {
const int error = WSAGetLastError();
if (error == WSAENOTCONN) {
return 0;
}
WSASetLastError(error);
SaveLastError();
}
return result;
}

JNIEXPORT jint JNICALL Java_io_questdb_client_network_Net_recv
(JNIEnv *e, jclass cl, jint fd, jlong addr, jint len) {
const int n = recv((SOCKET) fd, (char *) addr, len, 0);
Expand Down
15 changes: 11 additions & 4 deletions core/src/main/java/io/questdb/client/QuestDB.java
Original file line number Diff line number Diff line change
Expand Up @@ -130,10 +130,17 @@ static QuestDB connect(CharSequence configurationString) {
Sender borrowSender();

/**
* Shuts down the pools, closing every underlying {@link Sender} and
* query client. Idempotent. Threads currently blocked in
* {@link #borrowSender()} or {@link Query#submit()} are released with an
* error.
* Shuts down the pools and their published clients. Idempotent. Threads
* currently blocked in {@link #borrowSender()} or {@link Query#submit()}
* are released with an error.
* <p>
* In-progress client creation: close() waits up to the builder's
* {@link QuestDBBuilder#acquireTimeoutMillis(long) acquire timeout},
* hard-capped at 5 seconds, for a creation blocked in DNS, TCP, TLS, or a
* WebSocket handshake. If that budget expires, the creator retains cleanup
* ownership and closes its unpublished client (and releases any SF slot)
* when construction returns. This keeps close() bounded even when
* {@code connect_timeout} is unset without abandoning late resources.
* <p>
* Outstanding leases: a borrowed {@link Sender} is never torn down
* underneath the thread using it. Instead, close() waits up to the
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -248,6 +248,15 @@ public void close() {
}
}

/**
* Shuts down socket traffic without releasing the descriptor or freeing
* buffers that a concurrent I/O worker may still access. The owner must
* call {@link #close()} after joining the worker to complete cleanup.
*/
public void closeTraffic() {
socket.closeTraffic();
}

/**
* Connects to a WebSocket server.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,10 @@ public class QwpQueryClient implements QuietCloseable {
private final Random failoverRandom = new Random();
private long authTimeoutMs = DEFAULT_AUTH_TIMEOUT_MS;
private String authorizationHeader;
// Deterministic lifecycle barrier used by facade shutdown tests. Null in
// production; close() clears and invokes it after winning close ownership
// without allowing hook failures to prevent resource teardown.
private volatile Runnable beforeCloseHook;
// Upper bound (ms) on each TCP connect attempt. 0 (default) falls back to
// the OS connect timeout.
private int connectTimeoutMs = 0;
Expand Down Expand Up @@ -620,6 +624,16 @@ public void close() {
// scratch, double-freeing it.
return;
}
Runnable hook = beforeCloseHook;
beforeCloseHook = null;
if (hook != null) {
try {
hook.run();
} catch (Throwable ignored) {
// Omit diagnostics for this test-only hook: even rendering its
// failure must not prevent production resource cleanup.
}
}
connected = false;
lastCloseTimedOut = false;
try {
Expand Down Expand Up @@ -1004,6 +1018,11 @@ public void seedFailoverRandomForTest(long seed) {
}
}

@TestOnly
public void setBeforeCloseHookForTest(Runnable hook) {
beforeCloseHook = hook;
}

/**
* Returns true if the most recent {@link #close()} call abandoned the I/O thread
* because it failed to exit within the join timeout. The native buffer pool and
Expand Down
Loading
Loading