@peerbit/shared-log 8.0.7 → 9.0.0-55cebfe

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.
Files changed (79) hide show
  1. package/dist/benchmark/get-samples.d.ts +2 -0
  2. package/dist/benchmark/get-samples.d.ts.map +1 -0
  3. package/dist/benchmark/get-samples.js +69 -0
  4. package/dist/benchmark/get-samples.js.map +1 -0
  5. package/dist/benchmark/index.d.ts +2 -0
  6. package/dist/benchmark/index.d.ts.map +1 -0
  7. package/{lib/esm/__benchmark__ → dist/benchmark}/index.js +16 -16
  8. package/dist/benchmark/index.js.map +1 -0
  9. package/dist/benchmark/replication-prune.d.ts +2 -0
  10. package/dist/benchmark/replication-prune.d.ts.map +1 -0
  11. package/dist/benchmark/replication-prune.js +103 -0
  12. package/dist/benchmark/replication-prune.js.map +1 -0
  13. package/dist/benchmark/replication.d.ts +2 -0
  14. package/dist/benchmark/replication.d.ts.map +1 -0
  15. package/dist/benchmark/replication.js +91 -0
  16. package/dist/benchmark/replication.js.map +1 -0
  17. package/{lib/esm → dist/src}/blocks.d.ts +1 -0
  18. package/dist/src/blocks.d.ts.map +1 -0
  19. package/{lib/esm → dist/src}/blocks.js +1 -1
  20. package/dist/src/blocks.js.map +1 -0
  21. package/{lib/esm → dist/src}/cpu.d.ts +2 -1
  22. package/dist/src/cpu.d.ts.map +1 -0
  23. package/{lib/esm → dist/src}/cpu.js +2 -2
  24. package/dist/src/cpu.js.map +1 -0
  25. package/{lib/esm → dist/src}/exchange-heads.d.ts +2 -1
  26. package/dist/src/exchange-heads.d.ts.map +1 -0
  27. package/{lib/esm → dist/src}/exchange-heads.js +9 -7
  28. package/dist/src/exchange-heads.js.map +1 -0
  29. package/{lib/esm → dist/src}/index.d.ts +64 -54
  30. package/dist/src/index.d.ts.map +1 -0
  31. package/{lib/esm → dist/src}/index.js +569 -399
  32. package/dist/src/index.js.map +1 -0
  33. package/{lib/esm → dist/src}/message.d.ts +1 -0
  34. package/dist/src/message.d.ts.map +1 -0
  35. package/{lib/esm → dist/src}/pid.d.ts +1 -0
  36. package/dist/src/pid.d.ts.map +1 -0
  37. package/{lib/esm → dist/src}/pid.js +20 -20
  38. package/dist/src/pid.js.map +1 -0
  39. package/dist/src/ranges.d.ts +10 -0
  40. package/dist/src/ranges.d.ts.map +1 -0
  41. package/dist/src/ranges.js +645 -0
  42. package/dist/src/ranges.js.map +1 -0
  43. package/dist/src/replication.d.ts +112 -0
  44. package/dist/src/replication.d.ts.map +1 -0
  45. package/dist/src/replication.js +348 -0
  46. package/dist/src/replication.js.map +1 -0
  47. package/dist/src/role.d.ts +2 -0
  48. package/dist/src/role.d.ts.map +1 -0
  49. package/dist/src/role.js +106 -0
  50. package/dist/src/role.js.map +1 -0
  51. package/package.json +70 -43
  52. package/src/blocks.ts +1 -1
  53. package/src/cpu.ts +7 -6
  54. package/src/exchange-heads.ts +19 -19
  55. package/src/index.ts +881 -609
  56. package/src/pid.ts +22 -21
  57. package/src/ranges.ts +692 -148
  58. package/src/replication.ts +271 -19
  59. package/src/role.ts +63 -83
  60. package/LICENSE +0 -202
  61. package/lib/esm/__benchmark__/index.d.ts +0 -1
  62. package/lib/esm/__benchmark__/index.js.map +0 -1
  63. package/lib/esm/blocks.js.map +0 -1
  64. package/lib/esm/cpu.js.map +0 -1
  65. package/lib/esm/exchange-heads.js.map +0 -1
  66. package/lib/esm/index.js.map +0 -1
  67. package/lib/esm/pid.js.map +0 -1
  68. package/lib/esm/ranges.d.ts +0 -12
  69. package/lib/esm/ranges.js +0 -247
  70. package/lib/esm/ranges.js.map +0 -1
  71. package/lib/esm/replication.d.ts +0 -53
  72. package/lib/esm/replication.js +0 -105
  73. package/lib/esm/replication.js.map +0 -1
  74. package/lib/esm/role.d.ts +0 -38
  75. package/lib/esm/role.js +0 -130
  76. package/lib/esm/role.js.map +0 -1
  77. package/src/__benchmark__/index.ts +0 -115
  78. /package/{lib/esm → dist/src}/message.js +0 -0
  79. /package/{lib/esm → dist/src}/message.js.map +0 -0
@@ -7,39 +7,41 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
7
7
  var __metadata = (this && this.__metadata) || function (k, v) {
8
8
  if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
9
9
  };
10
- import { RPC } from "@peerbit/rpc";
11
- import { TransportMessage } from "./message.js";
12
- import { Entry, Log } from "@peerbit/log";
13
- import { Program } from "@peerbit/program";
14
10
  import { BinaryWriter, BorshError, field, variant } from "@dao-xyz/borsh";
15
- import { AccessError, sha256, sha256Base64Sync } from "@peerbit/crypto";
16
- import { logger as loggerFn } from "@peerbit/logger";
17
- import { ExchangeHeadsMessage, RequestIPrune, RequestMaybeSync, ResponseIPrune, ResponseMaybeSync, createExchangeHeadsMessages } from "./exchange-heads.js";
18
- import { AbortError, delay, waitFor } from "@peerbit/time";
19
- import { Observer, Replicator, Role } from "./role.js";
20
- import { AbsoluteReplicas, ReplicationError, RequestRoleMessage, ResponseRoleMessage, decodeReplicas, encodeReplicas, hashToUniformNumber, maxReplicas } from "./replication.js";
21
- import pDefer from "p-defer";
22
- import { Cache } from "@peerbit/cache";
23
11
  import { CustomEvent } from "@libp2p/interface";
24
- import yallist from "yallist";
25
- import { AcknowledgeDelivery, SilentDelivery, NotStartedError } from "@peerbit/stream-interface";
26
12
  import { AnyBlockStore, RemoteBlocks } from "@peerbit/blocks";
27
- import { BlocksMessage } from "./blocks.js";
13
+ import { Cache } from "@peerbit/cache";
14
+ import { AccessError, PublicSignKey, sha256, sha256Base64Sync, sha256Sync, } from "@peerbit/crypto";
15
+ import { And, ByteMatchQuery, CountRequest, DeleteRequest, IntegerCompare, Or, SearchRequest, Sort, StringMatch, SumRequest, toId, } from "@peerbit/indexer-interface";
16
+ import { Entry, Log, ShallowEntry, } from "@peerbit/log";
17
+ import { logger as loggerFn } from "@peerbit/logger";
18
+ import { Program } from "@peerbit/program";
19
+ import { SubscriptionEvent, UnsubcriptionEvent, } from "@peerbit/pubsub-interface";
20
+ import { RPC } from "@peerbit/rpc";
21
+ import { AcknowledgeDelivery, DeliveryMode, NotStartedError, SilentDelivery, } from "@peerbit/stream-interface";
22
+ import { AbortError, delay, waitFor } from "@peerbit/time";
28
23
  import debounce from "p-debounce";
29
- import { PIDReplicationController } from "./pid.js";
30
- export * from "./replication.js";
24
+ import pDefer, {} from "p-defer";
31
25
  import PQueue from "p-queue";
26
+ import { BlocksMessage } from "./blocks.js";
32
27
  import { CPUUsageIntervalLag } from "./cpu.js";
28
+ import { EntryWithRefs, ExchangeHeadsMessage, RequestIPrune, RequestMaybeSync, ResponseIPrune, ResponseMaybeSync, createExchangeHeadsMessages, } from "./exchange-heads.js";
29
+ import { TransportMessage } from "./message.js";
30
+ import { PIDReplicationController } from "./pid.js";
33
31
  import { getCoverSet, getSamples, isMatured } from "./ranges.js";
32
+ import { AbsoluteReplicas, ReplicationError, ReplicationIntent, ReplicationRange, ReplicationRangeIndexable, RequestReplicationInfoMessage, ResponseReplicationInfoMessage, StartedReplicating, StoppedReplicating, decodeReplicas, encodeReplicas, hashToUniformNumber, maxReplicas, } from "./replication.js";
33
+ import { SEGMENT_COORDINATE_SCALE } from "./role.js";
34
+ export * from "./replication.js";
34
35
  export { CPUUsageIntervalLag };
35
- export { Observer, Replicator, Role };
36
36
  export const logger = loggerFn({ module: "shared-log" });
37
37
  const groupByGid = async (entries) => {
38
38
  const groupByGid = new Map();
39
39
  for (const head of entries) {
40
40
  const gid = await (head instanceof Entry
41
41
  ? head.getGid()
42
- : head.entry.getGid());
42
+ : head instanceof ShallowEntry
43
+ ? head.meta.gid
44
+ : head.entry.getGid());
43
45
  let value = groupByGid.get(gid);
44
46
  if (!value) {
45
47
  value = [];
@@ -50,11 +52,16 @@ const groupByGid = async (entries) => {
50
52
  return groupByGid;
51
53
  };
52
54
  const isAdaptiveReplicatorOption = (options) => {
53
- if (options.limits ||
54
- options.factor == null) {
55
- return true;
55
+ if (typeof options === "number") {
56
+ return false;
57
+ }
58
+ if (typeof options === "boolean") {
59
+ return false;
56
60
  }
57
- return false;
61
+ if (options.factor != null) {
62
+ return false;
63
+ }
64
+ return true;
58
65
  };
59
66
  export const DEFAULT_MIN_REPLICAS = 2;
60
67
  export const WAIT_FOR_REPLICATOR_TIMEOUT = 9000;
@@ -65,14 +72,13 @@ let SharedLog = class SharedLog extends Program {
65
72
  log;
66
73
  rpc;
67
74
  // options
68
- _role;
69
- _roleConfig;
70
- _sortedPeersCache;
75
+ _replicationSettings;
76
+ _replicationRangeIndex;
71
77
  _totalParticipation;
72
78
  _gidPeersHistory;
73
79
  _onSubscriptionFn;
74
80
  _onUnsubscriptionFn;
75
- _canReplicate;
81
+ _isTrustedReplicator;
76
82
  _logProperties;
77
83
  _closeController;
78
84
  _gidParentCache;
@@ -91,39 +97,70 @@ let SharedLog = class SharedLog extends Program {
91
97
  distributeQueue;
92
98
  // Syncing and dedeplucation work
93
99
  syncMoreInterval;
100
+ // map of hash to public keys that we can ask for entries
94
101
  syncInFlightQueue;
95
102
  syncInFlightQueueInverted;
103
+ // map of hash to public keys that we have asked for entries
96
104
  syncInFlight;
97
105
  replicas;
98
106
  cpuUsage;
99
107
  timeUntilRoleMaturity;
100
108
  waitForReplicatorTimeout;
101
109
  distributionDebounceTime;
110
+ replicationController;
111
+ history;
112
+ pq;
102
113
  constructor(properties) {
103
114
  super();
104
115
  this.log = new Log(properties);
105
116
  this.rpc = new RPC();
106
117
  }
107
- /**
108
- * Returns the current role
109
- */
110
- get role() {
111
- return this._role;
112
- }
113
118
  /**
114
119
  * Return the
115
120
  */
116
- get roleConfig() {
117
- return this._roleConfig;
121
+ get replicationSettings() {
122
+ return this._replicationSettings;
123
+ }
124
+ async isReplicating() {
125
+ if (!this._replicationSettings) {
126
+ return false;
127
+ }
128
+ if (isAdaptiveReplicatorOption(this._replicationSettings)) {
129
+ return true;
130
+ }
131
+ if (this.replicationSettings.factor > 0) {
132
+ return true;
133
+ }
134
+ return (await this.countReplicationSegments()) > 0;
118
135
  }
119
136
  get totalParticipation() {
120
137
  return this._totalParticipation;
121
138
  }
139
+ async calculateTotalParticipation() {
140
+ const sum = await this.replicationIndex.sum(new SumRequest({ key: "width" }));
141
+ return Number(sum) / SEGMENT_COORDINATE_SCALE;
142
+ }
143
+ async countReplicationSegments() {
144
+ const count = await this.replicationIndex.count(new CountRequest({
145
+ query: new StringMatch({
146
+ key: "hash",
147
+ value: this.node.identity.publicKey.hashcode(),
148
+ }),
149
+ }));
150
+ return count;
151
+ }
122
152
  setupRebalanceDebounceFunction() {
123
- this.rebalanceParticipationDebounced = debounce(() => this.rebalanceParticipation(), Math.max(REBALANCE_DEBOUNCE_INTERVAL, Math.log((this.getReplicatorsSorted()?.length || 0) *
124
- REBALANCE_DEBOUNCE_INTERVAL)));
153
+ this.rebalanceParticipationDebounced = debounce(() => this.rebalanceParticipation(),
154
+ /* Math.max(
155
+ REBALANCE_DEBOUNCE_INTERVAL,
156
+ Math.log(
157
+ (this.getReplicatorsSorted()?.getSize() || 0) *
158
+ REBALANCE_DEBOUNCE_INTERVAL
159
+ )
160
+ ) */
161
+ REBALANCE_DEBOUNCE_INTERVAL);
125
162
  }
126
- setupRole(options) {
163
+ async setupReplicationSettings(options) {
127
164
  this.rebalanceParticipationDebounced = undefined;
128
165
  const setupDebouncedRebalancing = (options) => {
129
166
  this.cpuUsage?.stop?.();
@@ -135,9 +172,9 @@ let SharedLog = class SharedLog extends Program {
135
172
  ? {
136
173
  max: typeof options?.limits?.cpu === "object"
137
174
  ? options.limits.cpu.max
138
- : options?.limits?.cpu
175
+ : options?.limits?.cpu,
139
176
  }
140
- : undefined
177
+ : undefined,
141
178
  });
142
179
  this.cpuUsage =
143
180
  options?.limits?.cpu && typeof options?.limits?.cpu === "object"
@@ -146,75 +183,216 @@ let SharedLog = class SharedLog extends Program {
146
183
  this.cpuUsage?.start?.();
147
184
  this.setupRebalanceDebounceFunction();
148
185
  };
149
- if (options instanceof Observer || options instanceof Replicator) {
150
- throw new Error("Unsupported role option type");
151
- }
152
- else if (options === "observer") {
153
- this._roleConfig = new Observer();
154
- }
155
- else if (options === "replicator") {
156
- setupDebouncedRebalancing();
157
- this._roleConfig = { type: options };
158
- }
159
- else if (options) {
160
- if (options.type === "replicator") {
161
- if (isAdaptiveReplicatorOption(options)) {
162
- setupDebouncedRebalancing(options);
163
- this._roleConfig = options;
186
+ if (options) {
187
+ if (isAdaptiveReplicatorOption(options)) {
188
+ this._replicationSettings = options;
189
+ setupDebouncedRebalancing(this._replicationSettings);
190
+ }
191
+ else if (options === true ||
192
+ (options && Object.keys(options).length === 0)) {
193
+ this._replicationSettings = {};
194
+ setupDebouncedRebalancing(this._replicationSettings);
195
+ }
196
+ else {
197
+ if (typeof options === "number") {
198
+ this._replicationSettings = {
199
+ factor: options,
200
+ };
164
201
  }
165
202
  else {
166
- this._roleConfig = new Replicator({
167
- factor: options.factor,
168
- offset: this.getReplicationOffset()
169
- });
203
+ this._replicationSettings = { ...options };
170
204
  }
171
205
  }
172
- else {
173
- this._roleConfig = new Observer();
174
- }
175
206
  }
176
207
  else {
177
- // Default option
178
- setupDebouncedRebalancing();
179
- this._roleConfig = { type: "replicator" };
208
+ return;
180
209
  }
181
- // setup the initial role
182
- if (this._roleConfig instanceof Replicator ||
183
- this._roleConfig instanceof Observer) {
184
- this._role = this._roleConfig;
210
+ if (isAdaptiveReplicatorOption(this._replicationSettings)) {
211
+ // initial role in a dynamic setup
212
+ await this.getDynamicRange();
185
213
  }
186
214
  else {
187
- // initial role in a dynamic setup
188
- if (this._roleConfig?.limits) {
189
- this._role = new Replicator({
190
- factor: this._role instanceof Replicator ? this._role.factor : 0,
191
- offset: this.getReplicationOffset()
192
- });
215
+ // fixed
216
+ const range = new ReplicationRangeIndexable({
217
+ offset: this._replicationSettings.offset ??
218
+ Math.random(),
219
+ length: this._replicationSettings.factor,
220
+ publicKeyHash: this.node.identity.publicKey.hashcode(),
221
+ replicationIntent: ReplicationIntent.Explicit, // automatic means that this range might be reused later for dynamic replication behaviour
222
+ timestamp: BigInt(+new Date()),
223
+ id: sha256Sync(this.node.identity.publicKey.bytes),
224
+ });
225
+ await this.startAnnounceReplicating(range);
226
+ }
227
+ }
228
+ async replicate(range) {
229
+ if (range === false || range === 0) {
230
+ this._replicationSettings = undefined;
231
+ await this.removeReplicator(this.node.identity.publicKey);
232
+ }
233
+ else {
234
+ await this.rpc.subscribe();
235
+ if (range instanceof ReplicationRange) {
236
+ this.oldestOpenTime = Math.min(Number(range.timestamp), this.oldestOpenTime);
237
+ await this.startAnnounceReplicating(range.toReplicationRangeIndexable(this.node.identity.publicKey));
193
238
  }
194
239
  else {
195
- this._role = new Replicator({
196
- factor: this._role instanceof Replicator ? this._role.factor : 1,
197
- offset: this.getReplicationOffset()
198
- });
240
+ await this.setupReplicationSettings(range ?? true);
199
241
  }
200
242
  }
201
- return this._role;
243
+ // assume new role
244
+ await this.distribute();
202
245
  }
203
- async updateRole(role, onRoleChange = true) {
204
- return this._updateRole(this.setupRole(role), onRoleChange);
246
+ async removeReplicator(key) {
247
+ const fn = async () => {
248
+ let prev = await this.replicationIndex.query(new SearchRequest({
249
+ query: { hash: key.hashcode() },
250
+ fetch: 0xffffffff,
251
+ }), { reference: true });
252
+ if (prev.results.length === 0) {
253
+ return;
254
+ }
255
+ let sumWidth = prev.results.reduce((acc, x) => acc + x.value.widthNormalized, 0);
256
+ this._totalParticipation -= sumWidth;
257
+ let idMatcher = new Or(prev.results.map((x) => new ByteMatchQuery({ key: "id", value: x.value.id })));
258
+ await this.replicationIndex.del(new DeleteRequest({ query: idMatcher }));
259
+ const calculated = await this.calculateTotalParticipation();
260
+ if (Math.abs(this._totalParticipation - calculated) > 0.001) {
261
+ throw new Error("Total participation is out of sync");
262
+ }
263
+ await this.updateOldestTimestampFromIndex();
264
+ this.events.dispatchEvent(new CustomEvent("replication:change", {
265
+ detail: { publicKey: key },
266
+ }));
267
+ if (!key.equals(this.node.identity.publicKey)) {
268
+ this.rebalanceParticipationDebounced?.();
269
+ }
270
+ };
271
+ return this.pq.add(fn);
272
+ }
273
+ async updateOldestTimestampFromIndex() {
274
+ const oldestTimestampFromDB = (await this.replicationIndex.query(new SearchRequest({
275
+ fetch: 1,
276
+ sort: [new Sort({ key: "timestamp", direction: "asc" })],
277
+ }), { reference: true })).results[0]?.value.timestamp;
278
+ this.oldestOpenTime =
279
+ oldestTimestampFromDB != null
280
+ ? Number(oldestTimestampFromDB)
281
+ : +new Date();
282
+ }
283
+ async removeReplicationRange(id, from) {
284
+ const fn = async () => {
285
+ let idMatcher = new Or(id.map((x) => new ByteMatchQuery({ key: "id", value: x })));
286
+ // make sure we are not removing something that is owned by the replicator
287
+ let identityMatcher = new StringMatch({
288
+ key: "hash",
289
+ value: from.hashcode(),
290
+ });
291
+ let query = new And([idMatcher, identityMatcher]);
292
+ const prevSum = await this.replicationIndex.sum(new SumRequest({ query, key: "width" }));
293
+ const prevSumNormalized = Number(prevSum) / SEGMENT_COORDINATE_SCALE;
294
+ this._totalParticipation -= prevSumNormalized;
295
+ await this.replicationIndex.del(new DeleteRequest({ query }));
296
+ const calculated = await this.calculateTotalParticipation();
297
+ if (Math.abs(this._totalParticipation - calculated) > 0.001) {
298
+ throw new Error("Total participation is out of sync");
299
+ }
300
+ await this.updateOldestTimestampFromIndex();
301
+ this.events.dispatchEvent(new CustomEvent("replication:change", {
302
+ detail: { publicKey: from },
303
+ }));
304
+ if (!from.equals(this.node.identity.publicKey)) {
305
+ this.rebalanceParticipationDebounced?.();
306
+ }
307
+ };
308
+ return this.pq.add(fn);
205
309
  }
206
- async _updateRole(role = this._role, onRoleChange = true) {
310
+ async addReplicationRange(range, from) {
311
+ const fn = async () => {
312
+ if (this._isTrustedReplicator &&
313
+ !(await this._isTrustedReplicator(from))) {
314
+ if (this.node.identity.publicKey.equals(from)) {
315
+ if (range.replicationIntent === ReplicationIntent.Automatic) {
316
+ return false; // we dont want to replicate automatic ranges if not allowed by others
317
+ }
318
+ }
319
+ else {
320
+ return false;
321
+ }
322
+ }
323
+ range.id = new Uint8Array(range.id);
324
+ let prevCount = await this.replicationIndex.count(new CountRequest({
325
+ query: new StringMatch({ key: "hash", value: from.hashcode() }),
326
+ }));
327
+ const isNewReplicator = prevCount === 0;
328
+ let prev = await this.replicationIndex.get(toId(range.id));
329
+ if (prev) {
330
+ if (prev.value.equals(range)) {
331
+ return false;
332
+ }
333
+ this._totalParticipation -= prev.value.widthNormalized;
334
+ }
335
+ await this.replicationIndex.put(range);
336
+ let inserted = await this.replicationIndex.get(toId(range.id));
337
+ if (!inserted?.value.equals(range)) {
338
+ throw new Error("Failed to insert range");
339
+ }
340
+ this._totalParticipation += range.widthNormalized;
341
+ const calculated = await this.calculateTotalParticipation();
342
+ if (Math.abs(this._totalParticipation - calculated) > 0.001) {
343
+ throw new Error("Total participation is out of sync");
344
+ }
345
+ this.oldestOpenTime = Math.min(Number(range.timestamp), this.oldestOpenTime);
346
+ this.events.dispatchEvent(new CustomEvent("replication:change", {
347
+ detail: { publicKey: from },
348
+ }));
349
+ if (isNewReplicator) {
350
+ this.events.dispatchEvent(new CustomEvent("replicator:join", {
351
+ detail: { publicKey: from },
352
+ }));
353
+ }
354
+ if (!from.equals(this.node.identity.publicKey)) {
355
+ this.rebalanceParticipationDebounced?.();
356
+ }
357
+ return true;
358
+ };
359
+ return this.pq.add(fn);
360
+ }
361
+ async startAnnounceReplicating(range) {
362
+ const added = await this.addReplicationRange(range, this.node.identity.publicKey);
363
+ if (!added) {
364
+ logger.warn("Not allowed to replicate by canReplicate");
365
+ }
366
+ added &&
367
+ (await this.rpc.send(new StartedReplicating({ segments: [range.toReplicationRange()] }), {
368
+ priority: 1,
369
+ }));
370
+ }
371
+ /* async updateRole(role: InitialReplicationOptions, onRoleChange = true) {
372
+ await this.setupReplicationSettings(role)
373
+ return this._updateRole(, onRoleChange);
374
+ } */
375
+ /* private async _updateRole(
376
+ role: Observer | Replicator = this._role,
377
+ onRoleChange = true
378
+ ) {
207
379
  this._role = role;
208
- const { changed } = await this._modifyReplicators(this.role, this.node.identity.publicKey);
380
+ const { changed } = await this._modifyReplicators(
381
+ this.role,
382
+ this.node.identity.publicKey
383
+ );
384
+
209
385
  await this.rpc.subscribe();
210
- await this.rpc.send(new ResponseRoleMessage({ role: this._role }), {
386
+ await this.rpc.send(new ResponseReplicationInfoMessage({ segments: await }), {
211
387
  priority: 1
212
388
  });
389
+
213
390
  if (onRoleChange && changed !== "none") {
214
- this.onRoleChange(this._role, this.node.identity.publicKey);
391
+ await this.onRoleChange(this._role, this.node.identity.publicKey);
215
392
  }
393
+
216
394
  return changed;
217
- }
395
+ } */
218
396
  async append(data, options) {
219
397
  const appendOptions = { ...options };
220
398
  const minReplicasData = encodeReplicas(options?.replicas
@@ -224,12 +402,24 @@ let SharedLog = class SharedLog extends Program {
224
402
  : this.replicas.min);
225
403
  if (!appendOptions.meta) {
226
404
  appendOptions.meta = {
227
- data: minReplicasData
405
+ data: minReplicasData,
228
406
  };
229
407
  }
230
408
  else {
231
409
  appendOptions.meta.data = minReplicasData;
232
410
  }
411
+ if (options?.canAppend) {
412
+ appendOptions.canAppend = async (entry) => {
413
+ await this.canAppend(entry);
414
+ return options.canAppend(entry);
415
+ };
416
+ }
417
+ if (options?.onChange) {
418
+ appendOptions.onChange = async (change) => {
419
+ await this.onChange(change);
420
+ return options.onChange(change);
421
+ };
422
+ }
233
423
  const result = await this.log.append(data, appendOptions);
234
424
  let mode = undefined;
235
425
  for (const message of await createExchangeHeadsMessages(this.log, [result.entry], this._gidParentCache)) {
@@ -262,7 +452,7 @@ let SharedLog = class SharedLog extends Program {
262
452
  }
263
453
  // TODO add options for waiting ?
264
454
  this.rpc.send(message, {
265
- mode
455
+ mode,
266
456
  });
267
457
  }
268
458
  this.rebalanceParticipationDebounced?.();
@@ -279,7 +469,7 @@ let SharedLog = class SharedLog extends Program {
279
469
  ? typeof options?.replicas?.max === "number"
280
470
  ? new AbsoluteReplicas(options?.replicas?.max)
281
471
  : options.replicas.max
282
- : undefined
472
+ : undefined,
283
473
  };
284
474
  this._respondToIHaveTimeout = options?.respondToIHaveTimeout ?? 10 * 1000; // TODO make into arg
285
475
  this._pendingDeletes = new Map();
@@ -291,17 +481,17 @@ let SharedLog = class SharedLog extends Program {
291
481
  this.openTime = +new Date();
292
482
  this.oldestOpenTime = this.openTime;
293
483
  this.distributionDebounceTime =
294
- options?.distributionDebounceTime || DEFAULT_DISTRIBUTION_DEBOUNCE_TIME;
484
+ options?.distributionDebounceTime || DEFAULT_DISTRIBUTION_DEBOUNCE_TIME; // expect > 0
295
485
  this.timeUntilRoleMaturity =
296
- options?.timeUntilRoleMaturity || WAIT_FOR_ROLE_MATURITY;
486
+ options?.timeUntilRoleMaturity ?? WAIT_FOR_ROLE_MATURITY;
297
487
  this.waitForReplicatorTimeout =
298
488
  options?.waitForReplicatorTimeout || WAIT_FOR_REPLICATOR_TIMEOUT;
299
- this._gidParentCache = new Cache({ max: 1000 });
489
+ this._gidParentCache = new Cache({ max: 100 }); // TODO choose a good number
300
490
  this._closeController = new AbortController();
301
- this._canReplicate = options?.canReplicate;
491
+ this._isTrustedReplicator = options?.canReplicate;
302
492
  this.sync = options?.sync;
303
493
  this._logProperties = options;
304
- this.setupRole(options?.role);
494
+ this.pq = new PQueue({ concurrency: 1000 });
305
495
  const id = sha256Base64Sync(this.log.id);
306
496
  const storage = await this.node.storage.sublevel(id);
307
497
  const localBlocks = await new AnyBlockStore(await storage.sublevel("blocks"));
@@ -310,76 +500,60 @@ let SharedLog = class SharedLog extends Program {
310
500
  publish: (message, options) => this.rpc.send(new BlocksMessage(message), {
311
501
  mode: options?.to
312
502
  ? new SilentDelivery({ to: options.to, redundancy: 1 })
313
- : undefined
503
+ : undefined,
314
504
  }),
315
- waitFor: this.rpc.waitFor.bind(this.rpc)
505
+ waitFor: this.rpc.waitFor.bind(this.rpc),
316
506
  });
317
507
  await this.remoteBlocks.start();
318
- this._onSubscriptionFn = this._onSubscription.bind(this);
319
508
  this._totalParticipation = 0;
320
- this._sortedPeersCache = yallist.create();
509
+ const logScope = await this.node.indexer.scope(id);
510
+ const replicationIndex = await logScope.scope("replication");
511
+ this._replicationRangeIndex = await replicationIndex.init({
512
+ schema: ReplicationRangeIndexable,
513
+ });
514
+ const logIndex = await logScope.scope("log");
515
+ await this.node.indexer.start(); // TODO why do we need to start the indexer here?
516
+ this._totalParticipation = await this.calculateTotalParticipation();
321
517
  this._gidPeersHistory = new Map();
322
- const cache = await storage.sublevel("cache");
323
518
  await this.log.open(this.remoteBlocks, this.node.identity, {
324
519
  keychain: this.node.services.keychain,
325
520
  ...this._logProperties,
326
521
  onChange: (change) => {
327
- for (const added of change.added) {
328
- this.onEntryAdded(added);
329
- }
330
- for (const removed of change.removed) {
331
- this.onEntryRemoved(removed.hash);
332
- }
522
+ this.onChange(change);
333
523
  return this._logProperties?.onChange?.(change);
334
524
  },
335
525
  canAppend: async (entry) => {
336
- try {
337
- if (!entry.meta.data) {
338
- logger.warn("Received entry without meta data, skipping");
339
- return false;
340
- }
341
- const replicas = decodeReplicas(entry).getValue(this);
342
- if (Number.isFinite(replicas) === false) {
343
- return false;
344
- }
345
- // Don't verify entries that we have created (TODO should we? perf impact?)
346
- if (!entry.createdLocally && !(await entry.verifySignatures())) {
347
- return false;
348
- }
349
- }
350
- catch (error) {
351
- if (error instanceof BorshError ||
352
- error instanceof ReplicationError) {
353
- logger.warn("Received payload that could not be decoded, skipping");
354
- return false;
355
- }
356
- throw error;
526
+ if (!(await this.canAppend(entry))) {
527
+ return false;
357
528
  }
358
529
  return this._logProperties?.canAppend?.(entry) ?? true;
359
530
  },
360
531
  trim: this._logProperties?.trim && {
361
- ...this._logProperties?.trim
532
+ ...this._logProperties?.trim,
362
533
  },
363
- cache: cache
534
+ indexer: logIndex,
364
535
  });
365
536
  // Open for communcation
366
537
  await this.rpc.open({
367
538
  queryType: TransportMessage,
368
539
  responseType: TransportMessage,
369
540
  responseHandler: this._onMessage.bind(this),
370
- topic: this.topic
541
+ topic: this.topic,
371
542
  });
543
+ this._onSubscriptionFn =
544
+ this._onSubscriptionFn || this._onSubscription.bind(this);
372
545
  await this.node.services.pubsub.addEventListener("subscribe", this._onSubscriptionFn);
373
- this._onUnsubscriptionFn = this._onUnsubscription.bind(this);
546
+ this._onUnsubscriptionFn =
547
+ this._onUnsubscriptionFn || this._onUnsubscription.bind(this);
374
548
  await this.node.services.pubsub.addEventListener("unsubscribe", this._onUnsubscriptionFn);
375
- await this.log.load();
549
+ // await this.log.load();
376
550
  // TODO (do better)
377
551
  // we do this distribution interval to eliminate the sideeffects arriving from updating roles and joining entries continously.
378
552
  // an alternative to this would be to call distribute/maybe prune after every join if our role has changed
379
553
  this.distributeInterval = setInterval(() => {
380
554
  this.distribute();
381
555
  }, 7.5 * 1000);
382
- const requestSync = () => {
556
+ const requestSync = async () => {
383
557
  /**
384
558
  * This method fetches entries that we potentially want.
385
559
  * In a case in which we become replicator of a segment,
@@ -390,7 +564,7 @@ let SharedLog = class SharedLog extends Program {
390
564
  const requestHashes = [];
391
565
  const from = new Set();
392
566
  for (const [key, value] of this.syncInFlightQueue) {
393
- if (!this.log.has(key)) {
567
+ if (!(await this.log.has(key))) {
394
568
  // TODO test that this if statement actually does anymeaningfull
395
569
  if (value.length > 0) {
396
570
  requestHashes.push(key);
@@ -423,12 +597,13 @@ let SharedLog = class SharedLog extends Program {
423
597
  this.syncMoreInterval = setTimeout(requestSync, 1e4);
424
598
  });
425
599
  };
600
+ await this.replicate(options?.replicate);
426
601
  requestSync();
427
602
  }
428
603
  async afterOpen() {
429
604
  await super.afterOpen();
430
605
  // We do this here, because these calls requires this.closed == false
431
- await this._updateRole();
606
+ /* await this._updateRole(); */
432
607
  await this.rebalanceParticipation();
433
608
  // Take into account existing subscription
434
609
  (await this.node.services.pubsub.getSubscribers(this.topic))?.forEach((v, k) => {
@@ -438,25 +613,60 @@ let SharedLog = class SharedLog extends Program {
438
613
  this.handleSubscriptionChange(v, [this.topic], true);
439
614
  });
440
615
  }
616
+ async reload() {
617
+ await this.log.load({ reset: true, reload: true });
618
+ }
441
619
  async getMemoryUsage() {
442
- return (((await this.log.memory?.size()) || 0) + (await this.log.blocks.size()));
620
+ return this.log.blocks.size();
621
+ /* ((await this.log.entryIndex?.getMemoryUsage()) || 0) */ // + (await this.log.blocks.size())
443
622
  }
444
623
  get topic() {
445
624
  return this.log.idString;
446
625
  }
626
+ async onChange(change) {
627
+ for (const added of change.added) {
628
+ this.onEntryAdded(added);
629
+ }
630
+ for (const removed of change.removed) {
631
+ this.onEntryRemoved(removed.hash);
632
+ }
633
+ }
634
+ async canAppend(entry) {
635
+ try {
636
+ if (!entry.meta.data) {
637
+ logger.warn("Received entry without meta data, skipping");
638
+ return false;
639
+ }
640
+ const replicas = decodeReplicas(entry).getValue(this);
641
+ if (Number.isFinite(replicas) === false) {
642
+ return false;
643
+ }
644
+ // Don't verify entries that we have created (TODO should we? perf impact?)
645
+ if (!entry.createdLocally && !(await entry.verifySignatures())) {
646
+ return false;
647
+ }
648
+ return true;
649
+ }
650
+ catch (error) {
651
+ if (error instanceof BorshError || error instanceof ReplicationError) {
652
+ logger.warn("Received payload that could not be decoded, skipping");
653
+ return false;
654
+ }
655
+ throw error;
656
+ }
657
+ }
447
658
  async _close() {
448
659
  clearTimeout(this.syncMoreInterval);
449
660
  clearInterval(this.distributeInterval);
450
661
  this.distributeQueue?.clear();
451
662
  this._closeController.abort();
452
663
  this.node.services.pubsub.removeEventListener("subscribe", this._onSubscriptionFn);
453
- this._onUnsubscriptionFn = this._onUnsubscription.bind(this);
454
664
  this.node.services.pubsub.removeEventListener("unsubscribe", this._onUnsubscriptionFn);
455
- for (const [k, v] of this._pendingDeletes) {
665
+ for (const [_k, v] of this._pendingDeletes) {
456
666
  v.clear();
457
667
  v.promise.resolve(); // TODO or reject?
458
668
  }
459
- for (const [k, v] of this._pendingIHave) {
669
+ for (const [_k, v] of this._pendingIHave) {
460
670
  v.clear();
461
671
  }
462
672
  await this.remoteBlocks.stop();
@@ -468,8 +678,10 @@ let SharedLog = class SharedLog extends Program {
468
678
  this.syncInFlight.clear();
469
679
  this.latestRoleMessages.clear();
470
680
  this._gidPeersHistory.clear();
471
- this._sortedPeersCache = undefined;
681
+ this._replicationRangeIndex = undefined;
472
682
  this.cpuUsage?.stop?.();
683
+ this._totalParticipation = 0;
684
+ this.pq.clear();
473
685
  }
474
686
  async close(from) {
475
687
  const superClosed = await super.close(from);
@@ -508,11 +720,11 @@ let SharedLog = class SharedLog extends Program {
508
720
  if (heads) {
509
721
  const filteredHeads = [];
510
722
  for (const head of heads) {
511
- if (!this.log.has(head.entry.hash)) {
723
+ if (!(await this.log.has(head.entry.hash))) {
512
724
  head.entry.init({
513
725
  // we need to init because we perhaps need to decrypt gid
514
726
  keychain: this.log.keychain,
515
- encoding: this.log.encoding
727
+ encoding: this.log.encoding,
516
728
  });
517
729
  filteredHeads.push(head);
518
730
  }
@@ -527,17 +739,26 @@ let SharedLog = class SharedLog extends Program {
527
739
  const promises = [];
528
740
  for (const [gid, entries] of groupedByGid) {
529
741
  const fn = async () => {
530
- const headsWithGid = this.log.headsIndex.gids.get(gid);
531
- const maxReplicasFromHead = headsWithGid && headsWithGid.size > 0
742
+ const headsWithGid = await this.log.entryIndex
743
+ .getHeads(gid)
744
+ .all();
745
+ const maxReplicasFromHead = headsWithGid && headsWithGid.length > 0
532
746
  ? maxReplicas(this, [...headsWithGid.values()])
533
747
  : this.replicas.min.getValue(this);
534
748
  const maxReplicasFromNewEntries = maxReplicas(this, entries.map((x) => x.entry));
535
- const leaders = await (this.role instanceof Observer
536
- ? this.findLeaders(gid, Math.max(maxReplicasFromHead, maxReplicasFromNewEntries))
537
- : this.waitForIsLeader(gid, Math.max(maxReplicasFromHead, maxReplicasFromNewEntries)));
538
- const isLeader = !!leaders;
749
+ const isReplicating = await this.isReplicating();
750
+ let isLeader;
751
+ if (isReplicating) {
752
+ isLeader = await this.waitForIsLeader(gid, Math.max(maxReplicasFromHead, maxReplicasFromNewEntries));
753
+ }
754
+ else {
755
+ isLeader = await this.findLeaders(gid, Math.max(maxReplicasFromHead, maxReplicasFromNewEntries));
756
+ isLeader = isLeader.includes(this.node.identity.publicKey.hashcode())
757
+ ? isLeader
758
+ : false;
759
+ }
539
760
  if (isLeader) {
540
- if (leaders.find((x) => x === context.from.hashcode())) {
761
+ if (isLeader.find((x) => x === context.from.hashcode())) {
541
762
  let peerSet = this._gidPeersHistory.get(gid);
542
763
  if (!peerSet) {
543
764
  peerSet = new Set();
@@ -555,8 +776,8 @@ let SharedLog = class SharedLog extends Program {
555
776
  }
556
777
  else {
557
778
  for (const ref of entry.gidRefrences) {
558
- const map = this.log.headsIndex.gids.get(ref);
559
- if (map && map.size > 0) {
779
+ const map = await this.log.entryIndex.getHeads(ref).all();
780
+ if (map && map.length > 0) {
560
781
  toMerge.push(entry.entry);
561
782
  (toDelete || (toDelete = [])).push(entry.entry);
562
783
  continue outer;
@@ -592,8 +813,10 @@ let SharedLog = class SharedLog extends Program {
592
813
  }
593
814
  if (maybeDelete) {
594
815
  for (const entries of maybeDelete) {
595
- const headsWithGid = this.log.headsIndex.gids.get(entries[0].entry.meta.gid);
596
- if (headsWithGid && headsWithGid.size > 0) {
816
+ const headsWithGid = await this.log.entryIndex
817
+ .getHeads(entries[0].entry.meta.gid)
818
+ .all();
819
+ if (headsWithGid && headsWithGid.length > 0) {
597
820
  const minReplicas = maxReplicas(this, headsWithGid.values());
598
821
  const isLeader = await this.isLeader(entries[0].entry.meta.gid, minReplicas);
599
822
  if (!isLeader) {
@@ -609,11 +832,11 @@ let SharedLog = class SharedLog extends Program {
609
832
  else if (msg instanceof RequestIPrune) {
610
833
  const hasAndIsLeader = [];
611
834
  for (const hash of msg.hashes) {
612
- const indexedEntry = this.log.entryIndex.getShallow(hash);
835
+ const indexedEntry = await this.log.entryIndex.getShallow(hash);
613
836
  if (indexedEntry &&
614
- (await this.isLeader(indexedEntry.meta.gid, decodeReplicas(indexedEntry).getValue(this)))) {
837
+ (await this.isLeader(indexedEntry.value.meta.gid, decodeReplicas(indexedEntry.value).getValue(this)))) {
615
838
  this._gidPeersHistory
616
- .get(indexedEntry.meta.gid)
839
+ .get(indexedEntry.value.meta.gid)
617
840
  ?.delete(context.from.hashcode());
618
841
  hasAndIsLeader.push(hash);
619
842
  }
@@ -632,13 +855,13 @@ let SharedLog = class SharedLog extends Program {
632
855
  this.rpc.send(new ResponseIPrune({ hashes: [entry.hash] }), {
633
856
  mode: new SilentDelivery({
634
857
  to: [context.from],
635
- redundancy: 1
636
- })
858
+ redundancy: 1,
859
+ }),
637
860
  });
638
861
  }
639
862
  prevPendingIHave && prevPendingIHave.callback(entry);
640
863
  this._pendingIHave.delete(entry.hash);
641
- }
864
+ },
642
865
  };
643
866
  const timeout = setTimeout(() => {
644
867
  const pendingIHaveRef = this._pendingIHave.get(hash);
@@ -650,7 +873,7 @@ let SharedLog = class SharedLog extends Program {
650
873
  }
651
874
  }
652
875
  await this.rpc.send(new ResponseIPrune({ hashes: hasAndIsLeader }), {
653
- mode: new SilentDelivery({ to: [context.from], redundancy: 1 })
876
+ mode: new SilentDelivery({ to: [context.from], redundancy: 1 }),
654
877
  });
655
878
  }
656
879
  else if (msg instanceof ResponseIPrune) {
@@ -671,7 +894,7 @@ let SharedLog = class SharedLog extends Program {
671
894
  }
672
895
  inverted.add(hash);
673
896
  }
674
- else if (!this.log.has(hash)) {
897
+ else if (!(await this.log.has(hash))) {
675
898
  this.syncInFlightQueue.set(hash, []);
676
899
  requestHashes.push(hash); // request immediately (first time we have seen this hash)
677
900
  }
@@ -687,22 +910,25 @@ let SharedLog = class SharedLog extends Program {
687
910
  let p = Promise.resolve();
688
911
  for (const message of messages) {
689
912
  p = p.then(() => this.rpc.send(message, {
690
- mode: new SilentDelivery({ to: [context.from], redundancy: 1 })
913
+ mode: new SilentDelivery({ to: [context.from], redundancy: 1 }),
691
914
  })); // push in series, if one fails, then we should just stop
692
915
  }
693
916
  }
694
917
  else if (msg instanceof BlocksMessage) {
695
918
  await this.remoteBlocks.onMessage(msg.message);
696
919
  }
697
- else if (msg instanceof RequestRoleMessage) {
920
+ else if (msg instanceof RequestReplicationInfoMessage) {
698
921
  if (context.from.equals(this.node.identity.publicKey)) {
699
922
  return;
700
923
  }
701
- await this.rpc.send(new ResponseRoleMessage({ role: this.role }), {
702
- mode: new SilentDelivery({ to: [context.from], redundancy: 1 })
924
+ await this.rpc.send(new ResponseReplicationInfoMessage({
925
+ segments: (await this.getMyReplicationSegments()).map((x) => x.toReplicationRange()),
926
+ }), {
927
+ mode: new SilentDelivery({ to: [context.from], redundancy: 1 }),
703
928
  });
704
929
  }
705
- else if (msg instanceof ResponseRoleMessage) {
930
+ else if (msg instanceof ResponseReplicationInfoMessage ||
931
+ msg instanceof StartedReplicating) {
706
932
  if (context.from.equals(this.node.identity.publicKey)) {
707
933
  return;
708
934
  }
@@ -710,7 +936,7 @@ let SharedLog = class SharedLog extends Program {
710
936
  // but we don't know them as "subscribers" yet. i.e. they are not online
711
937
  this.waitFor(context.from, {
712
938
  signal: this._closeController.signal,
713
- timeout: this.waitForReplicatorTimeout
939
+ timeout: this.waitForReplicatorTimeout,
714
940
  })
715
941
  .then(async () => {
716
942
  // peer should not be online (for us)
@@ -719,7 +945,18 @@ let SharedLog = class SharedLog extends Program {
719
945
  return;
720
946
  }
721
947
  this.latestRoleMessages.set(context.from.hashcode(), context.timestamp);
722
- await this.modifyReplicators(msg.role, context.from);
948
+ if (msg instanceof ResponseReplicationInfoMessage) {
949
+ await this.removeReplicator(context.from);
950
+ }
951
+ let addedOnce = false;
952
+ for (const segment of msg.segments) {
953
+ const added = await this.addReplicationRange(segment.toReplicationRangeIndexable(context.from), context.from);
954
+ if (typeof added === "boolean") {
955
+ addedOnce = addedOnce || added;
956
+ }
957
+ }
958
+ addedOnce && (await this.distribute());
959
+ /* await this._modifyReplicators(msg.role, context.from!); */
723
960
  })
724
961
  .catch((e) => {
725
962
  if (e instanceof AbortError) {
@@ -728,9 +965,16 @@ let SharedLog = class SharedLog extends Program {
728
965
  if (e instanceof NotStartedError) {
729
966
  return;
730
967
  }
731
- logger.error("Failed to find peer who updated their role: " + e?.message);
968
+ logger.error("Failed to find peer who updated replication settings: " +
969
+ e?.message);
732
970
  });
733
971
  }
972
+ else if (msg instanceof StoppedReplicating) {
973
+ if (context.from.equals(this.node.identity.publicKey)) {
974
+ return;
975
+ }
976
+ await this.removeReplicationRange(msg.segmentIds, context.from);
977
+ }
734
978
  else {
735
979
  throw new Error("Unexpected message");
736
980
  }
@@ -750,24 +994,55 @@ let SharedLog = class SharedLog extends Program {
750
994
  logger.error(e);
751
995
  }
752
996
  }
753
- getReplicatorsSorted() {
754
- return this._sortedPeersCache;
997
+ async getMyReplicationSegments() {
998
+ const ranges = await this.replicationIndex.query(new SearchRequest({
999
+ query: [
1000
+ new StringMatch({
1001
+ key: "hash",
1002
+ value: this.node.identity.publicKey.hashcode(),
1003
+ }),
1004
+ ],
1005
+ fetch: 0xffffffff,
1006
+ }));
1007
+ return ranges.results.map((x) => x.value);
1008
+ }
1009
+ async getTotalParticipation() {
1010
+ // sum all of my replicator rects
1011
+ return (await this.getMyReplicationSegments()).reduce((acc, { widthNormalized }) => acc + widthNormalized, 0);
1012
+ }
1013
+ get replicationIndex() {
1014
+ if (!this._replicationRangeIndex) {
1015
+ throw new Error("Not open");
1016
+ }
1017
+ return this._replicationRangeIndex;
1018
+ }
1019
+ /**
1020
+ * TODO improve efficiency
1021
+ */
1022
+ async getReplicators() {
1023
+ let set = new Set();
1024
+ const results = await this.replicationIndex.query(new SearchRequest({ fetch: 0xfffffff }), { reference: true, shape: { hash: true } });
1025
+ results.results.forEach((result) => {
1026
+ set.add(result.value.hash);
1027
+ });
1028
+ return set;
755
1029
  }
756
1030
  async waitForReplicator(...keys) {
757
- const check = () => {
1031
+ const check = async () => {
758
1032
  for (const k of keys) {
759
- const rect = this.getReplicatorsSorted()
760
- ?.toArray()
761
- ?.find((x) => x.publicKey.equals(k));
1033
+ const rects = await this.replicationIndex?.query(new SearchRequest({
1034
+ query: [new StringMatch({ key: "hash", value: k.hashcode() })],
1035
+ }), { reference: true });
1036
+ const rect = await rects.results[0]?.value;
762
1037
  if (!rect ||
763
- !isMatured(rect.role, +new Date(), this.getDefaultMinRoleAge())) {
1038
+ !isMatured(rect, +new Date(), await this.getDefaultMinRoleAge())) {
764
1039
  return false;
765
1040
  }
766
1041
  }
767
1042
  return true;
768
1043
  };
769
1044
  return waitFor(() => check(), {
770
- signal: this._closeController.signal
1045
+ signal: this._closeController.signal,
771
1046
  }).catch((e) => {
772
1047
  if (e instanceof AbortError) {
773
1048
  // ignore error
@@ -780,36 +1055,33 @@ let SharedLog = class SharedLog extends Program {
780
1055
  const isLeader = (await this.findLeaders(slot, numberOfLeaders, options)).find((l) => l === this.node.identity.publicKey.hashcode());
781
1056
  return !!isLeader;
782
1057
  }
783
- getReplicationOffset() {
784
- return hashToUniformNumber(this.node.identity.publicKey.bytes);
785
- }
786
1058
  async waitForIsLeader(slot, numberOfLeaders, timeout = this.waitForReplicatorTimeout) {
787
- return new Promise((res, rej) => {
1059
+ return new Promise((resolve, reject) => {
788
1060
  const removeListeners = () => {
789
- this.events.removeEventListener("role", roleListener);
1061
+ this.events.removeEventListener("replication:change", roleListener);
790
1062
  this._closeController.signal.addEventListener("abort", abortListener);
791
1063
  };
792
1064
  const abortListener = () => {
793
1065
  removeListeners();
794
1066
  clearTimeout(timer);
795
- res(false);
1067
+ resolve(false);
796
1068
  };
797
1069
  const timer = setTimeout(() => {
798
1070
  removeListeners();
799
- res(false);
1071
+ resolve(false);
800
1072
  }, timeout);
801
1073
  const check = () => this.findLeaders(slot, numberOfLeaders).then((leaders) => {
802
1074
  const isLeader = leaders.find((l) => l === this.node.identity.publicKey.hashcode());
803
1075
  if (isLeader) {
804
1076
  removeListeners();
805
1077
  clearTimeout(timer);
806
- res(leaders);
1078
+ resolve(leaders);
807
1079
  }
808
1080
  });
809
1081
  const roleListener = () => {
810
1082
  check();
811
1083
  };
812
- this.events.addEventListener("role", roleListener);
1084
+ this.events.addEventListener("replication:change", roleListener); // TODO replication:change event ?
813
1085
  this._closeController.signal.addEventListener("abort", abortListener);
814
1086
  check();
815
1087
  });
@@ -829,179 +1101,51 @@ let SharedLog = class SharedLog extends Program {
829
1101
  const cursor = hashToUniformNumber(seed); // bounded between 0 and 1
830
1102
  return this.findLeadersFromUniformNumber(cursor, numberOfLeaders, options);
831
1103
  }
832
- getDefaultMinRoleAge() {
1104
+ async getDefaultMinRoleAge() {
1105
+ if ((await this.isReplicating()) === false) {
1106
+ return 0;
1107
+ }
833
1108
  const now = +new Date();
834
- const replLength = this.getReplicatorsSorted().length;
1109
+ const replLength = await this.replicationIndex.getSize();
835
1110
  const diffToOldest = replLength > 1 ? now - this.oldestOpenTime - 1 : Number.MAX_SAFE_INTEGER;
836
- return Math.min(this.timeUntilRoleMaturity, diffToOldest, (this.timeUntilRoleMaturity * Math.log(replLength)) / 3); // / 3 so that if 2 replicators and timeUntilRoleMaturity = 1e4 the result will be 1
1111
+ return Math.min(this.timeUntilRoleMaturity, diffToOldest, Math.round((this.timeUntilRoleMaturity * Math.log(replLength + 1)) / 3)); // / 3 so that if 2 replicators and timeUntilRoleMaturity = 1e4 the result will be 1
837
1112
  }
838
- findLeadersFromUniformNumber(cursor, numberOfLeaders, options) {
839
- const roleAge = options?.roleAge ?? this.getDefaultMinRoleAge(); // TODO -500 as is added so that i f someone else is just as new as us, then we treat them as mature as us. without -500 we might be slower syncing if two nodes starts almost at the same time
840
- const sampes = getSamples(cursor, this.getReplicatorsSorted(), numberOfLeaders, roleAge, this.role instanceof Replicator && this.role.factor === 0
841
- ? this.node.identity.publicKey.hashcode()
842
- : undefined);
843
- return sampes;
1113
+ async findLeadersFromUniformNumber(cursor, numberOfLeaders, options) {
1114
+ const roleAge = options?.roleAge ?? (await this.getDefaultMinRoleAge()); // TODO -500 as is added so that i f someone else is just as new as us, then we treat them as mature as us. without -500 we might be slower syncing if two nodes starts almost at the same time
1115
+ const samples = await getSamples(cursor, this.replicationIndex, numberOfLeaders, roleAge);
1116
+ return samples;
844
1117
  }
845
1118
  /**
846
1119
  *
847
1120
  * @returns groups where at least one in any group will have the entry you are looking for
848
1121
  */
849
- getReplicatorUnion(roleAge = this.getDefaultMinRoleAge()) {
1122
+ async getReplicatorUnion(roleAge) {
1123
+ roleAge = roleAge ?? (await this.getDefaultMinRoleAge());
850
1124
  if (this.closed === true) {
851
1125
  throw new Error("Closed");
852
1126
  }
853
1127
  // Total replication "width"
854
- const width = 1; //this.getParticipationSum(roleAge);
1128
+ const width = 1;
855
1129
  // How much width you need to "query" to
856
- const peers = this.getReplicatorsSorted(); // TODO types
857
- const minReplicas = Math.min(peers.length, this.replicas.min.getValue(this));
1130
+ const peers = this.replicationIndex; // TODO types
1131
+ const minReplicas = Math.min(await peers.getSize(), this.replicas.min.getValue(this));
858
1132
  // If min replicas = 2
859
1133
  // then we need to make sure we cover 0.5 of the total 'width' of the replication space
860
1134
  // to make sure we reach sufficient amount of nodes such that at least one one has
861
1135
  // the entry we are looking for
862
1136
  const coveringWidth = width / minReplicas;
863
- const set = getCoverSet(coveringWidth, peers, roleAge, this.role instanceof Replicator ? this.node.identity.publicKey : undefined);
1137
+ const set = await getCoverSet(coveringWidth, peers, roleAge, this.node.identity.publicKey);
864
1138
  // add all in flight
865
1139
  for (const [key, _] of this.syncInFlight) {
866
1140
  set.add(key);
867
1141
  }
868
1142
  return [...set];
869
1143
  }
870
- async replicator(entry, options) {
1144
+ async isReplicator(entry, options) {
871
1145
  return this.isLeader(entry.gid, decodeReplicas(entry).getValue(this), options);
872
1146
  }
873
- onRoleChange(role, publicKey) {
874
- if (this.closed) {
875
- return;
876
- }
877
- this.distribute();
878
- if (role instanceof Replicator) {
879
- const timer = setTimeout(async () => {
880
- this._closeController.signal.removeEventListener("abort", listener);
881
- await this.rebalanceParticipationDebounced?.();
882
- this.distribute();
883
- }, this.getDefaultMinRoleAge() + 100);
884
- const listener = () => {
885
- clearTimeout(timer);
886
- this._closeController.signal.removeEventListener("abort", listener);
887
- };
888
- this._closeController.signal.addEventListener("abort", listener);
889
- }
890
- this.events.dispatchEvent(new CustomEvent("role", {
891
- detail: { publicKey, role }
892
- }));
893
- }
894
- async modifyReplicators(role, publicKey) {
895
- const update = await this._modifyReplicators(role, publicKey);
896
- if (update.changed !== "none") {
897
- if (update.changed === "added" || update.changed === "removed") {
898
- this.setupRebalanceDebounceFunction();
899
- }
900
- await this.rebalanceParticipationDebounced?.(); /* await this.rebalanceParticipation(false); */
901
- if (update.changed === "added") {
902
- // TODO this message can be redudant, only send this when necessary (see conditions when rebalanceParticipation sends messages)
903
- await this.rpc.send(new ResponseRoleMessage({ role: this._role }), {
904
- mode: new SilentDelivery({
905
- to: [publicKey.hashcode()],
906
- redundancy: 1
907
- }),
908
- priority: 1
909
- });
910
- }
911
- this.onRoleChange(role, publicKey);
912
- return true;
913
- }
914
- return false;
915
- }
916
- async _modifyReplicators(role, publicKey) {
917
- // TODO can this call create race condition? _modifyReplicators might have to be queued
918
- // TODO should we remove replicators if they are already added?
919
- if (role instanceof Replicator &&
920
- this._canReplicate &&
921
- !(await this._canReplicate(publicKey, role))) {
922
- return { changed: "none" };
923
- }
924
- const sortedPeer = this._sortedPeersCache;
925
- if (!sortedPeer) {
926
- if (this.closed === false) {
927
- throw new Error("Unexpected, sortedPeersCache is undefined");
928
- }
929
- return { changed: "none" };
930
- }
931
- if (role instanceof Replicator) {
932
- // TODO use Set + list for fast lookup
933
- // check also that peer is online
934
- const isOnline = this.node.identity.publicKey.equals(publicKey) ||
935
- (await this.getReady()).has(publicKey.hashcode());
936
- if (!isOnline) {
937
- // TODO should we remove replicators if they are already added?
938
- return { changed: "none" };
939
- }
940
- this.oldestOpenTime = Math.min(this.oldestOpenTime, Number(role.timestamp));
941
- // insert or if already there do nothing
942
- const rect = {
943
- publicKey,
944
- role
945
- };
946
- let currentNode = sortedPeer.head;
947
- if (!currentNode) {
948
- sortedPeer.push(rect);
949
- this._totalParticipation += rect.role.factor;
950
- return { changed: "added" };
951
- }
952
- else {
953
- while (currentNode) {
954
- if (currentNode.value.publicKey.equals(publicKey)) {
955
- // update the value
956
- // rect.timestamp = currentNode.value.timestamp;
957
- const prev = currentNode.value;
958
- currentNode.value = rect;
959
- this._totalParticipation += rect.role.factor;
960
- this._totalParticipation -= prev.role.factor;
961
- // TODO change detection and only do change stuff if diff?
962
- return { prev: prev.role, changed: "updated" };
963
- }
964
- if (role.offset > currentNode.value.role.offset) {
965
- const next = currentNode?.next;
966
- if (next) {
967
- currentNode = next;
968
- continue;
969
- }
970
- else {
971
- break;
972
- }
973
- }
974
- else {
975
- currentNode = currentNode.prev;
976
- break;
977
- }
978
- }
979
- const prev = currentNode;
980
- if (!prev?.next?.value.publicKey.equals(publicKey)) {
981
- this._totalParticipation += rect.role.factor;
982
- _insertAfter(sortedPeer, prev || undefined, rect);
983
- }
984
- else {
985
- throw new Error("Unexpected");
986
- }
987
- return { changed: "added" };
988
- }
989
- }
990
- else {
991
- let currentNode = sortedPeer.head;
992
- while (currentNode) {
993
- if (currentNode.value.publicKey.equals(publicKey)) {
994
- sortedPeer.removeNode(currentNode);
995
- this._totalParticipation -= currentNode.value.role.factor;
996
- return { prev: currentNode.value.role, changed: "removed" };
997
- }
998
- currentNode = currentNode.next;
999
- }
1000
- return { changed: "none" };
1001
- }
1002
- }
1003
- async handleSubscriptionChange(publicKey, changes, subscribed) {
1004
- for (const topic of changes) {
1147
+ async handleSubscriptionChange(publicKey, topics, subscribed) {
1148
+ for (const topic of topics) {
1005
1149
  if (this.log.idString !== topic) {
1006
1150
  continue;
1007
1151
  }
@@ -1026,16 +1170,19 @@ let SharedLog = class SharedLog extends Program {
1026
1170
  this.syncInFlightQueueInverted.delete(publicKey.hashcode());
1027
1171
  }
1028
1172
  if (subscribed) {
1029
- if (this.role instanceof Replicator) {
1173
+ const replicationSegments = await this.getMyReplicationSegments();
1174
+ if (replicationSegments.length > 0) {
1030
1175
  this.rpc
1031
- .send(new ResponseRoleMessage({ role: this._role }), {
1032
- mode: new SilentDelivery({ redundancy: 1, to: [publicKey] })
1176
+ .send(new ResponseReplicationInfoMessage({
1177
+ segments: replicationSegments.map((x) => x.toReplicationRange()),
1178
+ }), {
1179
+ mode: new SilentDelivery({ redundancy: 1, to: [publicKey] }),
1033
1180
  })
1034
1181
  .catch((e) => logger.error(e.toString()));
1035
1182
  }
1036
1183
  }
1037
1184
  else {
1038
- await this.modifyReplicators(new Observer(), publicKey);
1185
+ await this.removeReplicator(publicKey);
1039
1186
  }
1040
1187
  }
1041
1188
  prune(entries, options) {
@@ -1043,7 +1190,7 @@ let SharedLog = class SharedLog extends Program {
1043
1190
  return entries.map((x) => {
1044
1191
  this._gidPeersHistory.delete(x.meta.gid);
1045
1192
  return this.log.remove(x, {
1046
- recursively: true
1193
+ recursively: true,
1047
1194
  });
1048
1195
  });
1049
1196
  }
@@ -1068,7 +1215,7 @@ let SharedLog = class SharedLog extends Program {
1068
1215
  const clear = () => {
1069
1216
  //pendingPrev?.clear();
1070
1217
  const pending = this._pendingDeletes.get(entry.hash);
1071
- if (pending?.promise == deferredPromise) {
1218
+ if (pending?.promise === deferredPromise) {
1072
1219
  this._pendingDeletes.delete(entry.hash);
1073
1220
  }
1074
1221
  clearTimeout(timeout);
@@ -1082,7 +1229,7 @@ let SharedLog = class SharedLog extends Program {
1082
1229
  deferredPromise.reject(e);
1083
1230
  };
1084
1231
  const timeout = setTimeout(() => {
1085
- reject(new Error("Timeout for checked pruning"));
1232
+ reject(new Error("Timeout for checked pruning: Closed: " + this.closed));
1086
1233
  }, options?.timeout ?? 10 * 1000);
1087
1234
  this._pendingDeletes.set(entry.hash, {
1088
1235
  promise: deferredPromise,
@@ -1095,7 +1242,7 @@ let SharedLog = class SharedLog extends Program {
1095
1242
  const minMinReplicasValue = this.replicas.max
1096
1243
  ? Math.min(minReplicasValue, this.replicas.max.getValue(this))
1097
1244
  : minReplicasValue;
1098
- const leaders = await this.findLeaders(entry.gid, minMinReplicasValue);
1245
+ const leaders = await this.findLeaders(entry.meta.gid, minMinReplicasValue);
1099
1246
  if (leaders.find((x) => x === this.node.identity.publicKey.hashcode())) {
1100
1247
  reject(new Error("Failed to delete, is leader"));
1101
1248
  return;
@@ -1103,10 +1250,11 @@ let SharedLog = class SharedLog extends Program {
1103
1250
  if (leaders.find((x) => x === publicKeyHash)) {
1104
1251
  existCounter.add(publicKeyHash);
1105
1252
  if (minMinReplicasValue <= existCounter.size) {
1253
+ clear();
1106
1254
  this._gidPeersHistory.delete(entry.meta.gid);
1107
1255
  this.log
1108
1256
  .remove(entry, {
1109
- recursively: true
1257
+ recursively: true,
1110
1258
  })
1111
1259
  .then(() => {
1112
1260
  resolve();
@@ -1116,27 +1264,27 @@ let SharedLog = class SharedLog extends Program {
1116
1264
  });
1117
1265
  }
1118
1266
  }
1119
- }
1267
+ },
1120
1268
  });
1121
1269
  promises.push(deferredPromise.promise);
1122
1270
  }
1123
- if (filteredEntries.length == 0) {
1124
- return [];
1271
+ if (filteredEntries.length === 0) {
1272
+ return promises;
1125
1273
  }
1126
1274
  this.rpc.send(new RequestIPrune({ hashes: filteredEntries.map((x) => x.hash) }));
1127
1275
  const onNewPeer = async (e) => {
1128
- if (e.detail.role instanceof Replicator) {
1276
+ if (e.detail.publicKey.equals(this.node.identity.publicKey) === false) {
1129
1277
  await this.rpc.send(new RequestIPrune({ hashes: filteredEntries.map((x) => x.hash) }), {
1130
1278
  mode: new SilentDelivery({
1131
1279
  to: [e.detail.publicKey.hashcode()],
1132
- redundancy: 1
1133
- })
1280
+ redundancy: 1,
1281
+ }),
1134
1282
  });
1135
1283
  }
1136
1284
  };
1137
1285
  // check joining peers
1138
- this.events.addEventListener("role", onNewPeer);
1139
- Promise.allSettled(promises).finally(() => this.events.removeEventListener("role", onNewPeer));
1286
+ this.events.addEventListener("replicator:join", onNewPeer);
1287
+ Promise.allSettled(promises).finally(() => this.events.removeEventListener("replicator:join", onNewPeer));
1140
1288
  return promises;
1141
1289
  }
1142
1290
  async distribute() {
@@ -1151,7 +1299,7 @@ let SharedLog = class SharedLog extends Program {
1151
1299
  (this.distributeQueue = new PQueue({ concurrency: 1 }));
1152
1300
  return queue
1153
1301
  .add(() => delay(Math.min(this.log.length, this.distributionDebounceTime), {
1154
- signal: this._closeController.signal
1302
+ signal: this._closeController.signal,
1155
1303
  }).then(() => this._distribute()))
1156
1304
  .catch(() => { }); // catch ignore delay abort errror
1157
1305
  }
@@ -1165,7 +1313,7 @@ let SharedLog = class SharedLog extends Program {
1165
1313
  }
1166
1314
  const changed = false;
1167
1315
  await this.log.trim();
1168
- const heads = await this.log.getHeads();
1316
+ const heads = await this.log.getHeads().all();
1169
1317
  const groupedByGid = await groupByGid(heads);
1170
1318
  const uncheckedDeliver = new Map();
1171
1319
  const allEntriesToDelete = [];
@@ -1177,13 +1325,12 @@ let SharedLog = class SharedLog extends Program {
1177
1325
  continue; // TODO maybe close store?
1178
1326
  }
1179
1327
  const oldPeersSet = this._gidPeersHistory.get(gid);
1180
- const currentPeers = await this.findLeaders(gid, maxReplicas(this, entries) // pick max replication policy of all entries, so all information is treated equally important as the most important
1181
- );
1328
+ const currentPeers = await this.findLeaders(gid, maxReplicas(this, entries));
1182
1329
  const isLeader = currentPeers.find((x) => x === this.node.identity.publicKey.hashcode());
1183
1330
  const currentPeersSet = new Set(currentPeers);
1184
1331
  this._gidPeersHistory.set(gid, currentPeersSet);
1185
1332
  for (const currentPeer of currentPeers) {
1186
- if (currentPeer == this.node.identity.publicKey.hashcode()) {
1333
+ if (currentPeer === this.node.identity.publicKey.hashcode()) {
1187
1334
  continue;
1188
1335
  }
1189
1336
  if (!oldPeersSet?.has(currentPeer)) {
@@ -1201,9 +1348,7 @@ let SharedLog = class SharedLog extends Program {
1201
1348
  if (currentPeers.length > 0) {
1202
1349
  // If we are observer, never prune locally created entries, since we dont really know who can store them
1203
1350
  // if we are replicator, we will always persist entries that we need to so filtering on createdLocally will not make a difference
1204
- let entriesToDelete = this._role instanceof Observer
1205
- ? entries.filter((e) => !e.createdLocally)
1206
- : entries;
1351
+ let entriesToDelete = entries;
1207
1352
  if (this.sync) {
1208
1353
  entriesToDelete = entriesToDelete.filter((entry) => this.sync(entry) === false);
1209
1354
  }
@@ -1212,20 +1357,20 @@ let SharedLog = class SharedLog extends Program {
1212
1357
  }
1213
1358
  else {
1214
1359
  for (const entry of entries) {
1215
- this._pendingDeletes
1360
+ await this._pendingDeletes
1216
1361
  .get(entry.hash)
1217
- ?.reject(new Error("Failed to delete, is leader again"));
1362
+ ?.reject(new Error("Failed to delete, is leader again. Closed: " + this.closed));
1218
1363
  }
1219
1364
  }
1220
1365
  }
1221
1366
  for (const [target, entries] of uncheckedDeliver) {
1222
1367
  this.rpc.send(new RequestMaybeSync({ hashes: entries.map((x) => x.hash) }), {
1223
- mode: new SilentDelivery({ to: [target], redundancy: 1 })
1368
+ mode: new SilentDelivery({ to: [target], redundancy: 1 }),
1224
1369
  });
1225
1370
  }
1226
1371
  if (allEntriesToDelete.length > 0) {
1227
1372
  Promise.allSettled(this.prune(allEntriesToDelete)).catch((e) => {
1228
- logger.error(e.toString());
1373
+ logger.info(e.toString());
1229
1374
  });
1230
1375
  }
1231
1376
  return changed;
@@ -1243,16 +1388,16 @@ let SharedLog = class SharedLog extends Program {
1243
1388
  }
1244
1389
  }
1245
1390
  await this.rpc.send(new ResponseMaybeSync({
1246
- hashes: hashes
1391
+ hashes: hashes,
1247
1392
  }), {
1248
- mode: new SilentDelivery({ to, redundancy: 1 })
1393
+ mode: new SilentDelivery({ to, redundancy: 1 }),
1249
1394
  });
1250
1395
  }
1251
1396
  async _onUnsubscription(evt) {
1252
1397
  logger.debug(`Peer disconnected '${evt.detail.from.hashcode()}' from '${JSON.stringify(evt.detail.unsubscriptions.map((x) => x))}'`);
1253
1398
  this.latestRoleMessages.delete(evt.detail.from.hashcode());
1254
- this.events.dispatchEvent(new CustomEvent("role", {
1255
- detail: { publicKey: evt.detail.from, role: new Observer() }
1399
+ this.events.dispatchEvent(new CustomEvent("replicator:leave", {
1400
+ detail: { publicKey: evt.detail.from },
1256
1401
  }));
1257
1402
  return this.handleSubscriptionChange(evt.detail.from, evt.detail.unsubscriptions, false);
1258
1403
  }
@@ -1261,8 +1406,6 @@ let SharedLog = class SharedLog extends Program {
1261
1406
  this.remoteBlocks.onReachable(evt.detail.from);
1262
1407
  return this.handleSubscriptionChange(evt.detail.from, evt.detail.subscriptions, true);
1263
1408
  }
1264
- replicationController;
1265
- history;
1266
1409
  async addToHistory(usedMemory, factor) {
1267
1410
  (this.history || (this.history = [])).push({ usedMemory, factor });
1268
1411
  // Keep only the last N entries in the history array (you can adjust N based on your needs)
@@ -1294,34 +1437,43 @@ let SharedLog = class SharedLog extends Program {
1294
1437
  return false;
1295
1438
  }
1296
1439
  // The role is fixed (no changes depending on memory usage or peer count etc)
1297
- if (this._roleConfig instanceof Role) {
1440
+ if (!this._replicationSettings) {
1298
1441
  return false;
1299
1442
  }
1300
- // TODO second condition: what if the current role is Observer?
1301
- if (this._roleConfig.type == "replicator" &&
1302
- this._role instanceof Replicator) {
1303
- const peers = this.getReplicatorsSorted();
1443
+ if (isAdaptiveReplicatorOption(this._replicationSettings)) {
1444
+ const peers = this.replicationIndex;
1304
1445
  const usedMemory = await this.getMemoryUsage();
1446
+ let dynamicRange = await this.getDynamicRange();
1447
+ if (!dynamicRange) {
1448
+ return; // not allowed to replicate
1449
+ }
1450
+ const peersSize = (await peers.getSize()) || 1;
1305
1451
  const newFactor = this.replicationController.step({
1306
1452
  memoryUsage: usedMemory,
1307
- currentFactor: this._role.factor,
1453
+ currentFactor: dynamicRange.widthNormalized,
1308
1454
  totalFactor: this._totalParticipation,
1309
- peerCount: peers?.length || 1,
1310
- cpuUsage: this.cpuUsage?.value()
1455
+ peerCount: peersSize,
1456
+ cpuUsage: this.cpuUsage?.value(),
1311
1457
  });
1312
- const relativeDifference = Math.abs(this._role.factor - newFactor) / this._role.factor;
1458
+ const relativeDifference = Math.abs(dynamicRange.widthNormalized - newFactor) /
1459
+ dynamicRange.widthNormalized;
1313
1460
  if (relativeDifference > 0.0001) {
1314
- const newRole = new Replicator({
1315
- factor: newFactor,
1316
- timestamp: this._role.timestamp,
1317
- offset: hashToUniformNumber(this.node.identity.publicKey.bytes)
1461
+ // TODO can not reuse old range, since it will (potentially) affect the index because of sideeffects
1462
+ dynamicRange = new ReplicationRangeIndexable({
1463
+ offset: hashToUniformNumber(this.node.identity.publicKey.bytes),
1464
+ length: newFactor,
1465
+ publicKeyHash: dynamicRange.hash,
1466
+ id: dynamicRange.id,
1467
+ replicationIntent: dynamicRange.replicationIntent,
1468
+ timestamp: dynamicRange.timestamp,
1318
1469
  });
1319
- const canReplicate = !this._canReplicate ||
1320
- (await this._canReplicate(this.node.identity.publicKey, newRole));
1470
+ const canReplicate = !this._isTrustedReplicator ||
1471
+ (await this._isTrustedReplicator(this.node.identity.publicKey));
1321
1472
  if (!canReplicate) {
1322
1473
  return false;
1323
1474
  }
1324
- await this._updateRole(newRole, onRoleChange);
1475
+ await this.startAnnounceReplicating(dynamicRange);
1476
+ /* await this._updateRole(newRole, onRoleChange); */
1325
1477
  this.rebalanceParticipationDebounced?.();
1326
1478
  return true;
1327
1479
  }
@@ -1332,6 +1484,39 @@ let SharedLog = class SharedLog extends Program {
1332
1484
  }
1333
1485
  return false;
1334
1486
  }
1487
+ async getDynamicRange() {
1488
+ let range = (await this.replicationIndex.query(new SearchRequest({
1489
+ query: [
1490
+ new StringMatch({
1491
+ key: "hash",
1492
+ value: this.node.identity.publicKey.hashcode(),
1493
+ }),
1494
+ new IntegerCompare({
1495
+ key: "replicationIntent",
1496
+ value: ReplicationIntent.Automatic,
1497
+ compare: "eq",
1498
+ }),
1499
+ ],
1500
+ fetch: 1,
1501
+ })))?.results[0]?.value;
1502
+ if (!range) {
1503
+ let seed = Math.random();
1504
+ range = new ReplicationRangeIndexable({
1505
+ offset: seed,
1506
+ length: 0,
1507
+ publicKeyHash: this.node.identity.publicKey.hashcode(),
1508
+ replicationIntent: ReplicationIntent.Automatic,
1509
+ timestamp: BigInt(+new Date()),
1510
+ id: sha256Sync(this.node.identity.publicKey.bytes),
1511
+ });
1512
+ const added = await this.addReplicationRange(range, this.node.identity.publicKey);
1513
+ if (!added) {
1514
+ logger.warn("Not allowed to replicate by canReplicate");
1515
+ return;
1516
+ }
1517
+ }
1518
+ return range;
1519
+ }
1335
1520
  clearSyncProcess(hash) {
1336
1521
  const inflight = this.syncInFlightQueue.get(hash);
1337
1522
  if (inflight) {
@@ -1372,19 +1557,4 @@ SharedLog = __decorate([
1372
1557
  __metadata("design:paramtypes", [Object])
1373
1558
  ], SharedLog);
1374
1559
  export { SharedLog };
1375
- function _insertAfter(self, node, value) {
1376
- const inserted = !node
1377
- ? new yallist.Node(value, null, self.head, self)
1378
- : new yallist.Node(value, node, node.next, self);
1379
- // is tail
1380
- if (inserted.next === null) {
1381
- self.tail = inserted;
1382
- }
1383
- // is head
1384
- if (inserted.prev === null) {
1385
- self.head = inserted;
1386
- }
1387
- self.length++;
1388
- return inserted;
1389
- }
1390
1560
  //# sourceMappingURL=index.js.map