@juzi/wechaty-puppet-service 1.0.125 → 1.0.126
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/dist/cjs/src/client/puppet-service.d.ts +0 -27
- package/dist/cjs/src/client/puppet-service.d.ts.map +1 -1
- package/dist/cjs/src/client/puppet-service.js +27 -228
- package/dist/cjs/src/client/puppet-service.js.map +1 -1
- package/dist/cjs/src/package-json.js +3 -3
- package/dist/esm/src/client/puppet-service.d.ts +0 -27
- package/dist/esm/src/client/puppet-service.d.ts.map +1 -1
- package/dist/esm/src/client/puppet-service.js +27 -228
- package/dist/esm/src/client/puppet-service.js.map +1 -1
- package/dist/esm/src/package-json.js +3 -3
- package/package.json +3 -3
- package/src/client/puppet-service.ts +30 -230
- package/src/package-json.ts +3 -3
- package/dist/cjs/tests/dirty-gen-guard.spec.d.ts +0 -3
- package/dist/cjs/tests/dirty-gen-guard.spec.d.ts.map +0 -1
- package/dist/cjs/tests/dirty-gen-guard.spec.js +0 -206
- package/dist/cjs/tests/dirty-gen-guard.spec.js.map +0 -1
- package/dist/cjs/tests/fast-dirty-completeness.spec.d.ts +0 -3
- package/dist/cjs/tests/fast-dirty-completeness.spec.d.ts.map +0 -1
- package/dist/cjs/tests/fast-dirty-completeness.spec.js +0 -59
- package/dist/cjs/tests/fast-dirty-completeness.spec.js.map +0 -1
- package/dist/cjs/tests/fast-dirty-room-member.spec.d.ts +0 -3
- package/dist/cjs/tests/fast-dirty-room-member.spec.d.ts.map +0 -1
- package/dist/cjs/tests/fast-dirty-room-member.spec.js +0 -179
- package/dist/cjs/tests/fast-dirty-room-member.spec.js.map +0 -1
- package/dist/cjs/tests/grpc-stream-dirty-order.spec.d.ts +0 -3
- package/dist/cjs/tests/grpc-stream-dirty-order.spec.d.ts.map +0 -1
- package/dist/cjs/tests/grpc-stream-dirty-order.spec.js +0 -135
- package/dist/cjs/tests/grpc-stream-dirty-order.spec.js.map +0 -1
- package/dist/cjs/tests/grpc-stream-dirty-parse.spec.d.ts +0 -3
- package/dist/cjs/tests/grpc-stream-dirty-parse.spec.d.ts.map +0 -1
- package/dist/cjs/tests/grpc-stream-dirty-parse.spec.js +0 -72
- package/dist/cjs/tests/grpc-stream-dirty-parse.spec.js.map +0 -1
- package/dist/esm/tests/dirty-gen-guard.spec.d.ts +0 -3
- package/dist/esm/tests/dirty-gen-guard.spec.d.ts.map +0 -1
- package/dist/esm/tests/dirty-gen-guard.spec.js +0 -178
- package/dist/esm/tests/dirty-gen-guard.spec.js.map +0 -1
- package/dist/esm/tests/fast-dirty-completeness.spec.d.ts +0 -3
- package/dist/esm/tests/fast-dirty-completeness.spec.d.ts.map +0 -1
- package/dist/esm/tests/fast-dirty-completeness.spec.js +0 -34
- package/dist/esm/tests/fast-dirty-completeness.spec.js.map +0 -1
- package/dist/esm/tests/fast-dirty-room-member.spec.d.ts +0 -3
- package/dist/esm/tests/fast-dirty-room-member.spec.d.ts.map +0 -1
- package/dist/esm/tests/fast-dirty-room-member.spec.js +0 -151
- package/dist/esm/tests/fast-dirty-room-member.spec.js.map +0 -1
- package/dist/esm/tests/grpc-stream-dirty-order.spec.d.ts +0 -3
- package/dist/esm/tests/grpc-stream-dirty-order.spec.d.ts.map +0 -1
- package/dist/esm/tests/grpc-stream-dirty-order.spec.js +0 -110
- package/dist/esm/tests/grpc-stream-dirty-order.spec.js.map +0 -1
- package/dist/esm/tests/grpc-stream-dirty-parse.spec.d.ts +0 -3
- package/dist/esm/tests/grpc-stream-dirty-parse.spec.d.ts.map +0 -1
- package/dist/esm/tests/grpc-stream-dirty-parse.spec.js +0 -47
- package/dist/esm/tests/grpc-stream-dirty-parse.spec.js.map +0 -1
|
@@ -110,21 +110,6 @@ class PuppetService extends PUPPET.Puppet {
|
|
|
110
110
|
|
|
111
111
|
protected _payloadStore: PayloadStore
|
|
112
112
|
|
|
113
|
-
/**
|
|
114
|
-
* Per-roomId serialization chain for RoomMember compound dirty events.
|
|
115
|
-
*
|
|
116
|
-
* The compound RoomMember handler is a `get → mutate → set` sequence
|
|
117
|
-
* across an async FlashStore. Two dirty events for the same roomId
|
|
118
|
-
* (e.g. member-A and member-B, arriving back-to-back) would otherwise
|
|
119
|
-
* both read the same snapshot and each write back with only their own
|
|
120
|
-
* key removed -- losing one of the deletes.
|
|
121
|
-
*
|
|
122
|
-
* Keyed by roomId, each entry chains the pending handler run so a
|
|
123
|
-
* follow-up dirty for the same room waits for the in-flight one to
|
|
124
|
-
* finish. Entries self-clean after their tail resolves.
|
|
125
|
-
*/
|
|
126
|
-
private _roomMemberDirtyChain: Map<string, Promise<void>> = new Map()
|
|
127
|
-
|
|
128
113
|
private timeoutMilliseconds: number
|
|
129
114
|
|
|
130
115
|
protected _grpcManager?: GrpcManager
|
|
@@ -383,41 +368,10 @@ class PuppetService extends PUPPET.Puppet {
|
|
|
383
368
|
)
|
|
384
369
|
}
|
|
385
370
|
break
|
|
386
|
-
case grpcPuppet.EventType.EVENT_TYPE_DIRTY:
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
* Bump the generation counter *before* awaiting fastDirty so the
|
|
390
|
-
* whole fastDirty window is covered: any raw fetch that started
|
|
391
|
-
* before this dirty and resolves while fastDirty is deleting the
|
|
392
|
-
* FlashStore row must be judged stale by `isFreshWrite` and skip
|
|
393
|
-
* its write-back -- otherwise it re-poisons the row we just
|
|
394
|
-
* cleared and the value only refreshes after a *second* dirty.
|
|
395
|
-
*
|
|
396
|
-
* Upstream cache-mixin.onDirty bumps the same (type, id) again
|
|
397
|
-
* after `emit('dirty')` below; double-bumping is harmless because
|
|
398
|
-
* the counter only moves forward and `isFreshWrite` compares by
|
|
399
|
-
* equality.
|
|
400
|
-
*/
|
|
401
|
-
this.cache.bumpGen(dirtyPayload.payloadType, dirtyPayload.payloadId)
|
|
402
|
-
/**
|
|
403
|
-
* A compound RoomMember dirty (`${roomId}${SEP}${memberId}`) must
|
|
404
|
-
* also bump the bare roomId key: roomMember write-backs are
|
|
405
|
-
* row-level read-modify-writes keyed by roomId, so a single-member
|
|
406
|
-
* dirty has to move that key too or an in-flight roomMember fetch
|
|
407
|
-
* could still write the whole row back stale.
|
|
408
|
-
*/
|
|
409
|
-
if (dirtyPayload.payloadType === PUPPET.types.Dirty.RoomMember
|
|
410
|
-
&& dirtyPayload.payloadId.includes(PUPPET.STRING_SPLITTER)
|
|
411
|
-
) {
|
|
412
|
-
const [ roomId ] = dirtyPayload.payloadId.split(PUPPET.STRING_SPLITTER)
|
|
413
|
-
if (roomId) {
|
|
414
|
-
this.cache.bumpGen(PUPPET.types.Dirty.RoomMember, roomId)
|
|
415
|
-
}
|
|
416
|
-
}
|
|
417
|
-
await this.fastDirty(dirtyPayload)
|
|
418
|
-
this.emit('dirty', dirtyPayload)
|
|
371
|
+
case grpcPuppet.EventType.EVENT_TYPE_DIRTY:
|
|
372
|
+
await this.fastDirty(JSON.parse(payload))
|
|
373
|
+
this.emit('dirty', JSON.parse(payload) as PUPPET.payloads.EventDirty)
|
|
419
374
|
break
|
|
420
|
-
}
|
|
421
375
|
case grpcPuppet.EventType.EVENT_TYPE_MESSAGE:
|
|
422
376
|
this.emit('message', JSON.parse(payload) as PUPPET.payloads.EventMessage)
|
|
423
377
|
break
|
|
@@ -542,87 +496,6 @@ class PuppetService extends PUPPET.Puppet {
|
|
|
542
496
|
}
|
|
543
497
|
}
|
|
544
498
|
|
|
545
|
-
/**
|
|
546
|
-
* Dirty handler registry, keyed by every `PUPPET.types.Dirty` value.
|
|
547
|
-
*
|
|
548
|
-
* Using a full `Record<...>` (rather than `Partial<...>`) means
|
|
549
|
-
* TypeScript catches a missing entry at compile time -- so when a
|
|
550
|
-
* new DirtyType is added to wechaty-puppet, callers get a build
|
|
551
|
-
* error instead of a silent runtime no-op.
|
|
552
|
-
*
|
|
553
|
-
* Note: `Unspecified` is treated as an upstream contract violation,
|
|
554
|
-
* not a benign no-op -- it logs an error and emits 'error' so hosts
|
|
555
|
-
* notice.
|
|
556
|
-
*/
|
|
557
|
-
private get _dirtyHandlerMap (): Record<PUPPET.types.Dirty, (id: string) => Promise<unknown>> {
|
|
558
|
-
return {
|
|
559
|
-
[PUPPET.types.Dirty.Contact]: async (id: string) => this._payloadStore.contact?.delete(id),
|
|
560
|
-
[PUPPET.types.Dirty.Friendship]: async (_: string) => {},
|
|
561
|
-
[PUPPET.types.Dirty.Message]: async (_: string) => {},
|
|
562
|
-
[PUPPET.types.Dirty.Post]: async (_: string) => {},
|
|
563
|
-
[PUPPET.types.Dirty.Room]: async (id: string) => this._payloadStore.room?.delete(id),
|
|
564
|
-
[PUPPET.types.Dirty.RoomMember]: async (id: string) => {
|
|
565
|
-
const [ roomId, memberId ] = id.split(PUPPET.STRING_SPLITTER)
|
|
566
|
-
if (!roomId) {
|
|
567
|
-
return
|
|
568
|
-
}
|
|
569
|
-
const store = this._payloadStore.roomMember
|
|
570
|
-
if (!store) {
|
|
571
|
-
return
|
|
572
|
-
}
|
|
573
|
-
// Bare roomId: the whole member set is stale, drop the row.
|
|
574
|
-
// Row-level delete is idempotent so it does not need serialization.
|
|
575
|
-
if (memberId === undefined) {
|
|
576
|
-
await store.delete(roomId)
|
|
577
|
-
return
|
|
578
|
-
}
|
|
579
|
-
// Compound id: `get → mutate → set` is a read-modify-write across
|
|
580
|
-
// an async store. Serialize per roomId so two concurrent compound
|
|
581
|
-
// dirties don't each read the same snapshot and each drop only
|
|
582
|
-
// their own key -- which would lose one of the two deletes.
|
|
583
|
-
const previous = this._roomMemberDirtyChain.get(roomId) ?? Promise.resolve()
|
|
584
|
-
const next = (async () => {
|
|
585
|
-
// Swallow the previous link's rejection so a failure upstream
|
|
586
|
-
// does not silently skip our own mutation. The prior handler
|
|
587
|
-
// already reported its error via fastDirty()'s try/catch.
|
|
588
|
-
try {
|
|
589
|
-
await previous
|
|
590
|
-
} catch (_) { /* ignore, upstream already reported */ }
|
|
591
|
-
const current = await store.get(roomId)
|
|
592
|
-
if (!current || !Object.prototype.hasOwnProperty.call(current, memberId)) {
|
|
593
|
-
return
|
|
594
|
-
}
|
|
595
|
-
const { [memberId]: _drop, ...rest } = current
|
|
596
|
-
if (Object.keys(rest).length === 0) {
|
|
597
|
-
await store.delete(roomId)
|
|
598
|
-
} else {
|
|
599
|
-
await store.set(roomId, rest)
|
|
600
|
-
}
|
|
601
|
-
})()
|
|
602
|
-
this._roomMemberDirtyChain.set(roomId, next)
|
|
603
|
-
try {
|
|
604
|
-
await next
|
|
605
|
-
} finally {
|
|
606
|
-
// Only clear the slot if we are still the tail; otherwise a
|
|
607
|
-
// later handler has already extended the chain and owns cleanup.
|
|
608
|
-
if (this._roomMemberDirtyChain.get(roomId) === next) {
|
|
609
|
-
this._roomMemberDirtyChain.delete(roomId)
|
|
610
|
-
}
|
|
611
|
-
}
|
|
612
|
-
},
|
|
613
|
-
[PUPPET.types.Dirty.Tag]: async (id: string) => this._payloadStore.tag?.delete(id),
|
|
614
|
-
[PUPPET.types.Dirty.TagGroup]: async (id: string) => this._payloadStore.tagGroup?.delete(id),
|
|
615
|
-
[PUPPET.types.Dirty.WxxdProduct]: async (_: string) => {},
|
|
616
|
-
[PUPPET.types.Dirty.WxxdOrder]: async (_: string) => {},
|
|
617
|
-
[PUPPET.types.Dirty.Call]: async (_: string) => {},
|
|
618
|
-
[PUPPET.types.Dirty.Unspecified]: async (id: string) => {
|
|
619
|
-
const msg = `fastDirty() received Unspecified dirty type (id=${id}); upstream puppet is leaking protobuf default — this is a server-side contract bug`
|
|
620
|
-
this.log.error('PuppetService', msg)
|
|
621
|
-
this.emit('error', new Error(msg))
|
|
622
|
-
},
|
|
623
|
-
}
|
|
624
|
-
}
|
|
625
|
-
|
|
626
499
|
/**
|
|
627
500
|
* `onDirty()` is called when the puppet emit `dirty` event.
|
|
628
501
|
* the event listener will be registered in `start()` from the `PuppetAbstract` class
|
|
@@ -635,18 +508,20 @@ class PuppetService extends PUPPET.Puppet {
|
|
|
635
508
|
): Promise<void> {
|
|
636
509
|
this.log.verbose('PuppetService', 'fastDirty(%s<%s>, %s)', PUPPET.types.Dirty[payloadType], payloadType, payloadId)
|
|
637
510
|
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
511
|
+
const dirtyMap: Partial<Record<PUPPET.types.Dirty, (id: string) => Promise<unknown>>> = {
|
|
512
|
+
[PUPPET.types.Dirty.Contact]: async (id: string) => this._payloadStore.contact?.delete(id),
|
|
513
|
+
[PUPPET.types.Dirty.Friendship]: async (_: string) => {},
|
|
514
|
+
[PUPPET.types.Dirty.Message]: async (_: string) => {},
|
|
515
|
+
[PUPPET.types.Dirty.Post]: async (_: string) => {},
|
|
516
|
+
[PUPPET.types.Dirty.Room]: async (id: string) => this._payloadStore.room?.delete(id),
|
|
517
|
+
[PUPPET.types.Dirty.RoomMember]: async (id: string) => this._payloadStore.roomMember?.delete(id),
|
|
518
|
+
[PUPPET.types.Dirty.Tag]: async (id: string) => this._payloadStore.tag?.delete(id),
|
|
519
|
+
[PUPPET.types.Dirty.TagGroup]: async (id: string) => this._payloadStore.tagGroup?.delete(id),
|
|
520
|
+
[PUPPET.types.Dirty.Unspecified]: async (id: string) => { throw new Error('Unspecified type with id: ' + id) },
|
|
646
521
|
}
|
|
647
522
|
|
|
648
523
|
try {
|
|
649
|
-
await
|
|
524
|
+
await dirtyMap[payloadType]?.(payloadId)
|
|
650
525
|
} catch (error) {
|
|
651
526
|
this.emit('error', error)
|
|
652
527
|
}
|
|
@@ -884,10 +759,6 @@ class PuppetService extends PUPPET.Puppet {
|
|
|
884
759
|
return cachedPayload
|
|
885
760
|
}
|
|
886
761
|
|
|
887
|
-
// Snapshot the gen before the raw fetch so a dirty that lands while
|
|
888
|
-
// the gRPC round-trip is in flight makes the write-back below stale.
|
|
889
|
-
const gen = this.cache.snapshotGen(PUPPET.types.Dirty.Contact, id)
|
|
890
|
-
|
|
891
762
|
const request = new grpcPuppet.ContactPayloadRequest()
|
|
892
763
|
request.setId(id)
|
|
893
764
|
|
|
@@ -930,12 +801,8 @@ class PuppetService extends PUPPET.Puppet {
|
|
|
930
801
|
aka : response.getAka(),
|
|
931
802
|
}
|
|
932
803
|
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
this.log.silly('PuppetService', 'contactRawPayload(%s) cache SET', id)
|
|
936
|
-
} else {
|
|
937
|
-
this.log.silly('PuppetService', 'contactRawPayload(%s) cache SET skipped: dirty landed during raw fetch', id)
|
|
938
|
-
}
|
|
804
|
+
await this._payloadStore.contact?.set(id, payload)
|
|
805
|
+
this.log.silly('PuppetService', 'contactRawPayload(%s) cache SET', id)
|
|
939
806
|
|
|
940
807
|
return payload
|
|
941
808
|
}
|
|
@@ -963,14 +830,6 @@ class PuppetService extends PUPPET.Puppet {
|
|
|
963
830
|
|
|
964
831
|
if (needGetSet.size > 0) {
|
|
965
832
|
try {
|
|
966
|
-
// Snapshot each id's gen before the batch fetch; write-backs below
|
|
967
|
-
// are validated per id so a dirty on one contact mid-flight only
|
|
968
|
-
// skips that contact's stale write, not the whole batch.
|
|
969
|
-
const genSnap = new Map<string, number>()
|
|
970
|
-
for (const contactId of needGetSet) {
|
|
971
|
-
genSnap.set(contactId, this.cache.snapshotGen(PUPPET.types.Dirty.Contact, contactId))
|
|
972
|
-
}
|
|
973
|
-
|
|
974
833
|
const request = new grpcPuppet.BatchContactPayloadRequest()
|
|
975
834
|
request.setIdsList(Array.from(needGetSet))
|
|
976
835
|
|
|
@@ -984,11 +843,7 @@ class PuppetService extends PUPPET.Puppet {
|
|
|
984
843
|
const contactId = payload.getId()
|
|
985
844
|
const puppetPayload = contactPbToPayload(payload)
|
|
986
845
|
result.set(contactId, puppetPayload)
|
|
987
|
-
|
|
988
|
-
await this._payloadStore.contact?.set(contactId, puppetPayload)
|
|
989
|
-
} else {
|
|
990
|
-
this.log.silly('PuppetService', 'batchContactRawPayload(%s) cache SET skipped: dirty landed during raw fetch', contactId)
|
|
991
|
-
}
|
|
846
|
+
await this._payloadStore.contact?.set(contactId, puppetPayload)
|
|
992
847
|
}
|
|
993
848
|
} catch (e) {
|
|
994
849
|
this.log.error('PuppetService', 'batchContactRawPayload(%s, %s) error: %s, use one by one method', contactIdList, needGetSet, e)
|
|
@@ -2352,10 +2207,6 @@ class PuppetService extends PUPPET.Puppet {
|
|
|
2352
2207
|
return cachedPayload
|
|
2353
2208
|
}
|
|
2354
2209
|
|
|
2355
|
-
// Snapshot the gen before the raw fetch so a dirty that lands while
|
|
2356
|
-
// the gRPC round-trip is in flight makes the write-back below stale.
|
|
2357
|
-
const gen = this.cache.snapshotGen(PUPPET.types.Dirty.Room, id)
|
|
2358
|
-
|
|
2359
2210
|
const request = new grpcPuppet.RoomPayloadRequest()
|
|
2360
2211
|
request.setId(id)
|
|
2361
2212
|
|
|
@@ -2382,12 +2233,8 @@ class PuppetService extends PUPPET.Puppet {
|
|
|
2382
2233
|
payload.createTime = millisecondsFromTimestamp(createTime)
|
|
2383
2234
|
}
|
|
2384
2235
|
|
|
2385
|
-
|
|
2386
|
-
|
|
2387
|
-
this.log.silly('PuppetService', 'roomRawPayload(%s) cache SET', id)
|
|
2388
|
-
} else {
|
|
2389
|
-
this.log.silly('PuppetService', 'roomRawPayload(%s) cache SET skipped: dirty landed during raw fetch', id)
|
|
2390
|
-
}
|
|
2236
|
+
await this._payloadStore.room?.set(id, payload)
|
|
2237
|
+
this.log.silly('PuppetService', 'roomRawPayload(%s) cache SET', id)
|
|
2391
2238
|
|
|
2392
2239
|
return payload
|
|
2393
2240
|
}
|
|
@@ -2415,14 +2262,6 @@ class PuppetService extends PUPPET.Puppet {
|
|
|
2415
2262
|
|
|
2416
2263
|
if (needGetSet.size > 0) {
|
|
2417
2264
|
try {
|
|
2418
|
-
// Snapshot each id's gen before the batch fetch; write-backs below
|
|
2419
|
-
// are validated per id so a dirty on one room mid-flight only skips
|
|
2420
|
-
// that room's stale write, not the whole batch.
|
|
2421
|
-
const genSnap = new Map<string, number>()
|
|
2422
|
-
for (const roomId of needGetSet) {
|
|
2423
|
-
genSnap.set(roomId, this.cache.snapshotGen(PUPPET.types.Dirty.Room, roomId))
|
|
2424
|
-
}
|
|
2425
|
-
|
|
2426
2265
|
const request = new grpcPuppet.BatchRoomPayloadRequest()
|
|
2427
2266
|
request.setIdsList(Array.from(needGetSet))
|
|
2428
2267
|
|
|
@@ -2451,11 +2290,7 @@ class PuppetService extends PUPPET.Puppet {
|
|
|
2451
2290
|
puppetPayload.createTime = millisecondsFromTimestamp(createTime)
|
|
2452
2291
|
}
|
|
2453
2292
|
result.set(roomId, puppetPayload)
|
|
2454
|
-
|
|
2455
|
-
await this._payloadStore.room?.set(roomId, puppetPayload)
|
|
2456
|
-
} else {
|
|
2457
|
-
this.log.silly('PuppetService', 'batchRoomRawPayload(%s) cache SET skipped: dirty landed during raw fetch', roomId)
|
|
2458
|
-
}
|
|
2293
|
+
await this._payloadStore.room?.set(roomId, puppetPayload)
|
|
2459
2294
|
}
|
|
2460
2295
|
} catch (e) {
|
|
2461
2296
|
this.log.error('PuppetService', 'batchRoomRawPayload(%s, %s) error: %s, use one by one method', roomIdList, needGetSet, e)
|
|
@@ -2735,13 +2570,6 @@ class PuppetService extends PUPPET.Puppet {
|
|
|
2735
2570
|
override async roomMemberRawPayload (roomId: string, contactId: string): Promise<PUPPET.payloads.RoomMember> {
|
|
2736
2571
|
this.log.verbose('PuppetService', 'roomMemberRawPayload(%s, %s)', roomId, contactId)
|
|
2737
2572
|
|
|
2738
|
-
// Row-level read-modify-write: the write-back merges the fetched
|
|
2739
|
-
// member into the existing row, so snapshot the room-level gen before
|
|
2740
|
-
// even reading the row. A room-level dirty (bare roomId, or the
|
|
2741
|
-
// room-level bump a compound RoomMember dirty triggers) that lands
|
|
2742
|
-
// anywhere in this window then skips the merge write-back.
|
|
2743
|
-
const roomGen = this.cache.snapshotGen(PUPPET.types.Dirty.RoomMember, roomId)
|
|
2744
|
-
|
|
2745
2573
|
const cachedPayload = await this._payloadStore.roomMember?.get(roomId)
|
|
2746
2574
|
const cachedRoomMemberPayload = cachedPayload && cachedPayload[contactId]
|
|
2747
2575
|
|
|
@@ -2770,15 +2598,11 @@ class PuppetService extends PUPPET.Puppet {
|
|
|
2770
2598
|
joinTime : response.getJoinTime(),
|
|
2771
2599
|
}
|
|
2772
2600
|
|
|
2773
|
-
|
|
2774
|
-
|
|
2775
|
-
|
|
2776
|
-
|
|
2777
|
-
|
|
2778
|
-
this.log.silly('PuppetService', 'roomMemberRawPayload(%s, %s) cache SET', roomId, contactId)
|
|
2779
|
-
} else {
|
|
2780
|
-
this.log.silly('PuppetService', 'roomMemberRawPayload(%s, %s) cache SET skipped: dirty landed during raw fetch', roomId, contactId)
|
|
2781
|
-
}
|
|
2601
|
+
await this._payloadStore.roomMember?.set(roomId, {
|
|
2602
|
+
...cachedPayload,
|
|
2603
|
+
[contactId]: payload,
|
|
2604
|
+
})
|
|
2605
|
+
this.log.silly('PuppetService', 'roomMemberRawPayload(%s, %s) cache SET', roomId, contactId)
|
|
2782
2606
|
|
|
2783
2607
|
return payload
|
|
2784
2608
|
}
|
|
@@ -2795,10 +2619,6 @@ class PuppetService extends PUPPET.Puppet {
|
|
|
2795
2619
|
const result = new Map<string, PUPPET.payloads.RoomMember>()
|
|
2796
2620
|
const contactIdSet = new Set<string>(contactIdList)
|
|
2797
2621
|
let needGetSet = new Set<string>()
|
|
2798
|
-
// Row-level read-modify-write keyed by roomId: snapshot the room-level
|
|
2799
|
-
// gen before reading the row so a dirty landing anywhere in the window
|
|
2800
|
-
// skips the merged write-back.
|
|
2801
|
-
const roomGen = this.cache.snapshotGen(PUPPET.types.Dirty.RoomMember, roomId)
|
|
2802
2622
|
const cachedPayload = await this._payloadStore.roomMember?.get(roomId) || {}
|
|
2803
2623
|
if (Object.keys(cachedPayload).length > 0) {
|
|
2804
2624
|
for (const contactId of contactIdSet) {
|
|
@@ -2831,11 +2651,7 @@ class PuppetService extends PUPPET.Puppet {
|
|
|
2831
2651
|
result.set(contactId, puppetPayload)
|
|
2832
2652
|
cachedPayload[contactId] = puppetPayload
|
|
2833
2653
|
}
|
|
2834
|
-
|
|
2835
|
-
await this._payloadStore.roomMember?.set(roomId, cachedPayload)
|
|
2836
|
-
} else {
|
|
2837
|
-
this.log.silly('PuppetService', 'batchRoomMemberRawPayload(%s) cache SET skipped: dirty landed during raw fetch', roomId)
|
|
2838
|
-
}
|
|
2654
|
+
await this._payloadStore.roomMember?.set(roomId, cachedPayload)
|
|
2839
2655
|
} catch (e) {
|
|
2840
2656
|
this.log.error('PuppetService', 'batchRoomMemberRawPayload(%s, %s) error: %s, use one by one method', roomId, needGetSet, e)
|
|
2841
2657
|
for (const contactId of needGetSet) {
|
|
@@ -3481,10 +3297,6 @@ class PuppetService extends PUPPET.Puppet {
|
|
|
3481
3297
|
return cachedPayload
|
|
3482
3298
|
}
|
|
3483
3299
|
|
|
3484
|
-
// Snapshot the gen before the raw fetch so a dirty that lands while
|
|
3485
|
-
// the gRPC round-trip is in flight makes the write-back below stale.
|
|
3486
|
-
const gen = this.cache.snapshotGen(PUPPET.types.Dirty.TagGroup, id)
|
|
3487
|
-
|
|
3488
3300
|
const request = new grpcPuppet.TagGroupPayloadRequest()
|
|
3489
3301
|
request.setGroupId(id)
|
|
3490
3302
|
|
|
@@ -3504,12 +3316,8 @@ class PuppetService extends PUPPET.Puppet {
|
|
|
3504
3316
|
type: grpcPayload.getType(),
|
|
3505
3317
|
}
|
|
3506
3318
|
|
|
3507
|
-
|
|
3508
|
-
|
|
3509
|
-
this.log.silly('PuppetService', 'tagGroupPayloadPuppet(%s) cache SET', id)
|
|
3510
|
-
} else {
|
|
3511
|
-
this.log.silly('PuppetService', 'tagGroupPayloadPuppet(%s) cache SET skipped: dirty landed during raw fetch', id)
|
|
3512
|
-
}
|
|
3319
|
+
await this._payloadStore.tagGroup?.set(id, payload)
|
|
3320
|
+
this.log.silly('PuppetService', 'tagGroupPayloadPuppet(%s) cache SET', id)
|
|
3513
3321
|
|
|
3514
3322
|
return payload
|
|
3515
3323
|
}
|
|
@@ -3523,10 +3331,6 @@ class PuppetService extends PUPPET.Puppet {
|
|
|
3523
3331
|
return cachedPayload
|
|
3524
3332
|
}
|
|
3525
3333
|
|
|
3526
|
-
// Snapshot the gen before the raw fetch so a dirty that lands while
|
|
3527
|
-
// the gRPC round-trip is in flight makes the write-back below stale.
|
|
3528
|
-
const gen = this.cache.snapshotGen(PUPPET.types.Dirty.Tag, tagId)
|
|
3529
|
-
|
|
3530
3334
|
const request = new grpcPuppet.TagPayloadRequest()
|
|
3531
3335
|
request.setTagId(tagId)
|
|
3532
3336
|
|
|
@@ -3547,12 +3351,8 @@ class PuppetService extends PUPPET.Puppet {
|
|
|
3547
3351
|
type: grpcPayload.getType(),
|
|
3548
3352
|
}
|
|
3549
3353
|
|
|
3550
|
-
|
|
3551
|
-
|
|
3552
|
-
this.log.silly('PuppetService', 'tagPayloadPuppet(%s) cache SET', tagId)
|
|
3553
|
-
} else {
|
|
3554
|
-
this.log.silly('PuppetService', 'tagPayloadPuppet(%s) cache SET skipped: dirty landed during raw fetch', tagId)
|
|
3555
|
-
}
|
|
3354
|
+
await this._payloadStore.tag?.set(tagId, payload)
|
|
3355
|
+
this.log.silly('PuppetService', 'tagPayloadPuppet(%s) cache SET', tagId)
|
|
3556
3356
|
|
|
3557
3357
|
return payload
|
|
3558
3358
|
}
|
package/src/package-json.ts
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
import type { PackageJson } from 'type-fest'
|
|
5
5
|
export const packageJson: PackageJson = {
|
|
6
6
|
"name": "@juzi/wechaty-puppet-service",
|
|
7
|
-
"version": "1.0.
|
|
7
|
+
"version": "1.0.126",
|
|
8
8
|
"description": "Puppet Service for Wechaty",
|
|
9
9
|
"type": "module",
|
|
10
10
|
"exports": {
|
|
@@ -55,7 +55,7 @@ export const packageJson: PackageJson = {
|
|
|
55
55
|
"@chatie/eslint-config": "^1.0.4",
|
|
56
56
|
"@chatie/semver": "^0.4.7",
|
|
57
57
|
"@chatie/tsconfig": "^4.6.3",
|
|
58
|
-
"@juzi/wechaty-puppet": "^1.0.
|
|
58
|
+
"@juzi/wechaty-puppet": "^1.0.151",
|
|
59
59
|
"@juzi/wechaty-puppet-mock": "^1.0.1",
|
|
60
60
|
"@swc/core": "1.3.39",
|
|
61
61
|
"@types/google-protobuf": "^3.15.5",
|
|
@@ -75,7 +75,7 @@ export const packageJson: PackageJson = {
|
|
|
75
75
|
"why-is-node-running": "^2.2.1"
|
|
76
76
|
},
|
|
77
77
|
"peerDependencies": {
|
|
78
|
-
"@juzi/wechaty-puppet": "^1.0.
|
|
78
|
+
"@juzi/wechaty-puppet": "^1.0.151"
|
|
79
79
|
},
|
|
80
80
|
"dependencies": {
|
|
81
81
|
"@juzi/wechaty-grpc": "^1.0.106",
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"dirty-gen-guard.spec.d.ts","sourceRoot":"","sources":["../../../tests/dirty-gen-guard.spec.ts"],"names":[],"mappings":""}
|
|
@@ -1,206 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env -S node --no-warnings --loader ts-node/esm
|
|
2
|
-
"use strict";
|
|
3
|
-
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
4
|
-
if (k2 === undefined) k2 = k;
|
|
5
|
-
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
6
|
-
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
7
|
-
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
8
|
-
}
|
|
9
|
-
Object.defineProperty(o, k2, desc);
|
|
10
|
-
}) : (function(o, m, k, k2) {
|
|
11
|
-
if (k2 === undefined) k2 = k;
|
|
12
|
-
o[k2] = m[k];
|
|
13
|
-
}));
|
|
14
|
-
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
15
|
-
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
16
|
-
}) : function(o, v) {
|
|
17
|
-
o["default"] = v;
|
|
18
|
-
});
|
|
19
|
-
var __importStar = (this && this.__importStar) || function (mod) {
|
|
20
|
-
if (mod && mod.__esModule) return mod;
|
|
21
|
-
var result = {};
|
|
22
|
-
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
23
|
-
__setModuleDefault(result, mod);
|
|
24
|
-
return result;
|
|
25
|
-
};
|
|
26
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
27
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
28
|
-
};
|
|
29
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
30
|
-
/**
|
|
31
|
-
* Regression tests for the FlashStore write-back generation guard.
|
|
32
|
-
*
|
|
33
|
-
* `_payloadStore` (FlashStore, on disk) is a cache layer independent of
|
|
34
|
-
* the puppet-side LRU. Before this fix it had no protection against the
|
|
35
|
-
* "dirty lands during a raw fetch" race:
|
|
36
|
-
*
|
|
37
|
-
* 1. A raw fetch (e.g. `contactRawPayload`) starts a gRPC round-trip.
|
|
38
|
-
* 2. An EVENT_TYPE_DIRTY arrives; `fastDirty` deletes the FlashStore row.
|
|
39
|
-
* 3. The in-flight fetch resolves and writes the *pre-dirty* payload
|
|
40
|
-
* back into the FlashStore -- re-poisoning the row that was just
|
|
41
|
-
* cleared. The value then only refreshes after a *second* dirty.
|
|
42
|
-
*
|
|
43
|
-
* The fix snapshots a per-(type, id) generation counter before the raw
|
|
44
|
-
* fetch and re-checks it with `isFreshWrite` before every FlashStore
|
|
45
|
-
* write-back; the EVENT_TYPE_DIRTY handler bumps that counter before it
|
|
46
|
-
* even awaits `fastDirty`, so the whole delete window is covered.
|
|
47
|
-
*/
|
|
48
|
-
const tstest_1 = require("tstest");
|
|
49
|
-
const os_1 = __importDefault(require("os"));
|
|
50
|
-
const path_1 = __importDefault(require("path"));
|
|
51
|
-
const fs_1 = __importDefault(require("fs"));
|
|
52
|
-
const PUPPET = __importStar(require("@juzi/wechaty-puppet"));
|
|
53
|
-
const wechaty_grpc_1 = require("@juzi/wechaty-grpc");
|
|
54
|
-
const mod_js_1 = require("../src/mod.js");
|
|
55
|
-
const makePuppet = async () => {
|
|
56
|
-
const token = `puppet_service_test_${Date.now()}_${Math.floor(Math.random() * 1e6)}`;
|
|
57
|
-
const puppet = new mod_js_1.PuppetService({ token });
|
|
58
|
-
const accountId = 'acct-test';
|
|
59
|
-
await puppet._payloadStore.start(accountId);
|
|
60
|
-
return { puppet, token };
|
|
61
|
-
};
|
|
62
|
-
const cleanup = async (puppet, token) => {
|
|
63
|
-
await puppet._payloadStore.stop();
|
|
64
|
-
try {
|
|
65
|
-
await fs_1.default.promises.rm(path_1.default.join(os_1.default.homedir(), '.wechaty', 'wechaty-puppet-service', token), { recursive: true, force: true });
|
|
66
|
-
}
|
|
67
|
-
catch (_) { /* ignore */ }
|
|
68
|
-
};
|
|
69
|
-
/**
|
|
70
|
-
* A one-shot gate for a fake gRPC method. The method resolves `entered`
|
|
71
|
-
* as soon as it is invoked (by which point the caller's gen snapshot has
|
|
72
|
-
* already been taken, since the snapshot precedes the fetch call), then
|
|
73
|
-
* holds its callback until `release()` is called. This lets the test slot
|
|
74
|
-
* a dirty deterministically between the fetch start (gen snapshot) and
|
|
75
|
-
* the fetch resolution (write-back) -- no timers, no flakiness.
|
|
76
|
-
*/
|
|
77
|
-
const makeGate = () => {
|
|
78
|
-
let release = () => { };
|
|
79
|
-
let markEntered = () => { };
|
|
80
|
-
const gate = new Promise(resolve => { release = resolve; });
|
|
81
|
-
const entered = new Promise(resolve => { markEntered = resolve; });
|
|
82
|
-
return { gate, entered, enter: () => markEntered(), release: () => release() };
|
|
83
|
-
};
|
|
84
|
-
(0, tstest_1.test)('contactRawPayload: a dirty mid-fetch skips the stale FlashStore write-back', async (t) => {
|
|
85
|
-
const { puppet, token } = await makePuppet();
|
|
86
|
-
const contactId = 'contact-race';
|
|
87
|
-
const response = new wechaty_grpc_1.puppet.ContactPayloadResponse();
|
|
88
|
-
response.setId(contactId);
|
|
89
|
-
response.setName('stale-name-from-inflight-fetch');
|
|
90
|
-
const { gate, entered, enter, release } = makeGate();
|
|
91
|
-
puppet._grpcManager = {
|
|
92
|
-
client: {
|
|
93
|
-
contactPayload: (_req, cb) => {
|
|
94
|
-
enter();
|
|
95
|
-
void (async () => {
|
|
96
|
-
await gate;
|
|
97
|
-
cb(null, response);
|
|
98
|
-
})();
|
|
99
|
-
},
|
|
100
|
-
},
|
|
101
|
-
};
|
|
102
|
-
// Start the fetch: it snapshots the gen, then blocks on the gate.
|
|
103
|
-
const fetchPromise = puppet.contactRawPayload(contactId);
|
|
104
|
-
// Deterministic: once the fake method is entered, the snapshot is taken.
|
|
105
|
-
await entered;
|
|
106
|
-
// A dirty arrives while the fetch is in flight: bump gen + clear store,
|
|
107
|
-
// exactly as the EVENT_TYPE_DIRTY handler does.
|
|
108
|
-
puppet.cache.bumpGen(PUPPET.types.Dirty.Contact, contactId);
|
|
109
|
-
await puppet.fastDirty({ payloadType: PUPPET.types.Dirty.Contact, payloadId: contactId });
|
|
110
|
-
// Now let the stale fetch resolve into the write-back path.
|
|
111
|
-
release();
|
|
112
|
-
const returned = await fetchPromise;
|
|
113
|
-
t.equal(returned.id, contactId, 'raw fetch still returns its payload to the caller');
|
|
114
|
-
const stored = await puppet._payloadStore.contact.get(contactId);
|
|
115
|
-
t.notOk(stored, 'stale in-flight fetch must NOT re-poison the FlashStore after the dirty');
|
|
116
|
-
await cleanup(puppet, token);
|
|
117
|
-
});
|
|
118
|
-
(0, tstest_1.test)('contactRawPayload: no dirty means the write-back proceeds as usual', async (t) => {
|
|
119
|
-
const { puppet, token } = await makePuppet();
|
|
120
|
-
const contactId = 'contact-happy';
|
|
121
|
-
const response = new wechaty_grpc_1.puppet.ContactPayloadResponse();
|
|
122
|
-
response.setId(contactId);
|
|
123
|
-
response.setName('fresh-name');
|
|
124
|
-
puppet._grpcManager = {
|
|
125
|
-
client: {
|
|
126
|
-
contactPayload: (_req, cb) => cb(null, response),
|
|
127
|
-
},
|
|
128
|
-
};
|
|
129
|
-
const returned = await puppet.contactRawPayload(contactId);
|
|
130
|
-
t.equal(returned.id, contactId, 'raw fetch returns its payload');
|
|
131
|
-
const stored = await puppet._payloadStore.contact.get(contactId);
|
|
132
|
-
t.ok(stored, 'without a racing dirty the payload is written to the FlashStore');
|
|
133
|
-
t.equal(stored && stored.id, contactId, 'stored payload matches the fetched id');
|
|
134
|
-
await cleanup(puppet, token);
|
|
135
|
-
});
|
|
136
|
-
(0, tstest_1.test)('EVENT_TYPE_DIRTY bumps gen before awaiting fastDirty so pre-event snapshots are stale', async (t) => {
|
|
137
|
-
const { puppet, token } = await makePuppet();
|
|
138
|
-
const contactId = 'contact-bump';
|
|
139
|
-
// A getter that snapshotted just before the event arrived.
|
|
140
|
-
const snapshot = puppet.cache.snapshotGen(PUPPET.types.Dirty.Contact, contactId);
|
|
141
|
-
const dirtyJson = JSON.stringify({
|
|
142
|
-
payloadType: PUPPET.types.Dirty.Contact,
|
|
143
|
-
payloadId: contactId,
|
|
144
|
-
});
|
|
145
|
-
await puppet.onGrpcStreamEvent({
|
|
146
|
-
getType: () => wechaty_grpc_1.puppet.EventType.EVENT_TYPE_DIRTY,
|
|
147
|
-
getPayload: () => dirtyJson,
|
|
148
|
-
getSeq: () => 0,
|
|
149
|
-
});
|
|
150
|
-
t.notOk(puppet.cache.isFreshWrite(PUPPET.types.Dirty.Contact, contactId, snapshot), 'a snapshot taken before the dirty event must be judged stale afterwards');
|
|
151
|
-
await cleanup(puppet, token);
|
|
152
|
-
});
|
|
153
|
-
(0, tstest_1.test)('EVENT_TYPE_DIRTY compound RoomMember bumps both the compound and the room-level gen', async (t) => {
|
|
154
|
-
const { puppet, token } = await makePuppet();
|
|
155
|
-
const roomId = 'room-bump';
|
|
156
|
-
const memberId = 'member-bump';
|
|
157
|
-
const compoundId = `${roomId}${PUPPET.STRING_SPLITTER}${memberId}`;
|
|
158
|
-
const compoundSnap = puppet.cache.snapshotGen(PUPPET.types.Dirty.RoomMember, compoundId);
|
|
159
|
-
const roomSnap = puppet.cache.snapshotGen(PUPPET.types.Dirty.RoomMember, roomId);
|
|
160
|
-
const dirtyJson = JSON.stringify({
|
|
161
|
-
payloadType: PUPPET.types.Dirty.RoomMember,
|
|
162
|
-
payloadId: compoundId,
|
|
163
|
-
});
|
|
164
|
-
await puppet.onGrpcStreamEvent({
|
|
165
|
-
getType: () => wechaty_grpc_1.puppet.EventType.EVENT_TYPE_DIRTY,
|
|
166
|
-
getPayload: () => dirtyJson,
|
|
167
|
-
getSeq: () => 0,
|
|
168
|
-
});
|
|
169
|
-
t.notOk(puppet.cache.isFreshWrite(PUPPET.types.Dirty.RoomMember, compoundId, compoundSnap), 'the compound (roomId+SEP+memberId) key must be bumped');
|
|
170
|
-
t.notOk(puppet.cache.isFreshWrite(PUPPET.types.Dirty.RoomMember, roomId, roomSnap), 'the bare room-level key must also be bumped so row-level write-backs are guarded');
|
|
171
|
-
await cleanup(puppet, token);
|
|
172
|
-
});
|
|
173
|
-
(0, tstest_1.test)('roomMemberRawPayload: a room-level dirty mid-fetch skips the row write-back', async (t) => {
|
|
174
|
-
const { puppet, token } = await makePuppet();
|
|
175
|
-
const roomId = 'room-member-race';
|
|
176
|
-
const contactId = 'member-race';
|
|
177
|
-
const response = new wechaty_grpc_1.puppet.RoomMemberPayloadResponse();
|
|
178
|
-
response.setId(contactId);
|
|
179
|
-
response.setName('stale-member-from-inflight-fetch');
|
|
180
|
-
const { gate, entered, enter, release } = makeGate();
|
|
181
|
-
puppet._grpcManager = {
|
|
182
|
-
client: {
|
|
183
|
-
roomMemberPayload: (_req, cb) => {
|
|
184
|
-
enter();
|
|
185
|
-
void (async () => {
|
|
186
|
-
await gate;
|
|
187
|
-
cb(null, response);
|
|
188
|
-
})();
|
|
189
|
-
},
|
|
190
|
-
},
|
|
191
|
-
};
|
|
192
|
-
// Start the fetch: it snapshots the room-level gen before reading the
|
|
193
|
-
// row, then blocks on the gate.
|
|
194
|
-
const fetchPromise = puppet.roomMemberRawPayload(roomId, contactId);
|
|
195
|
-
await entered;
|
|
196
|
-
// A bare-roomId dirty lands mid-fetch: the whole member set is stale.
|
|
197
|
-
puppet.cache.bumpGen(PUPPET.types.Dirty.RoomMember, roomId);
|
|
198
|
-
await puppet.fastDirty({ payloadType: PUPPET.types.Dirty.RoomMember, payloadId: roomId });
|
|
199
|
-
release();
|
|
200
|
-
const returned = await fetchPromise;
|
|
201
|
-
t.equal(returned.id, contactId, 'raw fetch still returns its member payload to the caller');
|
|
202
|
-
const stored = await puppet._payloadStore.roomMember.get(roomId);
|
|
203
|
-
t.notOk(stored, 'stale in-flight member fetch must NOT re-create the row after the dirty');
|
|
204
|
-
await cleanup(puppet, token);
|
|
205
|
-
});
|
|
206
|
-
//# sourceMappingURL=dirty-gen-guard.spec.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"dirty-gen-guard.spec.js","sourceRoot":"","sources":["../../../tests/dirty-gen-guard.spec.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"fast-dirty-completeness.spec.d.ts","sourceRoot":"","sources":["../../../tests/fast-dirty-completeness.spec.ts"],"names":[],"mappings":""}
|