@tdengine/websocket 3.2.2 → 3.3.0

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.
Files changed (275) hide show
  1. package/lib/package.json +64 -0
  2. package/lib/src/client/retryConfig.d.ts +10 -0
  3. package/lib/src/client/retryConfig.d.ts.map +1 -0
  4. package/lib/src/client/retryConfig.js +46 -0
  5. package/lib/src/client/wsClient.d.ts +24 -7
  6. package/lib/src/client/wsClient.d.ts.map +1 -1
  7. package/lib/src/client/wsClient.js +164 -137
  8. package/lib/src/client/wsConnector.d.ts +55 -9
  9. package/lib/src/client/wsConnector.d.ts.map +1 -1
  10. package/lib/src/client/wsConnector.js +522 -102
  11. package/lib/src/client/wsConnectorPool.d.ts +5 -1
  12. package/lib/src/client/wsConnectorPool.d.ts.map +1 -1
  13. package/lib/src/client/wsConnectorPool.js +67 -52
  14. package/lib/src/client/wsEventCallback.d.ts +3 -0
  15. package/lib/src/client/wsEventCallback.d.ts.map +1 -1
  16. package/lib/src/client/wsEventCallback.js +67 -8
  17. package/lib/src/client/wsResponse.d.ts +0 -3
  18. package/lib/src/client/wsResponse.d.ts.map +1 -1
  19. package/lib/src/client/wsResponse.js +0 -3
  20. package/lib/src/common/addressConnectionTracker.d.ts +11 -0
  21. package/lib/src/common/addressConnectionTracker.d.ts.map +1 -0
  22. package/lib/src/common/addressConnectionTracker.js +53 -0
  23. package/lib/src/common/config.d.ts +3 -0
  24. package/lib/src/common/config.d.ts.map +1 -1
  25. package/lib/src/common/config.js +6 -0
  26. package/lib/src/common/constant.d.ts +1 -0
  27. package/lib/src/common/constant.d.ts.map +1 -1
  28. package/lib/src/common/constant.js +6 -1
  29. package/lib/src/common/dsn.d.ts +30 -0
  30. package/lib/src/common/dsn.d.ts.map +1 -0
  31. package/lib/src/common/dsn.js +221 -0
  32. package/lib/src/common/taosResult.d.ts.map +1 -1
  33. package/lib/src/common/taosResult.js +0 -5
  34. package/lib/src/common/urlParser.d.ts +32 -0
  35. package/lib/src/common/urlParser.d.ts.map +1 -0
  36. package/lib/src/common/urlParser.js +201 -0
  37. package/lib/src/common/utils.d.ts +6 -1
  38. package/lib/src/common/utils.d.ts.map +1 -1
  39. package/lib/src/common/utils.js +74 -22
  40. package/lib/src/index.d.ts.map +1 -1
  41. package/lib/src/sql/wsSql.d.ts.map +1 -1
  42. package/lib/src/sql/wsSql.js +2 -2
  43. package/lib/src/stmt/FieldBindParams.d.ts.map +1 -1
  44. package/lib/src/stmt/wsColumnInfo.d.ts.map +1 -1
  45. package/lib/src/stmt/wsParams1.d.ts.map +1 -1
  46. package/lib/src/stmt/wsParams1.js +26 -26
  47. package/lib/src/stmt/wsParams2.d.ts.map +1 -1
  48. package/lib/src/stmt/wsParams2.js +0 -3
  49. package/lib/src/stmt/wsParamsBase.d.ts.map +1 -1
  50. package/lib/src/stmt/wsProto.d.ts.map +1 -1
  51. package/lib/src/stmt/wsProto.js +16 -16
  52. package/lib/src/stmt/wsStmt1.d.ts.map +1 -1
  53. package/lib/src/stmt/wsStmt2.d.ts +12 -4
  54. package/lib/src/stmt/wsStmt2.d.ts.map +1 -1
  55. package/lib/src/stmt/wsStmt2.js +182 -64
  56. package/lib/src/stmt/wsTableInfo.d.ts.map +1 -1
  57. package/lib/src/tmq/config.d.ts +4 -2
  58. package/lib/src/tmq/config.d.ts.map +1 -1
  59. package/lib/src/tmq/config.js +30 -13
  60. package/lib/src/tmq/constant.d.ts +4 -0
  61. package/lib/src/tmq/constant.d.ts.map +1 -1
  62. package/lib/src/tmq/constant.js +4 -0
  63. package/lib/src/tmq/tmqResponse.d.ts.map +1 -1
  64. package/lib/src/tmq/tmqResponse.js +1 -4
  65. package/lib/src/tmq/wsTmq.d.ts +4 -1
  66. package/lib/src/tmq/wsTmq.d.ts.map +1 -1
  67. package/lib/src/tmq/wsTmq.js +55 -27
  68. package/lib/test/bulkPulling/a.test.d.ts +2 -0
  69. package/lib/test/bulkPulling/a.test.d.ts.map +1 -0
  70. package/lib/test/bulkPulling/a.test.js +166 -0
  71. package/lib/test/bulkPulling/cloud.tmq.test.js +3 -2
  72. package/lib/test/bulkPulling/decimal.test.js +8 -8
  73. package/lib/test/bulkPulling/dsn.test.d.ts +2 -0
  74. package/lib/test/bulkPulling/dsn.test.d.ts.map +1 -0
  75. package/lib/test/bulkPulling/dsn.test.js +397 -0
  76. package/lib/test/bulkPulling/queryTables.test.js +1 -1
  77. package/lib/test/bulkPulling/retryConfig.test.d.ts +2 -0
  78. package/lib/test/bulkPulling/retryConfig.test.d.ts.map +1 -0
  79. package/lib/test/bulkPulling/retryConfig.test.js +34 -0
  80. package/lib/test/bulkPulling/schemaless.test.js +15 -14
  81. package/lib/test/bulkPulling/sql.failover.test.d.ts +2 -0
  82. package/lib/test/bulkPulling/sql.failover.test.d.ts.map +1 -0
  83. package/lib/test/bulkPulling/sql.failover.test.js +338 -0
  84. package/lib/test/bulkPulling/sql.test.js +116 -44
  85. package/lib/test/bulkPulling/stmt1.func.test.js +31 -30
  86. package/lib/test/bulkPulling/stmt1.type.test.js +1 -1
  87. package/lib/test/bulkPulling/stmt2.failover.test.d.ts +2 -0
  88. package/lib/test/bulkPulling/stmt2.failover.test.d.ts.map +1 -0
  89. package/lib/test/bulkPulling/stmt2.failover.test.js +313 -0
  90. package/lib/test/bulkPulling/stmt2.func.test.js +37 -36
  91. package/lib/test/bulkPulling/stmt2.init.failover.test.d.ts +2 -0
  92. package/lib/test/bulkPulling/stmt2.init.failover.test.d.ts.map +1 -0
  93. package/lib/test/bulkPulling/stmt2.init.failover.test.js +50 -0
  94. package/lib/test/bulkPulling/stmt2.type.test.js +1 -1
  95. package/lib/test/bulkPulling/tmq.config.test.d.ts +2 -0
  96. package/lib/test/bulkPulling/tmq.config.test.d.ts.map +1 -0
  97. package/lib/test/bulkPulling/tmq.config.test.js +77 -0
  98. package/lib/test/bulkPulling/tmq.failover.test.d.ts +2 -0
  99. package/lib/test/bulkPulling/tmq.failover.test.d.ts.map +1 -0
  100. package/lib/test/bulkPulling/tmq.failover.test.js +404 -0
  101. package/lib/test/bulkPulling/tmq.test.js +135 -14
  102. package/lib/test/bulkPulling/urlParser.test.d.ts +2 -0
  103. package/lib/test/bulkPulling/urlParser.test.d.ts.map +1 -0
  104. package/lib/test/bulkPulling/urlParser.test.js +452 -0
  105. package/lib/test/bulkPulling/wsClient.reconnect.integration.test.d.ts +2 -0
  106. package/lib/test/bulkPulling/wsClient.reconnect.integration.test.d.ts.map +1 -0
  107. package/lib/test/bulkPulling/wsClient.reconnect.integration.test.js +184 -0
  108. package/lib/test/bulkPulling/wsClient.recovery.test.d.ts +2 -0
  109. package/lib/test/bulkPulling/wsClient.recovery.test.d.ts.map +1 -0
  110. package/lib/test/bulkPulling/wsClient.recovery.test.js +104 -0
  111. package/lib/test/bulkPulling/wsConfig.dsn.test.d.ts +2 -0
  112. package/lib/test/bulkPulling/wsConfig.dsn.test.d.ts.map +1 -0
  113. package/lib/test/bulkPulling/wsConfig.dsn.test.js +39 -0
  114. package/lib/test/bulkPulling/wsConnectPool.test.js +7 -7
  115. package/lib/test/bulkPulling/wsConnector.failover.test.d.ts +2 -0
  116. package/lib/test/bulkPulling/wsConnector.failover.test.d.ts.map +1 -0
  117. package/lib/test/bulkPulling/wsConnector.failover.test.js +497 -0
  118. package/lib/test/bulkPulling/wsConnectorPool.key.test.d.ts +2 -0
  119. package/lib/test/bulkPulling/wsConnectorPool.key.test.d.ts.map +1 -0
  120. package/lib/test/bulkPulling/wsConnectorPool.key.test.js +52 -0
  121. package/lib/test/bulkPulling/wsConnectorPool.keyAuth.test.d.ts +2 -0
  122. package/lib/test/bulkPulling/wsConnectorPool.keyAuth.test.d.ts.map +1 -0
  123. package/lib/test/bulkPulling/wsConnectorPool.keyAuth.test.js +28 -0
  124. package/lib/test/bulkPulling/wsEventCallback.test.d.ts +2 -0
  125. package/lib/test/bulkPulling/wsEventCallback.test.d.ts.map +1 -0
  126. package/lib/test/bulkPulling/wsEventCallback.test.js +54 -0
  127. package/lib/test/bulkPulling/wsProxy.failover.integration.test.d.ts +2 -0
  128. package/lib/test/bulkPulling/wsProxy.failover.integration.test.d.ts.map +1 -0
  129. package/lib/test/bulkPulling/wsProxy.failover.integration.test.js +120 -0
  130. package/lib/test/bulkPulling/wsProxy.failover.test.d.ts +2 -0
  131. package/lib/test/bulkPulling/wsProxy.failover.test.d.ts.map +1 -0
  132. package/lib/test/bulkPulling/wsProxy.failover.test.js +465 -0
  133. package/lib/test/client/wsClient.recovery.test.d.ts +2 -0
  134. package/lib/test/client/wsClient.recovery.test.d.ts.map +1 -0
  135. package/lib/test/client/wsClient.recovery.test.js +122 -0
  136. package/lib/test/client/wsConnectPool.test.d.ts +2 -0
  137. package/lib/test/client/wsConnectPool.test.d.ts.map +1 -0
  138. package/lib/test/client/wsConnectPool.test.js +147 -0
  139. package/lib/test/client/wsConnector.failover.test.d.ts +2 -0
  140. package/lib/test/client/wsConnector.failover.test.d.ts.map +1 -0
  141. package/lib/test/client/wsConnector.failover.test.js +681 -0
  142. package/lib/test/client/wsConnector.leastConnections.test.d.ts +2 -0
  143. package/lib/test/client/wsConnector.leastConnections.test.d.ts.map +1 -0
  144. package/lib/test/client/wsConnector.leastConnections.test.js +71 -0
  145. package/lib/test/client/wsConnectorPool.key.test.d.ts +2 -0
  146. package/lib/test/client/wsConnectorPool.key.test.d.ts.map +1 -0
  147. package/lib/test/client/wsConnectorPool.key.test.js +127 -0
  148. package/lib/test/client/wsEventCallback.test.d.ts +2 -0
  149. package/lib/test/client/wsEventCallback.test.d.ts.map +1 -0
  150. package/lib/test/client/wsEventCallback.test.js +98 -0
  151. package/lib/test/common/addressConnectionTracker.test.d.ts +2 -0
  152. package/lib/test/common/addressConnectionTracker.test.d.ts.map +1 -0
  153. package/lib/test/common/addressConnectionTracker.test.js +74 -0
  154. package/lib/test/common/dsn.test.d.ts +2 -0
  155. package/lib/test/common/dsn.test.d.ts.map +1 -0
  156. package/lib/test/common/dsn.test.js +406 -0
  157. package/lib/test/common/log.test.d.ts +2 -0
  158. package/lib/test/common/log.test.d.ts.map +1 -0
  159. package/lib/test/common/log.test.js +54 -0
  160. package/lib/test/common/utils.test.d.ts +2 -0
  161. package/lib/test/common/utils.test.d.ts.map +1 -0
  162. package/lib/test/common/utils.test.js +13 -0
  163. package/lib/test/common/wsConfig.dsn.test.d.ts +2 -0
  164. package/lib/test/common/wsConfig.dsn.test.d.ts.map +1 -0
  165. package/lib/test/common/wsConfig.dsn.test.js +39 -0
  166. package/lib/test/helpers/utils.d.ts +27 -0
  167. package/lib/test/helpers/utils.d.ts.map +1 -0
  168. package/lib/test/helpers/utils.js +341 -0
  169. package/lib/test/helpers/wsFailoverProxy.d.ts +109 -0
  170. package/lib/test/helpers/wsFailoverProxy.d.ts.map +1 -0
  171. package/lib/test/helpers/wsFailoverProxy.js +420 -0
  172. package/lib/test/helpers/wsProxy.d.ts +110 -0
  173. package/lib/test/helpers/wsProxy.d.ts.map +1 -0
  174. package/lib/test/helpers/wsProxy.js +429 -0
  175. package/lib/test/sql/core/decimal.test.d.ts +2 -0
  176. package/lib/test/sql/core/decimal.test.d.ts.map +1 -0
  177. package/lib/test/sql/core/decimal.test.js +153 -0
  178. package/lib/test/sql/core/queryTables.test.d.ts +2 -0
  179. package/lib/test/sql/core/queryTables.test.d.ts.map +1 -0
  180. package/lib/test/sql/core/queryTables.test.js +506 -0
  181. package/lib/test/sql/core/schemaless.test.d.ts +2 -0
  182. package/lib/test/sql/core/schemaless.test.d.ts.map +1 -0
  183. package/lib/test/sql/core/schemaless.test.js +102 -0
  184. package/lib/test/sql/core/sql.test.d.ts +2 -0
  185. package/lib/test/sql/core/sql.test.d.ts.map +1 -0
  186. package/lib/test/sql/core/sql.test.js +324 -0
  187. package/lib/test/sql/decimal.test.d.ts +2 -0
  188. package/lib/test/sql/decimal.test.d.ts.map +1 -0
  189. package/lib/test/sql/decimal.test.js +153 -0
  190. package/lib/test/sql/failover/sql.failover.test.d.ts +2 -0
  191. package/lib/test/sql/failover/sql.failover.test.d.ts.map +1 -0
  192. package/lib/test/sql/failover/sql.failover.test.js +341 -0
  193. package/lib/test/sql/queryTables.test.d.ts +2 -0
  194. package/lib/test/sql/queryTables.test.d.ts.map +1 -0
  195. package/lib/test/sql/queryTables.test.js +506 -0
  196. package/lib/test/sql/schemaless.test.d.ts +2 -0
  197. package/lib/test/sql/schemaless.test.d.ts.map +1 -0
  198. package/lib/test/sql/schemaless.test.js +102 -0
  199. package/lib/test/sql/sql.failover.test.d.ts +2 -0
  200. package/lib/test/sql/sql.failover.test.d.ts.map +1 -0
  201. package/lib/test/sql/sql.failover.test.js +341 -0
  202. package/lib/test/sql/sql.test.d.ts +2 -0
  203. package/lib/test/sql/sql.test.d.ts.map +1 -0
  204. package/lib/test/sql/sql.test.js +324 -0
  205. package/lib/test/stmt/failover/stmt2.failover.mock.test.d.ts +2 -0
  206. package/lib/test/stmt/failover/stmt2.failover.mock.test.d.ts.map +1 -0
  207. package/lib/test/stmt/failover/stmt2.failover.mock.test.js +341 -0
  208. package/lib/test/stmt/failover/stmt2.failover.test.d.ts +2 -0
  209. package/lib/test/stmt/failover/stmt2.failover.test.d.ts.map +1 -0
  210. package/lib/test/stmt/failover/stmt2.failover.test.js +384 -0
  211. package/lib/test/stmt/stmt1.func.test.d.ts +2 -0
  212. package/lib/test/stmt/stmt1.func.test.d.ts.map +1 -0
  213. package/lib/test/stmt/stmt1.func.test.js +418 -0
  214. package/lib/test/stmt/stmt1.type.test.d.ts +2 -0
  215. package/lib/test/stmt/stmt1.type.test.d.ts.map +1 -0
  216. package/lib/test/stmt/stmt1.type.test.js +399 -0
  217. package/lib/test/stmt/stmt2.failover.mock.test.d.ts +2 -0
  218. package/lib/test/stmt/stmt2.failover.mock.test.d.ts.map +1 -0
  219. package/lib/test/stmt/stmt2.failover.mock.test.js +341 -0
  220. package/lib/test/stmt/stmt2.failover.test.d.ts +2 -0
  221. package/lib/test/stmt/stmt2.failover.test.d.ts.map +1 -0
  222. package/lib/test/stmt/stmt2.failover.test.js +384 -0
  223. package/lib/test/stmt/stmt2.func.test.d.ts +2 -0
  224. package/lib/test/stmt/stmt2.func.test.d.ts.map +1 -0
  225. package/lib/test/stmt/stmt2.func.test.js +537 -0
  226. package/lib/test/stmt/stmt2.type.test.d.ts +2 -0
  227. package/lib/test/stmt/stmt2.type.test.d.ts.map +1 -0
  228. package/lib/test/stmt/stmt2.type.test.js +401 -0
  229. package/lib/test/stmt/v1/stmt1.func.test.d.ts +2 -0
  230. package/lib/test/stmt/v1/stmt1.func.test.d.ts.map +1 -0
  231. package/lib/test/stmt/v1/stmt1.func.test.js +418 -0
  232. package/lib/test/stmt/v1/stmt1.type.test.d.ts +2 -0
  233. package/lib/test/stmt/v1/stmt1.type.test.d.ts.map +1 -0
  234. package/lib/test/stmt/v1/stmt1.type.test.js +399 -0
  235. package/lib/test/stmt/v2/stmt2.func.test.d.ts +2 -0
  236. package/lib/test/stmt/v2/stmt2.func.test.d.ts.map +1 -0
  237. package/lib/test/stmt/v2/stmt2.func.test.js +537 -0
  238. package/lib/test/stmt/v2/stmt2.type.test.d.ts +2 -0
  239. package/lib/test/stmt/v2/stmt2.type.test.d.ts.map +1 -0
  240. package/lib/test/stmt/v2/stmt2.type.test.js +401 -0
  241. package/lib/test/tmq/cloud/cloud.tmq.test.d.ts +2 -0
  242. package/lib/test/tmq/cloud/cloud.tmq.test.d.ts.map +1 -0
  243. package/lib/test/tmq/cloud/cloud.tmq.test.js +84 -0
  244. package/lib/test/tmq/cloud/tmq.cloud.test.d.ts +2 -0
  245. package/lib/test/tmq/cloud/tmq.cloud.test.d.ts.map +1 -0
  246. package/lib/test/tmq/cloud/tmq.cloud.test.js +82 -0
  247. package/lib/test/tmq/core/tmq.config.test.d.ts +2 -0
  248. package/lib/test/tmq/core/tmq.config.test.d.ts.map +1 -0
  249. package/lib/test/tmq/core/tmq.config.test.js +83 -0
  250. package/lib/test/tmq/core/tmq.test.d.ts +2 -0
  251. package/lib/test/tmq/core/tmq.test.d.ts.map +1 -0
  252. package/lib/test/tmq/core/tmq.test.js +513 -0
  253. package/lib/test/tmq/failover/tmq.failover.test.d.ts +2 -0
  254. package/lib/test/tmq/failover/tmq.failover.test.d.ts.map +1 -0
  255. package/lib/test/tmq/failover/tmq.failover.test.js +404 -0
  256. package/lib/test/tmq/tmq.cloud.test.d.ts +2 -0
  257. package/lib/test/tmq/tmq.cloud.test.d.ts.map +1 -0
  258. package/lib/test/tmq/tmq.cloud.test.js +82 -0
  259. package/lib/test/tmq/tmq.config.test.d.ts +2 -0
  260. package/lib/test/tmq/tmq.config.test.d.ts.map +1 -0
  261. package/lib/test/tmq/tmq.config.test.js +94 -0
  262. package/lib/test/tmq/tmq.failover.test.d.ts +2 -0
  263. package/lib/test/tmq/tmq.failover.test.d.ts.map +1 -0
  264. package/lib/test/tmq/tmq.failover.test.js +404 -0
  265. package/lib/test/tmq/tmq.test.d.ts +2 -0
  266. package/lib/test/tmq/tmq.test.d.ts.map +1 -0
  267. package/lib/test/tmq/tmq.test.js +513 -0
  268. package/lib/test/unit/connectionManager.test.d.ts +2 -0
  269. package/lib/test/unit/connectionManager.test.d.ts.map +1 -0
  270. package/lib/test/unit/connectionManager.test.js +91 -0
  271. package/lib/test/utils.d.ts +4 -0
  272. package/lib/test/utils.d.ts.map +1 -1
  273. package/lib/test/utils.js +11 -17
  274. package/package.json +1 -1
  275. package/readme.md +2 -2
@@ -0,0 +1,120 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const wsConnectorPool_1 = require("../../src/client/wsConnectorPool");
4
+ const config_1 = require("../../src/common/config");
5
+ const wsSql_1 = require("../../src/sql/wsSql");
6
+ const utils_1 = require("../utils");
7
+ const wsProxy_1 = require("../helpers/wsProxy");
8
+ function parseBinaryAction(rawData) {
9
+ if (typeof rawData === "string" || rawData.byteLength < 24) {
10
+ return null;
11
+ }
12
+ return rawData.readBigInt64LE(16);
13
+ }
14
+ describe("ws proxy failover integration", () => {
15
+ jest.setTimeout(120 * 1000);
16
+ afterEach(async () => {
17
+ wsConnectorPool_1.WebSocketConnectionPool.instance().destroyed();
18
+ jest.restoreAllMocks();
19
+ });
20
+ test("switches to next address when active proxy restarts with downtime", async () => {
21
+ let restartTriggered = false;
22
+ let proxyBHadActivity = false;
23
+ let wsSql = null;
24
+ const proxyA = await wsProxy_1.WsProxy.create({
25
+ host: "127.0.0.1",
26
+ port: 0,
27
+ onEvent: (event, control) => {
28
+ if (event.type !== "message") {
29
+ return;
30
+ }
31
+ if (event.direction !== "client_to_upstream" || !event.isBinary) {
32
+ return;
33
+ }
34
+ const action = parseBinaryAction(event.rawData);
35
+ if (action === 6n && !restartTriggered) {
36
+ restartTriggered = true;
37
+ void control.restart({
38
+ downtimeMs: 800,
39
+ reason: "trigger dual-address failover",
40
+ });
41
+ }
42
+ },
43
+ });
44
+ const proxyB = await wsProxy_1.WsProxy.create({
45
+ host: "127.0.0.1",
46
+ port: 0,
47
+ onEvent: (event) => {
48
+ if (event.type === "client_connected") {
49
+ proxyBHadActivity = true;
50
+ return;
51
+ }
52
+ if (event.type === "message" &&
53
+ event.direction === "client_to_upstream") {
54
+ proxyBHadActivity = true;
55
+ }
56
+ },
57
+ });
58
+ const randomSpy = jest.spyOn(Math, "random").mockReturnValue(0);
59
+ try {
60
+ const dsn = `ws://${(0, utils_1.testUsername)()}:${(0, utils_1.testPassword)()}` +
61
+ `@127.0.0.1:${proxyA.getPort()},127.0.0.1:${proxyB.getPort()}` +
62
+ `?retries=1&retry_backoff_ms=20&retry_backoff_max_ms=20`;
63
+ const conf = new config_1.WSConfig(dsn);
64
+ conf.setTimeOut(6000);
65
+ wsSql = await wsSql_1.WsSql.open(conf);
66
+ const result = await wsSql.exec("select server_version()");
67
+ expect(result).toBeTruthy();
68
+ expect(restartTriggered).toBe(true);
69
+ expect(proxyBHadActivity).toBe(true);
70
+ }
71
+ finally {
72
+ randomSpy.mockRestore();
73
+ if (wsSql) {
74
+ await wsSql.close();
75
+ }
76
+ await proxyA.stop("test cleanup");
77
+ await proxyB.stop("test cleanup");
78
+ }
79
+ });
80
+ test("reconnects to same address after single proxy hard restart", async () => {
81
+ let restartCount = 0;
82
+ let wsSql = null;
83
+ const proxy = await wsProxy_1.WsProxy.create({
84
+ host: "127.0.0.1",
85
+ port: 0,
86
+ onEvent: (event, control) => {
87
+ if (event.type !== "message") {
88
+ return;
89
+ }
90
+ if (event.direction !== "client_to_upstream" || !event.isBinary) {
91
+ return;
92
+ }
93
+ const action = parseBinaryAction(event.rawData);
94
+ if (action === 6n && restartCount === 0) {
95
+ restartCount += 1;
96
+ void control.restart({
97
+ downtimeMs: 120,
98
+ reason: "trigger single-address reconnect",
99
+ });
100
+ }
101
+ },
102
+ });
103
+ try {
104
+ const dsn = `ws://${(0, utils_1.testUsername)()}:${(0, utils_1.testPassword)()}@127.0.0.1:${proxy.getPort()}` +
105
+ `?retries=6&retry_backoff_ms=30&retry_backoff_max_ms=60`;
106
+ const conf = new config_1.WSConfig(dsn);
107
+ conf.setTimeOut(6000);
108
+ wsSql = await wsSql_1.WsSql.open(conf);
109
+ const result = await wsSql.exec("select server_version()");
110
+ expect(result).toBeTruthy();
111
+ expect(restartCount).toBe(1);
112
+ }
113
+ finally {
114
+ if (wsSql) {
115
+ await wsSql.close();
116
+ }
117
+ await proxy.stop("test cleanup");
118
+ }
119
+ });
120
+ });
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=wsProxy.failover.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"wsProxy.failover.test.d.ts","sourceRoot":"","sources":["../../../test/bulkPulling/wsProxy.failover.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,465 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const wsConnectorPool_1 = require("../../src/client/wsConnectorPool");
4
+ const config_1 = require("../../src/common/config");
5
+ const wsSql_1 = require("../../src/sql/wsSql");
6
+ const constant_1 = require("../../src/tmq/constant");
7
+ const wsTmq_1 = require("../../src/tmq/wsTmq");
8
+ const utils_1 = require("../helpers/utils");
9
+ const wsProxy_1 = require("../helpers/wsProxy");
10
+ function parseBinaryAction(rawData) {
11
+ if (typeof rawData === "string" || rawData.byteLength < 24) {
12
+ return null;
13
+ }
14
+ return rawData.readBigInt64LE(16);
15
+ }
16
+ function parseJsonAction(rawData) {
17
+ if (typeof rawData !== "string") {
18
+ return null;
19
+ }
20
+ try {
21
+ const parsed = JSON.parse(rawData);
22
+ return typeof parsed.action === "string" ? parsed.action : null;
23
+ }
24
+ catch (_err) {
25
+ return null;
26
+ }
27
+ }
28
+ describe("ws proxy failover", () => {
29
+ jest.setTimeout(120 * 1000);
30
+ afterEach(async () => {
31
+ wsConnectorPool_1.WebSocketConnectionPool.instance().destroyed();
32
+ jest.restoreAllMocks();
33
+ });
34
+ test("switches to next address when active proxy restarts with downtime", async () => {
35
+ let restartTriggered = false;
36
+ let proxyBHadActivity = false;
37
+ let wsSql = null;
38
+ const proxyA = await wsProxy_1.WsProxy.create({
39
+ host: "127.0.0.1",
40
+ port: 0,
41
+ onEvent: (event, control) => {
42
+ if (event.type !== "message") {
43
+ return;
44
+ }
45
+ if (event.direction !== "client_to_upstream" || !event.isBinary) {
46
+ return;
47
+ }
48
+ const action = parseBinaryAction(event.rawData);
49
+ if (action === 6n && !restartTriggered) {
50
+ restartTriggered = true;
51
+ void control.restart({
52
+ downtimeMs: 800,
53
+ reason: "trigger dual-address failover",
54
+ });
55
+ }
56
+ },
57
+ });
58
+ const proxyB = await wsProxy_1.WsProxy.create({
59
+ host: "127.0.0.1",
60
+ port: 0,
61
+ onEvent: (event) => {
62
+ if (event.type === "client_connected") {
63
+ proxyBHadActivity = true;
64
+ return;
65
+ }
66
+ if (event.type === "message" &&
67
+ event.direction === "client_to_upstream") {
68
+ proxyBHadActivity = true;
69
+ }
70
+ },
71
+ });
72
+ const randomSpy = jest.spyOn(Math, "random").mockReturnValue(0);
73
+ try {
74
+ const dsn = `ws://${(0, utils_1.testUsername)()}:${(0, utils_1.testPassword)()}` +
75
+ `@127.0.0.1:${proxyA.getPort()},127.0.0.1:${proxyB.getPort()}` +
76
+ `?retries=5&retry_backoff_ms=20&retry_backoff_max_ms=20`;
77
+ const conf = new config_1.WSConfig(dsn);
78
+ conf.setTimeOut(6000);
79
+ wsSql = await wsSql_1.WsSql.open(conf);
80
+ const result = await wsSql.exec("select server_version()");
81
+ expect(result).toBeTruthy();
82
+ expect(restartTriggered).toBe(true);
83
+ expect(proxyBHadActivity).toBe(true);
84
+ }
85
+ finally {
86
+ randomSpy.mockRestore();
87
+ if (wsSql) {
88
+ await wsSql.close();
89
+ }
90
+ await proxyA.stop("test cleanup");
91
+ await proxyB.stop("test cleanup");
92
+ }
93
+ });
94
+ test("reconnects to same address after single proxy hard restart", async () => {
95
+ let restartCount = 0;
96
+ let wsSql = null;
97
+ const proxy = await wsProxy_1.WsProxy.create({
98
+ host: "127.0.0.1",
99
+ port: 0,
100
+ onEvent: (event, control) => {
101
+ if (event.type !== "message") {
102
+ return;
103
+ }
104
+ if (event.direction !== "client_to_upstream" || !event.isBinary) {
105
+ return;
106
+ }
107
+ const action = parseBinaryAction(event.rawData);
108
+ if (action === 6n && restartCount === 0) {
109
+ restartCount += 1;
110
+ void control.restart({
111
+ downtimeMs: 120,
112
+ reason: "trigger single-address reconnect",
113
+ });
114
+ }
115
+ },
116
+ });
117
+ try {
118
+ const dsn = `ws://${(0, utils_1.testUsername)()}:${(0, utils_1.testPassword)()}@127.0.0.1:${proxy.getPort()}` +
119
+ `?retries=6&retry_backoff_ms=30&retry_backoff_max_ms=60`;
120
+ const conf = new config_1.WSConfig(dsn);
121
+ conf.setTimeOut(6000);
122
+ wsSql = await wsSql_1.WsSql.open(conf);
123
+ const result = await wsSql.exec("select server_version()");
124
+ expect(result).toBeTruthy();
125
+ expect(restartCount).toBe(1);
126
+ }
127
+ finally {
128
+ if (wsSql) {
129
+ await wsSql.close();
130
+ }
131
+ await proxy.stop("test cleanup");
132
+ }
133
+ });
134
+ test("keeps all 5000 rows with single-address random proxy restarts during inserts", async () => {
135
+ const targetRows = 5000;
136
+ const baseTs = 1700000000000;
137
+ const dbName = "test_1773988174";
138
+ const tableName = "t0";
139
+ let wsSql = null;
140
+ let setupSql = null;
141
+ let cleanupSql = null;
142
+ let writePhase = false;
143
+ let restartInFlight = false;
144
+ let restartCount = 0;
145
+ let forwardedInsertFrames = 0;
146
+ const localDsn = `ws://${(0, utils_1.testUsername)()}:${(0, utils_1.testPassword)()}@127.0.0.1:6041`;
147
+ const setupConf = new config_1.WSConfig(localDsn);
148
+ setupConf.setTimeOut(6000);
149
+ setupSql = await wsSql_1.WsSql.open(setupConf);
150
+ try {
151
+ await setupSql.exec(`drop database if exists ${dbName}`);
152
+ await setupSql.exec(`create database if not exists ${dbName}`);
153
+ }
154
+ finally {
155
+ await setupSql.close();
156
+ setupSql = null;
157
+ }
158
+ const proxy = await wsProxy_1.WsProxy.create({
159
+ host: "127.0.0.1",
160
+ port: 0,
161
+ onEvent: (event, control) => {
162
+ if (!writePhase || restartInFlight) {
163
+ return;
164
+ }
165
+ if (event.type !== "message") {
166
+ return;
167
+ }
168
+ if (event.direction !== "client_to_upstream" || !event.isBinary) {
169
+ return;
170
+ }
171
+ const action = parseBinaryAction(event.rawData);
172
+ if (action !== 6n) {
173
+ return;
174
+ }
175
+ forwardedInsertFrames += 1;
176
+ if (Math.random() < 0.003) {
177
+ restartInFlight = true;
178
+ restartCount += 1;
179
+ const downtimeMs = 10 + Math.floor(Math.random() * 60);
180
+ void control.restart({
181
+ downtimeMs,
182
+ reason: `random restart #${restartCount}`,
183
+ }).finally(() => {
184
+ restartInFlight = false;
185
+ });
186
+ }
187
+ },
188
+ });
189
+ try {
190
+ const dsn = `ws://${(0, utils_1.testUsername)()}:${(0, utils_1.testPassword)()}@127.0.0.1:${proxy.getPort()}` +
191
+ `?retries=30&retry_backoff_ms=5&retry_backoff_max_ms=20`;
192
+ const conf = new config_1.WSConfig(dsn);
193
+ conf.setDb(dbName);
194
+ conf.setTimeOut(10000);
195
+ wsSql = await wsSql_1.WsSql.open(conf);
196
+ await wsSql.exec(`create table ${tableName}(ts timestamp, c1 int)`);
197
+ writePhase = true;
198
+ for (let i = 0; i < targetRows; i++) {
199
+ await wsSql.exec(`insert into ${tableName} values(${baseTs + i}, ${i})`);
200
+ }
201
+ writePhase = false;
202
+ const countResult = await wsSql.exec(`select count(*) from ${tableName}`);
203
+ const countValue = countResult.getData()?.[0]?.[0];
204
+ const rowCount = typeof countValue === "bigint"
205
+ ? Number(countValue)
206
+ : Number(countValue || 0);
207
+ expect(rowCount).toBe(targetRows);
208
+ expect(forwardedInsertFrames).toBeGreaterThanOrEqual(targetRows);
209
+ expect(restartCount).toBeGreaterThan(0);
210
+ }
211
+ finally {
212
+ writePhase = false;
213
+ if (wsSql) {
214
+ await wsSql.close();
215
+ }
216
+ await proxy.stop("test cleanup");
217
+ const cleanupConf = new config_1.WSConfig(localDsn);
218
+ cleanupConf.setTimeOut(6000);
219
+ cleanupSql = await wsSql_1.WsSql.open(cleanupConf);
220
+ try {
221
+ await cleanupSql.exec(`drop database if exists ${dbName}`);
222
+ }
223
+ finally {
224
+ await cleanupSql.close();
225
+ cleanupSql = null;
226
+ }
227
+ }
228
+ }, 300 * 1000);
229
+ test("keeps all 5000 rows with three-address random proxy restarts during inserts", async () => {
230
+ const targetRows = 5000;
231
+ const baseTs = 1700010000000;
232
+ const dbName = "test_1773989170";
233
+ const tableName = "t0";
234
+ const proxyStates = new Map();
235
+ let wsSql = null;
236
+ let setupSql = null;
237
+ let cleanupSql = null;
238
+ let writePhase = false;
239
+ const localDsn = `ws://${(0, utils_1.testUsername)()}:${(0, utils_1.testPassword)()}@127.0.0.1:6041`;
240
+ const setupConf = new config_1.WSConfig(localDsn);
241
+ setupConf.setTimeOut(6000);
242
+ setupSql = await wsSql_1.WsSql.open(setupConf);
243
+ try {
244
+ await setupSql.exec(`drop database if exists ${dbName}`);
245
+ await setupSql.exec(`create database if not exists ${dbName}`);
246
+ }
247
+ finally {
248
+ await setupSql.close();
249
+ setupSql = null;
250
+ }
251
+ const createRandomRestartProxy = async (name) => {
252
+ proxyStates.set(name, {
253
+ forwarded: 0,
254
+ restarting: false,
255
+ restarts: 0,
256
+ });
257
+ return wsProxy_1.WsProxy.create({
258
+ host: "127.0.0.1",
259
+ port: 0,
260
+ onEvent: (event, control) => {
261
+ if (!writePhase) {
262
+ return;
263
+ }
264
+ if (event.type !== "message") {
265
+ return;
266
+ }
267
+ if (event.direction !== "client_to_upstream" || !event.isBinary) {
268
+ return;
269
+ }
270
+ const action = parseBinaryAction(event.rawData);
271
+ if (action !== 6n) {
272
+ return;
273
+ }
274
+ const state = proxyStates.get(name);
275
+ if (!state) {
276
+ return;
277
+ }
278
+ state.forwarded += 1;
279
+ if (state.restarting) {
280
+ return;
281
+ }
282
+ if (Math.random() >= 0.003) {
283
+ return;
284
+ }
285
+ state.restarting = true;
286
+ state.restarts += 1;
287
+ const downtimeMs = 80 + Math.floor(Math.random() * 120);
288
+ void control
289
+ .restart({
290
+ downtimeMs,
291
+ reason: `${name} random restart #${state.restarts}`,
292
+ })
293
+ .finally(() => {
294
+ const latestState = proxyStates.get(name);
295
+ if (latestState) {
296
+ latestState.restarting = false;
297
+ }
298
+ });
299
+ },
300
+ });
301
+ };
302
+ const proxyA = await createRandomRestartProxy("proxy_a");
303
+ const proxyB = await createRandomRestartProxy("proxy_b");
304
+ const proxyC = await createRandomRestartProxy("proxy_c");
305
+ try {
306
+ const dsn = `ws://${(0, utils_1.testUsername)()}:${(0, utils_1.testPassword)()}` +
307
+ `@127.0.0.1:${proxyA.getPort()},127.0.0.1:${proxyB.getPort()},127.0.0.1:${proxyC.getPort()}` +
308
+ `?retries=24&retry_backoff_ms=8&retry_backoff_max_ms=25`;
309
+ const conf = new config_1.WSConfig(dsn);
310
+ conf.setDb(dbName);
311
+ conf.setTimeOut(10000);
312
+ wsSql = await wsSql_1.WsSql.open(conf);
313
+ await wsSql.exec(`create table ${tableName}(ts timestamp, c1 int)`);
314
+ writePhase = true;
315
+ for (let i = 0; i < targetRows; i++) {
316
+ await wsSql.exec(`insert into ${tableName} values(${baseTs + i}, ${i})`);
317
+ }
318
+ writePhase = false;
319
+ const countResult = await wsSql.exec(`select count(*) from ${tableName}`);
320
+ const countValue = countResult.getData()?.[0]?.[0];
321
+ const rowCount = typeof countValue === "bigint"
322
+ ? Number(countValue)
323
+ : Number(countValue || 0);
324
+ const totalForwarded = Array.from(proxyStates.values()).reduce((sum, state) => sum + state.forwarded, 0);
325
+ const totalRestarts = Array.from(proxyStates.values()).reduce((sum, state) => sum + state.restarts, 0);
326
+ expect(rowCount).toBe(targetRows);
327
+ expect(totalForwarded).toBeGreaterThanOrEqual(targetRows);
328
+ expect(totalRestarts).toBeGreaterThan(0);
329
+ }
330
+ finally {
331
+ writePhase = false;
332
+ if (wsSql) {
333
+ await wsSql.close();
334
+ }
335
+ await Promise.all([
336
+ proxyA.stop("test cleanup"),
337
+ proxyB.stop("test cleanup"),
338
+ proxyC.stop("test cleanup"),
339
+ ]);
340
+ const cleanupConf = new config_1.WSConfig(localDsn);
341
+ cleanupConf.setTimeOut(6000);
342
+ cleanupSql = await wsSql_1.WsSql.open(cleanupConf);
343
+ try {
344
+ await cleanupSql.exec(`drop database if exists ${dbName}`);
345
+ }
346
+ finally {
347
+ await cleanupSql.close();
348
+ cleanupSql = null;
349
+ }
350
+ }
351
+ }, 300 * 1000);
352
+ test("tmq failover recovers subscribe context and replays inflight poll", async () => {
353
+ const dbName = "test_1774096925";
354
+ const tableName = "t0";
355
+ const topicName = "topic_1774096925";
356
+ const localDsn = `ws://${(0, utils_1.testUsername)()}:${(0, utils_1.testPassword)()}@127.0.0.1:6041`;
357
+ let setupSql = null;
358
+ let cleanupSql = null;
359
+ let consumer = null;
360
+ let restartTriggered = false;
361
+ let proxyBHadActivity = false;
362
+ setupSql = await wsSql_1.WsSql.open(new config_1.WSConfig(localDsn));
363
+ try {
364
+ await setupSql.exec(`drop topic if exists ${topicName}`);
365
+ await setupSql.exec(`drop database if exists ${dbName}`);
366
+ await setupSql.exec(`create database ${dbName}`);
367
+ await setupSql.exec(`create table ${dbName}.${tableName}(ts timestamp, c1 int)`);
368
+ await setupSql.exec(`insert into ${dbName}.${tableName} values(now - 1s, 1) (now, 2)`);
369
+ await setupSql.exec(`create topic ${topicName} as select * from ${dbName}.${tableName}`);
370
+ }
371
+ finally {
372
+ await setupSql.close();
373
+ setupSql = null;
374
+ }
375
+ const proxyA = await wsProxy_1.WsProxy.create({
376
+ host: "127.0.0.1",
377
+ port: 0,
378
+ onEvent: (event, control) => {
379
+ if (event.type !== "message") {
380
+ return;
381
+ }
382
+ if (event.direction !== "client_to_upstream") {
383
+ return;
384
+ }
385
+ const action = parseJsonAction(event.rawData);
386
+ if (action === "poll" && !restartTriggered) {
387
+ restartTriggered = true;
388
+ void control.restart({
389
+ downtimeMs: 350,
390
+ reason: "trigger tmq poll failover",
391
+ });
392
+ }
393
+ },
394
+ });
395
+ const proxyB = await wsProxy_1.WsProxy.create({
396
+ host: "127.0.0.1",
397
+ port: 0,
398
+ onEvent: (event) => {
399
+ if (event.type === "client_connected") {
400
+ proxyBHadActivity = true;
401
+ return;
402
+ }
403
+ if (event.type === "message" &&
404
+ event.direction === "client_to_upstream") {
405
+ proxyBHadActivity = true;
406
+ }
407
+ },
408
+ });
409
+ const tmqConf = new Map([
410
+ [constant_1.TMQConstants.GROUP_ID, `g_${Date.now()}`],
411
+ [constant_1.TMQConstants.CLIENT_ID, `c_${Date.now()}`],
412
+ [constant_1.TMQConstants.CONNECT_USER, (0, utils_1.testUsername)()],
413
+ [constant_1.TMQConstants.CONNECT_PASS, (0, utils_1.testPassword)()],
414
+ [constant_1.TMQConstants.AUTO_OFFSET_RESET, "earliest"],
415
+ [constant_1.TMQConstants.ENABLE_AUTO_COMMIT, false],
416
+ [constant_1.TMQConstants.AUTO_COMMIT_INTERVAL_MS, 1000],
417
+ [constant_1.TMQConstants.WS_URL,
418
+ `ws://${(0, utils_1.testUsername)()}:${(0, utils_1.testPassword)()}` +
419
+ `@127.0.0.1:${proxyA.getPort()},127.0.0.1:${proxyB.getPort()}` +
420
+ `?retries=6&retry_backoff_ms=20&retry_backoff_max_ms=60`
421
+ ],
422
+ ]);
423
+ const randomSpy = jest.spyOn(Math, "random").mockReturnValue(0);
424
+ try {
425
+ consumer = await wsTmq_1.WsConsumer.newConsumer(tmqConf);
426
+ await consumer.subscribe([topicName]);
427
+ let rows = 0;
428
+ for (let i = 0; i < 8 && rows === 0; i++) {
429
+ const res = await consumer.poll(800);
430
+ for (const [, value] of res) {
431
+ const data = value.getData();
432
+ rows += data?.length || 0;
433
+ }
434
+ }
435
+ expect(restartTriggered).toBe(true);
436
+ expect(proxyBHadActivity).toBe(true);
437
+ expect(rows).toBeGreaterThan(0);
438
+ }
439
+ finally {
440
+ if (consumer) {
441
+ try {
442
+ await consumer.unsubscribe();
443
+ }
444
+ catch (_err) {
445
+ // ignore cleanup error
446
+ }
447
+ await consumer.close();
448
+ }
449
+ await Promise.all([
450
+ proxyA.stop("test cleanup"),
451
+ proxyB.stop("test cleanup"),
452
+ ]);
453
+ randomSpy.mockRestore();
454
+ cleanupSql = await wsSql_1.WsSql.open(new config_1.WSConfig(localDsn));
455
+ try {
456
+ await cleanupSql.exec(`drop topic if exists ${topicName}`);
457
+ await cleanupSql.exec(`drop database if exists ${dbName}`);
458
+ }
459
+ finally {
460
+ await cleanupSql.close();
461
+ cleanupSql = null;
462
+ }
463
+ }
464
+ }, 180 * 1000);
465
+ });
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=wsClient.recovery.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"wsClient.recovery.test.d.ts","sourceRoot":"","sources":["../../../test/client/wsClient.recovery.test.ts"],"names":[],"mappings":""}