@mark1russell7/harness 0.11.4 → 0.11.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/build/schema.d.ts
CHANGED
|
@@ -16,7 +16,8 @@
|
|
|
16
16
|
// - ready: the reply to the first query: the connection is idle in the pool (idle, inflight), or
|
|
17
17
|
// the stream starts (stream).
|
|
18
18
|
// - sent, ack, fail: each query by its sequence number, with its error.
|
|
19
|
-
// - data, error: the end of the query in flight, as the TCP client of the harness tells it.
|
|
19
|
+
// - data, error: the end of the query in flight, as the TCP client of the harness tells it. In idle
|
|
20
|
+
// mode, the close of the socket of the first connection is an error line too: the pool closed it.
|
|
20
21
|
// - done, deadline: the end of the client.
|
|
21
22
|
//
|
|
22
23
|
// The modes are those of the pg client. idle sends one query, closes its connection (the
|
|
@@ -60,6 +61,7 @@ public final class ClientHikari {
|
|
|
60
61
|
private static volatile boolean stopped = false;
|
|
61
62
|
private static volatile HikariDataSource pool;
|
|
62
63
|
private static volatile String connected;
|
|
64
|
+
private static volatile Socket observed;
|
|
63
65
|
private static String host;
|
|
64
66
|
private static String port;
|
|
65
67
|
private static String query;
|
|
@@ -216,8 +218,8 @@ public final class ClientHikari {
|
|
|
216
218
|
|
|
217
219
|
// ---- each connection that the client borrows: its socket, read from pgjdbc
|
|
218
220
|
|
|
219
|
-
/** This function gives the
|
|
220
|
-
private static
|
|
221
|
+
/** This function gives the socket of a connection, from the PGStream of pgjdbc, or null. */
|
|
222
|
+
private static Socket socketOf(Connection connection) {
|
|
221
223
|
try {
|
|
222
224
|
PgConnection pg = connection.unwrap(PgConnection.class);
|
|
223
225
|
Object executor = pg.getQueryExecutor();
|
|
@@ -231,23 +233,50 @@ public final class ClientHikari {
|
|
|
231
233
|
}
|
|
232
234
|
if (field == null) return null;
|
|
233
235
|
field.setAccessible(true);
|
|
234
|
-
|
|
235
|
-
return socket.getLocalAddress().getHostAddress() + ":" + socket.getLocalPort();
|
|
236
|
+
return ((PGStream) field.get(executor)).getSocket();
|
|
236
237
|
} catch (Exception error) {
|
|
237
238
|
return null;
|
|
238
239
|
}
|
|
239
240
|
}
|
|
240
241
|
|
|
241
242
|
private static void seen(Connection connection) {
|
|
242
|
-
|
|
243
|
-
if (
|
|
243
|
+
Socket socket = socketOf(connection);
|
|
244
|
+
if (socket == null) return;
|
|
245
|
+
String local = socket.getLocalAddress().getHostAddress() + ":" + socket.getLocalPort();
|
|
246
|
+
if (!SEEN.add(local)) return;
|
|
244
247
|
emit("socket", "\"role\":\"pool\",\"local\":" + q(local));
|
|
245
248
|
if (connected == null) {
|
|
246
249
|
connected = local;
|
|
250
|
+
observed = socket;
|
|
247
251
|
emit("connected", "\"local\":" + q(local));
|
|
248
252
|
}
|
|
249
253
|
}
|
|
250
254
|
|
|
255
|
+
/**
|
|
256
|
+
* This function watches the socket of the first connection in idle mode, each 2 ms. The pool
|
|
257
|
+
* closes it when a check finds it dead: pgjdbc closes the socket after a read timeout or an I/O
|
|
258
|
+
* error. That close is the detection of an idle cell, thus it writes an error line.
|
|
259
|
+
*/
|
|
260
|
+
private static void watchIdle() {
|
|
261
|
+
Socket socket = observed;
|
|
262
|
+
if (socket == null) return;
|
|
263
|
+
Thread watcher = new Thread(() -> {
|
|
264
|
+
while (!stopped) {
|
|
265
|
+
if (socket.isClosed()) {
|
|
266
|
+
emit("error", "\"errno\":\"CLOSED\",\"local\":" + q(connected));
|
|
267
|
+
return;
|
|
268
|
+
}
|
|
269
|
+
try {
|
|
270
|
+
Thread.sleep(2);
|
|
271
|
+
} catch (InterruptedException stop) {
|
|
272
|
+
return;
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
}, "idle-watcher");
|
|
276
|
+
watcher.setDaemon(true);
|
|
277
|
+
watcher.start();
|
|
278
|
+
}
|
|
279
|
+
|
|
251
280
|
// ---- the counts of the pool, each 5 ms: a line for each change
|
|
252
281
|
|
|
253
282
|
private static void watchPool() {
|
|
@@ -332,6 +361,35 @@ public final class ClientHikari {
|
|
|
332
361
|
}
|
|
333
362
|
}
|
|
334
363
|
|
|
364
|
+
/**
|
|
365
|
+
* This function loads the code of a connect and of the start of a pool before the first line, on
|
|
366
|
+
* the loopback of the namespace. A new JVM loads each class at its first use, and that load would
|
|
367
|
+
* be in the band of a cell that starts into a fault. A pool with no check and no idle connection
|
|
368
|
+
* makes no connection: it loads the classes of the pool. It also sets the login timeout of
|
|
369
|
+
* DriverManager, thus pgjdbc then connects in a task of an executor (Driver.java:303-311,
|
|
370
|
+
* :730-739). A connect to port 1 of the loopback after it is refused at once: it loads that
|
|
371
|
+
* connect of pgjdbc and its errors. HikariCP gives each new connection a random UUID (PoolBase.java:363), and the
|
|
372
|
+
* first one starts the SecureRandom of the JVM.
|
|
373
|
+
*/
|
|
374
|
+
private static void warmUp() {
|
|
375
|
+
HikariConfig empty = new HikariConfig();
|
|
376
|
+
empty.setJdbcUrl("jdbc:postgresql://127.0.0.1:1/warmup");
|
|
377
|
+
empty.setUsername("warmup");
|
|
378
|
+
empty.setPoolName("warmup");
|
|
379
|
+
empty.setInitializationFailTimeout(-1);
|
|
380
|
+
empty.setMinimumIdle(0);
|
|
381
|
+
// the pool starts and closes with no connection
|
|
382
|
+
new HikariDataSource(empty).close();
|
|
383
|
+
Properties refused = new Properties();
|
|
384
|
+
refused.setProperty("user", "warmup");
|
|
385
|
+
try {
|
|
386
|
+
new org.postgresql.Driver().connect("jdbc:postgresql://127.0.0.1:1/warmup", refused);
|
|
387
|
+
} catch (SQLException expected) {
|
|
388
|
+
// the refusal is the purpose of the connect
|
|
389
|
+
}
|
|
390
|
+
java.util.UUID.randomUUID();
|
|
391
|
+
}
|
|
392
|
+
|
|
335
393
|
public static void main(String[] args) throws Exception {
|
|
336
394
|
List<String> argv = List.of(args);
|
|
337
395
|
String mode = flag(argv, "--mode", null);
|
|
@@ -347,6 +405,7 @@ public final class ClientHikari {
|
|
|
347
405
|
query = flag(argv, "--query", "SELECT 1");
|
|
348
406
|
boolean before = argv.contains("--before");
|
|
349
407
|
Map<String, String> options = parseObject(flag(argv, "--options", "{}"));
|
|
408
|
+
warmUp();
|
|
350
409
|
|
|
351
410
|
String bootId;
|
|
352
411
|
try {
|
|
@@ -413,6 +472,7 @@ public final class ClientHikari {
|
|
|
413
472
|
}
|
|
414
473
|
// the connection went back to the pool at the end of the query: it is idle there
|
|
415
474
|
emit("ready", "\"reply\":\"ack\"");
|
|
475
|
+
if (mode.equals("idle")) watchIdle();
|
|
416
476
|
if (mode.equals("inflight")) {
|
|
417
477
|
while (!Files.exists(go)) Thread.sleep(2);
|
|
418
478
|
Throwable error = operate(2);
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://json.schemastore.org/package",
|
|
3
3
|
"name": "@mark1russell7/harness",
|
|
4
|
-
"version": "0.11.
|
|
4
|
+
"version": "0.11.6",
|
|
5
5
|
"description": "A fault-injection harness for connection timers: a cell runs a client against a stock target in Docker, through a topology of routers and proxies, a replicated group under Sentinels or a replica set, injects a fault, measures the kernel's own records and judges a prediction. HTTP clients of Go, Node and undici race the keep-alive of a Node server, and the MongoDB driver meets the faults of a replica set. The WebSocket client of @procedures/client drops a silent connection on its own heartbeat, and the harness times that detection and the reconnect from the program's lines and the kernel's stamps.",
|
|
6
6
|
"keywords": [
|
|
7
7
|
"fault-injection",
|