@juzi/wechaty-puppet-service 1.0.124 → 1.0.125
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.map +1 -1
- package/dist/cjs/src/client/puppet-service.js +120 -16
- package/dist/cjs/src/client/puppet-service.js.map +1 -1
- package/dist/cjs/src/package-json.js +3 -3
- package/dist/cjs/tests/dirty-gen-guard.spec.d.ts +3 -0
- package/dist/cjs/tests/dirty-gen-guard.spec.d.ts.map +1 -0
- package/dist/cjs/tests/dirty-gen-guard.spec.js +206 -0
- package/dist/cjs/tests/dirty-gen-guard.spec.js.map +1 -0
- package/dist/esm/src/client/puppet-service.d.ts.map +1 -1
- package/dist/esm/src/client/puppet-service.js +120 -16
- package/dist/esm/src/client/puppet-service.js.map +1 -1
- package/dist/esm/src/package-json.js +3 -3
- package/dist/esm/tests/dirty-gen-guard.spec.d.ts +3 -0
- package/dist/esm/tests/dirty-gen-guard.spec.d.ts.map +1 -0
- package/dist/esm/tests/dirty-gen-guard.spec.js +178 -0
- package/dist/esm/tests/dirty-gen-guard.spec.js.map +1 -0
- package/package.json +3 -3
- package/src/client/puppet-service.ts +120 -16
- package/src/package-json.ts +3 -3
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
#!/usr/bin/env -S node --no-warnings --loader ts-node/esm
|
|
2
|
+
/**
|
|
3
|
+
* Regression tests for the FlashStore write-back generation guard.
|
|
4
|
+
*
|
|
5
|
+
* `_payloadStore` (FlashStore, on disk) is a cache layer independent of
|
|
6
|
+
* the puppet-side LRU. Before this fix it had no protection against the
|
|
7
|
+
* "dirty lands during a raw fetch" race:
|
|
8
|
+
*
|
|
9
|
+
* 1. A raw fetch (e.g. `contactRawPayload`) starts a gRPC round-trip.
|
|
10
|
+
* 2. An EVENT_TYPE_DIRTY arrives; `fastDirty` deletes the FlashStore row.
|
|
11
|
+
* 3. The in-flight fetch resolves and writes the *pre-dirty* payload
|
|
12
|
+
* back into the FlashStore -- re-poisoning the row that was just
|
|
13
|
+
* cleared. The value then only refreshes after a *second* dirty.
|
|
14
|
+
*
|
|
15
|
+
* The fix snapshots a per-(type, id) generation counter before the raw
|
|
16
|
+
* fetch and re-checks it with `isFreshWrite` before every FlashStore
|
|
17
|
+
* write-back; the EVENT_TYPE_DIRTY handler bumps that counter before it
|
|
18
|
+
* even awaits `fastDirty`, so the whole delete window is covered.
|
|
19
|
+
*/
|
|
20
|
+
import { test } from 'tstest';
|
|
21
|
+
import os from 'os';
|
|
22
|
+
import path from 'path';
|
|
23
|
+
import fs from 'fs';
|
|
24
|
+
import * as PUPPET from '@juzi/wechaty-puppet';
|
|
25
|
+
import { puppet as grpcPuppet } from '@juzi/wechaty-grpc';
|
|
26
|
+
import { PuppetService } from '../src/mod.js';
|
|
27
|
+
const makePuppet = async () => {
|
|
28
|
+
const token = `puppet_service_test_${Date.now()}_${Math.floor(Math.random() * 1e6)}`;
|
|
29
|
+
const puppet = new PuppetService({ token });
|
|
30
|
+
const accountId = 'acct-test';
|
|
31
|
+
await puppet._payloadStore.start(accountId);
|
|
32
|
+
return { puppet, token };
|
|
33
|
+
};
|
|
34
|
+
const cleanup = async (puppet, token) => {
|
|
35
|
+
await puppet._payloadStore.stop();
|
|
36
|
+
try {
|
|
37
|
+
await fs.promises.rm(path.join(os.homedir(), '.wechaty', 'wechaty-puppet-service', token), { recursive: true, force: true });
|
|
38
|
+
}
|
|
39
|
+
catch (_) { /* ignore */ }
|
|
40
|
+
};
|
|
41
|
+
/**
|
|
42
|
+
* A one-shot gate for a fake gRPC method. The method resolves `entered`
|
|
43
|
+
* as soon as it is invoked (by which point the caller's gen snapshot has
|
|
44
|
+
* already been taken, since the snapshot precedes the fetch call), then
|
|
45
|
+
* holds its callback until `release()` is called. This lets the test slot
|
|
46
|
+
* a dirty deterministically between the fetch start (gen snapshot) and
|
|
47
|
+
* the fetch resolution (write-back) -- no timers, no flakiness.
|
|
48
|
+
*/
|
|
49
|
+
const makeGate = () => {
|
|
50
|
+
let release = () => { };
|
|
51
|
+
let markEntered = () => { };
|
|
52
|
+
const gate = new Promise(resolve => { release = resolve; });
|
|
53
|
+
const entered = new Promise(resolve => { markEntered = resolve; });
|
|
54
|
+
return { gate, entered, enter: () => markEntered(), release: () => release() };
|
|
55
|
+
};
|
|
56
|
+
test('contactRawPayload: a dirty mid-fetch skips the stale FlashStore write-back', async (t) => {
|
|
57
|
+
const { puppet, token } = await makePuppet();
|
|
58
|
+
const contactId = 'contact-race';
|
|
59
|
+
const response = new grpcPuppet.ContactPayloadResponse();
|
|
60
|
+
response.setId(contactId);
|
|
61
|
+
response.setName('stale-name-from-inflight-fetch');
|
|
62
|
+
const { gate, entered, enter, release } = makeGate();
|
|
63
|
+
puppet._grpcManager = {
|
|
64
|
+
client: {
|
|
65
|
+
contactPayload: (_req, cb) => {
|
|
66
|
+
enter();
|
|
67
|
+
void (async () => {
|
|
68
|
+
await gate;
|
|
69
|
+
cb(null, response);
|
|
70
|
+
})();
|
|
71
|
+
},
|
|
72
|
+
},
|
|
73
|
+
};
|
|
74
|
+
// Start the fetch: it snapshots the gen, then blocks on the gate.
|
|
75
|
+
const fetchPromise = puppet.contactRawPayload(contactId);
|
|
76
|
+
// Deterministic: once the fake method is entered, the snapshot is taken.
|
|
77
|
+
await entered;
|
|
78
|
+
// A dirty arrives while the fetch is in flight: bump gen + clear store,
|
|
79
|
+
// exactly as the EVENT_TYPE_DIRTY handler does.
|
|
80
|
+
puppet.cache.bumpGen(PUPPET.types.Dirty.Contact, contactId);
|
|
81
|
+
await puppet.fastDirty({ payloadType: PUPPET.types.Dirty.Contact, payloadId: contactId });
|
|
82
|
+
// Now let the stale fetch resolve into the write-back path.
|
|
83
|
+
release();
|
|
84
|
+
const returned = await fetchPromise;
|
|
85
|
+
t.equal(returned.id, contactId, 'raw fetch still returns its payload to the caller');
|
|
86
|
+
const stored = await puppet._payloadStore.contact.get(contactId);
|
|
87
|
+
t.notOk(stored, 'stale in-flight fetch must NOT re-poison the FlashStore after the dirty');
|
|
88
|
+
await cleanup(puppet, token);
|
|
89
|
+
});
|
|
90
|
+
test('contactRawPayload: no dirty means the write-back proceeds as usual', async (t) => {
|
|
91
|
+
const { puppet, token } = await makePuppet();
|
|
92
|
+
const contactId = 'contact-happy';
|
|
93
|
+
const response = new grpcPuppet.ContactPayloadResponse();
|
|
94
|
+
response.setId(contactId);
|
|
95
|
+
response.setName('fresh-name');
|
|
96
|
+
puppet._grpcManager = {
|
|
97
|
+
client: {
|
|
98
|
+
contactPayload: (_req, cb) => cb(null, response),
|
|
99
|
+
},
|
|
100
|
+
};
|
|
101
|
+
const returned = await puppet.contactRawPayload(contactId);
|
|
102
|
+
t.equal(returned.id, contactId, 'raw fetch returns its payload');
|
|
103
|
+
const stored = await puppet._payloadStore.contact.get(contactId);
|
|
104
|
+
t.ok(stored, 'without a racing dirty the payload is written to the FlashStore');
|
|
105
|
+
t.equal(stored && stored.id, contactId, 'stored payload matches the fetched id');
|
|
106
|
+
await cleanup(puppet, token);
|
|
107
|
+
});
|
|
108
|
+
test('EVENT_TYPE_DIRTY bumps gen before awaiting fastDirty so pre-event snapshots are stale', async (t) => {
|
|
109
|
+
const { puppet, token } = await makePuppet();
|
|
110
|
+
const contactId = 'contact-bump';
|
|
111
|
+
// A getter that snapshotted just before the event arrived.
|
|
112
|
+
const snapshot = puppet.cache.snapshotGen(PUPPET.types.Dirty.Contact, contactId);
|
|
113
|
+
const dirtyJson = JSON.stringify({
|
|
114
|
+
payloadType: PUPPET.types.Dirty.Contact,
|
|
115
|
+
payloadId: contactId,
|
|
116
|
+
});
|
|
117
|
+
await puppet.onGrpcStreamEvent({
|
|
118
|
+
getType: () => grpcPuppet.EventType.EVENT_TYPE_DIRTY,
|
|
119
|
+
getPayload: () => dirtyJson,
|
|
120
|
+
getSeq: () => 0,
|
|
121
|
+
});
|
|
122
|
+
t.notOk(puppet.cache.isFreshWrite(PUPPET.types.Dirty.Contact, contactId, snapshot), 'a snapshot taken before the dirty event must be judged stale afterwards');
|
|
123
|
+
await cleanup(puppet, token);
|
|
124
|
+
});
|
|
125
|
+
test('EVENT_TYPE_DIRTY compound RoomMember bumps both the compound and the room-level gen', async (t) => {
|
|
126
|
+
const { puppet, token } = await makePuppet();
|
|
127
|
+
const roomId = 'room-bump';
|
|
128
|
+
const memberId = 'member-bump';
|
|
129
|
+
const compoundId = `${roomId}${PUPPET.STRING_SPLITTER}${memberId}`;
|
|
130
|
+
const compoundSnap = puppet.cache.snapshotGen(PUPPET.types.Dirty.RoomMember, compoundId);
|
|
131
|
+
const roomSnap = puppet.cache.snapshotGen(PUPPET.types.Dirty.RoomMember, roomId);
|
|
132
|
+
const dirtyJson = JSON.stringify({
|
|
133
|
+
payloadType: PUPPET.types.Dirty.RoomMember,
|
|
134
|
+
payloadId: compoundId,
|
|
135
|
+
});
|
|
136
|
+
await puppet.onGrpcStreamEvent({
|
|
137
|
+
getType: () => grpcPuppet.EventType.EVENT_TYPE_DIRTY,
|
|
138
|
+
getPayload: () => dirtyJson,
|
|
139
|
+
getSeq: () => 0,
|
|
140
|
+
});
|
|
141
|
+
t.notOk(puppet.cache.isFreshWrite(PUPPET.types.Dirty.RoomMember, compoundId, compoundSnap), 'the compound (roomId+SEP+memberId) key must be bumped');
|
|
142
|
+
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');
|
|
143
|
+
await cleanup(puppet, token);
|
|
144
|
+
});
|
|
145
|
+
test('roomMemberRawPayload: a room-level dirty mid-fetch skips the row write-back', async (t) => {
|
|
146
|
+
const { puppet, token } = await makePuppet();
|
|
147
|
+
const roomId = 'room-member-race';
|
|
148
|
+
const contactId = 'member-race';
|
|
149
|
+
const response = new grpcPuppet.RoomMemberPayloadResponse();
|
|
150
|
+
response.setId(contactId);
|
|
151
|
+
response.setName('stale-member-from-inflight-fetch');
|
|
152
|
+
const { gate, entered, enter, release } = makeGate();
|
|
153
|
+
puppet._grpcManager = {
|
|
154
|
+
client: {
|
|
155
|
+
roomMemberPayload: (_req, cb) => {
|
|
156
|
+
enter();
|
|
157
|
+
void (async () => {
|
|
158
|
+
await gate;
|
|
159
|
+
cb(null, response);
|
|
160
|
+
})();
|
|
161
|
+
},
|
|
162
|
+
},
|
|
163
|
+
};
|
|
164
|
+
// Start the fetch: it snapshots the room-level gen before reading the
|
|
165
|
+
// row, then blocks on the gate.
|
|
166
|
+
const fetchPromise = puppet.roomMemberRawPayload(roomId, contactId);
|
|
167
|
+
await entered;
|
|
168
|
+
// A bare-roomId dirty lands mid-fetch: the whole member set is stale.
|
|
169
|
+
puppet.cache.bumpGen(PUPPET.types.Dirty.RoomMember, roomId);
|
|
170
|
+
await puppet.fastDirty({ payloadType: PUPPET.types.Dirty.RoomMember, payloadId: roomId });
|
|
171
|
+
release();
|
|
172
|
+
const returned = await fetchPromise;
|
|
173
|
+
t.equal(returned.id, contactId, 'raw fetch still returns its member payload to the caller');
|
|
174
|
+
const stored = await puppet._payloadStore.roomMember.get(roomId);
|
|
175
|
+
t.notOk(stored, 'stale in-flight member fetch must NOT re-create the row after the dirty');
|
|
176
|
+
await cleanup(puppet, token);
|
|
177
|
+
});
|
|
178
|
+
//# sourceMappingURL=dirty-gen-guard.spec.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dirty-gen-guard.spec.js","sourceRoot":"","sources":["../../../tests/dirty-gen-guard.spec.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@juzi/wechaty-puppet-service",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.125",
|
|
4
4
|
"description": "Puppet Service for Wechaty",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
|
@@ -51,7 +51,7 @@
|
|
|
51
51
|
"@chatie/eslint-config": "^1.0.4",
|
|
52
52
|
"@chatie/semver": "^0.4.7",
|
|
53
53
|
"@chatie/tsconfig": "^4.6.3",
|
|
54
|
-
"@juzi/wechaty-puppet": "^1.0.
|
|
54
|
+
"@juzi/wechaty-puppet": "^1.0.150",
|
|
55
55
|
"@juzi/wechaty-puppet-mock": "^1.0.1",
|
|
56
56
|
"@swc/core": "1.3.39",
|
|
57
57
|
"@types/google-protobuf": "^3.15.5",
|
|
@@ -71,7 +71,7 @@
|
|
|
71
71
|
"why-is-node-running": "^2.2.1"
|
|
72
72
|
},
|
|
73
73
|
"peerDependencies": {
|
|
74
|
-
"@juzi/wechaty-puppet": "^1.0.
|
|
74
|
+
"@juzi/wechaty-puppet": "^1.0.150"
|
|
75
75
|
},
|
|
76
76
|
"dependencies": {
|
|
77
77
|
"@juzi/wechaty-grpc": "^1.0.106",
|
|
@@ -385,6 +385,35 @@ class PuppetService extends PUPPET.Puppet {
|
|
|
385
385
|
break
|
|
386
386
|
case grpcPuppet.EventType.EVENT_TYPE_DIRTY: {
|
|
387
387
|
const dirtyPayload = JSON.parse(payload) as PUPPET.payloads.EventDirty
|
|
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
|
+
}
|
|
388
417
|
await this.fastDirty(dirtyPayload)
|
|
389
418
|
this.emit('dirty', dirtyPayload)
|
|
390
419
|
break
|
|
@@ -855,6 +884,10 @@ class PuppetService extends PUPPET.Puppet {
|
|
|
855
884
|
return cachedPayload
|
|
856
885
|
}
|
|
857
886
|
|
|
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
|
+
|
|
858
891
|
const request = new grpcPuppet.ContactPayloadRequest()
|
|
859
892
|
request.setId(id)
|
|
860
893
|
|
|
@@ -897,8 +930,12 @@ class PuppetService extends PUPPET.Puppet {
|
|
|
897
930
|
aka : response.getAka(),
|
|
898
931
|
}
|
|
899
932
|
|
|
900
|
-
|
|
901
|
-
|
|
933
|
+
if (this.cache.isFreshWrite(PUPPET.types.Dirty.Contact, id, gen)) {
|
|
934
|
+
await this._payloadStore.contact?.set(id, payload)
|
|
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
|
+
}
|
|
902
939
|
|
|
903
940
|
return payload
|
|
904
941
|
}
|
|
@@ -926,6 +963,14 @@ class PuppetService extends PUPPET.Puppet {
|
|
|
926
963
|
|
|
927
964
|
if (needGetSet.size > 0) {
|
|
928
965
|
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
|
+
|
|
929
974
|
const request = new grpcPuppet.BatchContactPayloadRequest()
|
|
930
975
|
request.setIdsList(Array.from(needGetSet))
|
|
931
976
|
|
|
@@ -939,7 +984,11 @@ class PuppetService extends PUPPET.Puppet {
|
|
|
939
984
|
const contactId = payload.getId()
|
|
940
985
|
const puppetPayload = contactPbToPayload(payload)
|
|
941
986
|
result.set(contactId, puppetPayload)
|
|
942
|
-
|
|
987
|
+
if (this.cache.isFreshWrite(PUPPET.types.Dirty.Contact, contactId, genSnap.get(contactId) ?? 0)) {
|
|
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
|
+
}
|
|
943
992
|
}
|
|
944
993
|
} catch (e) {
|
|
945
994
|
this.log.error('PuppetService', 'batchContactRawPayload(%s, %s) error: %s, use one by one method', contactIdList, needGetSet, e)
|
|
@@ -2303,6 +2352,10 @@ class PuppetService extends PUPPET.Puppet {
|
|
|
2303
2352
|
return cachedPayload
|
|
2304
2353
|
}
|
|
2305
2354
|
|
|
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
|
+
|
|
2306
2359
|
const request = new grpcPuppet.RoomPayloadRequest()
|
|
2307
2360
|
request.setId(id)
|
|
2308
2361
|
|
|
@@ -2329,8 +2382,12 @@ class PuppetService extends PUPPET.Puppet {
|
|
|
2329
2382
|
payload.createTime = millisecondsFromTimestamp(createTime)
|
|
2330
2383
|
}
|
|
2331
2384
|
|
|
2332
|
-
|
|
2333
|
-
|
|
2385
|
+
if (this.cache.isFreshWrite(PUPPET.types.Dirty.Room, id, gen)) {
|
|
2386
|
+
await this._payloadStore.room?.set(id, payload)
|
|
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
|
+
}
|
|
2334
2391
|
|
|
2335
2392
|
return payload
|
|
2336
2393
|
}
|
|
@@ -2358,6 +2415,14 @@ class PuppetService extends PUPPET.Puppet {
|
|
|
2358
2415
|
|
|
2359
2416
|
if (needGetSet.size > 0) {
|
|
2360
2417
|
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
|
+
|
|
2361
2426
|
const request = new grpcPuppet.BatchRoomPayloadRequest()
|
|
2362
2427
|
request.setIdsList(Array.from(needGetSet))
|
|
2363
2428
|
|
|
@@ -2386,7 +2451,11 @@ class PuppetService extends PUPPET.Puppet {
|
|
|
2386
2451
|
puppetPayload.createTime = millisecondsFromTimestamp(createTime)
|
|
2387
2452
|
}
|
|
2388
2453
|
result.set(roomId, puppetPayload)
|
|
2389
|
-
|
|
2454
|
+
if (this.cache.isFreshWrite(PUPPET.types.Dirty.Room, roomId, genSnap.get(roomId) ?? 0)) {
|
|
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
|
+
}
|
|
2390
2459
|
}
|
|
2391
2460
|
} catch (e) {
|
|
2392
2461
|
this.log.error('PuppetService', 'batchRoomRawPayload(%s, %s) error: %s, use one by one method', roomIdList, needGetSet, e)
|
|
@@ -2666,6 +2735,13 @@ class PuppetService extends PUPPET.Puppet {
|
|
|
2666
2735
|
override async roomMemberRawPayload (roomId: string, contactId: string): Promise<PUPPET.payloads.RoomMember> {
|
|
2667
2736
|
this.log.verbose('PuppetService', 'roomMemberRawPayload(%s, %s)', roomId, contactId)
|
|
2668
2737
|
|
|
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
|
+
|
|
2669
2745
|
const cachedPayload = await this._payloadStore.roomMember?.get(roomId)
|
|
2670
2746
|
const cachedRoomMemberPayload = cachedPayload && cachedPayload[contactId]
|
|
2671
2747
|
|
|
@@ -2694,11 +2770,15 @@ class PuppetService extends PUPPET.Puppet {
|
|
|
2694
2770
|
joinTime : response.getJoinTime(),
|
|
2695
2771
|
}
|
|
2696
2772
|
|
|
2697
|
-
|
|
2698
|
-
|
|
2699
|
-
|
|
2700
|
-
|
|
2701
|
-
|
|
2773
|
+
if (this.cache.isFreshWrite(PUPPET.types.Dirty.RoomMember, roomId, roomGen)) {
|
|
2774
|
+
await this._payloadStore.roomMember?.set(roomId, {
|
|
2775
|
+
...cachedPayload,
|
|
2776
|
+
[contactId]: payload,
|
|
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
|
+
}
|
|
2702
2782
|
|
|
2703
2783
|
return payload
|
|
2704
2784
|
}
|
|
@@ -2715,6 +2795,10 @@ class PuppetService extends PUPPET.Puppet {
|
|
|
2715
2795
|
const result = new Map<string, PUPPET.payloads.RoomMember>()
|
|
2716
2796
|
const contactIdSet = new Set<string>(contactIdList)
|
|
2717
2797
|
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)
|
|
2718
2802
|
const cachedPayload = await this._payloadStore.roomMember?.get(roomId) || {}
|
|
2719
2803
|
if (Object.keys(cachedPayload).length > 0) {
|
|
2720
2804
|
for (const contactId of contactIdSet) {
|
|
@@ -2747,7 +2831,11 @@ class PuppetService extends PUPPET.Puppet {
|
|
|
2747
2831
|
result.set(contactId, puppetPayload)
|
|
2748
2832
|
cachedPayload[contactId] = puppetPayload
|
|
2749
2833
|
}
|
|
2750
|
-
|
|
2834
|
+
if (this.cache.isFreshWrite(PUPPET.types.Dirty.RoomMember, roomId, roomGen)) {
|
|
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
|
+
}
|
|
2751
2839
|
} catch (e) {
|
|
2752
2840
|
this.log.error('PuppetService', 'batchRoomMemberRawPayload(%s, %s) error: %s, use one by one method', roomId, needGetSet, e)
|
|
2753
2841
|
for (const contactId of needGetSet) {
|
|
@@ -3393,6 +3481,10 @@ class PuppetService extends PUPPET.Puppet {
|
|
|
3393
3481
|
return cachedPayload
|
|
3394
3482
|
}
|
|
3395
3483
|
|
|
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
|
+
|
|
3396
3488
|
const request = new grpcPuppet.TagGroupPayloadRequest()
|
|
3397
3489
|
request.setGroupId(id)
|
|
3398
3490
|
|
|
@@ -3412,8 +3504,12 @@ class PuppetService extends PUPPET.Puppet {
|
|
|
3412
3504
|
type: grpcPayload.getType(),
|
|
3413
3505
|
}
|
|
3414
3506
|
|
|
3415
|
-
|
|
3416
|
-
|
|
3507
|
+
if (this.cache.isFreshWrite(PUPPET.types.Dirty.TagGroup, id, gen)) {
|
|
3508
|
+
await this._payloadStore.tagGroup?.set(id, payload)
|
|
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
|
+
}
|
|
3417
3513
|
|
|
3418
3514
|
return payload
|
|
3419
3515
|
}
|
|
@@ -3427,6 +3523,10 @@ class PuppetService extends PUPPET.Puppet {
|
|
|
3427
3523
|
return cachedPayload
|
|
3428
3524
|
}
|
|
3429
3525
|
|
|
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
|
+
|
|
3430
3530
|
const request = new grpcPuppet.TagPayloadRequest()
|
|
3431
3531
|
request.setTagId(tagId)
|
|
3432
3532
|
|
|
@@ -3447,8 +3547,12 @@ class PuppetService extends PUPPET.Puppet {
|
|
|
3447
3547
|
type: grpcPayload.getType(),
|
|
3448
3548
|
}
|
|
3449
3549
|
|
|
3450
|
-
|
|
3451
|
-
|
|
3550
|
+
if (this.cache.isFreshWrite(PUPPET.types.Dirty.Tag, tagId, gen)) {
|
|
3551
|
+
await this._payloadStore.tag?.set(tagId, payload)
|
|
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
|
+
}
|
|
3452
3556
|
|
|
3453
3557
|
return payload
|
|
3454
3558
|
}
|
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.125",
|
|
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.150",
|
|
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.150"
|
|
79
79
|
},
|
|
80
80
|
"dependencies": {
|
|
81
81
|
"@juzi/wechaty-grpc": "^1.0.106",
|