autobee 2.2.1 → 2.2.2

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 (2) hide show
  1. package/lib/writers.js +64 -33
  2. package/package.json +2 -1
package/lib/writers.js CHANGED
@@ -4,6 +4,7 @@ const Triggers = require('./triggers.js')
4
4
  const { LEGACY_OPLOG_VERSION } = require('./constants.js')
5
5
  const { resolveWeight } = require('./witness.js')
6
6
  const { WriterEncryption } = require('autobee-encryption')
7
+ const ReadWriteLock = require('read-write-mutexify')
7
8
 
8
9
  const WRITER_PREFETCH = 10
9
10
 
@@ -25,6 +26,7 @@ class ActiveWriters {
25
26
 
26
27
  this._pendingBuffer = new Map()
27
28
  this._adding = new Map()
29
+ this._lock = new ReadWriteLock()
28
30
 
29
31
  this.localWriter = new Writer(this, auto.system, true, local, b4a.toString(local.key, 'hex'))
30
32
  this.localWriter.attach()
@@ -89,14 +91,27 @@ class ActiveWriters {
89
91
  }
90
92
 
91
93
  async rotateLocalWriter(core) {
92
- this.clearLocal()
93
- await this.localWriter.detachAndClose()
94
+ await this._lock.write.lock()
94
95
 
95
- this.localWriter = new Writer(this, this.auto.system, true, core, b4a.toString(core.key, 'hex'))
96
- this.localWriter.addPending()
97
- this.auto.emit('writer', this.localWriter)
96
+ try {
97
+ this.clearLocal()
98
+ await this.localWriter.detachAndClose()
99
+
100
+ const id = b4a.toString(core.key, 'hex')
101
+
102
+ // a peer may already have announced this core (wakeup hint / link), in which
103
+ // case an external writer tracks it - drop it so only one writer owns the key
104
+ const existing = this.active.get(id)
105
+ if (existing) await existing.detachAndClose()
98
106
 
99
- await this.updateLocalState()
107
+ this.localWriter = new Writer(this, this.auto.system, true, core, id)
108
+ // register in active like the constructor does, so add() finds it
109
+ this.localWriter.attach()
110
+
111
+ await this.updateLocalState()
112
+ } finally {
113
+ this._lock.write.unlock()
114
+ }
100
115
  }
101
116
 
102
117
  clearLocal() {
@@ -178,7 +193,7 @@ class ActiveWriters {
178
193
  return !!this.active.get(id)
179
194
  }
180
195
 
181
- add(key) {
196
+ async add(key) {
182
197
  const id = b4a.toString(key, 'hex')
183
198
 
184
199
  let adding = this._adding.get(id)
@@ -187,44 +202,60 @@ class ActiveWriters {
187
202
  adding = this._add(key, id)
188
203
  this._adding.set(id, adding)
189
204
 
190
- return adding.finally(() => {
205
+ try {
206
+ return await adding
207
+ } finally {
191
208
  this._adding.delete(id)
192
- })
209
+ }
193
210
  }
194
211
 
195
- // TODO: correct fix is writer as ReadyResource
196
212
  async _add(key, id) {
197
- const local = b4a.equals(key, this.auto.local.key)
198
- if (local) await this.updateLocalState()
213
+ await this._lock.read.lock()
199
214
 
200
- let w = this.active.get(id)
201
- if (w) return w
215
+ try {
216
+ if (this.auto._interrupting) return null
217
+
218
+ // judged against the writer we own, not auto.local - that flips before the
219
+ // rotation takes the write lock. an add that beats the rotation onto its
220
+ // key just makes an external writer, which the rotation then evicts
221
+ if (b4a.equals(key, this.localWriter.core.key)) {
222
+ await this.updateLocalState()
223
+ if (this.auto._interrupting) return null
224
+ // never track the local core with a second writer - reattach it if it
225
+ // was detached (i.e. removed earlier and now re-added)
226
+ this.localWriter.attach()
227
+ return this.localWriter
228
+ }
202
229
 
203
- // no new sessions once teardown started
204
- if (this.auto._interrupting) return null
230
+ let w = this.active.get(id)
231
+ if (w) return w
205
232
 
206
- const encryption = this.auto.encryptionKey ? new WriterEncryption(this.auto) : null
233
+ const local = b4a.equals(key, this.auto.local.key)
234
+ const encryption = this.auto.encryptionKey ? new WriterEncryption(this.auto) : null
207
235
 
208
- const core = this.auto.store.get({
209
- active: false,
210
- key,
211
- encryption,
212
- group: local || !this.auto.wakeupCapability ? null : this.auto.wakeupCapability.discoveryKey
213
- })
236
+ const core = this.auto.store.get({
237
+ active: false,
238
+ key,
239
+ encryption,
240
+ group: local || !this.auto.wakeupCapability ? null : this.auto.wakeupCapability.discoveryKey
241
+ })
214
242
 
215
- await core.ready()
216
- await core.setUserData('referrer', this.auto.key)
243
+ await core.ready()
244
+ await core.setUserData('referrer', this.auto.key)
217
245
 
218
- // teardown may have started underneath us
219
- if (this.auto._interrupting) {
220
- await core.close()
221
- return null
222
- }
246
+ // teardown may have started underneath us
247
+ if (this.auto._interrupting) {
248
+ await core.close()
249
+ return null
250
+ }
223
251
 
224
- w = new Writer(this, this.auto.system, false, core, id)
225
- w.attach()
252
+ w = new Writer(this, this.auto.system, false, core, id)
253
+ w.attach()
226
254
 
227
- return w
255
+ return w
256
+ } finally {
257
+ this._lock.read.unlock()
258
+ }
228
259
  }
229
260
 
230
261
  async remove(key) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "autobee",
3
- "version": "2.2.1",
3
+ "version": "2.2.2",
4
4
  "description": "Bee based autobase",
5
5
  "license": "Apache-2.0",
6
6
  "author": "Holepunch Inc.",
@@ -31,6 +31,7 @@
31
31
  "hypercore": "^11.33.2",
32
32
  "hypercore-id-encoding": "^1.3.0",
33
33
  "hyperschema": "^1.20.1",
34
+ "read-write-mutexify": "^2.1.0",
34
35
  "ready-guard": "^1.0.0",
35
36
  "ready-resource": "^1.2.0",
36
37
  "resolve-reject-promise": "^1.1.0",