mirror of
https://github.com/orbitdb/orbitdb.git
synced 2025-10-07 22:57:07 +00:00
Testing lists
This commit is contained in:
3
.gitignore
vendored
3
.gitignore
vendored
@@ -3,3 +3,6 @@ node_modules/
|
||||
debug.log
|
||||
WIP/
|
||||
.vagrant/
|
||||
.idea/
|
||||
isolate*.log
|
||||
dump.rdb
|
||||
198
MOCK.json
Normal file
198
MOCK.json
Normal file
@@ -0,0 +1,198 @@
|
||||
// Data set grouped and sorted
|
||||
"A" : [
|
||||
{ "id": "A", "seq": 0, "ver": 0, "prev": null},
|
||||
{ "id": "A", "seq": 0, "ver": 1, "prev": "A0.0"},
|
||||
{ "id": "A", "seq": 0, "ver": 2, "prev": "A0.1"},
|
||||
{ "id": "A", "seq": 0, "ver": 3, "prev": "A0.2"},
|
||||
{ "id": "A", "seq": 0, "ver": 4, "prev": "A0.3"},
|
||||
{ "id": "A", "seq": 2, "ver": 0, "prev": ["A0.4", "B1.1"]}
|
||||
],
|
||||
"B" : [
|
||||
{ "id": "B", "seq": 1, "ver": 0, "prev": ["A0.4", "C0.2"]},
|
||||
{ "id": "B", "seq": 1, "ver": 1, "prev": "B1.0"}
|
||||
],
|
||||
"C" : [
|
||||
{ "id": "C", "seq": 0, "ver": 0, "prev": null},
|
||||
{ "id": "C", "seq": 0, "ver": 1, "prev": "C0.0"},
|
||||
{ "id": "C", "seq": 0, "ver": 2, "prev": "C0.1"},
|
||||
{ "id": "C", "seq": 3, "ver": 0, "prev": ["C0.2", "A2.0]"]}
|
||||
]
|
||||
|
||||
A B C
|
||||
|
|
||||
0.0
|
||||
|
|
||||
0.1
|
||||
|
|
||||
0.2 0.0
|
||||
| |
|
||||
0.3 0.1
|
||||
| |
|
||||
0.4 0.2
|
||||
| \ / |
|
||||
| 1.0 |
|
||||
| | |
|
||||
| 1.1 |
|
||||
| / |
|
||||
2.0 |
|
||||
\ |
|
||||
\ |
|
||||
\ |
|
||||
3.0
|
||||
|
||||
// expected order A
|
||||
{ "id": "A", "seq": 0, "ver": 0, "prev": null},
|
||||
{ "id": "A", "seq": 0, "ver": 1, "prev": "A0.0"},
|
||||
{ "id": "A", "seq": 0, "ver": 2, "prev": "A0.1"},
|
||||
{ "id": "A", "seq": 0, "ver": 3, "prev": "A0.2"},
|
||||
{ "id": "A", "seq": 0, "ver": 4, "prev": "A0.3"},
|
||||
{ "id": "C", "seq": 0, "ver": 0, "prev": null},
|
||||
{ "id": "C", "seq": 0, "ver": 1, "prev": "C0.0"},
|
||||
{ "id": "C", "seq": 0, "ver": 2, "prev": "C0.1"},
|
||||
{ "id": "B", "seq": 1, "ver": 0, "prev": ["A0.4", "C0.2"]},
|
||||
{ "id": "B", "seq": 1, "ver": 1, "prev": "B1.0"}
|
||||
{ "id": "A", "seq": 2, "ver": 0, "prev": ["A0.4", "B1.1"]}
|
||||
{ "id": "C", "seq": 3, "ver": 0, "prev": ["C0.2", "A2.0]"]}
|
||||
|
||||
"VersionClock": {
|
||||
"seq": 0,
|
||||
"ver": 0
|
||||
}
|
||||
|
||||
"Item": {
|
||||
"id": "",
|
||||
"VersionClock": "<VersionClock>",
|
||||
"prev": []
|
||||
}
|
||||
|
||||
"List": {
|
||||
"items": ["<Item>"]
|
||||
}
|
||||
|
||||
/*
|
||||
list.add(data) {
|
||||
this.ver ++;
|
||||
const heads = _findHeads();
|
||||
const i = new Item(id, this.seq, this.ver, heads)
|
||||
outgoing.push(data)
|
||||
}
|
||||
*/
|
||||
|
||||
/*
|
||||
list.join(other) {
|
||||
// increase seq on join, reset version
|
||||
if(other.first.seq >= this.seq)
|
||||
this.seq = other.first.seq + 1
|
||||
this.ver = 0
|
||||
|
||||
items = items.concat(outgoing.concat(other))
|
||||
items = items.sortBy("seq", "id", "ver")
|
||||
outgoing = []
|
||||
}
|
||||
*/
|
||||
|
||||
/*
|
||||
nextHeads() {
|
||||
referenced = []
|
||||
heads = all.groupBy("id").map((items) => items[items.length - 1])
|
||||
cleaned = heads.reverse().filter((e) => !isReferencedInChain(referenced, e))
|
||||
return cleaned;
|
||||
}
|
||||
*/
|
||||
|
||||
/*
|
||||
isReferencedInChain(list, other) {
|
||||
const res = other.map((o) => {
|
||||
const ref = list.map((e) => !(e.id == o.id && e.seq == o.seq && e.ver == o.ver))
|
||||
if(!ref)
|
||||
list.push(ref)
|
||||
//return false
|
||||
|
||||
//list.concat(list.filter((e) => !(e.id == o.id && e.seq == o.seq && e.ver == o.ver)))
|
||||
if(o.prev)
|
||||
ref = isReferencedInChain(list, o.prev)
|
||||
|
||||
return ref
|
||||
})
|
||||
return res.anyEqual(true)
|
||||
}
|
||||
*/
|
||||
|
||||
|
||||
A B C
|
||||
0.0
|
||||
|
|
||||
0.0 0.1
|
||||
| |
|
||||
0.1 0.2
|
||||
\ / |
|
||||
1.0 |
|
||||
| |
|
||||
1.1 |
|
||||
/ |
|
||||
2.0 |
|
||||
\ |
|
||||
\ |
|
||||
\ |
|
||||
3.0
|
||||
|
||||
// Sequence, --> syncs to
|
||||
listA.add("mango") // { "id": "A", "seq": 0, "ver": 0, "prev": null}
|
||||
listA.add("banana") // { "id": "A", "seq": 0, "ver": 1, "prev": "A.0.0"}
|
||||
--> B
|
||||
|
||||
// A
|
||||
// { "id": "A", "seq": 0, "ver": 0, "prev": null}
|
||||
// { "id": "A", "seq": 0, "ver": 1, "prev": "A.0.0"}
|
||||
|
||||
listC.add("apple") // { "id": "C", "seq": 0, "ver": 0, "prev": null}
|
||||
listC.add("strawberry") // { "id": "C", "seq": 0, "ver": 1, "prev": "C.0.0"}
|
||||
listC.add("orange") // { "id": "C", "seq": 0, "ver": 2, "prev": "C.0.1"}
|
||||
--> A,B
|
||||
|
||||
// A
|
||||
// { "id": "A", "seq": 0, "ver": 0, "prev": null}
|
||||
// { "id": "A", "seq": 0, "ver": 1, "prev": "A.0.0"}
|
||||
// { "id": "C", "seq": 0, "ver": 0, "prev": null}
|
||||
// { "id": "C", "seq": 0, "ver": 1, "prev": "C.0.0"}
|
||||
// { "id": "C", "seq": 0, "ver": 2, "prev": "C.0.1"}
|
||||
|
||||
listB.add("pineapple") // { "id": "B", "seq": 1, "ver": 0, "prev": ["A.0.1", "C.0.2"]}
|
||||
listB.add("papaya") // { "id": "B", "seq": 1, "ver": 1, "prev": "B.1.0"}
|
||||
--> A
|
||||
|
||||
// A
|
||||
// { "id": "A", "seq": 0, "ver": 0, "prev": null}
|
||||
// { "id": "A", "seq": 0, "ver": 1, "prev": "A.0.0"}
|
||||
// { "id": "C", "seq": 0, "ver": 0, "prev": null}
|
||||
// { "id": "C", "seq": 0, "ver": 1, "prev": "C.0.0"}
|
||||
// { "id": "C", "seq": 0, "ver": 2, "prev": "C.0.1"}
|
||||
// { "id": "B", "seq": 1, "ver": 0, "prev": ["A.0.1", "C.0.2"]}
|
||||
// { "id": "B", "seq": 1, "ver": 1, "prev": "B.1.0"}
|
||||
|
||||
listA.add("kiwi") // { "id": "A", "seq": 2, "ver": 0, "prev": ["A.0.1", "B1.1", "C0.2"]}
|
||||
--> C
|
||||
|
||||
// A
|
||||
// { "id": "A", "seq": 0, "ver": 0, "prev": null}
|
||||
// { "id": "A", "seq": 0, "ver": 1, "prev": "A.0.0"}
|
||||
// { "id": "C", "seq": 0, "ver": 0, "prev": null}
|
||||
// { "id": "C", "seq": 0, "ver": 1, "prev": "C.0.0"}
|
||||
// { "id": "C", "seq": 0, "ver": 2, "prev": "C.0.1"}
|
||||
// { "id": "B", "seq": 1, "ver": 0, "prev": ["A.0.0", "C.0.2"]}
|
||||
// { "id": "B", "seq": 1, "ver": 1, "prev": "B.1.0"}
|
||||
// { "id": "A", "seq": 2, "ver": 0, "prev": ["A.0.1", "B1.1", "C0.2"]}
|
||||
|
||||
listC.add("blueberry") // { "id": "C", "seq": 3, "ver": 0, "prev": ["A.2.0", "C.0.2"]}
|
||||
--> A,B
|
||||
|
||||
// A
|
||||
// { "id": "A", "seq": 0, "ver": 0, "prev": null}
|
||||
// { "id": "A", "seq": 0, "ver": 1, "prev": "A.0.0"}
|
||||
// { "id": "C", "seq": 0, "ver": 0, "prev": null}
|
||||
// { "id": "C", "seq": 0, "ver": 1, "prev": "C.0.0"}
|
||||
// { "id": "C", "seq": 0, "ver": 2, "prev": "C.0.1"}
|
||||
// { "id": "B", "seq": 1, "ver": 0, "prev": ["A.0.0", "C.0.2"]}
|
||||
// { "id": "B", "seq": 1, "ver": 1, "prev": "B.1.0"}
|
||||
// { "id": "A", "seq": 2, "ver": 0, "prev": ["A.0.1", "B1.1", "C0.2"]}
|
||||
// { "id": "C", "seq": 3, "ver": 0, "prev": ["A.2.0", "C.0.2"]}
|
||||
@@ -30,7 +30,7 @@ let run = (async(() => {
|
||||
|
||||
console.log("Query...");
|
||||
let items = channel.iterator({ limit: 3 }).collect();
|
||||
console.log(`Found items ${items.length} items`);
|
||||
console.log(`Found ${items.length} items`);
|
||||
|
||||
var g = items.filter((e) => e.item.Payload.startsWith(id))
|
||||
var prev = -1;
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
"asyncawait": "^1.0.1",
|
||||
"bluebird": "^3.1.1",
|
||||
"bs58": "^3.0.0",
|
||||
"lodash": "^4.3.0",
|
||||
"orbit-common": "^0.1.0",
|
||||
"redis": "^2.4.2",
|
||||
"unirest": "^0.4.2"
|
||||
|
||||
@@ -144,14 +144,17 @@ class OrbitClient {
|
||||
// Create the hash cache item
|
||||
const hcItem = new HashCacheItem(operation, key, seq, target, metaInfo, null, pubkey, privkey, password);
|
||||
|
||||
console.log("1")
|
||||
// Save the item to ipfs
|
||||
const data = await (ipfsAPI.putObject(this.ipfs, JSON.stringify(hcItem)));
|
||||
console.log("2", data.Hash, head)
|
||||
let newHead = { Hash: data.Hash };
|
||||
|
||||
// If this is not the first item in the channel, patch with the previous (ie. link as next)
|
||||
if(seq > 0)
|
||||
newHead = await (ipfsAPI.patchObject(this.ipfs, data.Hash, head));
|
||||
|
||||
console.log("3")
|
||||
return { hash: newHead, seq: seq };
|
||||
}
|
||||
|
||||
@@ -170,17 +173,19 @@ class OrbitClient {
|
||||
_remove(channel, password, options) {
|
||||
const key = null;
|
||||
const target = options.key ? options.key : (options.hash ? options.hash : null);
|
||||
return this._createOperation(channel, password, HashCacheOps.Delete, key, target);
|
||||
return await(this._createOperation(channel, password, HashCacheOps.Delete, key, target));
|
||||
}
|
||||
|
||||
_createOperation(channel, password, operation, key, value, data) {
|
||||
let message, res = false;
|
||||
while(!res) {
|
||||
this.posting = true;
|
||||
console.log("posting...")
|
||||
message = this._createMessage(channel, password, operation, key, value);
|
||||
res = await(this._pubsub.publish(channel, message.hash, message.seq));
|
||||
if(!res) console.log("retry", message.hash, message.seq)
|
||||
}
|
||||
this.posting = false;
|
||||
console.log("posted")
|
||||
return message.hash.Hash;
|
||||
}
|
||||
|
||||
@@ -65,8 +65,11 @@ class PubSub {
|
||||
return new Promise((resolve, reject) => {
|
||||
if(this.publishQueue.length === 0) {
|
||||
this.publishQueue.splice(0, 0, { hash: message.Hash, callback: resolve });
|
||||
console.log("...")
|
||||
this.client2.publish(hash, JSON.stringify({ hash: message.Hash, seq: seq }));
|
||||
console.log("published")
|
||||
} else {
|
||||
console.log("queue full!")
|
||||
resolve(false);
|
||||
}
|
||||
});
|
||||
@@ -106,6 +109,7 @@ class PubSub {
|
||||
var isNewer = message.seq > this._subscriptions[hash].seq;
|
||||
if(isNewer) {
|
||||
console.log("NEW HEAD!")
|
||||
this.publishQueue.pop();
|
||||
this._updateSubscription(hash, message.head, message.seq);
|
||||
}
|
||||
}
|
||||
|
||||
34
test/list-perf-tests.js
Normal file
34
test/list-perf-tests.js
Normal file
@@ -0,0 +1,34 @@
|
||||
// 'use strict';
|
||||
|
||||
// var assert = require('assert');
|
||||
// var async = require('asyncawait/async');
|
||||
// var await = require('asyncawait/await');
|
||||
// var List = require('../test1').List;
|
||||
// var Node = require('../test1').Node;
|
||||
// var Timer = require('../examples/Timer');
|
||||
|
||||
// describe('List - Performance Measurement', function() {
|
||||
// this.timeout(60000);
|
||||
|
||||
// it('add', (done) => {
|
||||
// let ms = 0;
|
||||
|
||||
// for(let t = 1000; t <= 10000; t += 1000) {
|
||||
// const list = new List('A');
|
||||
// let timer = new Timer(true);
|
||||
|
||||
// for(let i = 0; i < t; i ++) {
|
||||
// list.add("hello" + i);
|
||||
// }
|
||||
|
||||
// ms = timer.stop(true);
|
||||
// console.log(` > ${t} took ${ms} ms`)
|
||||
|
||||
// // assert.equal(ms < t, true);
|
||||
// }
|
||||
|
||||
// assert.equal(true, true);
|
||||
// done();
|
||||
// });
|
||||
|
||||
// });
|
||||
458
test/list-tests.js
Normal file
458
test/list-tests.js
Normal file
@@ -0,0 +1,458 @@
|
||||
'use strict';
|
||||
|
||||
var assert = require('assert');
|
||||
var async = require('asyncawait/async');
|
||||
var await = require('asyncawait/await');
|
||||
var List = require('../test1').List;
|
||||
var Node = require('../test1').Node;
|
||||
|
||||
describe('Node', () => {
|
||||
describe('Constructor', () => {
|
||||
it('initializes member variables', (done) => {
|
||||
const node = new Node('A', 0, 0, 'hello', []);
|
||||
assert.equal(node.id, 'A');
|
||||
assert.equal(node.seq, 0);
|
||||
assert.equal(node.ver, 0);
|
||||
assert.equal(node.data, 'hello');
|
||||
assert.equal(node.next instanceof Array, true);
|
||||
done();
|
||||
});
|
||||
|
||||
it('initializes member variables without data and next', (done) => {
|
||||
const node = new Node('A', 0, 0);
|
||||
assert.equal(node.id, 'A');
|
||||
assert.equal(node.seq, 0);
|
||||
assert.equal(node.ver, 0);
|
||||
assert.equal(node.data, null);
|
||||
assert.equal(node.next instanceof Array, true);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
describe('compactId', () => {
|
||||
it('presents the node as a string with id, sequence and version', (done) => {
|
||||
const node1 = new Node('A', 0, 0);
|
||||
const node2 = new Node('B', 123, 456);
|
||||
assert.equal(node1.compactId, 'A.0.0');
|
||||
assert.equal(node2.compactId, 'B.123.456');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
describe('compact', () => {
|
||||
it('presents the node as a compacted object', (done) => {
|
||||
const node1 = new Node('A', 0, 0, 'hello');
|
||||
const node2 = new Node('B', 0, 0, 'hello', [node1]);
|
||||
const compacted1 = node1.compact();
|
||||
const compacted2 = node2.compact();
|
||||
|
||||
assert.notEqual(compacted1, null);
|
||||
assert.equal(compacted1.id, 'A');
|
||||
assert.equal(compacted1.seq, 0);
|
||||
assert.equal(compacted1.ver, 0);
|
||||
assert.equal(compacted1.data, 'hello');
|
||||
assert.equal(compacted1.next instanceof Array, true);
|
||||
assert.equal(compacted1.next.length, 0);
|
||||
|
||||
assert.equal(compacted2.id, 'B');
|
||||
assert.equal(compacted2.next.length, 1);
|
||||
assert.equal(compacted2.next[0], 'A.0.0');
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('List', () => {
|
||||
describe('Constructor', () => {
|
||||
it('initializes member variables', (done) => {
|
||||
const list = new List('A');
|
||||
assert.equal(list.id, 'A');
|
||||
assert.equal(list.seq, 0);
|
||||
assert.equal(list.ver, 0);
|
||||
assert.equal(list._items instanceof Array, true);
|
||||
assert.equal(list._currentBatch instanceof Array, true);
|
||||
assert.equal(list._items.length, 0);
|
||||
assert.equal(list._currentBatch.length, 0);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
describe('items', () => {
|
||||
it('returns items', (done) => {
|
||||
const list = new List('A');
|
||||
let items = list.items;
|
||||
assert.equal(list.items instanceof Array, true);
|
||||
assert.equal(list.items.length, 0);
|
||||
list.add("hello1")
|
||||
list.add("hello2")
|
||||
assert.equal(list.items instanceof Array, true);
|
||||
assert.equal(list.items.length, 2);
|
||||
assert.equal(list.items[0].data, 'hello1');
|
||||
assert.equal(list.items[1].data, 'hello2');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
describe('toJson', () => {
|
||||
it('presents the list in a compacted json form', (done) => {
|
||||
const list = new List('A');
|
||||
list.add("hello1")
|
||||
list.add("hello2")
|
||||
list.add("hello3")
|
||||
// list.add("hello4")
|
||||
// list.add("hello5")
|
||||
let compacted = list.toJson();
|
||||
// console.log(compacted)
|
||||
assert.equal(compacted.id, 'A');
|
||||
assert.equal(compacted.seq, 0);
|
||||
assert.equal(compacted.ver, 3);
|
||||
assert.equal(compacted.items.length, 3);
|
||||
assert.equal(compacted.items[0].id, 'A');
|
||||
assert.equal(compacted.items[0].seq, 0);
|
||||
assert.equal(compacted.items[0].ver, 0);
|
||||
assert.equal(compacted.items[0].next.length, 0);
|
||||
assert.equal(compacted.items[compacted.items.length - 1].id, 'A');
|
||||
assert.equal(compacted.items[compacted.items.length - 1].seq, 0);
|
||||
assert.equal(compacted.items[compacted.items.length - 1].ver, 2);
|
||||
assert.equal(compacted.items[compacted.items.length - 1].next.length, 1);
|
||||
assert.equal(compacted.items[compacted.items.length - 1].next[0], 'A.0.1');
|
||||
assert.equal(compacted.items[compacted.items.length - 2].id, 'A');
|
||||
assert.equal(compacted.items[compacted.items.length - 2].seq, 0);
|
||||
assert.equal(compacted.items[compacted.items.length - 2].ver, 1);
|
||||
assert.equal(compacted.items[compacted.items.length - 2].next.length, 1);
|
||||
assert.equal(compacted.items[compacted.items.length - 2].next[0], 'A.0.0');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
describe('add', () => {
|
||||
it('adds an item to an empty list', (done) => {
|
||||
const list = new List('A');
|
||||
list.add("hello1")
|
||||
const item = list.items[0];
|
||||
assert.equal(list.id, 'A');
|
||||
assert.equal(list.seq, 0);
|
||||
assert.equal(list.ver, 1);
|
||||
assert.equal(list.items.length, 1);
|
||||
assert.equal(list._currentBatch.length, 1);
|
||||
assert.equal(list._items.length, 0);
|
||||
assert.equal(item, list._currentBatch[0]);
|
||||
assert.equal(item.id, 'A');
|
||||
assert.equal(item.seq, 0);
|
||||
assert.equal(item.ver, 0);
|
||||
assert.equal(item.data, 'hello1');
|
||||
done();
|
||||
});
|
||||
|
||||
it('adds 100 items to a list', (done) => {
|
||||
const list = new List('A');
|
||||
|
||||
for(let i = 1; i < 101; i ++) {
|
||||
list.add("hello" + i);
|
||||
}
|
||||
|
||||
assert.equal(list.id, 'A');
|
||||
assert.equal(list.seq, 0);
|
||||
assert.equal(list.ver, 100);
|
||||
assert.equal(list.items.length, 100);
|
||||
assert.equal(list._currentBatch.length, 100);
|
||||
assert.equal(list._items.length, 0);
|
||||
|
||||
const item = list.items[list.items.length - 1];
|
||||
assert.equal(item, list._currentBatch[list._currentBatch.length - 1]);
|
||||
assert.equal(item.id, 'A');
|
||||
assert.equal(item.seq, 0);
|
||||
assert.equal(item.ver, 99);
|
||||
assert.equal(item.data, 'hello100');
|
||||
assert.equal(item.next, 'A.0.98');
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
describe('join', () => {
|
||||
it('increases the sequence and resets the version if other list has the same or higher sequence', (done) => {
|
||||
const list1 = new List('A');
|
||||
const list2 = new List('B');
|
||||
|
||||
list2.seq = 7;
|
||||
list1.add("helloA1")
|
||||
|
||||
assert.equal(list1.id, 'A');
|
||||
assert.equal(list1.seq, 0);
|
||||
assert.equal(list1.ver, 1);
|
||||
|
||||
list2.add("helloB1")
|
||||
list1.join(list2);
|
||||
|
||||
assert.equal(list1.id, 'A');
|
||||
assert.equal(list1.seq, 8);
|
||||
assert.equal(list1.ver, 0);
|
||||
done();
|
||||
});
|
||||
|
||||
it('increases the sequence by one if other list has lower sequence', (done) => {
|
||||
const list1 = new List('A');
|
||||
const list2 = new List('B');
|
||||
list1.seq = 4;
|
||||
list2.seq = 1;
|
||||
list2.add("helloB1")
|
||||
list1.join(list2);
|
||||
assert.equal(list1.id, 'A');
|
||||
assert.equal(list1.seq, 5);
|
||||
assert.equal(list1.ver, 0);
|
||||
done();
|
||||
});
|
||||
|
||||
it('finds the next head when adding a new element', (done) => {
|
||||
const list1 = new List('A');
|
||||
list1.add("helloA1")
|
||||
list1.add("helloA2")
|
||||
list1.add("helloA3")
|
||||
|
||||
// console.log(list1.toString())
|
||||
assert.equal(list1._currentBatch.length, 3);
|
||||
assert.equal(list1._currentBatch[2].next.length, 1);
|
||||
assert.equal(list1._currentBatch[2].next[0], 'A.0.1');
|
||||
done();
|
||||
});
|
||||
|
||||
it('finds the next heads after joining a list with another', (done) => {
|
||||
const list1 = new List('A');
|
||||
const list2 = new List('B');
|
||||
list1.add("helloA1")
|
||||
list2.add("helloB1")
|
||||
list2.add("helloB2")
|
||||
list1.join(list2);
|
||||
list1.add("helloA2")
|
||||
|
||||
assert.equal(list1._currentBatch.length, 1);
|
||||
assert.equal(list1._currentBatch[0].next.length, 2);
|
||||
assert.equal(list1._currentBatch[0].next[0], 'A.0.0');
|
||||
assert.equal(list1._currentBatch[0].next[1], 'B.0.1');
|
||||
done();
|
||||
});
|
||||
|
||||
it('finds the next head after a two-way join', (done) => {
|
||||
const list1 = new List('A');
|
||||
const list2 = new List('B');
|
||||
list1.add("helloA1")
|
||||
list2.add("helloB1")
|
||||
list2.add("helloB2")
|
||||
list1.join(list2);
|
||||
|
||||
list1.add("helloA2")
|
||||
list1.add("helloA3")
|
||||
|
||||
assert.equal(list1._currentBatch.length, 2);
|
||||
assert.equal(list1._currentBatch[1].next.length, 1);
|
||||
assert.equal(list1._currentBatch[1].next[0], 'A.1.0');
|
||||
done();
|
||||
});
|
||||
|
||||
it('find sthe next heads after join two-way join', (done) => {
|
||||
const list1 = new List('A');
|
||||
const list2 = new List('B');
|
||||
list1.add("helloA1")
|
||||
list1.add("helloA2")
|
||||
list2.add("helloB1")
|
||||
list2.add("helloB2")
|
||||
list1.join(list2);
|
||||
|
||||
list1.add("helloA3")
|
||||
|
||||
list1.join(list2);
|
||||
|
||||
list1.add("helloA4")
|
||||
list1.add("helloA5")
|
||||
|
||||
const lastItem = list1.items[list1.items.length - 1];
|
||||
// console.log(list1.toString())
|
||||
assert.equal(list1.items.length, 7);
|
||||
assert.equal(lastItem.next.length, 1);
|
||||
assert.equal(lastItem.next[0], 'A.2.0');
|
||||
done();
|
||||
});
|
||||
|
||||
it('joins list of one item with list of two items', (done) => {
|
||||
const list1 = new List('A');
|
||||
const list2 = new List('B');
|
||||
list1.add("helloA1")
|
||||
list2.add("helloB1")
|
||||
list2.add("helloB2")
|
||||
list1.join(list2);
|
||||
|
||||
// console.log(list1)
|
||||
// console.log(list2)
|
||||
|
||||
assert.equal(list1.id, 'A');
|
||||
assert.equal(list1.seq, 1);
|
||||
assert.equal(list1.ver, 0);
|
||||
assert.equal(list1._currentBatch.length, 0);
|
||||
assert.equal(list1._items.length, 3);
|
||||
assert.equal(list1._items[list1._items.length - 1].id, 'B');
|
||||
assert.equal(list1._items[list1._items.length - 1].seq, 0);
|
||||
assert.equal(list1._items[list1._items.length - 1].ver, 1);
|
||||
assert.equal(list1._items[list1._items.length - 1].data, 'helloB2');
|
||||
done();
|
||||
});
|
||||
|
||||
it('joins lists two ways', (done) => {
|
||||
const list1 = new List('A');
|
||||
const list2 = new List('B');
|
||||
list1.add("helloA1")
|
||||
list1.add("helloA2")
|
||||
list2.add("helloB1")
|
||||
list2.add("helloB2")
|
||||
list1.join(list2);
|
||||
list2.join(list1);
|
||||
|
||||
assert.equal(list1.id, 'A');
|
||||
assert.equal(list1.seq, 1);
|
||||
assert.equal(list1.ver, 0);
|
||||
assert.equal(list1._currentBatch.length, 0);
|
||||
assert.equal(list1._items.length, 4);
|
||||
assert.equal(list1._items[list1._items.length - 1].id, 'B');
|
||||
assert.equal(list1._items[list1._items.length - 1].seq, 0);
|
||||
assert.equal(list1._items[list1._items.length - 1].ver, 1);
|
||||
assert.equal(list1._items[list1._items.length - 1].data, 'helloB2');
|
||||
|
||||
assert.equal(list2.id, 'B');
|
||||
assert.equal(list2.seq, 2);
|
||||
assert.equal(list2.ver, 0);
|
||||
assert.equal(list2._currentBatch.length, 0);
|
||||
assert.equal(list2._items.length, 4);
|
||||
assert.equal(list2._items[list2._items.length - 1].id, 'A');
|
||||
assert.equal(list2._items[list2._items.length - 1].seq, 0);
|
||||
assert.equal(list2._items[list2._items.length - 1].ver, 1);
|
||||
assert.equal(list2._items[list2._items.length - 1].data, 'helloA2');
|
||||
done();
|
||||
});
|
||||
|
||||
it('joins lists twice', (done) => {
|
||||
const list1 = new List('A');
|
||||
const list2 = new List('B');
|
||||
|
||||
list1.add("helloA1")
|
||||
list2.add("helloB1")
|
||||
list2.join(list1);
|
||||
|
||||
list1.add("helloA2")
|
||||
list2.add("helloB2")
|
||||
list2.join(list1);
|
||||
|
||||
assert.equal(list2.id, 'B');
|
||||
assert.equal(list2.seq, 2);
|
||||
assert.equal(list2.ver, 0);
|
||||
assert.equal(list2._currentBatch.length, 0);
|
||||
assert.equal(list2._items.length, 4);
|
||||
assert.equal(list2._items[1].id, 'A');
|
||||
assert.equal(list2._items[1].seq, 0);
|
||||
assert.equal(list2._items[1].ver, 0);
|
||||
assert.equal(list2._items[1].data, 'helloA1');
|
||||
assert.equal(list2._items[list2._items.length - 1].id, 'A');
|
||||
assert.equal(list2._items[list2._items.length - 1].seq, 0);
|
||||
assert.equal(list2._items[list2._items.length - 1].ver, 1);
|
||||
assert.equal(list2._items[list2._items.length - 1].data, 'helloA2');
|
||||
done();
|
||||
});
|
||||
|
||||
it('joins 4 lists', (done) => {
|
||||
const list1 = new List('A');
|
||||
const list2 = new List('B');
|
||||
const list3 = new List('C');
|
||||
const list4 = new List('D');
|
||||
|
||||
list1.add("helloA1")
|
||||
list2.add("helloB1")
|
||||
// list2.join(list1);
|
||||
|
||||
list1.add("helloA2")
|
||||
list2.add("helloB2")
|
||||
// list2.join(list1);
|
||||
|
||||
list3.add("helloC1")
|
||||
list4.add("helloD1")
|
||||
// list2.join(list1);
|
||||
|
||||
list3.add("helloC2")
|
||||
list4.add("helloD2")
|
||||
list1.join(list2);
|
||||
list1.join(list3);
|
||||
list1.join(list4);
|
||||
|
||||
assert.equal(list1.id, 'A');
|
||||
// assert.equal(list1.seq, 1);
|
||||
// assert.equal(list1.ver, 1);
|
||||
assert.equal(list1._currentBatch.length, 0);
|
||||
assert.equal(list1._items.length, 8);
|
||||
assert.equal(list1._items[1].id, 'A');
|
||||
assert.equal(list1._items[1].seq, 0);
|
||||
assert.equal(list1._items[1].ver, 1);
|
||||
assert.equal(list1._items[1].data, 'helloA2');
|
||||
assert.equal(list1._items[list1._items.length - 1].id, 'D');
|
||||
assert.equal(list1._items[list1._items.length - 1].seq, 0);
|
||||
assert.equal(list1._items[list1._items.length - 1].ver, 1);
|
||||
assert.equal(list1._items[list1._items.length - 1].data, 'helloD2');
|
||||
done();
|
||||
});
|
||||
|
||||
it('joins 4 lists sequentally', (done) => {
|
||||
const list1 = new List('A');
|
||||
const list2 = new List('B');
|
||||
const list3 = new List('C');
|
||||
const list4 = new List('D');
|
||||
|
||||
list1.add("helloA1")
|
||||
list1.join(list2);
|
||||
list2.add("helloB1")
|
||||
list2.join(list1);
|
||||
|
||||
list1.add("helloA2")
|
||||
list2.add("helloB2")
|
||||
list1.join(list3);
|
||||
list3.join(list1);
|
||||
|
||||
list3.add("helloC1")
|
||||
list4.add("helloD1")
|
||||
|
||||
list3.add("helloC2")
|
||||
list4.add("helloD2")
|
||||
|
||||
list1.join(list3);
|
||||
list1.join(list2);
|
||||
list4.join(list2);
|
||||
list4.join(list1);
|
||||
list4.join(list3);
|
||||
|
||||
list4.add("helloD3")
|
||||
list4.add("helloD4")
|
||||
|
||||
// console.log(list4.toString());
|
||||
assert.equal(list4.id, 'D');
|
||||
assert.equal(list4.seq, 7);
|
||||
assert.equal(list4.ver, 2);
|
||||
assert.equal(list4._currentBatch.length, 2);
|
||||
assert.equal(list4._items.length, 8);
|
||||
assert.equal(list4._items[1].id, 'D');
|
||||
assert.equal(list4._items[1].seq, 0);
|
||||
assert.equal(list4._items[1].ver, 1);
|
||||
assert.equal(list4._items[1].data, 'helloD2');
|
||||
assert.equal(list4._items[list1._items.length - 1].id, 'A');
|
||||
assert.equal(list4._items[list1._items.length - 1].seq, 1);
|
||||
assert.equal(list4._items[list1._items.length - 1].ver, 0);
|
||||
assert.equal(list4._items[list1._items.length - 1].data, 'helloA2');
|
||||
assert.equal(list4.items[list4.items.length - 1].id, 'D');
|
||||
assert.equal(list4.items[list4.items.length - 1].seq, 7);
|
||||
assert.equal(list4.items[list4.items.length - 1].ver, 1);
|
||||
assert.equal(list4.items[list4.items.length - 1].data, 'helloD4');
|
||||
done();
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
it('solves a graph', (done) => {
|
||||
done();
|
||||
});
|
||||
|
||||
});
|
||||
File diff suppressed because it is too large
Load Diff
345
test1.js
Normal file
345
test1.js
Normal file
@@ -0,0 +1,345 @@
|
||||
'use strict';
|
||||
|
||||
const _ = require('lodash');
|
||||
const Timer = require('./examples/Timer');
|
||||
|
||||
Array.prototype.allEqual = function(val) {
|
||||
for(var i = 1; i < this.length; i++) {
|
||||
if(this[i] !== val || this[i] !== this[0])
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!Array.prototype.last){
|
||||
Array.prototype.last = function(){
|
||||
return this[this.length - 1];
|
||||
};
|
||||
}
|
||||
|
||||
class Node {
|
||||
constructor(id, seq, ver, data, next) {
|
||||
this.id = id;
|
||||
this.seq = seq;
|
||||
this.ver = ver;
|
||||
this.data = data || null;
|
||||
this.next = next ? next.map((f) => f.compactId ? f.compactId : f) : [];
|
||||
}
|
||||
|
||||
get compactId() {
|
||||
return "" + this.id + "." + this.seq + "." + this.ver;
|
||||
}
|
||||
|
||||
compact() {
|
||||
return { id: this.id, seq: this.seq, ver: this.ver, data: this.data, next: this.next }
|
||||
// return { id: this.id, seq: this.seq, ver: this.ver, data: this.data, next: this.next.map((f) => f.compactId) }
|
||||
}
|
||||
|
||||
// static parseCompact(t) {
|
||||
// let v = t.split('.');
|
||||
// return { id: v[0], seq: parseInt(v[1]), ver: parseInt(v[2]) };
|
||||
// }
|
||||
|
||||
}
|
||||
|
||||
class List {
|
||||
constructor(id) {
|
||||
this.id = id;
|
||||
this.seq = 0;
|
||||
this.ver = 0;
|
||||
this._items = [];
|
||||
this._currentBatch = [];
|
||||
this._referenced = [];
|
||||
}
|
||||
|
||||
static fromJson(json) {
|
||||
let list = new List(json.id);
|
||||
list.seq = json.seq;
|
||||
list.ver = json.ver;
|
||||
// list._items = json.items.map((f) => new Node(f.id, f.seq, f.ver, f.data, f.next));
|
||||
list._items = _.uniqWith(json.items.map((f) => {
|
||||
// console.log(JSON.stringify(f.next, null, 2));
|
||||
return new Node(f.id, f.seq, f.ver, f.data, f.next);
|
||||
}), _.isEqual);
|
||||
return list;
|
||||
}
|
||||
|
||||
get items() {
|
||||
return this._items.concat(this._currentBatch);
|
||||
}
|
||||
|
||||
toJson() {
|
||||
return {
|
||||
id: this.id,
|
||||
seq: this.seq,
|
||||
ver: this.ver,
|
||||
items: this._currentBatch.map((f) => f.compact())
|
||||
}
|
||||
}
|
||||
|
||||
toString() {
|
||||
const items = this.items.map((f) => JSON.stringify(f.compact())).join("\n");
|
||||
return `
|
||||
id: ${this.id},
|
||||
seq: ${this.seq},
|
||||
ver: ${this.ver},
|
||||
items: \n${items}
|
||||
`
|
||||
}
|
||||
|
||||
add(data) {
|
||||
// var ii = this.items.map((f) => f.compact());
|
||||
// console.log("--->", this.seq, this.ver)
|
||||
const heads = this._findHeads(this.items);//.map((f) => f.compact());
|
||||
// const heads = this._findHeads(this.items)
|
||||
// console.log("jjjj", this._referenced, "\n\n")
|
||||
// console.log("jjjj", this.items, "\n\n")
|
||||
const node = new Node(this.id, this.seq, this.ver, data, heads);
|
||||
// console.log("aaaa", node)
|
||||
this._currentBatch.push(node);
|
||||
// this._referenced.push(node);
|
||||
this.ver ++;
|
||||
}
|
||||
|
||||
join(other) {
|
||||
let ms;
|
||||
|
||||
if(other.seq && other.seq > this.seq) {
|
||||
this.seq = other.seq + 1;
|
||||
this.ver = 0;
|
||||
} else {
|
||||
this.seq = this.seq + 1;
|
||||
this.ver = 0;
|
||||
}
|
||||
// if(other.items.last() && other.items.last().seq > this.seq) {
|
||||
// this.seq = other.seq + 1;
|
||||
// this.ver = 0;
|
||||
// }
|
||||
|
||||
// return items that are only in this._currentBatch
|
||||
const current = _.differenceWith(this._currentBatch, this._items, this._equals);
|
||||
// return items that are only in other.items
|
||||
|
||||
let timer = new Timer(true);
|
||||
const others = _.differenceWith(other.items, this._items, this._equals);
|
||||
ms = timer.stop(true);
|
||||
// return unique items from e and d
|
||||
// const final = _.unionWith(others, this._currentBatch, this._equals);
|
||||
const final = _.unionWith(current, others, this._equals);
|
||||
// append new items to all items
|
||||
this._items = this._items.concat(final);
|
||||
|
||||
this._currentBatch = [];
|
||||
if(ms > 20) {
|
||||
console.log("OVER 20MS!!", other.items.length)
|
||||
console.log(`join took ${timer.stop(true)} ms`);
|
||||
}
|
||||
}
|
||||
|
||||
_findHeads(list) {
|
||||
let timer = new Timer(true);
|
||||
let ms;
|
||||
const grouped = _.groupBy(list, 'id');
|
||||
// const heads = Object.keys(grouped).sort((a, b) => a === this.id ? -1 : (a < b ? 1 : 0)).map((g) => grouped[g].last());
|
||||
const heads = Object.keys(grouped).map((g) => grouped[g].last());
|
||||
// const heads = _.differenceWith(list, this._referenced);
|
||||
// console.log("HEADS", JSON.stringify(heads));
|
||||
// const cleaned = heads.filter((e) => !this._isReferencedInChain(list, e, this._referenced, 0));
|
||||
const cleaned = heads.filter((e) => !this._isReferencedInChain2(list, e));
|
||||
// const cleaned = heads;
|
||||
// console.log("CLEANED", JSON.stringify(cleaned), "\n");
|
||||
// this._referenced = [];
|
||||
ms = timer.stop(true);
|
||||
if(ms > 20) {
|
||||
console.log("OVER 20MS!!")
|
||||
console.log(`_findHeads took ${ms} ms`);
|
||||
}
|
||||
// console.log(`_findHeads took ${ms} ms`);
|
||||
return cleaned;
|
||||
}
|
||||
|
||||
|
||||
_isReferencedInChain2(all, item) {
|
||||
// console.log("item:", item);
|
||||
let isReferenced = all.find((e) => this._references(e, item)) !== undefined;
|
||||
if(!isReferenced) {
|
||||
// console.log("check children", item.next)
|
||||
let childHasRef = item.next.map((f) => {
|
||||
const next = all.find((e) => this._equals(e, f));
|
||||
const res = next ? this._isReferencedInChain2(all, next) : false;
|
||||
return _.find(res, (e) => e === true) !== undefined;
|
||||
});
|
||||
isReferenced = _.find(childHasRef, (e) => e === true) !== undefined;
|
||||
// console.log("children have ref", isReferenced)
|
||||
}
|
||||
|
||||
// console.log("red:", isReferenced);
|
||||
return isReferenced;
|
||||
}
|
||||
|
||||
// _parse(t) {
|
||||
// let v = t.split('.');
|
||||
// return { id: v[0], seq: parseInt(v[1]), ver: parseInt(v[2]) };
|
||||
// }
|
||||
|
||||
_equals(a, b) {
|
||||
return a.id == b.id && a.seq == b.seq && a.ver == b.ver;
|
||||
}
|
||||
|
||||
_references(a, b) {
|
||||
// return _.includes(a.next, b.compactId);
|
||||
// faster than _.includes()
|
||||
for(let i = 0; i < a.next.length; i ++) {
|
||||
if(b.compactId === a.next[i]) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
_isReferencedInChain(all, item, next2, refs, depth) {
|
||||
console.log("...item:", item);
|
||||
depth += 1;
|
||||
let timer = new Timer(true);
|
||||
let ms;
|
||||
|
||||
if(!item)
|
||||
return false;
|
||||
|
||||
// let isReferenced = refs.find((e) => e.id == item.id && e.seq == item.seq && e.ver == item.ver) !== undefined;
|
||||
let isReferenced = refs.find((e) => this._equals(e, item)) !== undefined;
|
||||
|
||||
if(isReferenced)
|
||||
console.log("ref", item)
|
||||
|
||||
if(isReferenced)
|
||||
return true;
|
||||
|
||||
// if(item.id == 'C' && item.seq == 3 && item.ver == 1) {
|
||||
// console.log("!!!!", refs,"\n", item)
|
||||
// }
|
||||
refs.splice(0, 0, item);
|
||||
|
||||
var check = (o) => {
|
||||
const next = all.find((e) => this._equals(e, this._parse(o)));
|
||||
if(!next)
|
||||
return false;
|
||||
|
||||
return this._isReferencedInChain(all, item, next, refs, depth);
|
||||
};
|
||||
|
||||
// refs.push(item);
|
||||
|
||||
// console.log(item.next);
|
||||
// let res = item.next.map(this._parse).map(check);
|
||||
let res = item.next.map(check);
|
||||
|
||||
if(depth > 5) {
|
||||
console.log("WTF!!!", depth, item, refs, res);
|
||||
}
|
||||
|
||||
// refs.splice(0, 0, item);
|
||||
|
||||
// if(item.id == 'A' && item.seq == 0 && item.ver == 1) {
|
||||
// console.log("????", isReferenced, refs, "\n\n", item, "\n\n", res)
|
||||
// }
|
||||
|
||||
// if(item.id == 'A' && item.seq == 1 && item.ver == 2) {
|
||||
// console.log("+++", res)
|
||||
// }
|
||||
|
||||
// const hasRef2 = _.find(res, (e) => e === true) !== undefined;
|
||||
// const hasRef2 = res.allEqual(true)
|
||||
|
||||
ms = timer.stop(true);
|
||||
if(ms > 20) {
|
||||
console.log("OVER 20MS!!", refs.length)
|
||||
console.log(`_isReferencedInChain took ${ms} ms`);
|
||||
}
|
||||
let hasRef2 = _.find(res, (e) => e === true) !== undefined;
|
||||
// console.log("...res:", hasRef2, res);
|
||||
// if(hasRef2)
|
||||
// refs.splice(0, 0, item);
|
||||
|
||||
return hasRef2;
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
var run = () => {
|
||||
var redis = require("redis");
|
||||
this.client1 = redis.createClient({ host: "localhost", port: 6379 });
|
||||
this.client2 = redis.createClient({ host: "localhost", port: 6379 });
|
||||
var hash = "ccc"
|
||||
this.client1.subscribe(hash);
|
||||
this.client1.subscribe(hash);
|
||||
|
||||
|
||||
let listA = new List("A");
|
||||
let listB = new List("B");
|
||||
let listC = new List("C");
|
||||
|
||||
const handleMessage = (hash, event) => {
|
||||
const l = List.fromJson(JSON.parse(event));
|
||||
// console.log("LIST", l);
|
||||
|
||||
// var l = List.fromJson(JSON.parse(event));
|
||||
if(l.id === 'A') {
|
||||
listB.join(l);
|
||||
listC.join(l);
|
||||
// console.log(listB);
|
||||
} else if(l.id === 'B') {
|
||||
listA.join(l);
|
||||
listC.join(l);
|
||||
// console.log("ListA:", listA);
|
||||
} else if(l.id === 'C') {
|
||||
listA.join(l);
|
||||
// console.log("LIST", event);
|
||||
console.log("Items:", listA.items.length);
|
||||
// console.log(JSON.stringify(listA, null, 1));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
this.client1.on("message", handleMessage);
|
||||
this.client2.on("message", handleMessage);
|
||||
|
||||
setInterval(() => {
|
||||
// listC.join(listB);
|
||||
// listC.join(listA);
|
||||
}, 5000);
|
||||
|
||||
let h = 0;
|
||||
setInterval(() => {
|
||||
listC.add("C--"+h);
|
||||
this.client2.publish(hash, JSON.stringify(listC.toJson()));
|
||||
h++;
|
||||
}, 1000);
|
||||
|
||||
let i = 0;
|
||||
setInterval(() => {
|
||||
let a = 0;
|
||||
// for(let a = 0; a < 100; a ++) {
|
||||
listB.add("B--"+(i+a));
|
||||
// }
|
||||
this.client2.publish(hash, JSON.stringify(listB.toJson()));
|
||||
i++;
|
||||
}, 10);
|
||||
|
||||
let k = 0;
|
||||
setInterval(() => {
|
||||
listA.add("A--"+k);
|
||||
k++;
|
||||
listA.add("A--"+k);
|
||||
k++;
|
||||
listA.add("A--"+k);
|
||||
k++;
|
||||
this.client2.publish(hash, JSON.stringify(listA.toJson()));
|
||||
}, 100);
|
||||
};
|
||||
|
||||
run();
|
||||
|
||||
module.exports = {
|
||||
Node: Node,
|
||||
List: List
|
||||
};
|
||||
Reference in New Issue
Block a user