autobee 2.0.0-rc.31 → 2.0.0-rc.32

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/index.js CHANGED
@@ -129,12 +129,10 @@ module.exports = class Autobee extends ReadyResource {
129
129
  this._hasApply = !!handlers.apply
130
130
  this._hasUpdate = !!handlers.update
131
131
  this._needsUpdate = false
132
- this._ackRequired = false
133
132
  this._approvalCheck = true
134
133
  this._approvedPending = new Map()
135
134
  this._approvalRequests = []
136
135
  this._prefetchingApprovals = false
137
- this._ackedHeads = new Map()
138
136
  this._updateLocalCore = null
139
137
  this._host = new ApplyCalls(this)
140
138
  this._notifyHandler = null
@@ -385,7 +383,6 @@ module.exports = class Autobee extends ReadyResource {
385
383
  }
386
384
 
387
385
  this.local.setEncryption(this._getEncryptionProvider())
388
- this.local.setActive(true)
389
386
 
390
387
  this.writers = new ActiveWriters(this)
391
388
 
@@ -554,8 +551,6 @@ module.exports = class Autobee extends ReadyResource {
554
551
  else if (head) await this._bootFromHead(head, bootCondition, wait)
555
552
  }
556
553
 
557
- this._ackRequired = false
558
-
559
554
  const changes = this._hasUpdate ? new UpdateChanges(this) : null
560
555
  if (changes) changes.track()
561
556
 
@@ -578,16 +573,7 @@ module.exports = class Autobee extends ReadyResource {
578
573
  break // revaluate conditions...
579
574
  }
580
575
 
581
- if (await this._bumpPendingWriters()) {
582
- this._needsUpdate = true
583
- continue
584
- }
585
-
586
- if (!this._appendAck()) break
587
- this._needsUpdate = true
588
- }
589
-
590
- if (!this._interrupting && (await this._appendApprovals())) {
576
+ if (!(await this._bumpPendingWriters())) break
591
577
  this._needsUpdate = true
592
578
  }
593
579
 
@@ -647,24 +633,44 @@ module.exports = class Autobee extends ReadyResource {
647
633
  // wakeup hints keep arriving asynchronously
648
634
  async _applyWakeupHints() {
649
635
  const hints = this._wakeup.flush()
650
- if (!hints.size) return hints
636
+ if (!hints.size) return []
651
637
 
652
638
  this.previousDrain = Date.now()
639
+ const results = await this._filterHints(hints)
653
640
 
654
- for (const [hex, length] of hints) {
655
- const key = b4a.from(hex, 'hex')
641
+ for (const { key, length } of results) {
656
642
  // wakeup() itself no-ops the add for an already-active writer, but still
657
643
  // needs to run so it can hint() the announced length - skipping it here
658
644
  // for active writers left gc with no hint to protect them
659
645
  await this.writers.wakeup(key, length)
660
646
  }
661
647
 
662
- return hints
648
+ return results
649
+ }
650
+
651
+ async _filterHints(hints) {
652
+ const results = []
653
+ const promises = []
654
+ for (const [hex, length] of hints) {
655
+ const key = b4a.from(hex, 'hex')
656
+ promises.push(this._filterTracked(key, length, results))
657
+ }
658
+ await Promise.all(promises)
659
+ return results
660
+ }
661
+
662
+ async _filterTracked(key, length, results) {
663
+ if (length) {
664
+ const info = await this.system.get(key)
665
+ // if we've seen this already, ignore
666
+ if (info && info.length >= length) return
667
+ }
668
+ results.push({ key, length })
663
669
  }
664
670
 
665
671
  async _flushWakeup() {
666
672
  const hints = await this._applyWakeupHints()
667
- if (!hints.size) return
673
+ if (!hints.length) return
668
674
 
669
675
  if (!this._ffEnabled) return
670
676
 
@@ -688,10 +694,9 @@ module.exports = class Autobee extends ReadyResource {
688
694
  async _readCandidateHeads(hints, timeout) {
689
695
  const promises = []
690
696
 
691
- for (const [hex, length] of hints) {
697
+ for (const { key, length } of hints) {
692
698
  if (length === 0) continue
693
- const key = b4a.from(hex, 'hex')
694
- promises.push(this._getOplog(key, length, timeout ? { timeout } : null))
699
+ promises.push(this._resolveOplogHint(key, length, timeout ? { timeout } : null))
695
700
  }
696
701
 
697
702
  const ops = await Promise.all(promises)
@@ -714,6 +719,22 @@ module.exports = class Autobee extends ReadyResource {
714
719
  return heads
715
720
  }
716
721
 
722
+ async _resolveOplogHint(key, length, opts) {
723
+ const core = this.openCore(key)
724
+
725
+ try {
726
+ await core.ready()
727
+ const max = Math.max(length, core.length)
728
+
729
+ const block = await this.readOplog(core, max, opts)
730
+ if (max >= core.length) return block // best one
731
+
732
+ return await this.readOplog(core, core.length, opts)
733
+ } finally {
734
+ await core.close()
735
+ }
736
+ }
737
+
717
738
  async _getOplog(key, length, opts = null) {
718
739
  const core = this.openCore(key)
719
740
 
@@ -987,51 +1008,6 @@ module.exports = class Autobee extends ReadyResource {
987
1008
  }
988
1009
  }
989
1010
 
990
- async _appendApprovals() {
991
- if (!this.writers.writable) return false
992
-
993
- const approvals = await this._collectApprovals()
994
- if (!approvals) return false
995
-
996
- const links = this.system.getLinks(this.local.key)
997
- const t = Math.max(this._now(), this.system.timestamp)
998
- this.writers.appendLocal(null, t, { start: 0, end: 0 }, links, false, null, approvals)
999
- return true
1000
- }
1001
-
1002
- // append a null value node to ack writer
1003
- _appendAck() {
1004
- if (!this._ackRequired) return false
1005
- if (!this.writers.writable) return false
1006
-
1007
- if (this.writers.localWriter.pending !== null) return false
1008
-
1009
- const links = this.system.getLinks(this.local.key)
1010
- const t = Math.max(this._now(), this.system.timestamp)
1011
-
1012
- let unlinked = false
1013
- for (const { key, length } of links) {
1014
- const hex = b4a.toString(key, 'hex')
1015
- const acked = this._ackedHeads.get(hex) || 0
1016
- if (length > acked) {
1017
- unlinked = true
1018
- break
1019
- }
1020
- }
1021
- if (!unlinked) {
1022
- this._ackRequired = false
1023
- return false
1024
- }
1025
-
1026
- for (const { key, length } of links) {
1027
- this._ackedHeads.set(b4a.toString(key, 'hex'), length)
1028
- }
1029
-
1030
- this.writers.appendLocal(null, t, { start: 0, end: 0 }, links, false, null)
1031
- this._ackRequired = false
1032
- return true
1033
- }
1034
-
1035
1011
  async _bumpPendingWriters({ local = false } = {}) {
1036
1012
  if (!local && this._catchupMigratedNodes !== null) {
1037
1013
  const updated = await this._bumpMigratedWriters()
@@ -1398,10 +1374,9 @@ module.exports = class Autobee extends ReadyResource {
1398
1374
 
1399
1375
  async _bootAttempt(head, bootCondition, reference) {
1400
1376
  // peek, so the hints are still there for the drain once we have booted
1401
- const candidates = await this._readCandidateHeads(
1402
- this._wakeup.hints,
1403
- FastForward.DEFAULT_TIMEOUT
1404
- )
1377
+ const result = await this._filterHints(this._wakeup.hints)
1378
+
1379
+ const candidates = await this._readCandidateHeads(result, FastForward.DEFAULT_TIMEOUT)
1405
1380
 
1406
1381
  const ff = await FastForward.fromHeads(this, [head, ...candidates], {
1407
1382
  force: true,
@@ -1,5 +1,4 @@
1
1
  const ID = require('hypercore-id-encoding')
2
- const b4a = require('b4a')
3
2
 
4
3
  class ApplyCalls {
5
4
  constructor(auto) {
@@ -44,10 +43,6 @@ class ApplyCalls {
44
43
 
45
44
  ackWriter(key) {
46
45
  if (typeof key === 'string') key = ID.decode(key)
47
- // acking an optimistic node means we owe it a linking node
48
- if (this.applying && this.applying[0].optimistic && !b4a.equals(key, this.auto.local.key)) {
49
- this.auto._ackRequired = true
50
- }
51
46
  return this.auto.system.ackWriter(key)
52
47
  }
53
48
 
@@ -9,10 +9,6 @@ const { LEGACY_AUTOBASE_VERSION } = require('./constants.js')
9
9
  const DEFAULT_OP_TIMEOUT = 5_000
10
10
  const MIN_FF_GAP = 32
11
11
 
12
- // views are only stamped on the last node of a flush, so a head may sit above
13
- // the most recent one - how far back we look for it
14
- const MAX_FLUSH_SEARCH = 64
15
-
16
12
  module.exports = class FastForward {
17
13
  constructor(auto, head, { timeout = DEFAULT_OP_TIMEOUT } = {}) {
18
14
  this.auto = auto
@@ -147,18 +143,9 @@ module.exports = class FastForward {
147
143
  const core = auto.openCore(head.key)
148
144
 
149
145
  try {
150
- let length = head.length
151
-
152
- for (let i = 0; i < MAX_FLUSH_SEARCH; i++) {
153
- const oplog = await auto.readOplog(core, length, opts)
154
- if (oplog === null) return null
155
- if (oplog.op.views) return oplog
156
-
157
- length = oplog.length - 1
158
- if (length <= 0) return null
159
- }
160
-
161
- return null
146
+ const oplog = await auto.readOplog(core, head.length, opts)
147
+ if (oplog === null) return null
148
+ return oplog.op.views ? oplog : null
162
149
  } finally {
163
150
  await core.close()
164
151
  }
package/lib/migrations.js CHANGED
@@ -223,7 +223,9 @@ async function inflateLegacyOplog(buf, core, seq, timeout) {
223
223
 
224
224
  fetches.push(m.digest.pointer ? core.get(seq - m.digest.pointer, { timeout }) : buf)
225
225
  fetches.push(
226
- m.checkpoint.pointer ? core.get(seq - m.checkpoint.system.checkpointer, { timeout }) : buf
226
+ m.checkpoint.system.checkpointer
227
+ ? core.get(seq - m.checkpoint.system.checkpointer, { timeout })
228
+ : buf
227
229
  )
228
230
 
229
231
  const [digestNode, checkpointNode] = await Promise.all(fetches)
package/lib/system.js CHANGED
@@ -71,12 +71,12 @@ module.exports = class Systembee {
71
71
  const added = info && info.isAdded !== false
72
72
  if (added && !info.isRemoved && length <= info.length && !hint && pending === -1) return
73
73
 
74
- this.update(key, length, -1, -1, false, true, false, false, -1, hint, pending)
74
+ this.update(key, length, -1, -1, false, true, false, -1, hint, pending)
75
75
  return
76
76
  }
77
77
 
78
78
  const w = isGenesis ? effective : -1
79
- this.update(key, length, w, effective, isGenesis, true, false, false, -1, hint, pending)
79
+ this.update(key, length, w, effective, isGenesis, true, false, -1, hint, pending)
80
80
  }
81
81
 
82
82
  async ackWriter(key, { length = 0 } = {}) {
@@ -84,7 +84,7 @@ module.exports = class Systembee {
84
84
  const info = await this.get(key, { unflushed: false })
85
85
  length = info ? info.length : 0
86
86
  }
87
- this.update(key, length, -1, -1, false, false, false, true, -1, null)
87
+ this.update(key, length, -1, -1, false, false, false, -1, null)
88
88
  }
89
89
 
90
90
  async removeWriter(key, { length = 0 } = {}) {
@@ -92,17 +92,24 @@ module.exports = class Systembee {
92
92
  const info = await this.get(key, { unflushed: true })
93
93
  length = info ? info.length : 0
94
94
  }
95
- this.update(key, length, -1, 0, false, false, true, false, -1, null)
95
+ this.update(key, length, -1, 0, false, false, true, -1, null)
96
96
  }
97
97
 
98
98
  addAnchor(key) {
99
- this.update(key, 0, -1, -1, false, true, false, false, -1, null)
99
+ this.update(key, 0, -1, -1, false, true, false, -1, null)
100
100
  }
101
101
 
102
102
  isGenesis() {
103
103
  return this.bee.head() === null || this.bee.head().length === 0
104
104
  }
105
105
 
106
+ isHead(key) {
107
+ for (const head of this.heads) {
108
+ if (b4a.equals(head.key, key)) return true
109
+ }
110
+ return false
111
+ }
112
+
106
113
  async boot(view, { timeout } = {}) {
107
114
  await this.bee.ready()
108
115
  this.bee.move(view)
@@ -184,18 +191,7 @@ module.exports = class Systembee {
184
191
 
185
192
  // resolved sort weight, re-stamped on every reapplication - this is what
186
193
  // topo reads back out of the changes stream
187
- this.update(
188
- node.key,
189
- node.length,
190
- node.weight,
191
- -1,
192
- false,
193
- false,
194
- false,
195
- false,
196
- node.timestamp,
197
- null
198
- )
194
+ this.update(node.key, node.length, node.weight, -1, false, false, false, node.timestamp, null)
199
195
  }
200
196
 
201
197
  async canApply(key, optimistic) {
@@ -236,14 +232,12 @@ module.exports = class Systembee {
236
232
  isGenesis: upd.maxWeight !== -1 && !!upd.isGenesis,
237
233
  length: upd.length,
238
234
  clock: 0,
239
- isAdded: upd.isAdded,
240
- isAcked: upd.isAcked
235
+ isAdded: upd.isAdded
241
236
  }
242
237
  }
243
238
 
244
239
  if (upd.isAdded) info.isAdded = true
245
240
  if (upd.isRemoved) info.isRemoved = true
246
- if (upd.isAcked) info.isAcked = true
247
241
  if (upd.maxWeight !== -1) {
248
242
  info.isGenesis = upd.isGenesis
249
243
  info.maxWeight = upd.maxWeight
@@ -284,7 +278,6 @@ module.exports = class Systembee {
284
278
  isGenesis,
285
279
  isAdded,
286
280
  isRemoved,
287
- isAcked,
288
281
  timestamp,
289
282
  grant,
290
283
  pending = -1
@@ -308,7 +301,6 @@ module.exports = class Systembee {
308
301
  isGenesis: false,
309
302
  isRemoved: false,
310
303
  isAdded: false,
311
- isAcked: false,
312
304
  timestamp: 0,
313
305
  grant: null,
314
306
  pending: -1
@@ -330,9 +322,6 @@ module.exports = class Systembee {
330
322
  upd.isRemoved = true
331
323
  upd.isAdded = false
332
324
  }
333
- if (isAcked) {
334
- upd.isAcked = true
335
- }
336
325
  if (length > upd.length) {
337
326
  upd.length = length
338
327
  }
package/lib/witness.js CHANGED
@@ -1,6 +1,5 @@
1
1
  const b4a = require('b4a')
2
2
  const encoding = require('./encoding.js')
3
- const { DEFAULT_OP_TIMEOUT } = require('./constants.js')
4
3
 
5
4
  exports.currentWeight = currentWeight
6
5
  exports.resolveWeight = resolveWeight
@@ -56,7 +55,7 @@ async function readOplogNode(auto, key, length) {
56
55
 
57
56
  try {
58
57
  await core.ready()
59
- const block = await core.get(length - 1, { timeout: DEFAULT_OP_TIMEOUT })
58
+ const block = await core.get(length - 1)
60
59
  return encoding.decodeOplog(block)
61
60
  } finally {
62
61
  await core.close()
package/lib/writers.js CHANGED
@@ -18,19 +18,24 @@ class ActiveWriters {
18
18
 
19
19
  this.auto = auto
20
20
  this.active = new Map()
21
- this._adding = new Map()
22
21
  this.triggers = new Triggers()
23
- this.localWriter = new Writer(this, auto.system, true, local, b4a.toString(local.key, 'hex'))
22
+ this.localWriter = null
24
23
  this.writable = false
25
24
  this.pending = []
26
25
 
27
26
  this._pendingBuffer = new Map()
27
+ this._adding = new Map()
28
28
 
29
+ this.localWriter = new Writer(this, auto.system, true, local, b4a.toString(local.key, 'hex'))
29
30
  this.localWriter.attach()
30
31
 
31
32
  this._bootstrap()
32
33
  }
33
34
 
35
+ get size() {
36
+ return this.active.size
37
+ }
38
+
34
39
  [Symbol.iterator]() {
35
40
  return this.active.values()
36
41
  }
@@ -195,10 +200,7 @@ class ActiveWriters {
195
200
  if (local) await this.updateLocalState()
196
201
 
197
202
  let w = this.active.get(id)
198
- if (w) {
199
- w.couple() // couple if we weren't ready first time
200
- return w
201
- }
203
+ if (w) return w
202
204
 
203
205
  // no new sessions once teardown started
204
206
  if (this.auto._interrupting) return null
@@ -222,7 +224,6 @@ class ActiveWriters {
222
224
 
223
225
  w = new Writer(this, this.auto.system, false, core, id)
224
226
  w.attach()
225
- w.couple()
226
227
 
227
228
  return w
228
229
  }
@@ -260,7 +261,7 @@ class ActiveWriters {
260
261
  const id = b4a.toString(bootstrap.key, 'hex')
261
262
  let w = this.active.get(id)
262
263
  if (w) {
263
- w.isBootstrap = true
264
+ w.isHead = true
264
265
  w.couple() // couple if we weren't ready first time
265
266
  return
266
267
  }
@@ -269,7 +270,7 @@ class ActiveWriters {
269
270
  session.setEncryption(this.auto._getEncryptionProvider())
270
271
 
271
272
  w = new Writer(this, this.auto.system, false, session, id)
272
- w.isBootstrap = true // the root of the autobase, never gc'd
273
+ w.isHead = true // the root of the autobase, never gc'd
273
274
  w.attach()
274
275
  w.couple()
275
276
  }
@@ -298,7 +299,7 @@ class Writer {
298
299
  this.isRemoved = false
299
300
  this.isCoupled = false
300
301
  this.isFrozen = false
301
- this.isBootstrap = false
302
+ this.isHead = false
302
303
 
303
304
  this._onchangeBound = this._onchange.bind(this)
304
305
 
@@ -312,6 +313,10 @@ class Writer {
312
313
  return this.weight >= 2
313
314
  }
314
315
 
316
+ isSystemHead() {
317
+ return this.system.isHead(this.core.key)
318
+ }
319
+
315
320
  couple() {
316
321
  if (this.isCoupled) return
317
322
  // if we don't have coupler yet we can't add
@@ -357,7 +362,10 @@ class Writer {
357
362
 
358
363
  // a peer announced this length for us, so we're not caught up until we've seen it
359
364
  hint(length) {
360
- if (length > this.hintedLength) this.hintedLength = length
365
+ if (length > this.hintedLength) {
366
+ this.hintedLength = length
367
+ this.core.setActive(true)
368
+ }
361
369
  }
362
370
 
363
371
  _onchange() {
@@ -395,12 +403,31 @@ class Writer {
395
403
  this.waiting = null
396
404
  }
397
405
 
406
+ const isHead = this.isSystemHead()
407
+
408
+ if (isHead && !this.isHead) {
409
+ this.isHead = true
410
+ this.couple()
411
+ }
412
+
413
+ if (!isHead && this.isHead) {
414
+ this.isHead = false
415
+ this.decouple()
416
+ }
417
+
398
418
  const length = await this.update()
419
+ const active = length < this.core.length || length < this.hintedLength
420
+
421
+ if (this.isHead || active) {
422
+ this.core.setActive(true)
423
+ } else {
424
+ this.core.setActive(false)
425
+ }
399
426
 
400
427
  // don't drop any local nodes left in buffer, or work a peer told us about
401
428
  if (length < this.core.length || length < this.hintedLength || this.appending) {
402
429
  this.addPending()
403
- } else if (this !== this.writers.localWriter && !this.isIndexer && !this.isBootstrap) {
430
+ } else if (this !== this.writers.localWriter && !this.isHead) {
404
431
  await this.detachAndClose()
405
432
  }
406
433
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "autobee",
3
- "version": "2.0.0-rc.31",
3
+ "version": "2.0.0-rc.32",
4
4
  "description": "Bee based autobase",
5
5
  "license": "Apache-2.0",
6
6
  "author": "Holepunch Inc.",