@cloudbase/realtime 2.0.3-alpha.0 → 2.5.0-beta.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.
- package/.eslintrc.js +46 -0
- package/dist/cjs/common.js +1 -1
- package/dist/cjs/error.js +8 -12
- package/dist/cjs/index.js +3 -3
- package/dist/cjs/listener.js +2 -4
- package/dist/cjs/message.js +1 -1
- package/dist/cjs/snapshot.js +7 -7
- package/dist/cjs/utils.js +1 -1
- package/dist/cjs/virtual-websocket-client.d.ts +8 -8
- package/dist/cjs/virtual-websocket-client.js +281 -272
- package/dist/cjs/websocket-client.d.ts +21 -21
- package/dist/cjs/websocket-client.js +441 -444
- package/dist/cjs/ws-event.d.ts +2 -2
- package/dist/cjs/ws-event.js +47 -47
- package/dist/esm/common.js +1 -1
- package/dist/esm/error.js +8 -12
- package/dist/esm/index.js +3 -3
- package/dist/esm/listener.js +2 -4
- package/dist/esm/message.js +1 -1
- package/dist/esm/snapshot.js +7 -7
- package/dist/esm/utils.js +1 -1
- package/dist/esm/virtual-websocket-client.d.ts +8 -8
- package/dist/esm/virtual-websocket-client.js +282 -273
- package/dist/esm/websocket-client.d.ts +21 -21
- package/dist/esm/websocket-client.js +442 -445
- package/dist/esm/ws-event.d.ts +2 -2
- package/dist/esm/ws-event.js +46 -46
- package/package.json +9 -13
- package/src/common.ts +11 -11
- package/src/error.ts +13 -19
- package/src/index.ts +26 -26
- package/src/listener.ts +2 -4
- package/src/message.ts +2 -4
- package/src/snapshot.ts +6 -6
- package/src/utils.ts +1 -1
- package/src/virtual-websocket-client.ts +187 -387
- package/src/websocket-client.ts +385 -600
- package/src/ws-event.ts +24 -24
- package/.eslintrc +0 -30
|
@@ -12,31 +12,29 @@ import {
|
|
|
12
12
|
IRequestMsgType,
|
|
13
13
|
IResponseMessageNextEventMsg,
|
|
14
14
|
IRequestMessageCheckLastMsg,
|
|
15
|
-
IWatchOptions
|
|
15
|
+
IWatchOptions,
|
|
16
16
|
} from '@cloudbase/types/realtime'
|
|
17
|
-
import {
|
|
18
|
-
ISingleDBEvent
|
|
17
|
+
import {
|
|
18
|
+
ISingleDBEvent,
|
|
19
19
|
} from '@cloudbase/types/database'
|
|
20
|
-
// import Reporter from "./externals/public-lib/reporter"
|
|
21
20
|
import { RealtimeListener } from './listener'
|
|
22
21
|
import { Snapshot } from './snapshot'
|
|
23
22
|
import { IWSSendOptions, ILoginResult } from './websocket-client'
|
|
24
|
-
import {
|
|
23
|
+
import {
|
|
25
24
|
ERR_CODE,
|
|
26
25
|
CloudSDKError,
|
|
27
26
|
isTimeoutError,
|
|
28
27
|
CancelledError,
|
|
29
28
|
isCancelledError,
|
|
30
|
-
isRealtimeErrorMessageError,
|
|
31
|
-
RealtimeErrorMessageError,
|
|
32
|
-
TimeoutError
|
|
29
|
+
isRealtimeErrorMessageError,
|
|
30
|
+
RealtimeErrorMessageError,
|
|
31
|
+
TimeoutError,
|
|
33
32
|
} from './error'
|
|
34
33
|
import { sleep } from './utils'
|
|
35
34
|
|
|
36
35
|
// =============== Realtime Virtual WebSocket Client (Internal) ====================
|
|
37
36
|
|
|
38
37
|
interface IVirtualWebSocketClientConstructorOptions extends IWatchOptions {
|
|
39
|
-
// ws: RealtimeWebSocketClient
|
|
40
38
|
envId?: string
|
|
41
39
|
collectionName: string
|
|
42
40
|
query: string
|
|
@@ -71,11 +69,9 @@ interface IHandleWatchEstablishmentErrorOptions {
|
|
|
71
69
|
operationName: 'INIT_WATCH' | 'REBUILD_WATCH'
|
|
72
70
|
resolve: (value?: PromiseLike<void> | undefined) => void
|
|
73
71
|
reject: (e: any) => void
|
|
74
|
-
// retry: (refreshLogin?: boolean) => void
|
|
75
|
-
// abortWatch: (e: any) => void
|
|
76
72
|
}
|
|
77
73
|
|
|
78
|
-
enum
|
|
74
|
+
enum WatchStatus {
|
|
79
75
|
LOGGINGIN = 'LOGGINGIN',
|
|
80
76
|
INITING = 'INITING',
|
|
81
77
|
REBUILDING = 'REBUILDING',
|
|
@@ -119,17 +115,17 @@ export class VirtualWebSocketClient {
|
|
|
119
115
|
) => void
|
|
120
116
|
private debug?: boolean
|
|
121
117
|
|
|
122
|
-
private watchStatus:
|
|
123
|
-
private
|
|
124
|
-
private
|
|
125
|
-
private
|
|
126
|
-
private
|
|
118
|
+
private watchStatus: WatchStatus = WatchStatus.INITING
|
|
119
|
+
private availableRetries: Partial<Record<IRequestMsgType, number>>
|
|
120
|
+
private ackTimeoutId?: number
|
|
121
|
+
private initWatchPromise?: Promise<void>
|
|
122
|
+
private rebuildWatchPromise?: Promise<void>
|
|
127
123
|
|
|
128
124
|
// obtained
|
|
129
125
|
private sessionInfo?: IWatchSessionInfo
|
|
130
126
|
|
|
131
127
|
// internal
|
|
132
|
-
private
|
|
128
|
+
private waitExpectedTimeoutId?: number
|
|
133
129
|
|
|
134
130
|
constructor(options: IVirtualWebSocketClientConstructorOptions) {
|
|
135
131
|
this.watchId = `watchid_${+new Date()}_${Math.random()}`
|
|
@@ -147,18 +143,20 @@ export class VirtualWebSocketClient {
|
|
|
147
143
|
this.onWatchClose = options.onWatchClose
|
|
148
144
|
this.debug = options.debug
|
|
149
145
|
|
|
150
|
-
this.
|
|
146
|
+
this.availableRetries = {
|
|
151
147
|
INIT_WATCH: DEFAULT_MAX_AUTO_RETRY_ON_ERROR,
|
|
152
148
|
REBUILD_WATCH: DEFAULT_MAX_AUTO_RETRY_ON_ERROR,
|
|
153
|
-
CHECK_LAST: DEFAULT_MAX_SEND_ACK_AUTO_RETRY_ON_ERROR
|
|
149
|
+
CHECK_LAST: DEFAULT_MAX_SEND_ACK_AUTO_RETRY_ON_ERROR,
|
|
154
150
|
}
|
|
155
151
|
|
|
156
152
|
this.listener = new RealtimeListener({
|
|
157
|
-
close:
|
|
153
|
+
close: void (() => {
|
|
154
|
+
this.closeWatch()
|
|
155
|
+
}),
|
|
158
156
|
onChange: options.onChange,
|
|
159
157
|
onError: options.onError,
|
|
160
158
|
debug: this.debug,
|
|
161
|
-
virtualClient: this
|
|
159
|
+
virtualClient: this,
|
|
162
160
|
})
|
|
163
161
|
|
|
164
162
|
this.initWatch()
|
|
@@ -167,39 +165,31 @@ export class VirtualWebSocketClient {
|
|
|
167
165
|
onMessage(msg: IResponseMessage) {
|
|
168
166
|
// watchStatus sanity check
|
|
169
167
|
switch (this.watchStatus) {
|
|
170
|
-
case
|
|
168
|
+
case WatchStatus.PAUSED: {
|
|
171
169
|
// ignore all but error message
|
|
172
170
|
if (msg.msgType !== 'ERROR') {
|
|
173
171
|
return
|
|
174
172
|
}
|
|
175
173
|
break
|
|
176
174
|
}
|
|
177
|
-
case
|
|
178
|
-
case
|
|
179
|
-
case
|
|
180
|
-
console.warn(
|
|
181
|
-
`[realtime listener] internal non-fatal error: unexpected message received while ${this.watchStatus}`
|
|
182
|
-
)
|
|
175
|
+
case WatchStatus.LOGGINGIN:
|
|
176
|
+
case WatchStatus.INITING:
|
|
177
|
+
case WatchStatus.REBUILDING: {
|
|
178
|
+
console.warn(`[realtime listener] internal non-fatal error: unexpected message received while ${this.watchStatus}`)
|
|
183
179
|
return
|
|
184
180
|
}
|
|
185
|
-
case
|
|
186
|
-
console.warn(
|
|
187
|
-
'[realtime listener] internal non-fatal error: unexpected message received when the watch has closed'
|
|
188
|
-
)
|
|
181
|
+
case WatchStatus.CLOSED: {
|
|
182
|
+
console.warn('[realtime listener] internal non-fatal error: unexpected message received when the watch has closed')
|
|
189
183
|
return
|
|
190
184
|
}
|
|
191
|
-
case
|
|
192
|
-
console.warn(
|
|
193
|
-
'[realtime listener] internal non-fatal error: unexpected message received when the watch has ended with error'
|
|
194
|
-
)
|
|
185
|
+
case WatchStatus.ERRORED: {
|
|
186
|
+
console.warn('[realtime listener] internal non-fatal error: unexpected message received when the watch has ended with error')
|
|
195
187
|
return
|
|
196
188
|
}
|
|
197
189
|
}
|
|
198
190
|
|
|
199
191
|
if (!this.sessionInfo) {
|
|
200
|
-
console.warn(
|
|
201
|
-
'[realtime listener] internal non-fatal error: sessionInfo not found while message is received.'
|
|
202
|
-
)
|
|
192
|
+
console.warn('[realtime listener] internal non-fatal error: sessionInfo not found while message is received.')
|
|
203
193
|
return
|
|
204
194
|
}
|
|
205
195
|
|
|
@@ -227,105 +217,75 @@ export class VirtualWebSocketClient {
|
|
|
227
217
|
this.sessionInfo.expectEventId = msg.msgData.currEvent
|
|
228
218
|
this.clearWaitExpectedEvent()
|
|
229
219
|
// @ts-ignore
|
|
230
|
-
this.
|
|
220
|
+
this.waitExpectedTimeoutId = setTimeout(() => {
|
|
231
221
|
// must rebuild watch
|
|
232
222
|
this.rebuildWatch()
|
|
233
223
|
}, this.getWaitExpectedTimeoutLength())
|
|
234
224
|
|
|
235
|
-
|
|
236
|
-
console.log(
|
|
237
|
-
`[realtime] waitExpectedTimeoutLength ${this.getWaitExpectedTimeoutLength()}`
|
|
238
|
-
)
|
|
239
|
-
// }
|
|
225
|
+
console.log(`[realtime] waitExpectedTimeoutLength ${this.getWaitExpectedTimeoutLength()}`)
|
|
240
226
|
}
|
|
241
227
|
break
|
|
242
228
|
}
|
|
243
229
|
case 'ERROR': {
|
|
244
230
|
// receive server error
|
|
245
|
-
this.closeWithError(
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
})
|
|
250
|
-
)
|
|
231
|
+
this.closeWithError(new CloudSDKError({
|
|
232
|
+
errCode: ERR_CODE.SDK_DATABASE_REALTIME_LISTENER_SERVER_ERROR_MSG as string,
|
|
233
|
+
errMsg: `${msg.msgData.code} - ${msg.msgData.message}`,
|
|
234
|
+
}))
|
|
251
235
|
break
|
|
252
236
|
}
|
|
253
237
|
default: {
|
|
254
|
-
// if (process.env.DEBUG) {
|
|
255
238
|
console.warn(
|
|
256
239
|
`[realtime listener] virtual client receive unexpected msg ${msg.msgType}: `,
|
|
257
240
|
msg
|
|
258
241
|
)
|
|
259
|
-
// }
|
|
260
242
|
break
|
|
261
243
|
}
|
|
262
244
|
}
|
|
263
245
|
}
|
|
264
246
|
|
|
265
247
|
closeWithError(error: any) {
|
|
266
|
-
this.watchStatus =
|
|
248
|
+
this.watchStatus = WatchStatus.ERRORED
|
|
267
249
|
this.clearACKSchedule()
|
|
268
250
|
this.listener.onError(error)
|
|
269
|
-
// Reporter.surroundThirdByTryCatch(() => this.listener.onError(error))
|
|
270
251
|
this.onWatchClose(
|
|
271
252
|
this,
|
|
272
|
-
(this.sessionInfo
|
|
253
|
+
(this.sessionInfo?.queryID) || ''
|
|
273
254
|
)
|
|
274
255
|
|
|
275
|
-
|
|
276
|
-
console.log(
|
|
277
|
-
`[realtime] client closed (${this.collectionName} ${this.query}) (watchId ${this.watchId})`
|
|
278
|
-
)
|
|
279
|
-
// }
|
|
256
|
+
console.log(`[realtime] client closed (${this.collectionName} ${this.query}) (watchId ${this.watchId})`)
|
|
280
257
|
}
|
|
281
258
|
|
|
282
259
|
pause() {
|
|
283
|
-
this.watchStatus =
|
|
284
|
-
|
|
285
|
-
console.log(
|
|
286
|
-
`[realtime] client paused (${this.collectionName} ${this.query}) (watchId ${this.watchId})`
|
|
287
|
-
)
|
|
288
|
-
// }
|
|
260
|
+
this.watchStatus = WatchStatus.PAUSED
|
|
261
|
+
console.log(`[realtime] client paused (${this.collectionName} ${this.query}) (watchId ${this.watchId})`)
|
|
289
262
|
}
|
|
290
263
|
|
|
291
|
-
// resume() {
|
|
292
|
-
// return this.sessionInfo ? this.rebuildWatch() : this.initWatch()
|
|
293
|
-
// }
|
|
294
264
|
|
|
295
265
|
async resume(): Promise<void> {
|
|
296
|
-
this.watchStatus =
|
|
266
|
+
this.watchStatus = WatchStatus.RESUMING
|
|
297
267
|
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
this.sessionInfo ? 'REBUILD_WATCH' : 'INIT_WATCH'
|
|
302
|
-
} (${this.collectionName} ${this.query}) (${this.watchId})`
|
|
303
|
-
)
|
|
304
|
-
// }
|
|
268
|
+
console.log(`[realtime] client resuming with ${
|
|
269
|
+
this.sessionInfo ? 'REBUILD_WATCH' : 'INIT_WATCH'
|
|
270
|
+
} (${this.collectionName} ${this.query}) (${this.watchId})`)
|
|
305
271
|
|
|
306
272
|
try {
|
|
307
273
|
await (this.sessionInfo ? this.rebuildWatch() : this.initWatch())
|
|
308
274
|
|
|
309
|
-
|
|
310
|
-
console.log(
|
|
311
|
-
`[realtime] client successfully resumed (${this.collectionName} ${this.query}) (${this.watchId})`
|
|
312
|
-
)
|
|
313
|
-
// }
|
|
275
|
+
console.log(`[realtime] client successfully resumed (${this.collectionName} ${this.query}) (${this.watchId})`)
|
|
314
276
|
} catch (e) {
|
|
315
|
-
// if (process.env.DEBUG) {
|
|
316
277
|
console.error(
|
|
317
278
|
`[realtime] client resume failed (${this.collectionName} ${this.query}) (${this.watchId})`,
|
|
318
279
|
e
|
|
319
280
|
)
|
|
320
|
-
// }
|
|
321
281
|
}
|
|
322
282
|
}
|
|
323
283
|
|
|
324
|
-
private
|
|
284
|
+
private wsLogin = async (
|
|
325
285
|
envId?: string,
|
|
326
286
|
refresh?: boolean
|
|
327
287
|
): Promise<ILoginResult> => {
|
|
328
|
-
this.watchStatus =
|
|
288
|
+
this.watchStatus = WatchStatus.LOGGINGIN
|
|
329
289
|
const loginResult = await this.login(envId, refresh)
|
|
330
290
|
if (!this.envId) {
|
|
331
291
|
this.envId = loginResult.envId
|
|
@@ -334,32 +294,25 @@ export class VirtualWebSocketClient {
|
|
|
334
294
|
}
|
|
335
295
|
|
|
336
296
|
private initWatch = async (forceRefreshLogin?: boolean): Promise<void> => {
|
|
337
|
-
if (this.
|
|
338
|
-
return this.
|
|
297
|
+
if (this.initWatchPromise !== null && this.initWatchPromise !== undefined) {
|
|
298
|
+
return this.initWatchPromise
|
|
339
299
|
}
|
|
340
300
|
|
|
341
|
-
this.
|
|
342
|
-
async (
|
|
301
|
+
this.initWatchPromise = new Promise<void>((resolve, reject) => {
|
|
302
|
+
void (async () => {
|
|
343
303
|
try {
|
|
344
|
-
if (this.watchStatus ===
|
|
345
|
-
// if (process.env.DEBUG) {
|
|
304
|
+
if (this.watchStatus === WatchStatus.PAUSED) {
|
|
346
305
|
console.log('[realtime] initWatch cancelled on pause')
|
|
347
|
-
// }
|
|
348
306
|
return resolve()
|
|
349
307
|
}
|
|
350
308
|
|
|
351
|
-
const { envId } = await this.
|
|
352
|
-
|
|
353
|
-
// if (!this.sessionInfo) {
|
|
354
|
-
// throw new Error(`can not rebuildWatch without a successful initWatch (lack of sessionInfo)`)
|
|
355
|
-
// }
|
|
356
|
-
|
|
357
|
-
if ((this.watchStatus as WATCH_STATUS) === WATCH_STATUS.PAUSED) {
|
|
309
|
+
const { envId } = await this.wsLogin(this.envId, forceRefreshLogin)
|
|
310
|
+
if ((this.watchStatus as WatchStatus) === WatchStatus.PAUSED) {
|
|
358
311
|
console.log('[realtime] initWatch cancelled on pause')
|
|
359
312
|
return resolve()
|
|
360
313
|
}
|
|
361
314
|
|
|
362
|
-
this.watchStatus =
|
|
315
|
+
this.watchStatus = WatchStatus.INITING
|
|
363
316
|
|
|
364
317
|
const initWatchMsg: IRequestMessageInitWatchMsg = {
|
|
365
318
|
watchId: this.watchId,
|
|
@@ -370,15 +323,15 @@ export class VirtualWebSocketClient {
|
|
|
370
323
|
collName: this.collectionName,
|
|
371
324
|
query: this.query,
|
|
372
325
|
limit: this.limit,
|
|
373
|
-
orderBy: this.orderBy
|
|
374
|
-
}
|
|
326
|
+
orderBy: this.orderBy,
|
|
327
|
+
},
|
|
375
328
|
}
|
|
376
329
|
|
|
377
330
|
const initEventMsg = await this.send<IResponseMessageInitEventMsg>({
|
|
378
331
|
msg: initWatchMsg,
|
|
379
332
|
waitResponse: true,
|
|
380
333
|
skipOnMessage: true,
|
|
381
|
-
timeout: DEFAULT_INIT_WATCH_TIMEOUT
|
|
334
|
+
timeout: DEFAULT_INIT_WATCH_TIMEOUT,
|
|
382
335
|
})
|
|
383
336
|
|
|
384
337
|
const { events, currEvent } = initEventMsg.msgData
|
|
@@ -386,7 +339,7 @@ export class VirtualWebSocketClient {
|
|
|
386
339
|
this.sessionInfo = {
|
|
387
340
|
queryID: initEventMsg.msgData.queryID,
|
|
388
341
|
currentEventId: currEvent - 1,
|
|
389
|
-
currentDocs: []
|
|
342
|
+
currentDocs: [],
|
|
390
343
|
}
|
|
391
344
|
|
|
392
345
|
// FIX: in initEvent message, all events have id 0, which is inconsistent with currEvent
|
|
@@ -401,67 +354,60 @@ export class VirtualWebSocketClient {
|
|
|
401
354
|
id: currEvent,
|
|
402
355
|
docChanges: [],
|
|
403
356
|
docs: [],
|
|
404
|
-
type: 'init'
|
|
357
|
+
type: 'init',
|
|
405
358
|
})
|
|
406
359
|
this.listener.onChange(snapshot)
|
|
407
360
|
this.scheduleSendACK()
|
|
408
361
|
}
|
|
409
362
|
this.onWatchStart(this, this.sessionInfo.queryID)
|
|
410
|
-
this.watchStatus =
|
|
411
|
-
this.
|
|
363
|
+
this.watchStatus = WatchStatus.ACTIVE
|
|
364
|
+
this.availableRetries.INIT_WATCH = DEFAULT_MAX_AUTO_RETRY_ON_ERROR
|
|
412
365
|
resolve()
|
|
413
366
|
} catch (e) {
|
|
414
367
|
this.handleWatchEstablishmentError(e, {
|
|
415
368
|
operationName: 'INIT_WATCH',
|
|
416
369
|
resolve,
|
|
417
|
-
reject
|
|
370
|
+
reject,
|
|
418
371
|
})
|
|
419
372
|
}
|
|
420
|
-
}
|
|
421
|
-
)
|
|
373
|
+
})()
|
|
374
|
+
})
|
|
422
375
|
|
|
423
376
|
let success = false
|
|
424
377
|
|
|
425
378
|
try {
|
|
426
|
-
await this.
|
|
379
|
+
await this.initWatchPromise
|
|
427
380
|
success = true
|
|
428
381
|
} finally {
|
|
429
|
-
this.
|
|
382
|
+
this.initWatchPromise = undefined
|
|
430
383
|
}
|
|
431
|
-
|
|
432
|
-
// if (process.env.DEBUG) {
|
|
433
384
|
console.log(`[realtime] initWatch ${success ? 'success' : 'fail'}`)
|
|
434
|
-
// }
|
|
435
385
|
}
|
|
436
386
|
|
|
437
387
|
private rebuildWatch = async (forceRefreshLogin?: boolean): Promise<void> => {
|
|
438
|
-
if (this.
|
|
439
|
-
return this.
|
|
388
|
+
if (this.rebuildWatchPromise !== null && this.rebuildWatchPromise !== undefined) {
|
|
389
|
+
return this.rebuildWatchPromise
|
|
440
390
|
}
|
|
441
391
|
|
|
442
|
-
this.
|
|
443
|
-
async (
|
|
392
|
+
this.rebuildWatchPromise = new Promise<void>((resolve, reject) => {
|
|
393
|
+
void (async () => {
|
|
444
394
|
try {
|
|
445
|
-
if (this.watchStatus ===
|
|
446
|
-
// if (process.env.DEBUG) {
|
|
395
|
+
if (this.watchStatus === WatchStatus.PAUSED) {
|
|
447
396
|
console.log('[realtime] rebuildWatch cancelled on pause')
|
|
448
|
-
// }
|
|
449
397
|
return resolve()
|
|
450
398
|
}
|
|
451
|
-
const { envId } = await this.
|
|
399
|
+
const { envId } = await this.wsLogin(this.envId, forceRefreshLogin)
|
|
452
400
|
|
|
453
401
|
if (!this.sessionInfo) {
|
|
454
|
-
throw new Error(
|
|
455
|
-
'can not rebuildWatch without a successful initWatch (lack of sessionInfo)'
|
|
456
|
-
)
|
|
402
|
+
throw new Error('can not rebuildWatch without a successful initWatch (lack of sessionInfo)')
|
|
457
403
|
}
|
|
458
404
|
|
|
459
|
-
if ((this.watchStatus as
|
|
405
|
+
if ((this.watchStatus as WatchStatus) === WatchStatus.PAUSED) {
|
|
460
406
|
console.log('[realtime] rebuildWatch cancelled on pause')
|
|
461
407
|
return resolve()
|
|
462
408
|
}
|
|
463
409
|
|
|
464
|
-
this.watchStatus =
|
|
410
|
+
this.watchStatus = WatchStatus.REBUILDING
|
|
465
411
|
|
|
466
412
|
const rebuildWatchMsg: IRequestMessageRebuildWatchMsg = {
|
|
467
413
|
watchId: this.watchId,
|
|
@@ -471,44 +417,42 @@ export class VirtualWebSocketClient {
|
|
|
471
417
|
envId,
|
|
472
418
|
collName: this.collectionName,
|
|
473
419
|
queryID: this.sessionInfo.queryID,
|
|
474
|
-
eventID: this.sessionInfo.currentEventId
|
|
475
|
-
}
|
|
420
|
+
eventID: this.sessionInfo.currentEventId,
|
|
421
|
+
},
|
|
476
422
|
}
|
|
477
423
|
|
|
478
424
|
const nextEventMsg = await this.send<IResponseMessageNextEventMsg>({
|
|
479
425
|
msg: rebuildWatchMsg,
|
|
480
426
|
waitResponse: true,
|
|
481
427
|
skipOnMessage: false,
|
|
482
|
-
timeout: DEFAULT_REBUILD_WATCH_TIMEOUT
|
|
428
|
+
timeout: DEFAULT_REBUILD_WATCH_TIMEOUT,
|
|
483
429
|
})
|
|
484
430
|
|
|
485
431
|
this.handleServerEvents(nextEventMsg)
|
|
486
432
|
|
|
487
|
-
this.watchStatus =
|
|
488
|
-
this.
|
|
433
|
+
this.watchStatus = WatchStatus.ACTIVE
|
|
434
|
+
this.availableRetries.REBUILD_WATCH = DEFAULT_MAX_AUTO_RETRY_ON_ERROR
|
|
489
435
|
resolve()
|
|
490
436
|
} catch (e) {
|
|
491
437
|
this.handleWatchEstablishmentError(e, {
|
|
492
438
|
operationName: 'REBUILD_WATCH',
|
|
493
439
|
resolve,
|
|
494
|
-
reject
|
|
440
|
+
reject,
|
|
495
441
|
})
|
|
496
442
|
}
|
|
497
|
-
}
|
|
498
|
-
)
|
|
443
|
+
})()
|
|
444
|
+
})
|
|
499
445
|
|
|
500
446
|
let success = false
|
|
501
447
|
|
|
502
448
|
try {
|
|
503
|
-
await this.
|
|
449
|
+
await this.rebuildWatchPromise
|
|
504
450
|
success = true
|
|
505
451
|
} finally {
|
|
506
|
-
this.
|
|
452
|
+
this.rebuildWatchPromise = undefined
|
|
507
453
|
}
|
|
508
454
|
|
|
509
|
-
// if (process.env.DEBUG) {
|
|
510
455
|
console.log(`[realtime] rebuildWatch ${success ? 'success' : 'fail'}`)
|
|
511
|
-
// }
|
|
512
456
|
}
|
|
513
457
|
|
|
514
458
|
private handleWatchEstablishmentError = async (
|
|
@@ -519,24 +463,22 @@ export class VirtualWebSocketClient {
|
|
|
519
463
|
|
|
520
464
|
const abortWatch = () => {
|
|
521
465
|
// mock temp comment
|
|
522
|
-
this.closeWithError(
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
})
|
|
529
|
-
)
|
|
466
|
+
this.closeWithError(new CloudSDKError({
|
|
467
|
+
errCode: isInitWatch
|
|
468
|
+
? (ERR_CODE.SDK_DATABASE_REALTIME_LISTENER_INIT_WATCH_FAIL as string)
|
|
469
|
+
: (ERR_CODE.SDK_DATABASE_REALTIME_LISTENER_REBUILD_WATCH_FAIL as string),
|
|
470
|
+
errMsg: e,
|
|
471
|
+
}))
|
|
530
472
|
options.reject(e)
|
|
531
473
|
}
|
|
532
474
|
|
|
533
475
|
const retry = (refreshLogin?: boolean) => {
|
|
534
476
|
if (this.useRetryTicket(options.operationName)) {
|
|
535
477
|
if (isInitWatch) {
|
|
536
|
-
this.
|
|
478
|
+
this.initWatchPromise = undefined
|
|
537
479
|
options.resolve(this.initWatch(refreshLogin))
|
|
538
480
|
} else {
|
|
539
|
-
this.
|
|
481
|
+
this.rebuildWatchPromise = undefined
|
|
540
482
|
options.resolve(this.rebuildWatch(refreshLogin))
|
|
541
483
|
}
|
|
542
484
|
} else {
|
|
@@ -549,71 +491,67 @@ export class VirtualWebSocketClient {
|
|
|
549
491
|
onTimeoutError: () => retry(false),
|
|
550
492
|
onNotRetryableError: abortWatch,
|
|
551
493
|
onCancelledError: options.reject,
|
|
552
|
-
onUnknownError:
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
494
|
+
onUnknownError: () => {
|
|
495
|
+
(async () => {
|
|
496
|
+
try {
|
|
497
|
+
const onWSDisconnected = async () => {
|
|
498
|
+
this.pause()
|
|
499
|
+
await this.onceWSConnected()
|
|
500
|
+
retry(true)
|
|
501
|
+
}
|
|
559
502
|
|
|
560
|
-
|
|
561
|
-
await onWSDisconnected()
|
|
562
|
-
} else {
|
|
563
|
-
await sleep(DEFAULT_WAIT_TIME_ON_UNKNOWN_ERROR)
|
|
564
|
-
if (this.watchStatus === WATCH_STATUS.PAUSED) {
|
|
565
|
-
// cancel
|
|
566
|
-
options.reject(
|
|
567
|
-
new CancelledError(
|
|
568
|
-
`${options.operationName} cancelled due to pause after unknownError`
|
|
569
|
-
)
|
|
570
|
-
)
|
|
571
|
-
} else if (!this.isWSConnected()) {
|
|
503
|
+
if (!this.isWSConnected()) {
|
|
572
504
|
await onWSDisconnected()
|
|
573
505
|
} else {
|
|
574
|
-
|
|
506
|
+
await sleep(DEFAULT_WAIT_TIME_ON_UNKNOWN_ERROR)
|
|
507
|
+
if (this.watchStatus === WatchStatus.PAUSED) {
|
|
508
|
+
// cancel
|
|
509
|
+
options.reject(new CancelledError(`${options.operationName} cancelled due to pause after unknownError`))
|
|
510
|
+
} else if (!this.isWSConnected()) {
|
|
511
|
+
await onWSDisconnected()
|
|
512
|
+
} else {
|
|
513
|
+
retry(false)
|
|
514
|
+
}
|
|
575
515
|
}
|
|
516
|
+
} catch (e) {
|
|
517
|
+
// unexpected error while handling error, in order to provide maximum effort on SEAMINGLESS FAULT TOLERANCE, just retry
|
|
518
|
+
retry(true)
|
|
576
519
|
}
|
|
577
|
-
}
|
|
578
|
-
|
|
579
|
-
retry(true)
|
|
580
|
-
}
|
|
581
|
-
}
|
|
520
|
+
})()
|
|
521
|
+
},
|
|
582
522
|
})
|
|
583
523
|
}
|
|
584
524
|
|
|
585
525
|
private closeWatch = async () => {
|
|
586
526
|
const queryId = this.sessionInfo ? this.sessionInfo.queryID : ''
|
|
587
527
|
|
|
588
|
-
if (this.watchStatus !==
|
|
589
|
-
this.watchStatus =
|
|
528
|
+
if (this.watchStatus !== WatchStatus.ACTIVE) {
|
|
529
|
+
this.watchStatus = WatchStatus.CLOSED
|
|
590
530
|
this.onWatchClose(this, queryId)
|
|
591
531
|
return
|
|
592
532
|
}
|
|
593
533
|
|
|
594
534
|
try {
|
|
595
|
-
this.watchStatus =
|
|
535
|
+
this.watchStatus = WatchStatus.CLOSING
|
|
596
536
|
|
|
597
537
|
const closeWatchMsg: IRequestMessageCloseWatchMsg = {
|
|
598
538
|
watchId: this.watchId,
|
|
599
539
|
requestId: genRequestId(),
|
|
600
540
|
msgType: 'CLOSE_WATCH',
|
|
601
|
-
msgData: null
|
|
541
|
+
msgData: null,
|
|
602
542
|
}
|
|
603
543
|
|
|
604
544
|
await this.send({
|
|
605
|
-
msg: closeWatchMsg
|
|
545
|
+
msg: closeWatchMsg,
|
|
606
546
|
})
|
|
607
547
|
|
|
608
548
|
this.sessionInfo = undefined
|
|
609
|
-
this.watchStatus =
|
|
549
|
+
this.watchStatus = WatchStatus.CLOSED
|
|
610
550
|
} catch (e) {
|
|
611
|
-
this.closeWithError(
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
})
|
|
616
|
-
)
|
|
551
|
+
this.closeWithError(new CloudSDKError({
|
|
552
|
+
errCode: ERR_CODE.SDK_DATABASE_REALTIME_LISTENER_CLOSE_WATCH_FAIL as string,
|
|
553
|
+
errMsg: e,
|
|
554
|
+
}))
|
|
617
555
|
} finally {
|
|
618
556
|
this.onWatchClose(this, queryId)
|
|
619
557
|
}
|
|
@@ -624,8 +562,8 @@ export class VirtualWebSocketClient {
|
|
|
624
562
|
|
|
625
563
|
// TODO: should we check status after timeout
|
|
626
564
|
// @ts-ignore
|
|
627
|
-
this.
|
|
628
|
-
if (this.
|
|
565
|
+
this.ackTimeoutId = setTimeout(() => {
|
|
566
|
+
if (this.waitExpectedTimeoutId) {
|
|
629
567
|
this.scheduleSendACK()
|
|
630
568
|
} else {
|
|
631
569
|
this.sendACK()
|
|
@@ -634,22 +572,20 @@ export class VirtualWebSocketClient {
|
|
|
634
572
|
}
|
|
635
573
|
|
|
636
574
|
private clearACKSchedule = () => {
|
|
637
|
-
if (this.
|
|
638
|
-
clearTimeout(this.
|
|
575
|
+
if (this.ackTimeoutId) {
|
|
576
|
+
clearTimeout(this.ackTimeoutId)
|
|
639
577
|
}
|
|
640
578
|
}
|
|
641
579
|
|
|
642
580
|
private sendACK = async (): Promise<void> => {
|
|
643
581
|
try {
|
|
644
|
-
if (this.watchStatus !==
|
|
582
|
+
if (this.watchStatus !== WatchStatus.ACTIVE) {
|
|
645
583
|
this.scheduleSendACK()
|
|
646
584
|
return
|
|
647
585
|
}
|
|
648
586
|
|
|
649
587
|
if (!this.sessionInfo) {
|
|
650
|
-
console.warn(
|
|
651
|
-
'[realtime listener] can not send ack without a successful initWatch (lack of sessionInfo)'
|
|
652
|
-
)
|
|
588
|
+
console.warn('[realtime listener] can not send ack without a successful initWatch (lack of sessionInfo)')
|
|
653
589
|
return
|
|
654
590
|
}
|
|
655
591
|
|
|
@@ -659,12 +595,12 @@ export class VirtualWebSocketClient {
|
|
|
659
595
|
msgType: 'CHECK_LAST',
|
|
660
596
|
msgData: {
|
|
661
597
|
queryID: this.sessionInfo.queryID,
|
|
662
|
-
eventID: this.sessionInfo.currentEventId
|
|
663
|
-
}
|
|
598
|
+
eventID: this.sessionInfo.currentEventId,
|
|
599
|
+
},
|
|
664
600
|
}
|
|
665
601
|
|
|
666
602
|
await this.send({
|
|
667
|
-
msg: ackMsg
|
|
603
|
+
msg: ackMsg,
|
|
668
604
|
})
|
|
669
605
|
|
|
670
606
|
this.scheduleSendACK()
|
|
@@ -687,12 +623,10 @@ export class VirtualWebSocketClient {
|
|
|
687
623
|
case 'INVALIID_ENV':
|
|
688
624
|
case 'COLLECTION_PERMISSION_DENIED': {
|
|
689
625
|
// must throw
|
|
690
|
-
this.closeWithError(
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
})
|
|
695
|
-
)
|
|
626
|
+
this.closeWithError(new CloudSDKError({
|
|
627
|
+
errCode: ERR_CODE.SDK_DATABASE_REALTIME_LISTENER_CHECK_LAST_FAIL as string,
|
|
628
|
+
errMsg: msg.msgData.code,
|
|
629
|
+
}))
|
|
696
630
|
return
|
|
697
631
|
}
|
|
698
632
|
default: {
|
|
@@ -703,18 +637,16 @@ export class VirtualWebSocketClient {
|
|
|
703
637
|
|
|
704
638
|
// maybe retryable
|
|
705
639
|
if (
|
|
706
|
-
this.
|
|
707
|
-
this.
|
|
640
|
+
this.availableRetries.CHECK_LAST
|
|
641
|
+
&& this.availableRetries.CHECK_LAST > 0
|
|
708
642
|
) {
|
|
709
|
-
this.
|
|
643
|
+
this.availableRetries.CHECK_LAST -= 1
|
|
710
644
|
this.scheduleSendACK()
|
|
711
645
|
} else {
|
|
712
|
-
this.closeWithError(
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
})
|
|
717
|
-
)
|
|
646
|
+
this.closeWithError(new CloudSDKError({
|
|
647
|
+
errCode: ERR_CODE.SDK_DATABASE_REALTIME_LISTENER_CHECK_LAST_FAIL as string,
|
|
648
|
+
errMsg: e,
|
|
649
|
+
}))
|
|
718
650
|
}
|
|
719
651
|
}
|
|
720
652
|
}
|
|
@@ -764,55 +696,33 @@ export class VirtualWebSocketClient {
|
|
|
764
696
|
// credit a retry chance from availableRetries
|
|
765
697
|
private useRetryTicket(operationName: IRequestMsgType): boolean {
|
|
766
698
|
if (
|
|
767
|
-
this.
|
|
768
|
-
this.
|
|
699
|
+
this.availableRetries[operationName]
|
|
700
|
+
&& this.availableRetries[operationName]! > 0
|
|
769
701
|
) {
|
|
770
|
-
this.
|
|
771
|
-
|
|
772
|
-
// if (process.env.DEBUG) {
|
|
773
|
-
console.log(
|
|
774
|
-
`[realtime] ${operationName} use a retry ticket, now only ${this._availableRetries[operationName]} retry left`
|
|
775
|
-
)
|
|
776
|
-
// }
|
|
702
|
+
this.availableRetries[operationName]! -= 1
|
|
703
|
+
console.log(`[realtime] ${operationName} use a retry ticket, now only ${this.availableRetries[operationName]} retry left`)
|
|
777
704
|
|
|
778
705
|
return true
|
|
779
706
|
}
|
|
780
707
|
return false
|
|
781
708
|
}
|
|
782
709
|
|
|
783
|
-
private async handleServerEvents(
|
|
784
|
-
msg: IResponseMessageInitEventMsg | IResponseMessageNextEventMsg
|
|
785
|
-
) {
|
|
710
|
+
private async handleServerEvents(msg: IResponseMessageInitEventMsg | IResponseMessageNextEventMsg) {
|
|
786
711
|
try {
|
|
787
712
|
this.scheduleSendACK()
|
|
788
|
-
await this.
|
|
789
|
-
this.
|
|
713
|
+
await this.handleServerEventsInternel(msg)
|
|
714
|
+
this.postHandleServerEventsValidityCheck(msg)
|
|
790
715
|
} catch (e) {
|
|
791
|
-
// if (process.env.DEBUG) {
|
|
792
716
|
// TODO: report
|
|
793
717
|
console.error(
|
|
794
718
|
'[realtime listener] internal non-fatal error: handle server events failed with error: ',
|
|
795
719
|
e
|
|
796
720
|
)
|
|
797
|
-
|
|
798
|
-
// writeToFile(
|
|
799
|
-
// "wserror.txt",
|
|
800
|
-
// `[realtime listener] internal non-fatal error: handle server events failed with error: ${JSON.stringify(
|
|
801
|
-
// Object.assign({}, e, {
|
|
802
|
-
// requestId: msg.requestId,
|
|
803
|
-
// watchId: msg.watchId
|
|
804
|
-
// })
|
|
805
|
-
// )} \n`
|
|
806
|
-
// )
|
|
807
|
-
// }
|
|
808
|
-
|
|
809
721
|
throw e
|
|
810
722
|
}
|
|
811
723
|
}
|
|
812
724
|
|
|
813
|
-
private async
|
|
814
|
-
msg: IResponseMessageInitEventMsg | IResponseMessageNextEventMsg
|
|
815
|
-
) {
|
|
725
|
+
private async handleServerEventsInternel(msg: IResponseMessageInitEventMsg | IResponseMessageNextEventMsg) {
|
|
816
726
|
const { requestId } = msg
|
|
817
727
|
|
|
818
728
|
const { events } = msg.msgData
|
|
@@ -822,18 +732,16 @@ export class VirtualWebSocketClient {
|
|
|
822
732
|
return
|
|
823
733
|
}
|
|
824
734
|
|
|
825
|
-
const sessionInfo = this
|
|
735
|
+
const { sessionInfo } = this
|
|
826
736
|
|
|
827
737
|
let allChangeEvents: ISingleDBEvent[]
|
|
828
738
|
try {
|
|
829
739
|
allChangeEvents = events.map(getPublicEvent)
|
|
830
740
|
} catch (e) {
|
|
831
|
-
this.closeWithError(
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
})
|
|
836
|
-
)
|
|
741
|
+
this.closeWithError(new CloudSDKError({
|
|
742
|
+
errCode: ERR_CODE.SDK_DATABASE_REALTIME_LISTENER_RECEIVE_INVALID_SERVER_DATA as string,
|
|
743
|
+
errMsg: e,
|
|
744
|
+
}))
|
|
837
745
|
return
|
|
838
746
|
}
|
|
839
747
|
|
|
@@ -848,28 +756,11 @@ export class VirtualWebSocketClient {
|
|
|
848
756
|
// duplicate event, dropable
|
|
849
757
|
// TODO: report
|
|
850
758
|
// if (process.env.DEBUG) {
|
|
851
|
-
console.warn(
|
|
852
|
-
`[realtime] duplicate event received, cur ${sessionInfo.currentEventId} but got ${change.id}`
|
|
853
|
-
)
|
|
759
|
+
console.warn(`[realtime] duplicate event received, cur ${sessionInfo.currentEventId} but got ${change.id}`)
|
|
854
760
|
// }
|
|
855
761
|
} else {
|
|
856
762
|
// allChangeEvents should be in ascending order according to eventId, this should never happens, must report a non-fatal error
|
|
857
|
-
console.error(
|
|
858
|
-
`[realtime listener] server non-fatal error: events out of order (the latter event's id is smaller than that of the former) (requestId ${requestId})`
|
|
859
|
-
)
|
|
860
|
-
|
|
861
|
-
// writeToFile(
|
|
862
|
-
// "wserror.txt",
|
|
863
|
-
// `[realtime listener] server non-fatal error: events out of order (the latter event's id is smaller than that of the former) ${JSON.stringify(
|
|
864
|
-
// Object.assign(
|
|
865
|
-
// {},
|
|
866
|
-
// {
|
|
867
|
-
// requestId: msg.requestId,
|
|
868
|
-
// watchId: msg.watchId
|
|
869
|
-
// }
|
|
870
|
-
// )
|
|
871
|
-
// )} \n`
|
|
872
|
-
// )
|
|
763
|
+
console.error(`[realtime listener] server non-fatal error: events out of order (the latter event's id is smaller than that of the former) (requestId ${requestId})`)
|
|
873
764
|
}
|
|
874
765
|
continue
|
|
875
766
|
} else if (sessionInfo.currentEventId === change.id - 1) {
|
|
@@ -891,9 +782,9 @@ export class VirtualWebSocketClient {
|
|
|
891
782
|
const doc = cloneDeep(localDoc)
|
|
892
783
|
|
|
893
784
|
if (change.updatedFields) {
|
|
894
|
-
|
|
785
|
+
Object.keys(change.updatedFields).forEach((fieldPath) => {
|
|
895
786
|
set(doc, fieldPath, change.updatedFields[fieldPath])
|
|
896
|
-
}
|
|
787
|
+
})
|
|
897
788
|
}
|
|
898
789
|
|
|
899
790
|
if (change.removedFields) {
|
|
@@ -905,22 +796,7 @@ export class VirtualWebSocketClient {
|
|
|
905
796
|
change.doc = doc
|
|
906
797
|
} else {
|
|
907
798
|
// TODO report
|
|
908
|
-
console.error(
|
|
909
|
-
'[realtime listener] internal non-fatal server error: unexpected update dataType event where no doc is associated.'
|
|
910
|
-
)
|
|
911
|
-
|
|
912
|
-
// writeToFile(
|
|
913
|
-
// "wserror.txt",
|
|
914
|
-
// `[realtime listener] internal non-fatal server error: unexpected update dataType event where no doc is associated. ${JSON.stringify(
|
|
915
|
-
// Object.assign(
|
|
916
|
-
// {},
|
|
917
|
-
// {
|
|
918
|
-
// requestId: msg.requestId,
|
|
919
|
-
// watchId: msg.watchId
|
|
920
|
-
// }
|
|
921
|
-
// )
|
|
922
|
-
// )} \n`
|
|
923
|
-
// )
|
|
799
|
+
console.error('[realtime listener] internal non-fatal server error: unexpected update dataType event where no doc is associated.')
|
|
924
800
|
}
|
|
925
801
|
break
|
|
926
802
|
}
|
|
@@ -928,7 +804,7 @@ export class VirtualWebSocketClient {
|
|
|
928
804
|
// doc is provided by server, this should never occur
|
|
929
805
|
const err = new CloudSDKError({
|
|
930
806
|
errCode: ERR_CODE.SDK_DATABASE_REALTIME_LISTENER_UNEXPECTED_FATAL_ERROR as string,
|
|
931
|
-
errMsg: `HandleServerEvents: full doc is not provided with dataType="update" and queueType="enqueue" (requestId ${msg.requestId})
|
|
807
|
+
errMsg: `HandleServerEvents: full doc is not provided with dataType="update" and queueType="enqueue" (requestId ${msg.requestId})`,
|
|
932
808
|
})
|
|
933
809
|
this.closeWithError(err)
|
|
934
810
|
throw err
|
|
@@ -946,7 +822,7 @@ export class VirtualWebSocketClient {
|
|
|
946
822
|
// doc is provided by server, this should never occur
|
|
947
823
|
const err = new CloudSDKError({
|
|
948
824
|
errCode: ERR_CODE.SDK_DATABASE_REALTIME_LISTENER_UNEXPECTED_FATAL_ERROR as string,
|
|
949
|
-
errMsg: `HandleServerEvents: full doc is not provided with dataType="replace" (requestId ${msg.requestId})
|
|
825
|
+
errMsg: `HandleServerEvents: full doc is not provided with dataType="replace" (requestId ${msg.requestId})`,
|
|
950
826
|
})
|
|
951
827
|
this.closeWithError(err)
|
|
952
828
|
throw err
|
|
@@ -959,36 +835,19 @@ export class VirtualWebSocketClient {
|
|
|
959
835
|
change.doc = doc
|
|
960
836
|
} else {
|
|
961
837
|
// TODO report
|
|
962
|
-
console.error(
|
|
963
|
-
'[realtime listener] internal non-fatal server error: unexpected remove event where no doc is associated.'
|
|
964
|
-
)
|
|
965
|
-
|
|
966
|
-
// writeToFile(
|
|
967
|
-
// "wserror.txt",
|
|
968
|
-
// `[realtime listener] internal non-fatal server error: unexpected remove event where no doc is associated. ${JSON.stringify(
|
|
969
|
-
// Object.assign(
|
|
970
|
-
// {},
|
|
971
|
-
// {
|
|
972
|
-
// requestId: msg.requestId,
|
|
973
|
-
// watchId: msg.watchId
|
|
974
|
-
// }
|
|
975
|
-
// )
|
|
976
|
-
// )} \n`
|
|
977
|
-
// )
|
|
838
|
+
console.error('[realtime listener] internal non-fatal server error: unexpected remove event where no doc is associated.')
|
|
978
839
|
}
|
|
979
840
|
break
|
|
980
841
|
}
|
|
981
842
|
case 'limit': {
|
|
982
843
|
if (!change.doc) {
|
|
983
|
-
switch(change.queueType) {
|
|
844
|
+
switch (change.queueType) {
|
|
984
845
|
case 'dequeue': {
|
|
985
846
|
const doc = docs.find(doc => doc._id === change.docId)
|
|
986
847
|
if (doc) {
|
|
987
848
|
change.doc = doc
|
|
988
849
|
} else {
|
|
989
|
-
console.error(
|
|
990
|
-
'[realtime listener] internal non-fatal server error: unexpected limit dataType event where no doc is associated.'
|
|
991
|
-
)
|
|
850
|
+
console.error('[realtime listener] internal non-fatal server error: unexpected limit dataType event where no doc is associated.')
|
|
992
851
|
}
|
|
993
852
|
break
|
|
994
853
|
}
|
|
@@ -996,7 +855,7 @@ export class VirtualWebSocketClient {
|
|
|
996
855
|
// doc is provided by server, this should never occur
|
|
997
856
|
const err = new CloudSDKError({
|
|
998
857
|
errCode: ERR_CODE.SDK_DATABASE_REALTIME_LISTENER_UNEXPECTED_FATAL_ERROR as string,
|
|
999
|
-
errMsg: `HandleServerEvents: full doc is not provided with dataType="limit" and queueType="enqueue" (requestId ${msg.requestId})
|
|
858
|
+
errMsg: `HandleServerEvents: full doc is not provided with dataType="limit" and queueType="enqueue" (requestId ${msg.requestId})`,
|
|
1000
859
|
})
|
|
1001
860
|
this.closeWithError(err)
|
|
1002
861
|
throw err
|
|
@@ -1030,61 +889,25 @@ export class VirtualWebSocketClient {
|
|
|
1030
889
|
docs.splice(ind, 1)
|
|
1031
890
|
} else {
|
|
1032
891
|
// TODO report
|
|
1033
|
-
console.error(
|
|
1034
|
-
'[realtime listener] internal non-fatal server error: unexpected dequeue event where no doc is associated.'
|
|
1035
|
-
)
|
|
1036
|
-
|
|
1037
|
-
// writeToFile(
|
|
1038
|
-
// "wserror.txt",
|
|
1039
|
-
// `[realtime listener] internal non-fatal server error: unexpected dequeue event where no doc is associated. ${JSON.stringify(
|
|
1040
|
-
// Object.assign(
|
|
1041
|
-
// {},
|
|
1042
|
-
// {
|
|
1043
|
-
// requestId: msg.requestId,
|
|
1044
|
-
// watchId: msg.watchId
|
|
1045
|
-
// }
|
|
1046
|
-
// )
|
|
1047
|
-
// )} \n`
|
|
1048
|
-
// )
|
|
892
|
+
console.error('[realtime listener] internal non-fatal server error: unexpected dequeue event where no doc is associated.')
|
|
1049
893
|
}
|
|
1050
894
|
break
|
|
1051
895
|
}
|
|
1052
896
|
case 'update': {
|
|
1053
|
-
// writeToFile(
|
|
1054
|
-
// "wserror.txt",
|
|
1055
|
-
// `[realtime listener] docs ${JSON.stringify(
|
|
1056
|
-
// docs
|
|
1057
|
-
// )} change doc ${JSON.stringify(change)} \n`
|
|
1058
|
-
// )
|
|
1059
897
|
const ind = docs.findIndex(doc => doc._id === change.docId)
|
|
1060
898
|
if (ind > -1) {
|
|
1061
899
|
docs[ind] = change.doc
|
|
1062
900
|
} else {
|
|
1063
901
|
// TODO report
|
|
1064
|
-
console.error(
|
|
1065
|
-
'[realtime listener] internal non-fatal server error: unexpected queueType update event where no doc is associated.'
|
|
1066
|
-
)
|
|
1067
|
-
|
|
1068
|
-
// writeToFile(
|
|
1069
|
-
// "wserror.txt",
|
|
1070
|
-
// `[realtime listener] internal non-fatal server error: unexpected queueType update event where no doc is associated. ${JSON.stringify(
|
|
1071
|
-
// Object.assign(
|
|
1072
|
-
// {},
|
|
1073
|
-
// {
|
|
1074
|
-
// requestId: msg.requestId,
|
|
1075
|
-
// watchId: msg.watchId
|
|
1076
|
-
// }
|
|
1077
|
-
// )
|
|
1078
|
-
// )} \n`
|
|
1079
|
-
// )
|
|
902
|
+
console.error('[realtime listener] internal non-fatal server error: unexpected queueType update event where no doc is associated.')
|
|
1080
903
|
}
|
|
1081
904
|
break
|
|
1082
905
|
}
|
|
1083
906
|
}
|
|
1084
907
|
|
|
1085
908
|
if (
|
|
1086
|
-
i === len - 1
|
|
1087
|
-
(allChangeEvents[i + 1] && allChangeEvents[i + 1].id !== change.id)
|
|
909
|
+
i === len - 1
|
|
910
|
+
|| (allChangeEvents[i + 1] && allChangeEvents[i + 1].id !== change.id)
|
|
1088
911
|
) {
|
|
1089
912
|
// a shallow slice creates a shallow snapshot
|
|
1090
913
|
const docsSnapshot = [...docs]
|
|
@@ -1102,20 +925,16 @@ export class VirtualWebSocketClient {
|
|
|
1102
925
|
id: change.id,
|
|
1103
926
|
docChanges,
|
|
1104
927
|
docs: docsSnapshot,
|
|
1105
|
-
msgType
|
|
928
|
+
msgType,
|
|
1106
929
|
})
|
|
1107
930
|
|
|
1108
|
-
// Reporter.surroundThirdByTryCatch(() =>
|
|
1109
931
|
this.listener.onChange(snapshot)
|
|
1110
|
-
// )()
|
|
1111
932
|
}
|
|
1112
933
|
} else {
|
|
1113
934
|
// out-of-order event
|
|
1114
935
|
// if (process.env.DEBUG) {
|
|
1115
936
|
// TODO: report
|
|
1116
|
-
console.warn(
|
|
1117
|
-
`[realtime listener] event received is out of order, cur ${this.sessionInfo.currentEventId} but got ${change.id}`
|
|
1118
|
-
)
|
|
937
|
+
console.warn(`[realtime listener] event received is out of order, cur ${this.sessionInfo.currentEventId} but got ${change.id}`)
|
|
1119
938
|
// }
|
|
1120
939
|
// rebuild watch
|
|
1121
940
|
await this.rebuildWatch()
|
|
@@ -1124,48 +943,29 @@ export class VirtualWebSocketClient {
|
|
|
1124
943
|
}
|
|
1125
944
|
}
|
|
1126
945
|
|
|
1127
|
-
private
|
|
1128
|
-
msg: IResponseMessageInitEventMsg | IResponseMessageNextEventMsg
|
|
1129
|
-
) {
|
|
946
|
+
private postHandleServerEventsValidityCheck(msg: IResponseMessageInitEventMsg | IResponseMessageNextEventMsg) {
|
|
1130
947
|
if (!this.sessionInfo) {
|
|
1131
|
-
console.error(
|
|
1132
|
-
'[realtime listener] internal non-fatal error: sessionInfo lost after server event handling, this should never occur'
|
|
1133
|
-
)
|
|
1134
|
-
|
|
1135
|
-
// writeToFile(
|
|
1136
|
-
// "wserror.txt",
|
|
1137
|
-
// `[realtime listener] internal non-fatal error: sessionInfo lost after server event handling, this should never occur ${JSON.stringify(
|
|
1138
|
-
// Object.assign(
|
|
1139
|
-
// {},
|
|
1140
|
-
// {
|
|
1141
|
-
// requestId: msg.requestId,
|
|
1142
|
-
// watchId: msg.watchId
|
|
1143
|
-
// }
|
|
1144
|
-
// )
|
|
1145
|
-
// )} \n`
|
|
1146
|
-
// )
|
|
948
|
+
console.error('[realtime listener] internal non-fatal error: sessionInfo lost after server event handling, this should never occur')
|
|
1147
949
|
return
|
|
1148
950
|
}
|
|
1149
951
|
|
|
1150
952
|
if (
|
|
1151
|
-
this.sessionInfo.expectEventId
|
|
1152
|
-
this.sessionInfo.currentEventId >= this.sessionInfo.expectEventId
|
|
953
|
+
this.sessionInfo.expectEventId
|
|
954
|
+
&& this.sessionInfo.currentEventId >= this.sessionInfo.expectEventId
|
|
1153
955
|
) {
|
|
1154
956
|
this.clearWaitExpectedEvent()
|
|
1155
957
|
}
|
|
1156
958
|
|
|
1157
959
|
if (this.sessionInfo.currentEventId < msg.msgData.currEvent) {
|
|
1158
|
-
console.warn(
|
|
1159
|
-
'[realtime listener] internal non-fatal error: client eventId does not match with server event id after server event handling'
|
|
1160
|
-
)
|
|
960
|
+
console.warn('[realtime listener] internal non-fatal error: client eventId does not match with server event id after server event handling')
|
|
1161
961
|
return
|
|
1162
962
|
}
|
|
1163
963
|
}
|
|
1164
964
|
|
|
1165
965
|
private clearWaitExpectedEvent() {
|
|
1166
|
-
if (this.
|
|
1167
|
-
clearTimeout(this.
|
|
1168
|
-
this.
|
|
966
|
+
if (this.waitExpectedTimeoutId) {
|
|
967
|
+
clearTimeout(this.waitExpectedTimeoutId)
|
|
968
|
+
this.waitExpectedTimeoutId = undefined
|
|
1169
969
|
}
|
|
1170
970
|
}
|
|
1171
971
|
}
|
|
@@ -1176,7 +976,7 @@ function getPublicEvent(event: IDBEvent): ISingleDBEvent {
|
|
|
1176
976
|
dataType: event.DataType,
|
|
1177
977
|
queueType: event.QueueType,
|
|
1178
978
|
docId: event.DocID,
|
|
1179
|
-
doc: event.Doc && event.Doc !== '{}' ? JSON.parse(event.Doc) : undefined
|
|
979
|
+
doc: event.Doc && event.Doc !== '{}' ? JSON.parse(event.Doc) : undefined,
|
|
1180
980
|
}
|
|
1181
981
|
|
|
1182
982
|
if (event.DataType === 'update') {
|