@live-change/db-store-observable-db 0.1.0 → 0.5.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.
package/.idea/misc.xml ADDED
@@ -0,0 +1,6 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <project version="4">
3
+ <component name="ProjectRootManager">
4
+ <output url="file://$PROJECT_DIR$/out" />
5
+ </component>
6
+ </project>
@@ -0,0 +1,8 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <project version="4">
3
+ <component name="ProjectModuleManager">
4
+ <modules>
5
+ <module fileurl="file://$PROJECT_DIR$/db-store-observable-db.iml" filepath="$PROJECT_DIR$/db-store-observable-db.iml" />
6
+ </modules>
7
+ </component>
8
+ </project>
package/.idea/vcs.xml ADDED
@@ -0,0 +1,6 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <project version="4">
3
+ <component name="VcsDirectoryMappings">
4
+ <mapping directory="" vcs="Git" />
5
+ </component>
6
+ </project>
@@ -0,0 +1,9 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <module type="WEB_MODULE" version="4">
3
+ <component name="NewModuleRootManager" inherit-compiler-output="true">
4
+ <exclude-output />
5
+ <content url="file://$MODULE_DIR$" />
6
+ <orderEntry type="inheritedJdk" />
7
+ <orderEntry type="sourceFolder" forTests="false" />
8
+ </component>
9
+ </module>
package/lib/Store.js CHANGED
@@ -1,6 +1,8 @@
1
1
  const ReactiveDao = require("@live-change/dao")
2
2
  const WebSocket = require('ws')
3
3
 
4
+ const debug = false
5
+
4
6
  const opCodes = {
5
7
  Ping : 0,
6
8
  Pong : 1,
@@ -50,13 +52,14 @@ const rangeFlags = {
50
52
 
51
53
  const resultPutFlags = {
52
54
  Found: 0x1,
53
- Created: 0x2,
55
+ Created: 0x2,
54
56
  Last: 0x4
55
- };
57
+ }
56
58
 
57
59
  class DatabaseConnection {
58
60
  constructor(url) {
59
61
  this.url = url
62
+ this.creatingStores = new Map()
60
63
  this.openingStores = new Map()
61
64
  this.openStores = []
62
65
  this.websocket = null
@@ -69,11 +72,14 @@ class DatabaseConnection {
69
72
  connect() {
70
73
  this.websocket = new WebSocket(this.url)
71
74
  this.websocket.binaryType = 'nodebuffer'
72
- this.websocket.on('close', () => {
75
+ this.websocket.on('close', (reason) => {
76
+ console.error("WEBSOCKET CLOSED", reason)
77
+ process.exit(1)
73
78
  this.openedStores = []
74
79
  })
75
- this.websocket.on('error', () => {
76
-
80
+ this.websocket.on('error', (error) => {
81
+ console.error("WEBSOCKET ERROR", error)
82
+ process.exit(1)
77
83
  })
78
84
  this.websocket.on('message', (message) => {
79
85
  if(typeof message != 'string') { // Buffer
@@ -91,10 +97,11 @@ class DatabaseConnection {
91
97
  //console.log("RESPONSE TO", requestId, request, "IS", message)
92
98
  if(request) {
93
99
  if(!request.handlePacket(message)) {
100
+ if(debug) console.log("REQUEST FINISHED", requestId)
94
101
  this.waitingRequests.delete(requestId)
95
102
  }
96
103
  } else {
97
- console.log("unknown response", requestId)
104
+ if(debug) console.log("unknown response", requestId, message)
98
105
  }
99
106
  }
100
107
  } else { // text
@@ -112,6 +119,12 @@ class DatabaseConnection {
112
119
  }
113
120
  }
114
121
  })
122
+ setInterval(() => {
123
+ const packet = Buffer.alloc(1+8)
124
+ packet.writeUInt8(opCodes.Ping, 0)
125
+ packet.writeFloatBE(Date.now(), 1)
126
+ this.websocket.send(packet)
127
+ }, 2000)
115
128
  }
116
129
 
117
130
  close() {
@@ -121,6 +134,7 @@ class DatabaseConnection {
121
134
 
122
135
  addRequest(request) {
123
136
  this.waitingRequests.set(request.id, request)
137
+ if(debug) console.log("ADD REQUEST", request.id,"AT STATE", this.websocket.readyState)
124
138
  if(this.websocket.readyState == 1) { // 1 => open
125
139
  this.websocket.send(request.requestPacket)
126
140
  }
@@ -204,6 +218,7 @@ class DatabaseConnection {
204
218
  nameBuffer.copy(packet, 1+4+1)
205
219
  packet.writeUInt16BE(settingsBuffer.length, 1+4+1+nameBuffer.length)
206
220
  settingsBuffer.copy(packet, 1+4+1+nameBuffer.length+2)
221
+ if(debug) console.log("SEND CREATE DB", packet)
207
222
  return this.requestOkError(packet)
208
223
  }
209
224
 
@@ -217,18 +232,34 @@ class DatabaseConnection {
217
232
  }
218
233
 
219
234
  async createStore(databaseName, storeName) {
235
+ const storeKey = JSON.stringify([databaseName, storeName])
236
+ const createPromise = this.creatingStores.get(storeKey)
237
+ console.log("CREATING STORE PROMISE", createPromise)
238
+ if(createPromise) return createPromise
239
+ console.log("CREATING STORE", storeKey)
220
240
  const databaseNameBuffer = Buffer.from(databaseName)
221
241
  const storeNameBuffer = Buffer.from(storeName)
222
- const packet = Buffer.alloc(1+4+1+databaseNameBuffer.length+1+storeNameBuffer.length)
242
+ const packet = Buffer.alloc(1 + 4 + 1 + databaseNameBuffer.length + 1 + storeNameBuffer.length)
223
243
  packet.writeUInt8(opCodes.CreateStore, 0)
224
- packet.writeUInt8(databaseNameBuffer.length, 1+4)
225
- databaseNameBuffer.copy(packet, 1+4+1)
226
- packet.writeUInt8(storeNameBuffer.length, 1+4+1+databaseNameBuffer.length)
227
- storeNameBuffer.copy(packet, 1+4+1+databaseNameBuffer.length+1)
228
- return this.requestOkError(packet)
244
+ packet.writeUInt8(databaseNameBuffer.length, 1 + 4)
245
+ databaseNameBuffer.copy(packet, 1 + 4 + 1)
246
+ packet.writeUInt8(storeNameBuffer.length, 1 + 4 + 1 + databaseNameBuffer.length)
247
+ storeNameBuffer.copy(packet, 1 + 4 + 1 + databaseNameBuffer.length + 1)
248
+ const promise = this.requestOkError(packet)
249
+ this.creatingStores.set(storeKey, promise)
250
+ promise.then(() => {
251
+ this.creatingStores.delete(storeKey)
252
+ console.log("CREATED STORE", storeKey)
253
+ })
254
+ return promise
229
255
  }
230
256
 
231
257
  async deleteStore(databaseName, storeName) {
258
+ if(debug) console.log("DELETE STORE", databaseName, storeName)
259
+ for(let i = 0; i < this.openStores.length; i++) {
260
+ if(this.openStores[i] && this.openStores[i].storeName == storeName
261
+ && this.openStores[i].databaseName == databaseName) this.openStore[i] = null
262
+ }
232
263
  const databaseNameBuffer = Buffer.from(databaseName)
233
264
  const storeNameBuffer = Buffer.from(storeName)
234
265
  const packet = Buffer.alloc(1+4+1+databaseNameBuffer.length+1+storeNameBuffer.length)
@@ -245,25 +276,40 @@ class DatabaseConnection {
245
276
  if(this.openStores[i] && this.openStores[i].storeName == storeName
246
277
  && this.openStores[i].databaseName == databaseName) return i
247
278
  }
248
- const currentPromise = this.openingStores.get(JSON.stringify(databaseName, storeName))
279
+ const storeKey = JSON.stringify([databaseName, storeName])
280
+ console.log("OPEN STORE!", storeKey)
281
+ const createPromise = this.creatingStores.get(storeKey)
282
+ if(createPromise) await createPromise
283
+ const currentPromise = this.openingStores.get(storeKey)
249
284
  if(currentPromise) return currentPromise
250
- const openStoreId = this.openStores.length
251
- const databaseNameBuffer = Buffer.from(databaseName)
252
- const storeNameBuffer = Buffer.from(storeName)
253
- const packet = Buffer.alloc(1+4+1+databaseNameBuffer.length+1+storeNameBuffer.length)
254
- packet.writeUInt8(opCodes.OpenStore, 0)
255
- packet.writeUInt8(databaseNameBuffer.length, 1+4)
256
- databaseNameBuffer.copy(packet, 1+4+1)
257
- packet.writeUInt8(storeNameBuffer.length, 1+4+1+databaseNameBuffer.length)
258
- storeNameBuffer.copy(packet, 1+4+1+databaseNameBuffer.length+1)
259
- this.openStores.push(null)
260
- return this.requestOkError(packet).then(ok => {
261
- this.openStores[openStoreId] = { storeName, databaseName }
262
- return openStoreId
263
- }).catch(error => {
264
- this.openStores[openStoreId] = null
265
- throw error
285
+ console.log("DO OPEN STORE!", storeKey)
286
+ const open = async () => {
287
+ const openStoreId = this.openStores.length
288
+ const databaseNameBuffer = Buffer.from(databaseName)
289
+ const storeNameBuffer = Buffer.from(storeName)
290
+ const packet = Buffer.alloc(1 + 4 + 1 + databaseNameBuffer.length + 1 + storeNameBuffer.length)
291
+ packet.writeUInt8(opCodes.OpenStore, 0)
292
+ packet.writeUInt8(databaseNameBuffer.length, 1 + 4)
293
+ databaseNameBuffer.copy(packet, 1 + 4 + 1)
294
+ packet.writeUInt8(storeNameBuffer.length, 1 + 4 + 1 + databaseNameBuffer.length)
295
+ storeNameBuffer.copy(packet, 1 + 4 + 1 + databaseNameBuffer.length + 1)
296
+ this.openStores.push(null)
297
+ return this.requestOkError(packet).then(ok => {
298
+ this.openStores[openStoreId] = { storeName, databaseName }
299
+ return openStoreId
300
+ }).catch(error => {
301
+ console.error("OPEN STORE FAILED!", error)
302
+ this.openStores[openStoreId] = null
303
+ throw error
304
+ })
305
+ }
306
+ const openPromise = open()
307
+ this.openingStores.set(storeKey, openPromise)
308
+ openPromise.then(() => {
309
+ console.log("STORE OPENED!", storeKey)
310
+ this.openingStores.delete(storeKey)
266
311
  })
312
+ return openPromise
267
313
  }
268
314
 
269
315
  async closeStore(databaseName, storeName) {
@@ -297,7 +343,7 @@ class DatabaseConnection {
297
343
  packet.writeUInt32BE(storeId, 1+4)
298
344
  packet.writeUInt16BE(keyBuffer.length, 1+4+4)
299
345
  keyBuffer.copy(packet, 1+4+4+2)
300
- console.log("PUT VALUE", value, "BUF", valueBuffer)
346
+ if(debug) console.log("PUT", databaseName, "/", storeName , key, "VALUE", value, "BUF")
301
347
  packet.writeUInt32BE(valueBuffer.length, 1+4+4+2+keyBuffer.length)
302
348
  valueBuffer.copy(packet, 1+4+4+2+keyBuffer.length+4)
303
349
  return this.requestSingleResult(packet)
@@ -366,10 +412,11 @@ class DatabaseConnection {
366
412
  //console.log("HANDLE PACKET", packet)
367
413
  const opcode = packet.readInt8(0)
368
414
  if(opcode == opCodes.ResultPut) {
369
- const keySize = packet.readUInt16BE(5)
370
- const key = packet.toString('utf8', 7, 7 + keySize)
371
- const valueSize = packet.readUInt32BE(7 + keySize)
372
- const value = packet.toString('utf8', 7 + keySize + 4, 7 + keySize + 4 + valueSize)
415
+ const flags = packet.readUInt8(5) // ignore flags
416
+ const keySize = packet.readUInt16BE(6)
417
+ const key = packet.toString('utf8', 8, 8 + keySize)
418
+ const valueSize = packet.readUInt32BE(8 + keySize)
419
+ const value = packet.toString('utf8', 8 + keySize + 4, 8 + keySize + 4 + valueSize)
373
420
  results.push({ key, value })
374
421
  return true // need more data
375
422
  } else if(opcode == opCodes.ResultsDone) {
@@ -431,8 +478,9 @@ class DatabaseConnection {
431
478
  packet.writeUInt32BE(storeId, 1+4)
432
479
  packet.writeUInt16BE(keyBuffer.length, 1+4+4)
433
480
  keyBuffer.copy(packet, 1+4+4+2)
434
- return this.rawRequest(packet, (packet) => {
435
- //console.log("HANDLE PACKET", packet)
481
+ if(debug) console.log("OBSERVE KEY", key)
482
+ const requestId = this.rawRequest(packet, (packet) => {
483
+ if(debug) console.log("HANDLE OBSERVE PACKET", packet)
436
484
  const opcode = packet.readInt8(0)
437
485
  if(opcode == opCodes.Result) {
438
486
  return onValue(packet.toString('utf8', 5))
@@ -441,19 +489,24 @@ class DatabaseConnection {
441
489
  } else if(opcode == opCodes.Error) {
442
490
  const error = packet.toString('utf8', 5)
443
491
  onError(error)
492
+ return true
444
493
  } else {
445
494
  console.error("Unknown opcode", opCodeToString[opcode])
446
495
  onError("Unknown opcode: " + opCodeToString[opcode])
447
496
  }
497
+ process.exit(1)
448
498
  return false// finish waiting
449
499
  })
500
+ //console.log("OBSERVE", key, "=>", requestId)
501
+ return requestId
450
502
  }
451
503
 
452
504
  async observeRange(databaseName, storeName, range, onResultPut, onChanges, onError) {
453
505
  const storeId = await this.openStore(databaseName, storeName)
454
506
  const packet = this.rangePacket(storeId, opCodes.ObserveRange, range)
507
+ if(debug) console.log("OBSERVE RANGE", databaseName, storeName, range)
455
508
  return this.rawRequest(packet, (packet) => {
456
- console.log("OBSERVE RANGE HANDLE PACKET", packet)
509
+ if(debug) console.log("OBSERVE RANGE HANDLE PACKET", packet)
457
510
  const opcode = packet.readInt8(0)
458
511
  if(opcode == opCodes.ResultPut) {
459
512
  const flags = packet.readUInt8(5)
@@ -485,7 +538,7 @@ class DatabaseConnection {
485
538
  const storeId = await this.openStore(databaseName, storeName)
486
539
  const packet = this.rangePacket(storeId, opCodes.ObserveCount, range)
487
540
  return this.rawRequest(packet, (packet) => {
488
- console.log("OBSERVE RANGE HANDLE PACKET", packet)
541
+ if(debug) console.log("OBSERVE RANGE HANDLE PACKET", packet)
489
542
  const opcode = packet.readInt8(0)
490
543
  if(opcode == opCodes.ResultCount) {
491
544
  const count = packet.readUInt32BE(5)
@@ -502,10 +555,11 @@ class DatabaseConnection {
502
555
  }
503
556
 
504
557
  async unobserve(requestId) {
505
- console.log("UNOBSERVE", requestId, this.waitingRequests)
558
+ //console.log("UNOBSERVE", requestId)
506
559
  if(!this.waitingRequests.get(requestId)) {
507
560
  throw new Error("unobserve of not observed! "+requestId)
508
561
  }
562
+ if(debug) console.error("REQUEST UNOBSERVED", requestId)
509
563
  this.waitingRequests.delete(requestId)
510
564
  const packet = Buffer.alloc(1+4)
511
565
  packet.writeUInt8(opCodes.Unobserve, 0)
@@ -538,7 +592,7 @@ class Store {
538
592
  if(!id) throw new Error("id is required")
539
593
  const observableValue = new ReactiveDao.ObservableValue()
540
594
  const onValue = (value) => {
541
- console.log("OBJ", id, "ON VALUE", value)
595
+ if(debug) console.log("OBJ", id, "ON VALUE", value)
542
596
  observableValue.set(value && { id, ...JSON.parse(value) })
543
597
  return !observableValue.isDisposed()
544
598
  }
@@ -550,7 +604,9 @@ class Store {
550
604
  const oldDispose = observableValue.dispose
551
605
  const oldRespawn = observableValue.respawn
552
606
  observableValue.dispose = () => {
553
- this.connection.unobserve(observableValue.requestId)
607
+ //console.trace("DISPOSE!")
608
+ //console.log("DISPOSE", observableValue.requestId, this.disposed)
609
+ //if(!this.disposed) this.connection.unobserve(observableValue.requestId)
554
610
  oldDispose.apply(this)
555
611
  }
556
612
  observableValue.respawn = async () => {
@@ -577,6 +633,10 @@ class Store {
577
633
  let results = []
578
634
  let changesMode = false
579
635
  const onResultPut = (found, last, key, value) => {
636
+ if(observableList.isDisposed()) {
637
+ console.error("PUT WHEN DISPOSED!", range, found, last, key, value)
638
+ return
639
+ }
580
640
  if(changesMode) {
581
641
  if(found) {
582
642
  if(last) {
@@ -596,18 +656,22 @@ class Store {
596
656
  return !observableList.isDisposed()
597
657
  }
598
658
  const onChanges = () => {
659
+ if(observableList.isDisposed()) console.error("CHANGES WHEN DISPOSED!")
599
660
  changesMode = true
600
661
  observableList.set(results)
601
- results = null
662
+ results = []
602
663
  return !observableList.isDisposed()
603
664
  }
604
665
  const oldDispose = observableList.dispose
605
666
  const oldRespawn = observableList.respawn
606
667
  observableList.dispose = () => {
607
668
  this.connection.unobserve(observableList.requestId)
669
+ //console.log("UNOBSERVE STATE", observableList.list)
670
+ //console.trace("UNOBSERVE")
608
671
  oldDispose.apply(this)
609
672
  }
610
673
  observableList.respawn = async () => {
674
+ changesMode = false
611
675
  observableList.requestId = await this.connection.observeRange(this.databaseName, this.storeName, range,
612
676
  onResultPut, onChanges)
613
677
  oldRespawn.apply(this)
@@ -622,19 +686,20 @@ class Store {
622
686
  countObservable(range) {
623
687
  const observableValue = new ReactiveDao.ObservableValue()
624
688
  const onValue = (value) => {
625
- console.log("COUNT", range, "ON VALUE", value)
689
+ if(debug) console.log("COUNT", range, "ON VALUE", value)
626
690
  observableValue.set(value)
627
691
  return !observableValue.isDisposed()
628
692
  }
629
693
  const onError = (error) => {
630
- console.log("COUNT", range, "ON ERROR", error)
694
+ if(debug) console.log("COUNT", range, "ON ERROR", error)
631
695
  observableValue.error(error)
632
696
  return !observableValue.isDisposed()
633
697
  }
634
698
  const oldDispose = observableValue.dispose
635
699
  const oldRespawn = observableValue.respawn
636
700
  observableValue.dispose = () => {
637
- this.connection.unobserve(observableValue.requestId)
701
+ if(observableValue.requestId >= 0) this.connection.unobserve(observableValue.requestId)
702
+ observableValue.requestId = -1
638
703
  oldDispose.apply(this)
639
704
  }
640
705
  observableValue.respawn = async () => {
@@ -662,14 +727,17 @@ class Store {
662
727
  async put(object) {
663
728
  const id = object.id
664
729
  if(typeof id != 'string') throw new Error(`ID is not string: ${JSON.stringify(id)}`)
730
+ if(debug) console.log("PUT!!!", object)
665
731
  if(!id) throw new Error("ID must not be empty string!")
666
- return this.connection.put(this.databaseName, this.storeName, id,
732
+ const putResult = await this.connection.put(this.databaseName, this.storeName, id,
667
733
  JSON.stringify({ ...object, id: undefined }))
734
+ return putResult ? { id, ...JSON.parse(putResult) } : null
668
735
  }
669
736
 
670
737
  async delete(id) {
671
738
  if(!id) throw new Error("ID must not be empty string!")
672
- return this.connection.delete(this.databaseName, this.storeName, id)
739
+ const deleteResult = await this.connection.delete(this.databaseName, this.storeName, id)
740
+ return deleteResult ? { id, ...JSON.parse(deleteResult) } : null
673
741
  }
674
742
 
675
743
  async close() {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@live-change/db-store-observable-db",
3
- "version": "0.1.0",
3
+ "version": "0.5.2",
4
4
  "description": "Database backend protocol",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -28,5 +28,6 @@
28
28
  "bufferutil": "^4.0.3",
29
29
  "utf-8-validate": "^5.0.4",
30
30
  "ws": "^7.4.5"
31
- }
31
+ },
32
+ "gitHead": "af756efb8db9ba90d79774786201cffe9c2162cf"
32
33
  }
@@ -23,7 +23,7 @@ test("store non-reactive properties", t => {
23
23
  })
24
24
 
25
25
  t.test("non reactive operations", async t => {
26
- t.plan(24)
26
+ t.plan(25)
27
27
 
28
28
  t.test("put value", async t => {
29
29
  t.plan(1)
@@ -78,6 +78,13 @@ test("store non-reactive properties", t => {
78
78
  t.deepEqual(values, 3, 'range count' )
79
79
  })
80
80
 
81
+ t.test("count all", async t => {
82
+ t.plan(1)
83
+ let values = await store.countGet({ })
84
+ t.deepEqual(values, 3, 'full count' )
85
+ })
86
+
87
+
81
88
  t.test("get reverse range [c,a]", async t => {
82
89
  t.plan(1)
83
90
  let values = await store.rangeGet({ gte: 'a', lte: 'c', reverse: true })