Testing lists

This commit is contained in:
haad
2016-02-16 09:20:53 +01:00
parent 8b35764952
commit 0aa0e0afd3
10 changed files with 1630 additions and 582 deletions

3
.gitignore vendored
View File

@@ -3,3 +3,6 @@ node_modules/
debug.log
WIP/
.vagrant/
.idea/
isolate*.log
dump.rdb

198
MOCK.json Normal file
View 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"]}

View File

@@ -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;

View File

@@ -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"

View File

@@ -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;
}

View File

@@ -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
View 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
View 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
View 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
};