@mark1russell7/harness 0.11.6 → 0.11.7

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
@@ -1,4 +1,4 @@
1
- import { J as Subject, _ as ImageSource, i as ClientImage, r as Client } from "./cell-lSCgFB54.js";
1
+ import { J as Subject, _ as ImageSource, i as ClientImage, r as Client } from "./cell-7HFQIHlK.js";
2
2
  import { z } from "zod";
3
3
  //#region src/schema.d.ts
4
4
  /**
@@ -1,5 +1,5 @@
1
1
  // client-hikari HOST PORT --mode idle|inflight|stream [--interval-ms N] [--max-s N] [--go PATH]
2
- // [--query TEXT] [--before] [--options JSON]
2
+ // [--query TEXT] [--inflight-query TEXT] [--before] [--options JSON]
3
3
  //
4
4
  // The client of a HikariCP cell: HikariCP 7.1.0 over pgjdbc 42.7.13, on Java 21. Each event is a
5
5
  // JSON line with `ev` and `t`, the time on CLOCK_MONOTONIC in nanoseconds (System.nanoTime, which
@@ -24,7 +24,8 @@
24
24
  // connection goes back to the pool), then holds until --max-s. inflight sends one query, waits for
25
25
  // the file --go, sends one more query into the fault, and holds after its end. stream sends a query
26
26
  // each --interval-ms after the reply before. --before starts the client into a fault that holds
27
- // already: the start of its pool meets it.
27
+ // already: the start of its pool meets it. --inflight-query is the text of the query in flight
28
+ // (query 2 of inflight), and --query is the text of each other query.
28
29
  //
29
30
  // The options are one flat JSON object. A name with the prefix `dataSource.` is a property of
30
31
  // pgjdbc. Each other name is a property of HikariConfig. A cell gives each option that it changes,
@@ -53,6 +54,8 @@ import java.util.Set;
53
54
  import java.util.concurrent.ConcurrentHashMap;
54
55
  import org.postgresql.core.PGStream;
55
56
  import org.postgresql.jdbc.PgConnection;
57
+ import org.postgresql.util.PSQLException;
58
+ import org.postgresql.util.ServerErrorMessage;
56
59
 
57
60
  public final class ClientHikari {
58
61
  private static final PrintStream OUT = new PrintStream(new FileOutputStream(FileDescriptor.out), false, StandardCharsets.UTF_8);
@@ -65,6 +68,7 @@ public final class ClientHikari {
65
68
  private static String host;
66
69
  private static String port;
67
70
  private static String query;
71
+ private static String inflightQuery;
68
72
  private static HikariConfig config;
69
73
 
70
74
  private ClientHikari() {}
@@ -334,8 +338,13 @@ public final class ClientHikari {
334
338
  Runtime.getRuntime().halt(code);
335
339
  }
336
340
 
337
- /** This function sends query n through the pool. Its stamp is before the borrow. It gives null for a reply, or the error. */
341
+ /** This function sends query n through the pool, with the text of each query. Its stamp is before the borrow. It gives null for a reply, or the error. */
338
342
  private static Throwable operate(int n) {
343
+ return operate(n, query);
344
+ }
345
+
346
+ /** This function sends query n through the pool, with its text. Its stamp is before the borrow. It gives null for a reply, or the error. */
347
+ private static Throwable operate(int n, String text) {
339
348
  long at = now();
340
349
  if (n == 1) emit("connecting", at, "\"at\":" + q(host + ":" + port));
341
350
  emit("sent", at, "\"n\":" + n);
@@ -346,7 +355,7 @@ public final class ClientHikari {
346
355
  try (Connection connection = pool.getConnection()) {
347
356
  seen(connection);
348
357
  try (Statement statement = connection.createStatement()) {
349
- if (statement.execute(query)) {
358
+ if (statement.execute(text)) {
350
359
  try (ResultSet result = statement.getResultSet()) {
351
360
  while (result.next()) rows += 1;
352
361
  }
@@ -368,8 +377,10 @@ public final class ClientHikari {
368
377
  * makes no connection: it loads the classes of the pool. It also sets the login timeout of
369
378
  * DriverManager, thus pgjdbc then connects in a task of an executor (Driver.java:303-311,
370
379
  * :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.
380
+ * connect of pgjdbc and its errors. HikariCP gives each new connection a random UUID
381
+ * (PoolBase.java:363), and the first one starts the SecureRandom of the JVM. An error of the
382
+ * server that pgjdbc parses loads the classes of an ERROR of a statement, for example of its
383
+ * timeout.
373
384
  */
374
385
  private static void warmUp() {
375
386
  HikariConfig empty = new HikariConfig();
@@ -388,6 +399,10 @@ public final class ClientHikari {
388
399
  // the refusal is the purpose of the connect
389
400
  }
390
401
  java.util.UUID.randomUUID();
402
+ // the fields of an ErrorResponse, each a code and a text with a NUL after it
403
+ PSQLException cancelled = new PSQLException(new ServerErrorMessage("SERROR\0VERROR\0C57014\0Mwarm-up\0"));
404
+ errnoOf(cancelled);
405
+ failure(cancelled);
391
406
  }
392
407
 
393
408
  public static void main(String[] args) throws Exception {
@@ -403,6 +418,7 @@ public final class ClientHikari {
403
418
  long max = Long.parseLong(flag(argv, "--max-s", "120")) * 1000;
404
419
  Path go = Path.of(flag(argv, "--go", "/tmp/go"));
405
420
  query = flag(argv, "--query", "SELECT 1");
421
+ inflightQuery = flag(argv, "--inflight-query", query);
406
422
  boolean before = argv.contains("--before");
407
423
  Map<String, String> options = parseObject(flag(argv, "--options", "{}"));
408
424
  warmUp();
@@ -424,7 +440,7 @@ public final class ClientHikari {
424
440
  + ",\"client\":\"hikari\",\"driver\":" + q(org.postgresql.util.DriverInfo.DRIVER_VERSION)
425
441
  + ",\"pool\":" + q(hikari == null ? "7.1.0" : hikari)
426
442
  + ",\"java\":" + q(System.getProperty("java.version"))
427
- + ",\"mode\":" + q(mode) + ",\"query\":" + q(query) + ",\"options\":" + given);
443
+ + ",\"mode\":" + q(mode) + ",\"query\":" + q(query) + ",\"inflight_query\":" + q(inflightQuery) + ",\"options\":" + given);
428
444
 
429
445
  Properties properties = new Properties();
430
446
  for (Map.Entry<String, String> each : options.entrySet()) properties.setProperty(each.getKey(), each.getValue());
@@ -475,7 +491,7 @@ public final class ClientHikari {
475
491
  if (mode.equals("idle")) watchIdle();
476
492
  if (mode.equals("inflight")) {
477
493
  while (!Files.exists(go)) Thread.sleep(2);
478
- Throwable error = operate(2);
494
+ Throwable error = operate(2, inflightQuery);
479
495
  if (error == null) emit("data", "\"n\":2");
480
496
  else emit("error", "\"errno\":" + q(errnoOf(error)) + ",\"message\":" + q(error.getMessage()));
481
497
  }
@@ -1,5 +1,5 @@
1
1
  // client-pg HOST PORT --mode idle|inflight|stream [--interval-ms N] [--max-s N] [--go PATH]
2
- // [--query TEXT] [--before] [--options JSON]
2
+ // [--query TEXT] [--inflight-query TEXT] [--before] [--options JSON]
3
3
  //
4
4
  // The client of a PostgreSQL cell: node-postgres (pg 8.23.1 with pg-pool 3.14.0) on Node 24.21.0.
5
5
  // Each event is a JSON line with `ev` and `t`, the time on CLOCK_MONOTONIC in nanoseconds
@@ -24,6 +24,9 @@
24
24
  // inflight sends one query, waits for the file --go, sends one more query into the fault, and holds
25
25
  // after its end. stream sends a query each --interval-ms after the reply before, until --max-s or
26
26
  // SIGTERM. --before starts the client into a fault that holds already: its first connect meets it.
27
+ // --inflight-query is the text of the query in flight (query 2 of inflight), and --query is the
28
+ // text of each other query. Thus a slow query in flight does not delay the reply that makes the
29
+ // client ready.
27
30
 
28
31
  import { readFileSync } from "node:fs";
29
32
  import { existsSync } from "node:fs";
@@ -46,6 +49,7 @@ const interval = Number(flag("--interval-ms") ?? 50);
46
49
  const max = Number(flag("--max-s") ?? 120) * 1000;
47
50
  const go = flag("--go") ?? "/tmp/go";
48
51
  const query = flag("--query") ?? "SELECT 1";
52
+ const inflightQuery = flag("--inflight-query") ?? query;
49
53
  const before = argv.includes("--before");
50
54
  // the options of the pool. A cell gives each option that it changes, and pg-pool gives the others
51
55
  const options = JSON.parse(flag("--options") ?? "{}");
@@ -60,7 +64,7 @@ const bootId = () => {
60
64
  return null;
61
65
  }
62
66
  };
63
- emit("start", { boot_id: bootId(), client: "pg", driver: versionOf("pg"), pool: versionOf("pg-pool"), node: process.versions.node, mode, query, options });
67
+ emit("start", { boot_id: bootId(), client: "pg", driver: versionOf("pg"), pool: versionOf("pg-pool"), node: process.versions.node, mode, query, inflight_query: inflightQuery, options });
64
68
 
65
69
  const pool = new pg.Pool({ host, port: Number(port), user: "postgres", database: "postgres", ...options });
66
70
 
@@ -116,10 +120,10 @@ setTimeout(() => {
116
120
  stop(0);
117
121
  }, max);
118
122
 
119
- /** This function sends query n through the pool. Its stamp is before the call. It gives null for a reply, or the error. */
120
- const operate = async (n) => {
123
+ /** This function sends query n through the pool, with the text of n. Its stamp is before the call. It gives null for a reply, or the error. */
124
+ const operate = async (n, text = query) => {
121
125
  const at = now();
122
- const reply = pool.query(query);
126
+ const reply = pool.query(text);
123
127
  // a band of a cell that starts into a fault counts from the start of the first connect
124
128
  if (n === 1) emit("connecting", { at: `${host}:${port}` }, at);
125
129
  emit("sent", { n }, at);
@@ -158,7 +162,7 @@ if (mode === "stream") {
158
162
  resolve();
159
163
  }, 2);
160
164
  });
161
- const error = await operate(2);
165
+ const error = await operate(2, inflightQuery);
162
166
  if (error === null) emit("data", { n: 2 });
163
167
  else emit("error", { errno: errnoOf(error), message: error.message });
164
168
  }
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.6",
4
+ "version": "0.11.7",
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",