Mercurial > hg > monetdb-java
comparison tests/OnClientTester.java @ 527:ccf9c7fbdb50 onclient
Add watchdog timer to catch hangs
author | Joeri van Ruth <joeri.van.ruth@monetdbsolutions.com> |
---|---|
date | Thu, 26 Aug 2021 16:25:09 +0200 (2021-08-26) |
parents | 6060ca8c5c1a |
children | 2d14abd1fc52 |
comparison
equal
deleted
inserted
replaced
526:6060ca8c5c1a | 527:ccf9c7fbdb50 |
---|---|
18 int failureCount = 0; | 18 int failureCount = 0; |
19 private MonetConnection conn; | 19 private MonetConnection conn; |
20 private PrintWriter out; | 20 private PrintWriter out; |
21 private Statement stmt; | 21 private Statement stmt; |
22 private StringWriter outBuffer; | 22 private StringWriter outBuffer; |
23 private WatchDog watchDog; | |
23 | 24 |
24 public static void main(String[] args) throws SQLException, NoSuchMethodException { | 25 public static void main(String[] args) throws SQLException, NoSuchMethodException { |
25 String jdbcUrl = null; | 26 String jdbcUrl = null; |
26 String requiredPrefix = null; | 27 String requiredPrefix = null; |
27 int verbosity = 0; | 28 int verbosity = 0; |
57 this.jdbcUrl = jdbcUrl; | 58 this.jdbcUrl = jdbcUrl; |
58 this.verbosity = verbosity; | 59 this.verbosity = verbosity; |
59 } | 60 } |
60 | 61 |
61 private void runTests(String testPrefix) throws SQLException, NoSuchMethodException { | 62 private void runTests(String testPrefix) throws SQLException, NoSuchMethodException { |
62 String initialPrefix = "test_"; | 63 watchDog = new WatchDog(); |
63 String methodPrefix = testPrefix == null ? initialPrefix : initialPrefix + testPrefix; | 64 try { |
64 | 65 String initialPrefix = "test_"; |
65 for (Method method : this.getClass().getDeclaredMethods()) { | 66 String methodPrefix = testPrefix == null ? initialPrefix : initialPrefix + testPrefix; |
66 String methodName = method.getName(); | 67 |
67 if (methodName.startsWith(methodPrefix) && method.getParameterCount() == 0) { | 68 for (Method method : this.getClass().getDeclaredMethods()) { |
68 String testName = methodName.substring(initialPrefix.length()); | 69 String methodName = method.getName(); |
69 runTest(testName, method); | 70 if (methodName.startsWith(methodPrefix) && method.getParameterCount() == 0) { |
70 } | 71 String testName = methodName.substring(initialPrefix.length()); |
71 } | 72 runTest(testName, method); |
72 } | 73 } |
73 | 74 } |
74 private void runTest(String testName) throws SQLException, NoSuchMethodException { | 75 } finally { |
75 String methodName = "test_" + testName; | 76 watchDog.kill(); |
76 Method method = this.getClass().getDeclaredMethod(methodName); | 77 watchDog = null; |
77 runTest(testName, method); | 78 } |
78 } | 79 } |
79 | 80 |
80 private synchronized void runTest(String testName, Method method) throws SQLException { | 81 private synchronized void runTest(String testName, Method method) throws SQLException { |
82 watchDog.setContext("test " + testName); | |
83 watchDog.setDuration(3_000); | |
81 outBuffer = new StringWriter(); | 84 outBuffer = new StringWriter(); |
82 out = new PrintWriter(outBuffer); | 85 out = new PrintWriter(outBuffer); |
83 | 86 |
84 Connection genericConnection = DriverManager.getConnection(jdbcUrl); | 87 Connection genericConnection = DriverManager.getConnection(jdbcUrl); |
85 conn = genericConnection.unwrap(MonetConnection.class); | 88 conn = genericConnection.unwrap(MonetConnection.class); |
86 stmt = conn.createStatement(); | 89 stmt = conn.createStatement(); |
87 | 90 |
88 boolean failed = false; | 91 boolean failed = false; |
89 try { | 92 try { |
93 long duration; | |
90 try { | 94 try { |
95 long t0 = System.currentTimeMillis(); | |
91 method.invoke(this); | 96 method.invoke(this); |
97 long t1 = System.currentTimeMillis(); | |
98 duration = t1 - t0; | |
92 } catch (InvocationTargetException e) { | 99 } catch (InvocationTargetException e) { |
93 Throwable cause = e.getCause(); | 100 Throwable cause = e.getCause(); |
94 if (cause instanceof Failure) | 101 if (cause instanceof Failure) |
95 throw (Failure)cause; | 102 throw (Failure)cause; |
96 else if (cause instanceof Exception) { | 103 else if (cause instanceof Exception) { |
101 } | 108 } |
102 | 109 |
103 if (verbosity > VERBOSITY_ON) | 110 if (verbosity > VERBOSITY_ON) |
104 System.out.println(); | 111 System.out.println(); |
105 if (verbosity >= VERBOSITY_ON) | 112 if (verbosity >= VERBOSITY_ON) |
106 System.out.println("Test " + testName + " succeeded"); | 113 System.out.println("Test " + testName + " succeeded in " + duration + "ms"); |
107 if (verbosity >= VERBOSITY_SHOW_ALL) | 114 if (verbosity >= VERBOSITY_SHOW_ALL) |
108 dumpOutput(testName); | 115 dumpOutput(testName); |
109 } catch (Failure e) { | 116 } catch (Failure e) { |
110 failed = true; | 117 failed = true; |
111 System.out.println(); | 118 System.out.println(); |
125 System.out.println("Innermost cause was " + t); | 132 System.out.println("Innermost cause was " + t); |
126 if (t.getStackTrace().length > 0) { | 133 if (t.getStackTrace().length > 0) { |
127 System.out.println(" at " + t.getStackTrace()[0]); | 134 System.out.println(" at " + t.getStackTrace()[0]); |
128 } | 135 } |
129 } finally { | 136 } finally { |
137 watchDog.setContext(null); | |
130 testCount++; | 138 testCount++; |
131 if (failed) | 139 if (failed) |
132 failureCount++; | 140 failureCount++; |
133 if (failed && verbosity == VERBOSITY_ON) { | 141 if (failed && verbosity == VERBOSITY_ON) { |
134 // next test case will not print separator | 142 // next test case will not print separator |
171 fail("Expected <" + quantity + "' to be " + expected + "> got " + actual); | 179 fail("Expected <" + quantity + "' to be " + expected + "> got " + actual); |
172 } | 180 } |
173 } | 181 } |
174 | 182 |
175 protected boolean execute(String query) throws SQLException { | 183 protected boolean execute(String query) throws SQLException { |
176 out.println("EXECUTE: " + query); | 184 try { |
177 boolean result; | 185 watchDog.start(); |
178 result = stmt.execute(query); | 186 out.println("EXECUTE: " + query); |
179 if (result) { | 187 boolean result; |
180 out.println(" OK"); | 188 result = stmt.execute(query); |
181 } else { | 189 if (result) { |
182 out.println(" OK, updated " + stmt.getUpdateCount() + " rows"); | 190 out.println(" OK"); |
183 } | 191 } else { |
184 return result; | 192 out.println(" OK, updated " + stmt.getUpdateCount() + " rows"); |
193 } | |
194 return result; | |
195 } finally { | |
196 watchDog.stop(); | |
197 } | |
185 } | 198 } |
186 | 199 |
187 protected void update(String query, int expectedUpdateCount) throws SQLException, Failure { | 200 protected void update(String query, int expectedUpdateCount) throws SQLException, Failure { |
188 execute(query); | 201 execute(query); |
189 int updateCount = stmt.getUpdateCount(); | 202 int updateCount = stmt.getUpdateCount(); |
352 super(message, cause); | 365 super(message, cause); |
353 } | 366 } |
354 | 367 |
355 } | 368 } |
356 | 369 |
370 static class WatchDog { | |
371 private long duration = 1000; | |
372 private long started = 0; | |
373 private String context = "no context"; | |
374 | |
375 WatchDog() { | |
376 Thread watchDog = new Thread(this::work); | |
377 watchDog.setName("watchdog_timer"); | |
378 watchDog.setDaemon(true); | |
379 watchDog.start(); | |
380 } | |
381 | |
382 synchronized void setContext(String context) { | |
383 this.context = context; | |
384 } | |
385 synchronized void setDuration(long duration) { | |
386 if (duration <= 0) | |
387 throw new IllegalArgumentException("duration should be > 0"); | |
388 this.duration = duration; | |
389 this.notifyAll(); | |
390 } | |
391 | |
392 synchronized void start() { | |
393 started = System.currentTimeMillis(); | |
394 this.notifyAll(); | |
395 } | |
396 | |
397 synchronized void stop() { | |
398 started = 0; | |
399 this.notifyAll(); | |
400 } | |
401 | |
402 synchronized void kill() { | |
403 started = -1; | |
404 this.notifyAll(); | |
405 } | |
406 | |
407 private synchronized void work() { | |
408 long now; | |
409 try { | |
410 while (true) { | |
411 now = System.currentTimeMillis(); | |
412 final long sleepTime; | |
413 if (started < 0) { | |
414 // client asked us to go away | |
415 // System.err.println("++ EXIT"); | |
416 return; | |
417 } else if (started == 0) { | |
418 // wait for client to start us | |
419 sleepTime = 600_000; | |
420 } else { | |
421 long deadline = started + duration; | |
422 sleepTime = deadline - now; | |
423 } | |
424 // System.err.printf("++ now=%d, started=now%+d, duration=%d, sleep=%d%n", | |
425 // now, started - now, duration, sleepTime | |
426 // ); | |
427 if (sleepTime > 0) { | |
428 this.wait(sleepTime); | |
429 } else { | |
430 trigger(); | |
431 return; | |
432 } | |
433 } | |
434 } catch (InterruptedException e) { | |
435 System.err.println("WATCHDOG TIMER INTERRUPTED, SHOULDN'T HAPPEN"); | |
436 System.exit(4); | |
437 } | |
438 } | |
439 | |
440 private void trigger() { | |
441 String c = context != null ? context : "no context"; | |
442 System.err.println(); | |
443 System.err.println(); | |
444 System.err.println("WATCHDOG TIMER EXPIRED [" + c + "], KILLING TESTS"); | |
445 System.exit(3); | |
446 } | |
447 } | |
448 | |
357 public void test_Upload() throws Exception { | 449 public void test_Upload() throws Exception { |
358 prepare(); | 450 prepare(); |
359 conn.setUploadHandler(new MyUploadHandler(100)); | 451 conn.setUploadHandler(new MyUploadHandler(100)); |
360 update("COPY INTO foo FROM 'banana' ON CLIENT", 100); | 452 update("COPY INTO foo FROM 'banana' ON CLIENT", 100); |
361 queryInt("SELECT COUNT(*) FROM foo", 100); | 453 queryInt("SELECT COUNT(*) FROM foo", 100); |
423 expectError("COPY (SELECT * FROM foo) INTO 'banana' ON CLIENT", "download refused"); | 515 expectError("COPY (SELECT * FROM foo) INTO 'banana' ON CLIENT", "download refused"); |
424 queryInt("SELECT 42 -- check if the connection still works", 42); | 516 queryInt("SELECT 42 -- check if the connection still works", 42); |
425 } | 517 } |
426 | 518 |
427 public void test_LargeUpload() throws SQLException, Failure { | 519 public void test_LargeUpload() throws SQLException, Failure { |
520 watchDog.setDuration(25_000); | |
428 prepare(); | 521 prepare(); |
429 int n = 4_000_000; | 522 int n = 4_000_000; |
430 MyUploadHandler handler = new MyUploadHandler(n); | 523 MyUploadHandler handler = new MyUploadHandler(n); |
431 conn.setUploadHandler(handler); | 524 conn.setUploadHandler(handler); |
432 handler.setChunkSize(1024 * 1024); | 525 handler.setChunkSize(1024 * 1024); |
433 update("COPY INTO foo FROM 'banana' ON CLIENT", n); | 526 update("COPY INTO foo FROM 'banana' ON CLIENT", n); |
434 queryInt("SELECT COUNT(DISTINCT i) FROM foo", n); | 527 queryInt("SELECT COUNT(DISTINCT i) FROM foo", n); |
435 } | 528 } |
436 | 529 |
437 public void test_LargeDownload() throws SQLException, Failure { | 530 public void test_LargeDownload() throws SQLException, Failure { |
531 watchDog.setDuration(25_000); | |
438 test_Download(4_000_000); | 532 test_Download(4_000_000); |
439 } | 533 } |
440 | 534 |
441 public void test_UploadFromStream() throws SQLException, Failure { | 535 public void test_UploadFromStream() throws SQLException, Failure { |
442 prepare(); | 536 prepare(); |