use RBTree for in-memory storage

This commit is contained in:
Kevin Jahns 2015-07-08 21:25:36 +02:00
parent fe4564542b
commit a1026bc365
9 changed files with 48 additions and 43 deletions

View File

@ -13,7 +13,7 @@ class AbstractTransaction { //eslint-disable-line no-unused-vars
if (op.id[1] === state.clock){ if (op.id[1] === state.clock){
state.clock++; state.clock++;
yield* this.setState(state); yield* this.setState(state);
yield* this.setOperation(op); this.os.add(op);
this.store.operationAdded(op); this.store.operationAdded(op);
return true; return true;
} else if (op.id[1] < state.clock) { } else if (op.id[1] < state.clock) {
@ -23,7 +23,6 @@ class AbstractTransaction { //eslint-disable-line no-unused-vars
} }
} }
} }
Y.AbstractTransaction = AbstractTransaction;
type Listener = { type Listener = {
f : GeneratorFunction, // is called when all operations are available f : GeneratorFunction, // is called when all operations are available
@ -187,4 +186,3 @@ class AbstractOperationStore { //eslint-disable-line no-unused-vars
ls.push(f); ls.push(f);
} }
} }
Y.AbstractOperationStore = AbstractOperationStore;

View File

@ -29,14 +29,13 @@ Y.Memory = (function(){ //eslint-disable-line no-unused-vars
this.os = store.os; this.os = store.os;
} }
*setOperation (op) { *setOperation (op) {
if (op.struct === "Insert" && op.right === undefined) { // TODO: you can remove this step! probs..
throw new Error("here!"); var n = this.os.findNode(op.id);
} n.val = op;
this.os[JSON.stringify(op.id)] = op;
return op; return op;
} }
*getOperation (id) { *getOperation (id) {
var op = this.os[JSON.stringify(id)]; var op = this.os.find(id);
if (op == null) { if (op == null) {
throw new Error("Op does not exist.."); throw new Error("Op does not exist..");
} else { } else {
@ -44,7 +43,7 @@ Y.Memory = (function(){ //eslint-disable-line no-unused-vars
} }
} }
*removeOperation (id) { *removeOperation (id) {
delete this.os[JSON.stringify(id)]; this.os.delete(id);
} }
*setState (state : State) : State { *setState (state : State) : State {
this.ss[state.user] = state.clock; this.ss[state.user] = state.clock;
@ -61,7 +60,6 @@ Y.Memory = (function(){ //eslint-disable-line no-unused-vars
} }
*getStateVector () : StateVector { *getStateVector () : StateVector {
var stateVector = []; var stateVector = [];
for (var user in this.ss) { for (var user in this.ss) {
var clock = this.ss[user]; var clock = this.ss[user];
stateVector.push({ stateVector.push({
@ -75,6 +73,7 @@ Y.Memory = (function(){ //eslint-disable-line no-unused-vars
return this.ss; return this.ss;
} }
*getOperations (startSS : StateSet) { *getOperations (startSS : StateSet) {
// TODO: use bounds here!
if (startSS == null){ if (startSS == null){
startSS = {}; startSS = {};
} }
@ -89,15 +88,15 @@ Y.Memory = (function(){ //eslint-disable-line no-unused-vars
var startPos = startSS[user] || 0; var startPos = startSS[user] || 0;
var endPos = endState.clock; var endPos = endState.clock;
for (var clock = startPos; clock <= endPos; clock++) { this.os.iterate([user, startPos], [user, endPos], function(op){//eslint-disable-line
var op = this.os[JSON.stringify([user, clock])]; ops.push(Struct[op.struct].encode(op));
if (op != null) { });
op = Struct[op.struct].encode(op);
ops.push(yield* this.makeOperationReady.call(this, startSS, op));
} }
var res = [];
for (var op of ops) {
res.push(yield* this.makeOperationReady.call(this, startSS, op));
} }
} return res;
return ops;
} }
*makeOperationReady (ss, op) { *makeOperationReady (ss, op) {
// instead of ss, you could use currSS (a ss that increments when you add an operation) // instead of ss, you could use currSS (a ss that increments when you add an operation)
@ -119,7 +118,7 @@ Y.Memory = (function(){ //eslint-disable-line no-unused-vars
class OperationStore extends AbstractOperationStore { //eslint-disable-line no-undef class OperationStore extends AbstractOperationStore { //eslint-disable-line no-undef
constructor (y) { constructor (y) {
super(y); super(y);
this.os = {}; this.os = new RBTree();
this.ss = {}; this.ss = {};
} }
requestTransaction (makeGen : Function) { requestTransaction (makeGen : Function) {

View File

@ -1,6 +1,4 @@
class N { class N {
// A created node is always red! // A created node is always red!
constructor (val) { constructor (val) {

View File

@ -1,7 +1,7 @@
/* @flow */ /* @flow */
/*eslint-env browser,jasmine,console */ /*eslint-env browser,jasmine,console */
var numberOfTests = 10009; var numberOfRBTreeTests = 1000;
function itRedNodesDoNotHaveBlackChildren (tree) { function itRedNodesDoNotHaveBlackChildren (tree) {
it("Red nodes do not have black children", function(){ it("Red nodes do not have black children", function(){
@ -107,16 +107,15 @@ describe("RedBlack Tree", function(){
itRootNodeIsBlack(tree, []); itRootNodeIsBlack(tree, []);
itBlackHeightOfSubTreesAreEqual(tree, []); itBlackHeightOfSubTreesAreEqual(tree, []);
itRedNodesDoNotHaveBlackChildren(tree, []);
}); });
describe(`After adding&deleting (0.8/0.2) ${numberOfTests} times`, function () { describe(`After adding&deleting (0.8/0.2) ${numberOfRBTreeTests} times`, function () {
var elements = []; var elements = [];
var tree = new RBTree(); var tree = new RBTree();
for(var i = 0; i < numberOfTests; i++) { for(var i = 0; i < numberOfRBTreeTests; i++) {
var r = Math.random(); var r = Math.random();
if (r < 0.8) { if (r < 0.8) {
var obj = Math.floor(Math.random() * numberOfTests * 10000); var obj = Math.floor(Math.random() * numberOfRBTreeTests * 10000);
elements.push(obj); elements.push(obj);
tree.add({id: obj}); tree.add({id: obj});
} else if (elements.length > 0) { } else if (elements.length > 0) {

View File

@ -382,5 +382,3 @@ var Struct = {
} }
} }
}; };
Y.Struct = Struct;

View File

@ -31,3 +31,7 @@ class Y { //eslint-disable-line no-unused-vars
}; };
} }
} }
Y.AbstractTransaction = AbstractTransaction;
Y.AbstractOperationStore = AbstractOperationStore;
Y.Struct = Struct;

View File

@ -21,16 +21,17 @@ function getRandomNumber(n) {
return Math.floor(Math.random() * n); return Math.floor(Math.random() * n);
} }
var keys = ["a", "b", "c", "d", "e", "f", 1, 2, 3, 4, 5, 6]; var keys = ["a", "b", "c", "d", "e", "f", 1, 2, 3, 4, 5, 6];
var numberOfTests = 500; var numberOfYMapTests = 20;
function applyRandomTransactions (users, transactions) { function applyRandomTransactions (users, transactions, numberOfTransactions) {
function* randomTransaction (root) { function* randomTransaction (root) {
var f = getRandom(transactions); var f = getRandom(transactions);
yield* f(root); yield* f(root);
} }
for(var i = 0; i < numberOfTests; i++) { for(var i = 0; i < numberOfTransactions; i++) {
var r = getRandomNumber(100); var r = Math.random();
if (r >= 50) { if (r >= 0.9) {
// 10% chance to flush
users[0].connector.flushOne(); users[0].connector.flushOne();
} else { } else {
getRandom(users).transact(randomTransaction); getRandom(users).transact(randomTransaction);
@ -53,8 +54,15 @@ function compareAllUsers(users){
u1.transact(t1); u1.transact(t1);
u2.transact(t2); u2.transact(t2);
expect(s1).toEqual(s2); expect(s1).toEqual(s2);
var db1 = u1.db.os; var db1 = [];
var db2 = u2.db.os; var db2 = [];
u1.db.os.iterate(null, null, function(o){//eslint-disable-line
db1.push(o);
});
u2.db.os.iterate(null, null, function(o){//eslint-disable-line
db2.push(o);
});
for (var key in db1) { for (var key in db1) {
expect(db1[key]).toEqual(db2[key]); expect(db1[key]).toEqual(db2[key]);
} }
@ -195,13 +203,13 @@ describe("Yjs", function(){
yield* map.val("getRandom(keys)", getRandomNumber()); yield* map.val("getRandom(keys)", getRandomNumber());
} }
]; ];
it(`succeed after ${numberOfTests} actions with flush before transactions`, function(){ it(`succeed after ${numberOfYMapTests} actions with flush before transactions`, function(){
this.users[0].connector.flushAll(); // TODO: Remove!! this.users[0].connector.flushAll();
applyRandomTransactions(this.users, randomMapTransactions); applyRandomTransactions(this.users, randomMapTransactions, numberOfYMapTests);
compareAllUsers(this.users); compareAllUsers(this.users);
}); });
it(`succeed after ${numberOfTests} actions without flush before transactions`, function(){ it(`succeed after ${numberOfYMapTests} actions without flush before transactions`, function(){
applyRandomTransactions(this.users, randomMapTransactions); applyRandomTransactions(this.users, randomMapTransactions, numberOfYMapTests);
compareAllUsers(this.users); compareAllUsers(this.users);
}); });
}); });

5
y.js

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long