@agentunion/fastaun 0.5.24 → 0.5.26
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/CHANGELOG.md +29 -0
- package/_packed_docs/CHANGELOG.md +29 -0
- package/_packed_docs/TS/346/263/250/345/206/214/350/207/252/345/212/250/347/273/221/345/256/232Owner/347/232/204E2E/351/252/214/350/257/201/344/273/243/347/240/201.md +196 -0
- package/_packed_docs/protocol/Ownership/345/215/217/350/256/256/345/256/232/344/271/2112.1.md +2 -2
- package/_packed_docs/sdk/0.5.24-SDK/345/272/224/347/224/250/351/233/206/346/210/220/346/214/207/345/215/227.md +401 -0
- package/_packed_docs/sdk/0.5.24-SDK/345/274/200/345/217/221/344/270/216/347/247/273/346/244/215/346/214/207/345/215/227.md +354 -0
- package/_packed_docs/sdk/AUN_DOCS_GUIDE.md +4 -0
- package/_packed_docs/sdk/INDEX.md +9 -0
- package/_packed_docs/sdk/agent.md/344/270/216group.index/347/232/204ETag/350/247/202/345/257/237/344/270/216/345/220/214/346/255/245/346/234/272/345/210/266.md +240 -0
- package/dist/agent-md.d.ts +1 -0
- package/dist/agent-md.js +38 -10
- package/dist/agent-md.js.map +1 -1
- package/dist/auth.d.ts +1 -0
- package/dist/auth.js +44 -10
- package/dist/auth.js.map +1 -1
- package/dist/client/delivery.d.ts +3 -0
- package/dist/client/delivery.js +197 -47
- package/dist/client/delivery.js.map +1 -1
- package/dist/client/rpc-pipeline.js +10 -0
- package/dist/client/rpc-pipeline.js.map +1 -1
- package/dist/client/v2-e2ee.js +17 -5
- package/dist/client/v2-e2ee.js.map +1 -1
- package/dist/client.d.ts +1 -0
- package/dist/client.js +43 -25
- package/dist/client.js.map +1 -1
- package/dist/group-fs.js +5 -0
- package/dist/group-fs.js.map +1 -1
- package/dist/keystore/local-identity-store.js +16 -1
- package/dist/keystore/local-identity-store.js.map +1 -1
- package/dist/keystore/local-token-store.js +16 -1
- package/dist/keystore/local-token-store.js.map +1 -1
- package/dist/ownership-v21.d.ts +2 -0
- package/dist/ownership-v21.js +61 -30
- package/dist/ownership-v21.js.map +1 -1
- package/dist/tools/cross-sdk-agent.d.ts +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +2 -1
- package/src/certs/root.crt +13 -0
package/dist/client/delivery.js
CHANGED
|
@@ -149,6 +149,7 @@ export class MessageDeliveryEngine {
|
|
|
149
149
|
peerPageChains = new Map();
|
|
150
150
|
automaticPullFailures = new Map();
|
|
151
151
|
terminalGroupPulls = new Map();
|
|
152
|
+
groupPullVersions = new Map();
|
|
152
153
|
automaticPullOperations = new WeakSet();
|
|
153
154
|
automaticPullContext = new AsyncLocalStorage();
|
|
154
155
|
automaticPullAbort = new AbortController();
|
|
@@ -160,6 +161,7 @@ export class MessageDeliveryEngine {
|
|
|
160
161
|
this.automaticPullAbort = new AbortController();
|
|
161
162
|
this.automaticPullFailures.clear();
|
|
162
163
|
this.terminalGroupPulls.clear();
|
|
164
|
+
this.groupPullVersions.clear();
|
|
163
165
|
}
|
|
164
166
|
groupPullNamespace(namespace) {
|
|
165
167
|
const groupId = namespace.startsWith('group_event:') ? namespace.slice('group_event:'.length)
|
|
@@ -173,15 +175,43 @@ export class MessageDeliveryEngine {
|
|
|
173
175
|
if (!this.automaticPullContext.getStore())
|
|
174
176
|
this.terminalGroupPulls.delete(this.groupPullNamespace(namespace));
|
|
175
177
|
}
|
|
178
|
+
observeGroupUnblocked(groupId) {
|
|
179
|
+
const normalized = normalizeGroupAid(groupId);
|
|
180
|
+
if (!normalized)
|
|
181
|
+
return;
|
|
182
|
+
const canonical = `group:${normalized}`;
|
|
183
|
+
this.groupPullVersions.set(canonical, {});
|
|
184
|
+
const stopped = this.terminalGroupPulls.get(canonical);
|
|
185
|
+
if (!stopped)
|
|
186
|
+
return;
|
|
187
|
+
this.terminalGroupPulls.delete(canonical);
|
|
188
|
+
const client = this.runtime.client;
|
|
189
|
+
if (client._sessionOptions?.background_sync === false || client._closing || client.state !== ConnectionState.READY)
|
|
190
|
+
return;
|
|
191
|
+
const namespaces = [...stopped.namespaces, ...Object.keys(client._seqTracker.exportSyncState?.() ?? client._seqTracker.exportState())];
|
|
192
|
+
for (const prefix of ['group:', 'group_event:']) {
|
|
193
|
+
const ns = namespaces.find((candidate) => candidate.startsWith(prefix) && this.groupPullNamespace(candidate) === canonical) ?? `${prefix}${groupId}`;
|
|
194
|
+
this.recordBackgroundPullNeed(ns, client._seqTracker.getMaxSeenSeq(ns), true, true);
|
|
195
|
+
}
|
|
196
|
+
this.requestBackgroundPullRecovery();
|
|
197
|
+
}
|
|
176
198
|
stopAutomaticGroupPull(namespace, error) {
|
|
177
199
|
const canonical = this.groupPullNamespace(namespace);
|
|
178
200
|
if (!canonical)
|
|
179
201
|
return;
|
|
180
|
-
this.terminalGroupPulls.
|
|
202
|
+
const namespaces = this.terminalGroupPulls.get(canonical)?.namespaces ?? new Set();
|
|
203
|
+
namespaces.add(namespace);
|
|
204
|
+
for (const ns of [...this.backgroundPullTasks, ...(this.realtimeSyncing ?? []), ...(this.onlineUnreadHintTasks?.keys() ?? [])]) {
|
|
205
|
+
if (this.groupPullNamespace(ns) === canonical)
|
|
206
|
+
namespaces.add(ns);
|
|
207
|
+
}
|
|
208
|
+
this.terminalGroupPulls.set(canonical, { error, namespaces });
|
|
181
209
|
for (const pending of [this.automaticPullFailures, this.backgroundPullNeeds, this.pendingGroupPullUpper, this.onlineUnreadHintTargets]) {
|
|
182
210
|
for (const ns of pending?.keys() ?? []) {
|
|
183
|
-
if (this.groupPullNamespace(ns) === canonical)
|
|
211
|
+
if (this.groupPullNamespace(ns) === canonical) {
|
|
212
|
+
namespaces.add(ns);
|
|
184
213
|
pending.delete(ns);
|
|
214
|
+
}
|
|
185
215
|
}
|
|
186
216
|
}
|
|
187
217
|
}
|
|
@@ -194,10 +224,13 @@ export class MessageDeliveryEngine {
|
|
|
194
224
|
async runAutomaticPull(namespace, operation, schedule) {
|
|
195
225
|
this.ensurePeerContext();
|
|
196
226
|
if (this.automaticGroupPullStopped(namespace))
|
|
197
|
-
throw this.terminalGroupPulls.get(this.groupPullNamespace(namespace));
|
|
227
|
+
throw this.terminalGroupPulls.get(this.groupPullNamespace(namespace)).error;
|
|
198
228
|
const nested = this.automaticPullContext.getStore() === namespace;
|
|
199
229
|
const generation = this.captureInlineAckGeneration();
|
|
200
230
|
const signal = this.automaticPullAbort.signal;
|
|
231
|
+
const groupNamespace = this.groupPullNamespace(namespace);
|
|
232
|
+
const groupVersion = this.groupPullVersions.get(groupNamespace);
|
|
233
|
+
const groupCurrent = () => this.groupPullVersions.get(groupNamespace) === groupVersion;
|
|
201
234
|
const client = this.runtime.client;
|
|
202
235
|
while ((this.automaticPullFailures.get(namespace)?.retryAt ?? 0) > Date.now()) {
|
|
203
236
|
const delay = this.automaticPullFailures.get(namespace).retryAt - Date.now();
|
|
@@ -210,20 +243,26 @@ export class MessageDeliveryEngine {
|
|
|
210
243
|
signal.addEventListener('abort', abort, { once: true });
|
|
211
244
|
});
|
|
212
245
|
}
|
|
213
|
-
if (signal.aborted || generation !== this.captureInlineAckGeneration() || client._closing || client.state !== ConnectionState.READY) {
|
|
246
|
+
if (signal.aborted || generation !== this.captureInlineAckGeneration() || !groupCurrent() || client._closing || client.state !== ConnectionState.READY) {
|
|
214
247
|
throw new Error('pull invalidated');
|
|
215
248
|
}
|
|
216
249
|
const attempt = async () => {
|
|
250
|
+
if (!groupCurrent())
|
|
251
|
+
throw new Error('pull invalidated');
|
|
217
252
|
if (this.automaticGroupPullStopped(namespace))
|
|
218
|
-
throw this.terminalGroupPulls.get(this.groupPullNamespace(namespace));
|
|
253
|
+
throw this.terminalGroupPulls.get(this.groupPullNamespace(namespace)).error;
|
|
219
254
|
const previousFailure = this.automaticPullFailures.get(namespace);
|
|
220
255
|
try {
|
|
221
256
|
const result = await this.automaticPullContext.run(namespace, operation);
|
|
257
|
+
if (!groupCurrent())
|
|
258
|
+
throw new Error('pull invalidated');
|
|
222
259
|
if (!nested && !signal.aborted && generation === this.captureInlineAckGeneration())
|
|
223
260
|
this.automaticPullFailures.delete(namespace);
|
|
224
261
|
return result;
|
|
225
262
|
}
|
|
226
263
|
catch (error) {
|
|
264
|
+
if (!groupCurrent())
|
|
265
|
+
throw new Error('pull invalidated');
|
|
227
266
|
if (!signal.aborted && generation === this.captureInlineAckGeneration()
|
|
228
267
|
&& this.groupPullNamespace(namespace) && isTerminalGroupPullError(error)) {
|
|
229
268
|
this.stopAutomaticGroupPull(namespace, error);
|
|
@@ -627,6 +666,11 @@ export class MessageDeliveryEngine {
|
|
|
627
666
|
&& this.peerCatalogVersion === version.catalog && this.runtime.client._seqTracker.getMaxSeenSeq(namespace) <= committedAck
|
|
628
667
|
&& this.peerPullPriority(namespace) <= committedAck && this.syncState(namespace).head <= committedAck;
|
|
629
668
|
}
|
|
669
|
+
isPeerHeadCovered(namespace, head) {
|
|
670
|
+
const committedAck = this.peerCommittedAck(namespace);
|
|
671
|
+
return Number.isSafeInteger(head) && head > 0 && head <= committedAck
|
|
672
|
+
&& (this.peerServerConfirmedAcks.get(namespace) ?? 0) >= committedAck;
|
|
673
|
+
}
|
|
630
674
|
shouldTailPeer(namespace, head, limit, expectedForwardNeed) {
|
|
631
675
|
const state = this.syncState(namespace);
|
|
632
676
|
const knownHead = Math.max(head, state.head, this.runtime.client._seqTracker.getMaxSeenSeq(namespace), this.peerPullPriority(namespace));
|
|
@@ -644,7 +688,7 @@ export class MessageDeliveryEngine {
|
|
|
644
688
|
&& this.peerPullPriority(namespace) <= committedAck
|
|
645
689
|
&& this.peerPushVersions.get(namespace) === pushVersion
|
|
646
690
|
&& this.peerCatalogVersion === catalogVersion
|
|
647
|
-
&& (!need || (need === expectedNeed &&
|
|
691
|
+
&& (!need || (need === expectedNeed && need.head <= committedAck));
|
|
648
692
|
}
|
|
649
693
|
async pullPeers(params = {}, background = false) {
|
|
650
694
|
const client = this.runtime.client;
|
|
@@ -794,12 +838,16 @@ export class MessageDeliveryEngine {
|
|
|
794
838
|
}
|
|
795
839
|
}
|
|
796
840
|
const namespace = this.peerNamespace(sender);
|
|
797
|
-
|
|
841
|
+
if (!Number.isSafeInteger(head) || head <= 0 || volatile)
|
|
842
|
+
this.peerPushVersions.set(namespace, {});
|
|
843
|
+
else if (this.initializedPeers.has(namespace) && client._seqTracker.snapshotNamespace(namespace) !== null)
|
|
844
|
+
client._seqTracker.updateMaxSeen(namespace, head);
|
|
798
845
|
if (!this.hasP2pApplicationSubscription() || client._sessionOptions?.background_sync === false) {
|
|
799
846
|
this.peerForwardNeeds.set(namespace, {});
|
|
800
847
|
return;
|
|
801
848
|
}
|
|
802
849
|
const need = this.peerRecoveryNeeds.get(sender) ?? { head: 0, volatile: false, pending: false };
|
|
850
|
+
need.paused = false;
|
|
803
851
|
need.head = Math.max(need.head, head);
|
|
804
852
|
need.volatile ||= volatile;
|
|
805
853
|
need.pending = true;
|
|
@@ -812,6 +860,8 @@ export class MessageDeliveryEngine {
|
|
|
812
860
|
if (!this.hasP2pApplicationSubscription())
|
|
813
861
|
return;
|
|
814
862
|
const namespace = this.peerNamespace(sender);
|
|
863
|
+
if (Number.isSafeInteger(need.head) && need.head > 0)
|
|
864
|
+
client._seqTracker.updateMaxSeen(namespace, need.head);
|
|
815
865
|
if (this.shouldTailPeer(namespace, need.head, _INTERNAL_MESSAGE_FLOW_LIMITS.pullPageSize)) {
|
|
816
866
|
const forwardNeed = {};
|
|
817
867
|
this.peerForwardNeeds.set(namespace, forwardNeed);
|
|
@@ -822,19 +872,28 @@ export class MessageDeliveryEngine {
|
|
|
822
872
|
const tail = await this.runAutomaticPull(namespace, async () => {
|
|
823
873
|
const beforeAck = this.peerCommittedAck(namespace);
|
|
824
874
|
const beforeVolatile = this.peerVolatilePullCursor(sender);
|
|
875
|
+
const beforeWindow = this.syncState(namespace);
|
|
825
876
|
const result = await client._v2E2EE.pullPeerV2(sender, { window_mode: 'tail', background: true, retainMessages: false, budget: { remaining: 1 }, autoTail });
|
|
826
|
-
|
|
827
|
-
|
|
877
|
+
need.volatile ||= result.volatile_has_more === true;
|
|
878
|
+
const afterWindow = this.syncState(namespace);
|
|
879
|
+
if (autoTail.forward && (!result.complete || Number(result.ack_seq) < need.head) && this.peerCommittedAck(namespace) <= beforeAck && this.peerVolatilePullCursor(sender) === beforeVolatile
|
|
880
|
+
&& afterWindow.tail === beforeWindow.tail && afterWindow.head === beforeWindow.head) {
|
|
881
|
+
if (!result.pending_ack_seq && !need.pending && this.peerPushVersions.get(namespace) === pushVersion && this.peerCatalogVersion === catalogVersion)
|
|
882
|
+
need.paused = true;
|
|
883
|
+
else if (result.pending_ack_seq)
|
|
884
|
+
throw new Error('peer recovery made no progress');
|
|
828
885
|
}
|
|
829
886
|
return result;
|
|
830
887
|
});
|
|
831
888
|
if (generation !== this.captureInlineAckGeneration() || this.peerRecoveryNeeds.get(sender) !== need)
|
|
832
889
|
return;
|
|
890
|
+
if (need.paused)
|
|
891
|
+
return;
|
|
833
892
|
if (autoTail.forward) {
|
|
834
893
|
if (tail.complete && this.peerForwardNeeds.get(namespace) === forwardNeed
|
|
835
894
|
&& this.peerPushVersions.get(namespace) === pushVersion && this.peerCatalogVersion === catalogVersion)
|
|
836
895
|
this.peerForwardNeeds.delete(namespace);
|
|
837
|
-
if (tail.complete && Number(tail.ack_seq) >= need.head
|
|
896
|
+
if (tail.complete && Number(tail.ack_seq) >= need.head
|
|
838
897
|
&& this.peerPushVersions.get(namespace) === pushVersion && this.peerCatalogVersion === catalogVersion) {
|
|
839
898
|
this.peerRecoveryNeeds.delete(sender);
|
|
840
899
|
return;
|
|
@@ -856,18 +915,27 @@ export class MessageDeliveryEngine {
|
|
|
856
915
|
const result = await this.runAutomaticPull(namespace, async () => {
|
|
857
916
|
const beforeAck = this.peerCommittedAck(namespace);
|
|
858
917
|
const beforeVolatile = this.peerVolatilePullCursor(sender);
|
|
918
|
+
const beforeWindow = this.syncState(namespace);
|
|
859
919
|
const result = await client._v2E2EE.pullPeerV2(sender, { background: true, retainMessages: false, budget: { remaining: 100 } });
|
|
860
|
-
|
|
861
|
-
|
|
920
|
+
need.volatile ||= result.volatile_has_more === true;
|
|
921
|
+
const afterWindow = this.syncState(namespace);
|
|
922
|
+
if ((!result.complete || result.ack_seq < need.head) && this.peerCommittedAck(namespace) <= beforeAck && this.peerVolatilePullCursor(sender) === beforeVolatile
|
|
923
|
+
&& afterWindow.tail === beforeWindow.tail && afterWindow.head === beforeWindow.head) {
|
|
924
|
+
if (!result.pending_ack_seq && !need.pending && this.peerPushVersions.get(namespace) === pushVersion && this.peerCatalogVersion === catalogVersion)
|
|
925
|
+
need.paused = true;
|
|
926
|
+
else if (result.pending_ack_seq)
|
|
927
|
+
throw new Error('peer recovery made no progress');
|
|
862
928
|
}
|
|
863
929
|
return result;
|
|
864
930
|
});
|
|
865
931
|
if (generation !== this.captureInlineAckGeneration() || this.peerRecoveryNeeds.get(sender) !== need)
|
|
866
932
|
return;
|
|
933
|
+
if (need.paused)
|
|
934
|
+
return;
|
|
867
935
|
if (result.complete && this.peerForwardNeeds.get(namespace) === forwardNeed
|
|
868
936
|
&& this.peerPushVersions.get(namespace) === pushVersion && this.peerCatalogVersion === catalogVersion)
|
|
869
937
|
this.peerForwardNeeds.delete(namespace);
|
|
870
|
-
if (result.complete && result.ack_seq >= need.head
|
|
938
|
+
if (result.complete && result.ack_seq >= need.head
|
|
871
939
|
&& this.peerPushVersions.get(namespace) === pushVersion && this.peerCatalogVersion === catalogVersion) {
|
|
872
940
|
this.peerRecoveryNeeds.delete(sender);
|
|
873
941
|
break;
|
|
@@ -879,12 +947,18 @@ export class MessageDeliveryEngine {
|
|
|
879
947
|
client._safeAsync(task.catch(() => {
|
|
880
948
|
if (generation !== this.captureInlineAckGeneration())
|
|
881
949
|
return;
|
|
882
|
-
const timer = setTimeout(() => {
|
|
883
|
-
this.
|
|
950
|
+
const timer = setTimeout(() => {
|
|
951
|
+
if (generation === this.captureInlineAckGeneration() && this.peerRecoveryNeeds.get(sender) === need
|
|
952
|
+
&& !need.paused && !this.peerRecoveryTasks.has(sender))
|
|
953
|
+
this.schedulePeerRecovery(sender, need.head, need.volatile);
|
|
954
|
+
}, BACKGROUND_PULL_RETRY_DELAY_MS);
|
|
884
955
|
timer.unref?.();
|
|
885
956
|
}).finally(() => {
|
|
886
957
|
if (generation === this.captureInlineAckGeneration()) {
|
|
887
958
|
this.peerRecoveryTasks.delete(sender);
|
|
959
|
+
const pending = this.peerRecoveryNeeds.get(sender);
|
|
960
|
+
if (pending?.pending && !pending.paused)
|
|
961
|
+
this.requestBackgroundPullRecovery();
|
|
888
962
|
if (this.peerCatalogDirty && this.peerRecoveryTasks.size === 0)
|
|
889
963
|
this.refreshPendingPeers();
|
|
890
964
|
}
|
|
@@ -1587,9 +1661,9 @@ export class MessageDeliveryEngine {
|
|
|
1587
1661
|
return this.pendingGroupPullUpper?.get(ns) ?? 0;
|
|
1588
1662
|
}
|
|
1589
1663
|
hasAnyPendingPull() {
|
|
1590
|
-
return this.peerCatalogDirty || this.peerRecoveryNeeds.
|
|
1664
|
+
return this.peerCatalogDirty || [...this.peerRecoveryNeeds.values()].some((need) => !need.paused) || this.peerRecoveryTasks.size > 0 || Boolean(this.peerCatalogTask)
|
|
1591
1665
|
|| [...(this.pendingGroupPullUpper?.values() ?? [])].some((seq) => seq > 0)
|
|
1592
|
-
|| this.backgroundPullNeeds.
|
|
1666
|
+
|| [...this.backgroundPullNeeds.values()].some((need) => need.retryAt !== Infinity)
|
|
1593
1667
|
|| Boolean(this.onlineUnreadHintTasks?.size)
|
|
1594
1668
|
|| Boolean(this.realtimeSyncing?.size)
|
|
1595
1669
|
|| Boolean(this.runtime.client._v2E2EE?.hasPendingSenderIKWork?.());
|
|
@@ -1604,7 +1678,7 @@ export class MessageDeliveryEngine {
|
|
|
1604
1678
|
}
|
|
1605
1679
|
this.schedulePendingGroupPullIfNeeded(ns, 'pull-gate-idle');
|
|
1606
1680
|
}
|
|
1607
|
-
recordBackgroundPullNeed(ns, throughSeq = 0, probe = false) {
|
|
1681
|
+
recordBackgroundPullNeed(ns, throughSeq = 0, probe = false, restart = false) {
|
|
1608
1682
|
if (this.automaticGroupPullStopped(ns))
|
|
1609
1683
|
return;
|
|
1610
1684
|
const client = this.runtime.client;
|
|
@@ -1612,8 +1686,14 @@ export class MessageDeliveryEngine {
|
|
|
1612
1686
|
|| !(ns === `p2p:${client._aid}` || ns.startsWith('group:') || ns.startsWith('group_event:')))
|
|
1613
1687
|
return;
|
|
1614
1688
|
const need = this.backgroundPullNeeds.get(ns) ?? {
|
|
1615
|
-
throughSeq: 0, hasMore: false, probe, lastAfter: -1, retryAt: 0,
|
|
1689
|
+
throughSeq: 0, hasMore: false, probe, lastAfter: -1, retryAt: 0, empty: false, trigger: {},
|
|
1616
1690
|
};
|
|
1691
|
+
if (restart) {
|
|
1692
|
+
need.trigger = {};
|
|
1693
|
+
need.retryAt = 0;
|
|
1694
|
+
need.empty = false;
|
|
1695
|
+
need.progress = undefined;
|
|
1696
|
+
}
|
|
1617
1697
|
need.throughSeq = Math.max(need.throughSeq, throughSeq);
|
|
1618
1698
|
need.probe ||= probe;
|
|
1619
1699
|
this.backgroundPullNeeds.set(ns, need);
|
|
@@ -1627,30 +1707,46 @@ export class MessageDeliveryEngine {
|
|
|
1627
1707
|
return;
|
|
1628
1708
|
const cursor = isJsonObject(response.cursor)
|
|
1629
1709
|
? response.cursor : null;
|
|
1630
|
-
const
|
|
1710
|
+
const serverHead = Math.max(positiveSafeSequenceHint(cursor?.latest_seq), positiveSafeSequenceHint(response.latest_seq), positiveSafeSequenceHint(response.latest_message_seq), positiveSafeSequenceHint(response.latest_event_seq));
|
|
1711
|
+
const throughSeq = Math.max(serverHead, ...rows.map((row) => row && typeof row === 'object' && row.storage !== 'volatile'
|
|
1631
1712
|
? positiveSafeSequenceHint(row.seq ?? row.event_seq) : 0));
|
|
1632
1713
|
this.recordBackgroundPullNeed(ns, throughSeq);
|
|
1633
1714
|
const need = this.backgroundPullNeeds.get(ns);
|
|
1634
1715
|
if (!need || afterSeq < need.lastAfter)
|
|
1635
1716
|
return;
|
|
1636
1717
|
need.lastAfter = afterSeq;
|
|
1718
|
+
need.progress = { ...this.syncState(ns), volatile: this.volatilePullCursor() };
|
|
1719
|
+
need.empty = rows.length === 0;
|
|
1637
1720
|
need.hasMore = response.has_more === true || positiveSafeSequenceHint(response.remaining) > 0
|
|
1638
|
-
|| (!('has_more' in response) && limit > 0 && rows.length >= limit);
|
|
1721
|
+
|| (!('has_more' in response) && serverHead === 0 && limit > 0 && rows.length >= limit);
|
|
1639
1722
|
need.probe = false;
|
|
1640
1723
|
}
|
|
1641
|
-
finishBackgroundPull(ns, generation, afterSeq, failed) {
|
|
1724
|
+
finishBackgroundPull(ns, generation, afterSeq, failed, trigger) {
|
|
1642
1725
|
if (generation !== this.captureInlineAckGeneration())
|
|
1643
1726
|
return;
|
|
1644
1727
|
if (this.automaticGroupPullStopped(ns))
|
|
1645
1728
|
return;
|
|
1646
1729
|
const need = this.backgroundPullNeeds.get(ns);
|
|
1730
|
+
if (need && trigger && need.trigger !== trigger) {
|
|
1731
|
+
need.retryAt = Date.now() + BACKGROUND_PULL_RETRY_DELAY_MS;
|
|
1732
|
+
this.requestBackgroundPullRecovery();
|
|
1733
|
+
return;
|
|
1734
|
+
}
|
|
1647
1735
|
if (need) {
|
|
1648
1736
|
if (failed)
|
|
1649
1737
|
need.probe = true;
|
|
1650
1738
|
else
|
|
1651
1739
|
need.probe = false;
|
|
1652
|
-
|
|
1653
|
-
|
|
1740
|
+
const client = this.runtime.client;
|
|
1741
|
+
const state = this.syncState(ns);
|
|
1742
|
+
const ack = state.ack;
|
|
1743
|
+
const coordinator = client._v2E2EE;
|
|
1744
|
+
const pendingAck = coordinator?.pendingForwardAck?.(ns, ack) ?? 0;
|
|
1745
|
+
const noProgress = need.progress ? ack <= need.progress.ack && state.tail === need.progress.tail && state.head === need.progress.head
|
|
1746
|
+
&& this.volatilePullCursor() === need.progress.volatile : need.empty && ack <= Math.max(afterSeq, need.lastAfter);
|
|
1747
|
+
need.retryAt = !failed && noProgress && trigger === need.trigger
|
|
1748
|
+
&& !pendingAck && !this.pendingP2PInlineAcks?.has(ns) && !this.pendingGroupInlineAcks?.has(ns)
|
|
1749
|
+
? Infinity : failed || ack <= afterSeq ? Date.now() + BACKGROUND_PULL_RETRY_DELAY_MS : 0;
|
|
1654
1750
|
}
|
|
1655
1751
|
this.requestBackgroundPullRecovery();
|
|
1656
1752
|
}
|
|
@@ -1670,7 +1766,7 @@ export class MessageDeliveryEngine {
|
|
|
1670
1766
|
if (this.peerCatalogDirty && !this.peerCatalogTask)
|
|
1671
1767
|
this.refreshPendingPeers();
|
|
1672
1768
|
for (const [sender, need] of this.peerRecoveryNeeds) {
|
|
1673
|
-
if (!this.peerRecoveryTasks.has(sender))
|
|
1769
|
+
if (!need.paused && !this.peerRecoveryTasks.has(sender))
|
|
1674
1770
|
this.schedulePeerRecovery(sender, need.head, need.volatile);
|
|
1675
1771
|
}
|
|
1676
1772
|
}
|
|
@@ -1741,6 +1837,8 @@ export class MessageDeliveryEngine {
|
|
|
1741
1837
|
|| client.state !== ConnectionState.READY || client._closing)
|
|
1742
1838
|
return;
|
|
1743
1839
|
const ns = `p2p:${client._aid}`;
|
|
1840
|
+
if (this.backgroundPullNeeds.get(ns)?.retryAt === Infinity)
|
|
1841
|
+
return;
|
|
1744
1842
|
const state = this.syncState(ns);
|
|
1745
1843
|
const maxSeen = client._seqTracker.getMaxSeenSeq(ns);
|
|
1746
1844
|
if (maxSeen <= state.ack && state.ack >= state.tail - 1)
|
|
@@ -2930,6 +3028,7 @@ export class MessageDeliveryEngine {
|
|
|
2930
3028
|
return;
|
|
2931
3029
|
client._gapFillDone.set(dedupKey, Date.now());
|
|
2932
3030
|
this.recordBackgroundPullNeed(ns, 0, true);
|
|
3031
|
+
const pullTrigger = this.backgroundPullNeeds.get(ns)?.trigger;
|
|
2933
3032
|
client._clientLog.debug(`P2P message gap fill start: after_seq=${afterSeq}`);
|
|
2934
3033
|
let filled = 0;
|
|
2935
3034
|
let failed = false;
|
|
@@ -2953,7 +3052,7 @@ export class MessageDeliveryEngine {
|
|
|
2953
3052
|
return;
|
|
2954
3053
|
client._gapFillDone.delete(dedupKey);
|
|
2955
3054
|
client._schedulePendingP2pPullIfNeeded?.(ns, 'gap-fill-complete');
|
|
2956
|
-
this.finishBackgroundPull(ns, generation, afterSeq, failed);
|
|
3055
|
+
this.finishBackgroundPull(ns, generation, afterSeq, failed, pullTrigger);
|
|
2957
3056
|
}
|
|
2958
3057
|
}
|
|
2959
3058
|
async fillGroupGap(groupId) {
|
|
@@ -2973,6 +3072,7 @@ export class MessageDeliveryEngine {
|
|
|
2973
3072
|
return;
|
|
2974
3073
|
client._gapFillDone.set(dedupKey, Date.now());
|
|
2975
3074
|
this.recordBackgroundPullNeed(ns, 0, true);
|
|
3075
|
+
const pullTrigger = this.backgroundPullNeeds.get(ns)?.trigger;
|
|
2976
3076
|
client._clientLog.debug(`group message gap fill start: group=${groupId}, after_seq=${afterSeq}`);
|
|
2977
3077
|
let filled = 0;
|
|
2978
3078
|
let failed = false;
|
|
@@ -2994,7 +3094,7 @@ export class MessageDeliveryEngine {
|
|
|
2994
3094
|
return;
|
|
2995
3095
|
client._gapFillDone.delete(dedupKey);
|
|
2996
3096
|
this.schedulePendingGroupPullIfNeeded(ns, 'gap-fill-complete');
|
|
2997
|
-
this.finishBackgroundPull(ns, generation, afterSeq, failed);
|
|
3097
|
+
this.finishBackgroundPull(ns, generation, afterSeq, failed, pullTrigger);
|
|
2998
3098
|
}
|
|
2999
3099
|
}
|
|
3000
3100
|
scheduleRealtimeWindowForward(ns, groupId = '') {
|
|
@@ -3058,6 +3158,7 @@ export class MessageDeliveryEngine {
|
|
|
3058
3158
|
}
|
|
3059
3159
|
client._gapFillDone.set(dedupKey, Date.now());
|
|
3060
3160
|
this.recordBackgroundPullNeed(ns, client._seqTracker.getMaxSeenSeq(ns), true);
|
|
3161
|
+
const pullTrigger = this.backgroundPullNeeds.get(ns)?.trigger;
|
|
3061
3162
|
this.syncStarted(ns);
|
|
3062
3163
|
let filled = 0;
|
|
3063
3164
|
let continuationAfterSeq = 0;
|
|
@@ -3245,7 +3346,7 @@ export class MessageDeliveryEngine {
|
|
|
3245
3346
|
_online_hint_after_event_seq: continuationAfterSeq,
|
|
3246
3347
|
});
|
|
3247
3348
|
}
|
|
3248
|
-
this.finishBackgroundPull(ns, generation, afterSeq, failed);
|
|
3349
|
+
this.finishBackgroundPull(ns, generation, afterSeq, failed, pullTrigger);
|
|
3249
3350
|
if (!client._rpcPipeline)
|
|
3250
3351
|
this.syncStopped(failed ? 'aborted' : 'pull_drained');
|
|
3251
3352
|
}
|
|
@@ -3257,7 +3358,7 @@ export class MessageDeliveryEngine {
|
|
|
3257
3358
|
return;
|
|
3258
3359
|
if (!groupId || client._sessionOptions?.background_sync === false)
|
|
3259
3360
|
return;
|
|
3260
|
-
this.recordBackgroundPullNeed(`group_event:${groupId}`, positiveSafeSequenceHint(data.event_seq), true);
|
|
3361
|
+
this.recordBackgroundPullNeed(`group_event:${groupId}`, positiveSafeSequenceHint(data.event_seq), true, true);
|
|
3261
3362
|
const rawAfter = Number(data._online_hint_after_event_seq ?? 0);
|
|
3262
3363
|
const afterSeq = Number.isSafeInteger(rawAfter) && rawAfter > 0 ? rawAfter : 0;
|
|
3263
3364
|
client._safeAsync(this.fillGroupEventGap(groupId, {
|
|
@@ -4341,17 +4442,36 @@ export class MessageDeliveryEngine {
|
|
|
4341
4442
|
if (!isJsonObject(data) || !client._aid || !this.messageTargetsCurrentInstance(data))
|
|
4342
4443
|
return;
|
|
4343
4444
|
const head = data;
|
|
4344
|
-
|
|
4445
|
+
const legacy = isJsonObject(head.legacy_v1) ? head.legacy_v1 : null;
|
|
4446
|
+
const completeVolatile = this.isVolatileMessage(head) && (head.payload != null || legacy?.payload != null
|
|
4447
|
+
|| (typeof head.envelope_json === 'string' && head.envelope_json.trim()));
|
|
4448
|
+
const sender = String(head.sender_aid ?? (completeVolatile ? head.from_aid ?? head.from : undefined) ?? '');
|
|
4449
|
+
if (String(head.kind ?? '') === 'message.online_unread_hint' || !sender) {
|
|
4345
4450
|
this.refreshPendingPeers();
|
|
4346
4451
|
return;
|
|
4347
4452
|
}
|
|
4348
|
-
if (head.cursor_scope !== 'peer') {
|
|
4453
|
+
if (head.cursor_scope !== 'peer' && (head.cursor_scope !== undefined || !completeVolatile)) {
|
|
4349
4454
|
this.refreshPendingPeers();
|
|
4350
4455
|
return;
|
|
4351
4456
|
}
|
|
4352
|
-
const sender = String(head.sender_aid);
|
|
4353
4457
|
this.ensurePeerContext();
|
|
4354
|
-
|
|
4458
|
+
if (completeVolatile) {
|
|
4459
|
+
await client._v2E2EE.publishV2VolatileMessages([head], 'push', false);
|
|
4460
|
+
return;
|
|
4461
|
+
}
|
|
4462
|
+
const namespace = this.peerNamespace(sender);
|
|
4463
|
+
const seq = positiveSafeSequenceHint(head.seq);
|
|
4464
|
+
const committedAck = this.peerCommittedAck(namespace);
|
|
4465
|
+
if (seq > 0 && !this.isVolatileMessage(head) && !this.isVolatileMessage(head.inline_message)
|
|
4466
|
+
&& head.volatile_has_more !== true && seq <= committedAck
|
|
4467
|
+
&& !this.peerRecoveryNeeds.get(sender)?.volatile
|
|
4468
|
+
&& (this.peerServerConfirmedAcks.get(namespace) ?? 0) >= committedAck)
|
|
4469
|
+
return;
|
|
4470
|
+
const unbounded = !seq || this.isVolatileMessage(head) || this.isVolatileMessage(head.inline_message)
|
|
4471
|
+
|| head.volatile_has_more === true || this.peerRecoveryNeeds.get(sender)?.volatile === true;
|
|
4472
|
+
if (unbounded)
|
|
4473
|
+
this.peerPushVersions.set(namespace, {});
|
|
4474
|
+
const peerPullVersion = this.capturePeerTailVersion(sender);
|
|
4355
4475
|
if (!hasNonEmptyInlineMessage(head.inline_message) && !this.isVolatileMessage(head) && !this.hasP2pApplicationSubscription())
|
|
4356
4476
|
return;
|
|
4357
4477
|
const generation = this.captureInlineAckGeneration();
|
|
@@ -4364,7 +4484,6 @@ export class MessageDeliveryEngine {
|
|
|
4364
4484
|
this.schedulePeerRecovery(sender, positiveSafeSequenceHint(head.seq) ?? 0, this.isVolatileMessage(head));
|
|
4365
4485
|
throw error;
|
|
4366
4486
|
}
|
|
4367
|
-
const seq = positiveSafeSequenceHint(head.seq);
|
|
4368
4487
|
if (this.isVolatileMessage(head)) {
|
|
4369
4488
|
await client._v2E2EE.publishV2VolatileMessages([head], 'push', false);
|
|
4370
4489
|
return;
|
|
@@ -4410,16 +4529,20 @@ export class MessageDeliveryEngine {
|
|
|
4410
4529
|
this.schedulePeerRecovery(sender, seq);
|
|
4411
4530
|
throw error;
|
|
4412
4531
|
});
|
|
4413
|
-
if (consumed?.acquired && consumed.value)
|
|
4532
|
+
if (consumed?.acquired && consumed.value && !unbounded)
|
|
4414
4533
|
return;
|
|
4415
4534
|
}
|
|
4416
|
-
if (
|
|
4535
|
+
if ((!unbounded && this.isPeerHeadCovered(ns, seq)) || !this.hasP2pApplicationSubscription())
|
|
4417
4536
|
return;
|
|
4418
4537
|
if (client._sessionOptions?.background_sync !== false) {
|
|
4419
|
-
|
|
4538
|
+
if (unbounded)
|
|
4539
|
+
this.schedulePeerRecovery(sender, seq, true);
|
|
4540
|
+
else
|
|
4541
|
+
this.schedulePeerRecovery(sender, seq);
|
|
4420
4542
|
}
|
|
4421
4543
|
else {
|
|
4422
4544
|
await this.runAutomaticPull(ns, () => client._v2E2EE.pullPeerV2(sender, { limit: _INTERNAL_MESSAGE_FLOW_LIMITS.pullPageSize, budget: { remaining: 1 },
|
|
4545
|
+
...(!unbounded ? { autoPeerHead: seq, peerPullVersion } : {}),
|
|
4423
4546
|
...(this.shouldTailPeer(ns, seq, _INTERNAL_MESSAGE_FLOW_LIMITS.pullPageSize) ? { window_mode: 'tail', autoTail: { head: seq, forward: false } } : {}) }));
|
|
4424
4547
|
}
|
|
4425
4548
|
}
|
|
@@ -4486,13 +4609,15 @@ export class MessageDeliveryEngine {
|
|
|
4486
4609
|
}
|
|
4487
4610
|
if (!this.hasP2pApplicationSubscription())
|
|
4488
4611
|
return;
|
|
4489
|
-
this.recordBackgroundPullNeed(ns, pushSeq);
|
|
4612
|
+
this.recordBackgroundPullNeed(ns, pushSeq, false, true);
|
|
4613
|
+
const pullTrigger = this.backgroundPullNeeds.get(ns)?.trigger;
|
|
4490
4614
|
const pullActive = client._rpcPipeline?.hasPullActivity?.(ns) === true;
|
|
4491
4615
|
if (pullActive || !this.beginRealtimeSync(ns)) {
|
|
4492
4616
|
client._recordPendingP2pPull(ns, pushSeq);
|
|
4493
4617
|
client._clientLog.debug(`V2 P2P push queued behind in-flight realtime sync: ns=${ns} seq=${pushSeq}`);
|
|
4494
4618
|
return;
|
|
4495
4619
|
}
|
|
4620
|
+
let pullFailed = false;
|
|
4496
4621
|
try {
|
|
4497
4622
|
await this.runP2PRealtimeSync(ns, pushSeq, 50, false);
|
|
4498
4623
|
if (inlineGeneration !== undefined && !this.inlineGenerationIsCurrent(inlineGeneration))
|
|
@@ -4500,12 +4625,13 @@ export class MessageDeliveryEngine {
|
|
|
4500
4625
|
// Tail 期间新到达的 Push 已由 realtime sync 在同一周期内补至多一页 Forward。
|
|
4501
4626
|
}
|
|
4502
4627
|
catch (exc) {
|
|
4628
|
+
pullFailed = true;
|
|
4503
4629
|
client._clientLog.warn(`V2 P2P push auto-sync failed: ${formatDeliveryError(exc)}`);
|
|
4504
4630
|
}
|
|
4505
4631
|
finally {
|
|
4506
4632
|
if (syncGeneration === this.captureInlineAckGeneration()
|
|
4507
4633
|
&& (inlineGeneration === undefined || this.inlineGenerationIsCurrent(inlineGeneration))) {
|
|
4508
|
-
this.finishBackgroundPull(ns, syncGeneration, this.syncState(ns).ack,
|
|
4634
|
+
this.finishBackgroundPull(ns, syncGeneration, this.syncState(ns).ack, pullFailed, pullTrigger);
|
|
4509
4635
|
this.endRealtimeSync(ns);
|
|
4510
4636
|
client._schedulePendingP2pPullIfNeeded?.(ns, 'push-auto-pull-complete');
|
|
4511
4637
|
}
|
|
@@ -4519,14 +4645,34 @@ export class MessageDeliveryEngine {
|
|
|
4519
4645
|
const ns = `group:${groupId}`;
|
|
4520
4646
|
if (this.automaticGroupPullStopped(ns))
|
|
4521
4647
|
return;
|
|
4648
|
+
this.rememberCommittedSeqState(ns);
|
|
4522
4649
|
const generation = this.captureInlineAckGeneration();
|
|
4523
|
-
const
|
|
4524
|
-
|
|
4650
|
+
const state = this.syncState(ns);
|
|
4651
|
+
const afterSeq = state.ack;
|
|
4652
|
+
const hintSeq = positiveSafeSequenceHint(data.seq);
|
|
4653
|
+
const committedAck = Math.min(afterSeq, this.committedSeqTrackerStates.get(ns)?.ack ?? afterSeq);
|
|
4654
|
+
if (hintSeq > 0 && committedAck >= Math.max(hintSeq, state.head, Number(client._seqTracker.getMaxSeenSeq?.(ns) ?? 0), this.pendingPullUpper(ns))
|
|
4655
|
+
&& !client._pendingOrderedMsgs.get(ns)?.size) {
|
|
4656
|
+
const coordinator = client._v2E2EE;
|
|
4657
|
+
const pendingAckSeq = Number(coordinator.pendingForwardAck?.(ns, committedAck) ?? 0);
|
|
4658
|
+
if (pendingAckSeq > 0) {
|
|
4659
|
+
client._safeAsync(coordinator.confirmForwardGroupAck(groupId, pendingAckSeq, String(data.group_aid ?? '').trim() || undefined, true));
|
|
4660
|
+
}
|
|
4661
|
+
else {
|
|
4662
|
+
const pendingNs = this.resolvePendingGroupInlineAckNamespace(ns);
|
|
4663
|
+
const pending = this.pendingGroupInlineAcks?.get(pendingNs);
|
|
4664
|
+
if (pending)
|
|
4665
|
+
this.retryPendingGroupInlineAck(pendingNs, pending.seq, 'covered online hint', committedAck);
|
|
4666
|
+
}
|
|
4667
|
+
return;
|
|
4668
|
+
}
|
|
4669
|
+
this.recordBackgroundPullNeed(ns, hintSeq, false, true);
|
|
4670
|
+
const pullTrigger = this.backgroundPullNeeds.get(ns)?.trigger;
|
|
4525
4671
|
const targets = this.onlineUnreadHintTargets ?? new Map();
|
|
4526
4672
|
const tasks = this.onlineUnreadHintTasks ?? new Map();
|
|
4527
4673
|
this.onlineUnreadHintTargets = targets;
|
|
4528
4674
|
this.onlineUnreadHintTasks = tasks;
|
|
4529
|
-
targets.set(ns, Math.max(targets.get(ns) ?? 0,
|
|
4675
|
+
targets.set(ns, Math.max(targets.get(ns) ?? 0, hintSeq));
|
|
4530
4676
|
if (tasks.has(ns))
|
|
4531
4677
|
return;
|
|
4532
4678
|
let attemptAfterSeq = afterSeq;
|
|
@@ -4550,7 +4696,7 @@ export class MessageDeliveryEngine {
|
|
|
4550
4696
|
targets.delete(ns);
|
|
4551
4697
|
if (tasks.get(ns) === task)
|
|
4552
4698
|
tasks.delete(ns);
|
|
4553
|
-
this.finishBackgroundPull(ns, generation, attemptAfterSeq, failed);
|
|
4699
|
+
this.finishBackgroundPull(ns, generation, attemptAfterSeq, failed, pullTrigger);
|
|
4554
4700
|
client._rpcPipeline?.requestPullDrainedCheck?.();
|
|
4555
4701
|
}
|
|
4556
4702
|
});
|
|
@@ -4624,25 +4770,28 @@ export class MessageDeliveryEngine {
|
|
|
4624
4770
|
}
|
|
4625
4771
|
return;
|
|
4626
4772
|
}
|
|
4627
|
-
this.recordBackgroundPullNeed(ns, seq);
|
|
4773
|
+
this.recordBackgroundPullNeed(ns, seq, false, true);
|
|
4774
|
+
const pullTrigger = this.backgroundPullNeeds.get(ns)?.trigger;
|
|
4628
4775
|
const pullActive = client._rpcPipeline?.hasPullActivity?.(ns) === true;
|
|
4629
4776
|
if (pullActive || !this.beginRealtimeSync(ns)) {
|
|
4630
4777
|
this.recordPendingGroupPull(ns, seq);
|
|
4631
4778
|
client._clientLog.debug(`V2 group push queued behind in-flight realtime sync: ns=${ns} seq=${seq}`);
|
|
4632
4779
|
return;
|
|
4633
4780
|
}
|
|
4781
|
+
let pullFailed = false;
|
|
4634
4782
|
void this.runGroupRealtimeSync(groupId, ns, seq, 50, false).then(async () => {
|
|
4635
4783
|
if (inlineGeneration !== undefined && !this.inlineGenerationIsCurrent(inlineGeneration))
|
|
4636
4784
|
return;
|
|
4637
4785
|
// Tail 期间新到达的 Push 已由 realtime sync 在同一周期内补至多一页 Forward。
|
|
4638
4786
|
}).catch((exc) => {
|
|
4787
|
+
pullFailed = true;
|
|
4639
4788
|
if (inlineGeneration !== undefined && !this.inlineGenerationIsCurrent(inlineGeneration))
|
|
4640
4789
|
return;
|
|
4641
4790
|
client._clientLog.warn(`V2 group push auto-sync failed: group=${groupId} err=${formatDeliveryError(exc)}`);
|
|
4642
4791
|
}).finally(() => {
|
|
4643
4792
|
if (syncGeneration === this.captureInlineAckGeneration()
|
|
4644
4793
|
&& (inlineGeneration === undefined || this.inlineGenerationIsCurrent(inlineGeneration))) {
|
|
4645
|
-
this.finishBackgroundPull(ns, syncGeneration, this.syncState(ns).ack,
|
|
4794
|
+
this.finishBackgroundPull(ns, syncGeneration, this.syncState(ns).ack, pullFailed, pullTrigger);
|
|
4646
4795
|
this.endRealtimeSync(ns);
|
|
4647
4796
|
this.schedulePendingGroupPullIfNeeded(ns, 'push-auto-pull-complete');
|
|
4648
4797
|
}
|
|
@@ -4721,12 +4870,13 @@ export class MessageDeliveryEngine {
|
|
|
4721
4870
|
const ns = `p2p:${client._aid}`;
|
|
4722
4871
|
const generation = this.captureInlineAckGeneration();
|
|
4723
4872
|
const afterSeq = this.syncState(ns).ack;
|
|
4724
|
-
this.recordBackgroundPullNeed(ns, positiveSafeSequenceHint(data.seq), true);
|
|
4873
|
+
this.recordBackgroundPullNeed(ns, positiveSafeSequenceHint(data.seq), true, true);
|
|
4874
|
+
const pullTrigger = this.backgroundPullNeeds.get(ns)?.trigger;
|
|
4725
4875
|
let failed = false;
|
|
4726
4876
|
client._safeAsync(this.runP2PForwardRecovery(ns, 50, true).catch((exc) => {
|
|
4727
4877
|
failed = true;
|
|
4728
4878
|
client._clientLog.debug(`P2P online unread hint background Forward failed: ns=${ns} err=${formatDeliveryError(exc)}`);
|
|
4729
|
-
}).finally(() => this.finishBackgroundPull(ns, generation, afterSeq, failed)));
|
|
4879
|
+
}).finally(() => this.finishBackgroundPull(ns, generation, afterSeq, failed, pullTrigger)));
|
|
4730
4880
|
}
|
|
4731
4881
|
async saveSeqTrackerState() {
|
|
4732
4882
|
const client = this.runtime.client;
|