spice-couchbase 2.0.63 → 2.0.64

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.
@@ -1,16 +1,21 @@
1
1
  "use strict";
2
2
 
3
- function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); keys.push.apply(keys, symbols); } return keys; }
4
-
5
- function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(Object(source), true).forEach(function (key) { _defineProperty(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; }
6
-
7
- function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
8
-
9
- function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { Promise.resolve(value).then(_next, _throw); } }
10
-
11
- function _asyncToGenerator(fn) { return function () { var self = this, args = arguments; return new Promise(function (resolve, reject) { var gen = fn.apply(self, args); function _next(value) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value); } function _throw(err) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "throw", err); } _next(undefined); }); }; }
12
-
13
- var couchbase = require("couchbase"); // Connection health monitor and self-healing reconnect.
3
+ function _slicedToArray(r, e) { return _arrayWithHoles(r) || _iterableToArrayLimit(r, e) || _unsupportedIterableToArray(r, e) || _nonIterableRest(); }
4
+ function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); }
5
+ function _unsupportedIterableToArray(r, a) { if (r) { if ("string" == typeof r) return _arrayLikeToArray(r, a); var t = {}.toString.call(r).slice(8, -1); return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0; } }
6
+ function _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length); for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e]; return n; }
7
+ function _iterableToArrayLimit(r, l) { var t = null == r ? null : "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (null != t) { var e, n, i, u, a = [], f = !0, o = !1; try { if (i = (t = t.call(r)).next, 0 === l) { if (Object(t) !== t) return; f = !1; } else for (; !(f = (e = i.call(t)).done) && (a.push(e.value), a.length !== l); f = !0); } catch (r) { o = !0, n = r; } finally { try { if (!f && null != t.return && (u = t.return(), Object(u) !== u)) return; } finally { if (o) throw n; } } return a; } }
8
+ function _arrayWithHoles(r) { if (Array.isArray(r)) return r; }
9
+ function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
10
+ function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
11
+ function _defineProperty(e, r, t) { return (r = _toPropertyKey(r)) in e ? Object.defineProperty(e, r, { value: t, enumerable: !0, configurable: !0, writable: !0 }) : e[r] = t, e; }
12
+ function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == typeof i ? i : i + ""; }
13
+ function _toPrimitive(t, e) { if ("object" != typeof t || !t) return t; var r; if ("undefined" != typeof Symbol && void 0 !== (r = t[Symbol.toPrimitive])) { var i = r.call(t, e || "default"); if ("object" != typeof i) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === e ? String : Number)(t); }
14
+ function asyncGeneratorStep(n, t, e, r, o, a, c) { try { var i = n[a](c), u = i.value; } catch (n) { return void e(n); } i.done ? t(u) : Promise.resolve(u).then(r, o); }
15
+ function _asyncToGenerator(n) { return function () { var t = this, e = arguments; return new Promise(function (r, o) { var a = n.apply(t, e); function _next(n) { asyncGeneratorStep(a, r, o, _next, _throw, "next", n); } function _throw(n) { asyncGeneratorStep(a, r, o, _next, _throw, "throw", n); } _next(void 0); }); }; }
16
+ var couchbase = require("couchbase");
17
+
18
+ // Connection health monitor and self-healing reconnect.
14
19
  //
15
20
  // The cluster object is created once at boot and stored on the `spice.database`
16
21
  // globals. When the SDK wedges (every operation times out, but the SDK never
@@ -28,18 +33,14 @@ var couchbase = require("couchbase"); // Connection health monitor and self-heal
28
33
  // 4. Escalation: if the connection stays unhealthy for too long, terminate
29
34
  // the process so the supervisor (PM2) restarts it.
30
35
 
31
-
32
36
  function envNumber(name, fallback) {
33
37
  var raw = process.env[name];
34
-
35
38
  if (raw === undefined || raw === null || String(raw).trim() === "") {
36
39
  return fallback;
37
40
  }
38
-
39
41
  var value = Number(raw);
40
42
  return Number.isFinite(value) && value >= 0 ? value : fallback;
41
43
  }
42
-
43
44
  function defaultSettings() {
44
45
  return {
45
46
  // 0 disables the interval probe (the passive signal still works).
@@ -59,94 +60,77 @@ function defaultSettings() {
59
60
  exitAfterMs: envNumber("SPICE_COUCHBASE_EXIT_AFTER_MS", 300000)
60
61
  };
61
62
  }
62
-
63
63
  var CONNECTIVITY_ERRORS = [couchbase.TimeoutError, couchbase.RequestCanceledError, couchbase.ServiceNotAvailableError].filter(type => typeof type === "function");
64
-
65
64
  function isConnectivityError(error) {
66
65
  if (!error || typeof error !== "object") return false;
67
- if (CONNECTIVITY_ERRORS.some(type => error instanceof type)) return true; // Our own deadlines, and SDK errors that crossed a module boundary.
68
-
66
+ if (CONNECTIVITY_ERRORS.some(type => error instanceof type)) return true;
67
+ // Our own deadlines, and SDK errors that crossed a module boundary.
69
68
  return error.spiceConnectivity === true || ["TimeoutError", "AmbiguousTimeoutError", "UnambiguousTimeoutError", "RequestCanceledError", "ServiceNotAvailableError"].includes(error.name);
70
69
  }
71
-
72
70
  function deadline(promise, ms, label) {
73
71
  var timer;
74
72
  return Promise.race([promise, new Promise((_resolve, reject) => {
75
73
  var _timer$unref, _timer;
76
-
77
74
  timer = setTimeout(() => {
78
75
  var error = new Error("".concat(label, " timed out after ").concat(ms, "ms"));
79
76
  error.spiceConnectivity = true;
80
77
  reject(error);
81
78
  }, ms);
82
- (_timer$unref = (_timer = timer).unref) === null || _timer$unref === void 0 ? void 0 : _timer$unref.call(_timer);
79
+ (_timer$unref = (_timer = timer).unref) === null || _timer$unref === void 0 || _timer$unref.call(_timer);
83
80
  })]).finally(() => clearTimeout(timer));
84
81
  }
85
-
86
82
  function describe(error) {
87
83
  return (error === null || error === void 0 ? void 0 : error.message) || String(error);
88
84
  }
89
-
90
85
  function closeQuietly(_x, _x2, _x3) {
91
86
  return _closeQuietly.apply(this, arguments);
92
87
  }
93
-
94
88
  function _closeQuietly() {
95
89
  _closeQuietly = _asyncToGenerator(function* (cluster, log, label) {
96
90
  try {
97
91
  var _cluster$close;
98
-
99
- yield cluster === null || cluster === void 0 ? void 0 : (_cluster$close = cluster.close) === null || _cluster$close === void 0 ? void 0 : _cluster$close.call(cluster);
92
+ yield cluster === null || cluster === void 0 || (_cluster$close = cluster.close) === null || _cluster$close === void 0 ? void 0 : _cluster$close.call(cluster);
100
93
  } catch (error) {
101
94
  log.warn("[spice-couchbase] Closing ".concat(label, " failed: ").concat(describe(error)));
102
95
  }
103
96
  });
104
97
  return _closeQuietly.apply(this, arguments);
105
98
  }
106
-
107
99
  function buildConnectOptions() {
108
100
  var config = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
109
101
  var options = {
110
102
  username: config.username,
111
103
  password: config.password
112
104
  };
113
-
114
105
  if (config.configProfile) {
115
106
  options.configProfile = config.configProfile;
116
107
  }
117
-
118
108
  var timeouts = {};
119
-
120
109
  for (var key of ["kvTimeout", "kvDurableTimeout", "queryTimeout", "searchTimeout", "analyticsTimeout", "managementTimeout", "connectTimeout", "bootstrapTimeout", "resolveTimeout"]) {
121
110
  var _config$timeouts;
122
-
123
111
  var raw = (_config$timeouts = config.timeouts) === null || _config$timeouts === void 0 ? void 0 : _config$timeouts[key];
124
112
  if (raw === undefined || raw === null || raw === "") continue;
125
113
  var value = Number(raw);
126
114
  if (Number.isFinite(value) && value > 0) timeouts[key] = value;
127
115
  }
128
-
129
116
  if (Object.keys(timeouts).length > 0) {
130
117
  options.timeouts = timeouts;
131
118
  }
132
-
133
119
  return options;
134
120
  }
121
+
135
122
  /**
136
123
  * Opens a cluster and resolves the bucket, scope and collection handles for a
137
124
  * connection config. Shared by the boot-time connect and the reconnect path.
138
125
  */
139
-
140
-
141
126
  function openHandles(_x4) {
142
127
  return _openHandles.apply(this, arguments);
143
128
  }
144
-
145
129
  function _openHandles() {
146
130
  _openHandles = _asyncToGenerator(function* (config) {
147
- var {
148
- connect = couchbase.connect
149
- } = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
131
+ var _ref6 = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {},
132
+ _ref6$connect = _ref6.connect,
133
+ connect = _ref6$connect === void 0 ? couchbase.connect : _ref6$connect;
150
134
  var cluster = yield connect(config.nodes, buildConnectOptions(config));
151
135
  var bucket = cluster.bucket(config.bucket);
152
136
  var scope = bucket.scope(config.scope || "_default");
@@ -160,38 +144,36 @@ function _openHandles() {
160
144
  });
161
145
  return _openHandles.apply(this, arguments);
162
146
  }
163
-
164
147
  function createHealthMonitor() {
165
- var {
166
- settings: overrides = {},
167
- connect = couchbase.connect,
168
- now = () => Date.now(),
169
- random = Math.random,
170
- log = console,
171
- getGlobals = () => typeof spice !== "undefined" ? spice : undefined,
172
- terminate
173
- } = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
174
-
175
- var settings = _objectSpread({}, defaultSettings(), {}, overrides);
176
-
148
+ var _ref = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {},
149
+ _ref$settings = _ref.settings,
150
+ overrides = _ref$settings === void 0 ? {} : _ref$settings,
151
+ _ref$connect = _ref.connect,
152
+ connect = _ref$connect === void 0 ? couchbase.connect : _ref$connect,
153
+ _ref$now = _ref.now,
154
+ now = _ref$now === void 0 ? () => Date.now() : _ref$now,
155
+ _ref$random = _ref.random,
156
+ random = _ref$random === void 0 ? Math.random : _ref$random,
157
+ _ref$log = _ref.log,
158
+ log = _ref$log === void 0 ? console : _ref$log,
159
+ _ref$getGlobals = _ref.getGlobals,
160
+ getGlobals = _ref$getGlobals === void 0 ? () => typeof spice !== "undefined" ? spice : undefined : _ref$getGlobals,
161
+ terminate = _ref.terminate;
162
+ var settings = _objectSpread(_objectSpread({}, defaultSettings()), overrides);
177
163
  var states = new Map();
178
164
  var stopped = false;
179
-
180
165
  var terminateProcess = terminate || (() => {
181
166
  var _fallback$unref;
182
-
183
167
  // SIGTERM runs the application's graceful shutdown when it has one,
184
168
  // and terminates the process when it does not. The fallback covers a
185
169
  // shutdown that hangs on the very connection that is stuck.
186
170
  process.exitCode = 1;
187
171
  var fallback = setTimeout(() => process.exit(1), 15000);
188
- (_fallback$unref = fallback.unref) === null || _fallback$unref === void 0 ? void 0 : _fallback$unref.call(fallback);
172
+ (_fallback$unref = fallback.unref) === null || _fallback$unref === void 0 || _fallback$unref.call(fallback);
189
173
  process.kill(process.pid, "SIGTERM");
190
174
  });
191
-
192
175
  function stateFor(name) {
193
176
  var state = states.get(name);
194
-
195
177
  if (!state) {
196
178
  state = {
197
179
  name,
@@ -213,13 +195,10 @@ function createHealthMonitor() {
213
195
  };
214
196
  states.set(name, state);
215
197
  }
216
-
217
198
  return state;
218
199
  }
219
-
220
200
  function currentHandles(name) {
221
201
  var _getGlobals, _database$clusters, _database$buckets, _database$scopes, _database$connections;
222
-
223
202
  var database = (_getGlobals = getGlobals()) === null || _getGlobals === void 0 ? void 0 : _getGlobals.database;
224
203
  if (!database) return null;
225
204
  return {
@@ -230,118 +209,138 @@ function createHealthMonitor() {
230
209
  };
231
210
  }
232
211
 
233
- function probe(_x5) {
212
+ // KV is checked with a bucket ping, not a document operation: the boot-time
213
+ // collection handle can name a collection that does not exist (a named
214
+ // scope has no `_default` collection), and KV operations against it retry
215
+ // until they time out even though every real request succeeds.
216
+ function probeKv(_x5, _x6) {
217
+ return _probeKv.apply(this, arguments);
218
+ }
219
+ function _probeKv() {
220
+ _probeKv = _asyncToGenerator(function* (bucket, timeout) {
221
+ var _report;
222
+ var report;
223
+ try {
224
+ report = yield bucket.ping({
225
+ serviceTypes: [couchbase.ServiceType.KeyValue],
226
+ timeout
227
+ });
228
+ } catch (error) {
229
+ throw labelled(error, "KV ping");
230
+ }
231
+ var endpoints = Object.values(((_report = report) === null || _report === void 0 ? void 0 : _report.services) || {}).flat();
232
+ // One node answering is enough. A single node being down is the cluster's
233
+ // problem, and a fresh connection would not fix it.
234
+ if (!endpoints.some(endpoint => (endpoint === null || endpoint === void 0 ? void 0 : endpoint.state) === couchbase.PingState.Ok)) {
235
+ var _states = endpoints.map(endpoint => {
236
+ var _couchbase$PingState$;
237
+ return (_couchbase$PingState$ = couchbase.PingState[endpoint === null || endpoint === void 0 ? void 0 : endpoint.state]) !== null && _couchbase$PingState$ !== void 0 ? _couchbase$PingState$ : endpoint === null || endpoint === void 0 ? void 0 : endpoint.state;
238
+ }).join(", ");
239
+ throw labelled(new Error("no endpoint responded (".concat(_states || "no endpoints", ")")), "KV ping");
240
+ }
241
+ });
242
+ return _probeKv.apply(this, arguments);
243
+ }
244
+ function labelled(error, label) {
245
+ var wrapped = new Error("".concat(label, " failed: ").concat(describe(error)));
246
+ wrapped.cause = error;
247
+ wrapped.spiceConnectivity = true;
248
+ return wrapped;
249
+ }
250
+ function probe(_x7) {
234
251
  return _probe.apply(this, arguments);
235
252
  }
236
-
237
253
  function _probe() {
238
254
  _probe = _asyncToGenerator(function* (handles) {
239
- if (!(handles === null || handles === void 0 ? void 0 : handles.cluster) || !(handles === null || handles === void 0 ? void 0 : handles.collection)) {
255
+ if (!(handles !== null && handles !== void 0 && handles.cluster) || !(handles !== null && handles !== void 0 && handles.bucket)) {
240
256
  var error = new Error("No cluster handles to probe");
241
257
  error.spiceConnectivity = true;
242
258
  throw error;
243
259
  }
244
-
245
- var timeout = settings.probeTimeoutMs; // The hard deadline is the point: a wedged SDK may never honour its own.
246
-
247
- yield deadline(Promise.all([handles.collection.exists("__spice_health_probe__", {
248
- timeout
249
- }), handles.cluster.query("SELECT 1", {
260
+ var timeout = settings.probeTimeoutMs;
261
+ // The hard deadline is the point: a wedged SDK may never honour its own.
262
+ yield deadline(Promise.all([probeKv(handles.bucket, timeout), handles.cluster.query("SELECT 1", {
250
263
  timeout
264
+ }).catch(error => {
265
+ throw labelled(error, "Query probe");
251
266
  })]), timeout + 1000, "Couchbase health probe");
252
267
  });
253
268
  return _probe.apply(this, arguments);
254
269
  }
255
-
256
270
  function markHealthy(state) {
257
271
  state.probeFailures = 0;
258
272
  state.failures = [];
259
273
  state.reconnectAttempts = 0;
260
274
  state.nextReconnectAt = 0;
261
-
262
275
  if (!state.healthy) {
263
276
  var downFor = state.unhealthySince ? now() - state.unhealthySince : 0;
264
277
  log.log("[spice-couchbase] Connection \"".concat(state.name, "\" healthy again ") + "(generation ".concat(state.generation, ", unhealthy for ").concat(downFor, "ms)"));
265
278
  }
266
-
267
279
  state.healthy = true;
268
280
  state.unhealthySince = null;
269
281
  state.lastError = null;
270
282
  }
271
-
272
283
  function markUnhealthy(state, error) {
273
284
  state.lastError = describe(error);
274
-
275
285
  if (state.healthy) {
276
286
  state.healthy = false;
277
287
  state.unhealthySince = now();
278
288
  log.warn("[spice-couchbase] Connection \"".concat(state.name, "\" unhealthy: ").concat(state.lastError));
279
289
  }
280
290
  }
281
-
282
291
  function escalateIfStuck(state) {
283
292
  if (state.terminated || stopped || !settings.exitAfterMs || !state.unhealthySince || now() - state.unhealthySince < settings.exitAfterMs) {
284
293
  return;
285
294
  }
286
-
287
295
  state.terminated = true;
288
296
  log.error("[spice-couchbase] Connection \"".concat(state.name, "\" has been unhealthy for ") + "".concat(now() - state.unhealthySince, "ms after ").concat(state.reconnectAttempts, " ") + "failed reconnect(s). Terminating so the supervisor restarts the process.");
289
297
  terminateProcess();
290
298
  }
291
-
292
299
  function retire(state, cluster) {
293
300
  var _timer$unref2;
294
-
295
301
  if (!cluster) return;
296
302
  state.retired.add(cluster);
297
303
  var timer = setTimeout(() => {
298
304
  state.retired.delete(cluster);
299
305
  closeQuietly(cluster, log, "retired cluster for \"".concat(state.name, "\""));
300
306
  }, settings.retireGraceMs);
301
- (_timer$unref2 = timer.unref) === null || _timer$unref2 === void 0 ? void 0 : _timer$unref2.call(timer);
307
+ (_timer$unref2 = timer.unref) === null || _timer$unref2 === void 0 || _timer$unref2.call(timer);
302
308
  }
303
-
304
309
  function backoffDelay(attempt) {
305
- var base = Math.min(settings.reconnectBackoffMaxMs, settings.reconnectBackoffMinMs * 2 ** Math.max(0, attempt - 1)); // +/- 20% jitter so a fleet of pods does not reconnect in lockstep.
306
-
310
+ var base = Math.min(settings.reconnectBackoffMaxMs, settings.reconnectBackoffMinMs * 2 ** Math.max(0, attempt - 1));
311
+ // +/- 20% jitter so a fleet of pods does not reconnect in lockstep.
307
312
  return Math.round(base * (0.8 + random() * 0.4));
308
313
  }
309
-
310
- function doReconnect(_x6) {
314
+ function doReconnect(_x8) {
311
315
  return _doReconnect.apply(this, arguments);
312
316
  }
313
-
314
317
  function _doReconnect() {
315
318
  _doReconnect = _asyncToGenerator(function* (state) {
316
319
  var _database$clusters2;
317
-
318
320
  var started = now();
319
321
  var attempt = state.reconnectAttempts + 1;
320
322
  log.warn("[spice-couchbase] Reconnecting \"".concat(state.name, "\" (attempt ").concat(attempt, ")"));
321
323
  var abandoned = false;
322
324
  var handles;
323
-
324
325
  try {
325
326
  var _handles$bucket;
326
-
327
327
  var opening = openHandles(state.config, {
328
328
  connect
329
- }); // If the connect outlives the deadline, close whatever it produces.
330
-
329
+ });
330
+ // If the connect outlives the deadline, close whatever it produces.
331
331
  opening.then(late => {
332
332
  if (abandoned) closeQuietly(late.cluster, log, "abandoned cluster");
333
333
  }).catch(() => {});
334
334
  handles = yield deadline(opening, settings.reconnectTimeoutMs, "Couchbase reconnect");
335
-
336
335
  if (typeof ((_handles$bucket = handles.bucket) === null || _handles$bucket === void 0 ? void 0 : _handles$bucket.waitUntilReady) === "function") {
337
336
  var _couchbase$ServiceTyp, _couchbase$ServiceTyp2;
338
-
339
337
  var serviceTypes = [(_couchbase$ServiceTyp = couchbase.ServiceType) === null || _couchbase$ServiceTyp === void 0 ? void 0 : _couchbase$ServiceTyp.KeyValue, (_couchbase$ServiceTyp2 = couchbase.ServiceType) === null || _couchbase$ServiceTyp2 === void 0 ? void 0 : _couchbase$ServiceTyp2.Query].filter(Boolean);
340
338
  yield deadline(handles.bucket.waitUntilReady(settings.reconnectTimeoutMs, serviceTypes.length ? {
341
339
  serviceTypes
342
- } : undefined), settings.reconnectTimeoutMs + 1000, "Couchbase waitUntilReady");
340
+ } : undefined).catch(error => {
341
+ throw labelled(error, "waitUntilReady");
342
+ }), settings.reconnectTimeoutMs + 1000, "Couchbase waitUntilReady");
343
343
  }
344
-
345
344
  yield probe(handles);
346
345
  } catch (error) {
347
346
  abandoned = true;
@@ -353,31 +352,25 @@ function createHealthMonitor() {
353
352
  log.error("[spice-couchbase] Reconnect of \"".concat(state.name, "\" failed: ") + "".concat(state.lastError, ". Next attempt in ~").concat(delay, "ms"));
354
353
  return false;
355
354
  }
356
-
357
355
  if (stopped) {
358
356
  closeQuietly(handles.cluster, log, "cluster opened during shutdown");
359
357
  return false;
360
358
  }
361
-
362
359
  var globals = getGlobals();
363
360
  var database = globals === null || globals === void 0 ? void 0 : globals.database;
364
- var previous = database === null || database === void 0 ? void 0 : (_database$clusters2 = database.clusters) === null || _database$clusters2 === void 0 ? void 0 : _database$clusters2[state.name];
365
-
361
+ var previous = database === null || database === void 0 || (_database$clusters2 = database.clusters) === null || _database$clusters2 === void 0 ? void 0 : _database$clusters2[state.name];
366
362
  if (database) {
367
363
  database.clusters[state.name] = handles.cluster;
368
364
  database.buckets[state.name] = handles.bucket;
369
365
  database.scopes[state.name] = handles.scope;
370
366
  database.connections[state.name] = handles.collection;
371
- } // Collection existence was learnt through the old cluster; re-learn it.
372
-
373
-
367
+ }
368
+ // Collection existence was learnt through the old cluster; re-learn it.
374
369
  if (globals) {
375
- var _globals$db, _globals$db$collectio, _globals$db$collectio2;
376
-
370
+ var _globals$db, _globals$db$clear;
377
371
  globals.checked_collections = {};
378
- (_globals$db = globals.db) === null || _globals$db === void 0 ? void 0 : (_globals$db$collectio = _globals$db.collections) === null || _globals$db$collectio === void 0 ? void 0 : (_globals$db$collectio2 = _globals$db$collectio.clear) === null || _globals$db$collectio2 === void 0 ? void 0 : _globals$db$collectio2.call(_globals$db$collectio);
372
+ (_globals$db = globals.db) === null || _globals$db === void 0 || (_globals$db = _globals$db.collections) === null || _globals$db === void 0 || (_globals$db$clear = _globals$db.clear) === null || _globals$db$clear === void 0 || _globals$db$clear.call(_globals$db);
379
373
  }
380
-
381
374
  state.generation += 1;
382
375
  state.lastReconnectAt = now();
383
376
  if (previous && previous !== handles.cluster) retire(state, previous);
@@ -387,11 +380,9 @@ function createHealthMonitor() {
387
380
  });
388
381
  return _doReconnect.apply(this, arguments);
389
382
  }
390
-
391
383
  function reconnect(name) {
392
384
  var state = stateFor(name);
393
385
  if (stopped || !state.config) return Promise.resolve(false);
394
-
395
386
  if (!state.reconnecting) {
396
387
  state.reconnecting = doReconnect(state).catch(error => {
397
388
  log.error("[spice-couchbase] Reconnect of \"".concat(name, "\" crashed: ").concat(describe(error)));
@@ -400,80 +391,66 @@ function createHealthMonitor() {
400
391
  state.reconnecting = null;
401
392
  });
402
393
  }
403
-
404
394
  return state.reconnecting;
405
395
  }
406
-
407
- function doCheck(_x7) {
396
+ function doCheck(_x9) {
408
397
  return _doCheck.apply(this, arguments);
409
398
  }
410
399
  /** Probes the connection now. Single-flight per connection. */
411
-
412
-
413
400
  function _doCheck() {
414
401
  _doCheck = _asyncToGenerator(function* (state) {
415
- var {
416
- suspected = false
417
- } = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
418
-
402
+ var _ref5 = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {},
403
+ _ref5$suspected = _ref5.suspected,
404
+ suspected = _ref5$suspected === void 0 ? false : _ref5$suspected;
419
405
  try {
420
406
  yield probe(currentHandles(state.name));
421
407
  markHealthy(state);
422
408
  return true;
423
409
  } catch (error) {
424
- state.probeFailures += 1; // A failed probe that follows a burst of operation timeouts is two
410
+ state.probeFailures += 1;
411
+ // A failed probe that follows a burst of operation timeouts is two
425
412
  // independent signals, so it does not wait for a second probe.
426
-
427
413
  if (suspected || state.probeFailures >= settings.probeFailureThreshold) {
428
414
  markUnhealthy(state, error);
429
415
  }
430
-
431
416
  if (!state.healthy) {
432
417
  if (!state.reconnecting && now() >= state.nextReconnectAt) {
433
418
  yield reconnect(state.name);
434
419
  }
435
-
436
420
  escalateIfStuck(state);
437
421
  }
438
-
439
422
  return false;
440
423
  }
441
424
  });
442
425
  return _doCheck.apply(this, arguments);
443
426
  }
444
-
445
427
  function check(name, options) {
446
428
  var state = stateFor(name);
447
429
  if (stopped || !state.config) return Promise.resolve(state.healthy);
448
430
  if (state.reconnecting) return state.reconnecting;
449
-
450
431
  if (!state.probing) {
451
432
  state.probing = doCheck(state, options).finally(() => {
452
433
  state.probing = null;
453
434
  });
454
435
  }
455
-
456
436
  return state.probing;
457
437
  }
438
+
458
439
  /**
459
440
  * Called by the driver after an operation. Only connectivity errors count,
460
441
  * and only inside the sliding window, so a busy cluster that answers most
461
442
  * requests is not torn down for one slow statement.
462
443
  */
463
-
464
-
465
444
  function recordOutcome(name, error) {
466
445
  if (stopped || !name || !isConnectivityError(error)) return;
467
446
  var state = states.get(name);
468
- if (!(state === null || state === void 0 ? void 0 : state.config)) return;
447
+ if (!(state !== null && state !== void 0 && state.config)) return;
469
448
  var at = now();
470
449
  state.failures.push(at);
471
450
  var cutoff = at - settings.failureWindowMs;
472
-
473
451
  while (state.failures.length && state.failures[0] < cutoff) {
474
452
  state.failures.shift();
475
453
  }
476
-
477
454
  if (state.failures.length >= settings.failureThreshold) {
478
455
  state.failures = [];
479
456
  check(name, {
@@ -481,31 +458,28 @@ function createHealthMonitor() {
481
458
  }).catch(() => {});
482
459
  }
483
460
  }
484
- /** Starts monitoring a connection. Called once its first connect succeeds. */
485
-
486
461
 
462
+ /** Starts monitoring a connection. Called once its first connect succeeds. */
487
463
  function register(name, config) {
488
464
  var _state$timer$unref, _state$timer;
489
-
490
465
  var state = stateFor(name);
491
466
  state.config = config;
492
467
  if (stopped || state.timer || !settings.healthIntervalMs) return;
493
468
  state.timer = setInterval(() => {
494
469
  check(name).catch(() => {});
495
470
  }, settings.healthIntervalMs);
496
- (_state$timer$unref = (_state$timer = state.timer).unref) === null || _state$timer$unref === void 0 ? void 0 : _state$timer$unref.call(_state$timer);
471
+ (_state$timer$unref = (_state$timer = state.timer).unref) === null || _state$timer$unref === void 0 || _state$timer$unref.call(_state$timer);
497
472
  }
498
-
499
473
  function generation(name) {
500
474
  var _states$get;
501
-
502
475
  return ((_states$get = states.get(name)) === null || _states$get === void 0 ? void 0 : _states$get.generation) || 1;
503
476
  }
504
-
505
477
  function status() {
506
478
  var result = {};
507
-
508
- for (var [name, state] of states) {
479
+ for (var _ref4 of states) {
480
+ var _ref3 = _slicedToArray(_ref4, 2);
481
+ var name = _ref3[0];
482
+ var state = _ref3[1];
509
483
  result[name] = {
510
484
  healthy: state.healthy,
511
485
  reconnecting: Boolean(state.reconnecting),
@@ -518,41 +492,33 @@ function createHealthMonitor() {
518
492
  lastError: state.lastError
519
493
  };
520
494
  }
521
-
522
495
  return result;
523
496
  }
497
+
524
498
  /**
525
499
  * Stops probing and reconnecting, and closes clusters that were replaced
526
500
  * but are still in their grace period. The current clusters stay open: the
527
501
  * application closes those through `spice.database.clusters`.
528
502
  */
529
-
530
-
531
503
  function stop() {
532
504
  return _stop.apply(this, arguments);
533
505
  }
534
-
535
506
  function _stop() {
536
507
  _stop = _asyncToGenerator(function* () {
537
508
  stopped = true;
538
509
  var closing = [];
539
-
540
510
  for (var state of states.values()) {
541
511
  if (state.timer) clearInterval(state.timer);
542
512
  state.timer = null;
543
-
544
513
  for (var cluster of state.retired) {
545
514
  closing.push(closeQuietly(cluster, log, "retired cluster for \"".concat(state.name, "\"")));
546
515
  }
547
-
548
516
  state.retired.clear();
549
517
  }
550
-
551
518
  yield Promise.all(closing);
552
519
  });
553
520
  return _stop.apply(this, arguments);
554
521
  }
555
-
556
522
  return {
557
523
  settings,
558
524
  register,
@@ -564,7 +530,6 @@ function createHealthMonitor() {
564
530
  stop
565
531
  };
566
532
  }
567
-
568
533
  module.exports = {
569
534
  createHealthMonitor,
570
535
  openHandles,