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){
state.clock++;
yield* this.setState(state);
yield* this.setOperation(op);
this.os.add(op);
this.store.operationAdded(op);
return true;
} else if (op.id[1] < state.clock) {
@ -23,7 +23,6 @@ class AbstractTransaction { //eslint-disable-line no-unused-vars
}
}
}
Y.AbstractTransaction = AbstractTransaction;
type Listener = {
f : GeneratorFunction, // is called when all operations are available
@ -187,4 +186,3 @@ class AbstractOperationStore { //eslint-disable-line no-unused-vars
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;
}
*setOperation (op) {
if (op.struct === "Insert" && op.right === undefined) {
throw new Error("here!");
}
this.os[JSON.stringify(op.id)] = op;
// TODO: you can remove this step! probs..
var n = this.os.findNode(op.id);
n.val = op;
return op;
}
*getOperation (id) {
var op = this.os[JSON.stringify(id)];
var op = this.os.find(id);
if (op == null) {
throw new Error("Op does not exist..");
} else {
@ -44,7 +43,7 @@ Y.Memory = (function(){ //eslint-disable-line no-unused-vars
}
}
*removeOperation (id) {
delete this.os[JSON.stringify(id)];
this.os.delete(id);
}
*setState (state : State) : State {
this.ss[state.user] = state.clock;
@ -61,7 +60,6 @@ Y.Memory = (function(){ //eslint-disable-line no-unused-vars
}
*getStateVector () : StateVector {
var stateVector = [];
for (var user in this.ss) {
var clock = this.ss[user];
stateVector.push({
@ -75,6 +73,7 @@ Y.Memory = (function(){ //eslint-disable-line no-unused-vars
return this.ss;
}
*getOperations (startSS : StateSet) {
// TODO: use bounds here!
if (startSS == null){
startSS = {};
}
@ -89,15 +88,15 @@ Y.Memory = (function(){ //eslint-disable-line no-unused-vars
var startPos = startSS[user] || 0;
var endPos = endState.clock;
for (var clock = startPos; clock <= endPos; clock++) {
var op = this.os[JSON.stringify([user, clock])];
if (op != null) {
op = Struct[op.struct].encode(op);
ops.push(yield* this.makeOperationReady.call(this, startSS, op));
}
}
this.os.iterate([user, startPos], [user, endPos], function(op){//eslint-disable-line
ops.push(Struct[op.struct].encode(op));
});
}
return ops;
var res = [];
for (var op of ops) {
res.push(yield* this.makeOperationReady.call(this, startSS, op));
}
return res;
}
*makeOperationReady (ss, op) {
// 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
constructor (y) {
super(y);
this.os = {};
this.os = new RBTree();
this.ss = {};
}
requestTransaction (makeGen : Function) {

View File

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

View File

@ -1,7 +1,7 @@
/* @flow */
/*eslint-env browser,jasmine,console */
var numberOfTests = 10009;
var numberOfRBTreeTests = 1000;
function itRedNodesDoNotHaveBlackChildren (tree) {
it("Red nodes do not have black children", function(){
@ -107,16 +107,15 @@ describe("RedBlack Tree", function(){
itRootNodeIsBlack(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 tree = new RBTree();
for(var i = 0; i < numberOfTests; i++) {
for(var i = 0; i < numberOfRBTreeTests; i++) {
var r = Math.random();
if (r < 0.8) {
var obj = Math.floor(Math.random() * numberOfTests * 10000);
var obj = Math.floor(Math.random() * numberOfRBTreeTests * 10000);
elements.push(obj);
tree.add({id: obj});
} 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);
}
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) {
var f = getRandom(transactions);
yield* f(root);
}
for(var i = 0; i < numberOfTests; i++) {
var r = getRandomNumber(100);
if (r >= 50) {
for(var i = 0; i < numberOfTransactions; i++) {
var r = Math.random();
if (r >= 0.9) {
// 10% chance to flush
users[0].connector.flushOne();
} else {
getRandom(users).transact(randomTransaction);
@ -53,8 +54,15 @@ function compareAllUsers(users){
u1.transact(t1);
u2.transact(t2);
expect(s1).toEqual(s2);
var db1 = u1.db.os;
var db2 = u2.db.os;
var db1 = [];
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) {
expect(db1[key]).toEqual(db2[key]);
}
@ -195,13 +203,13 @@ describe("Yjs", function(){
yield* map.val("getRandom(keys)", getRandomNumber());
}
];
it(`succeed after ${numberOfTests} actions with flush before transactions`, function(){
this.users[0].connector.flushAll(); // TODO: Remove!!
applyRandomTransactions(this.users, randomMapTransactions);
it(`succeed after ${numberOfYMapTests} actions with flush before transactions`, function(){
this.users[0].connector.flushAll();
applyRandomTransactions(this.users, randomMapTransactions, numberOfYMapTests);
compareAllUsers(this.users);
});
it(`succeed after ${numberOfTests} actions without flush before transactions`, function(){
applyRandomTransactions(this.users, randomMapTransactions);
it(`succeed after ${numberOfYMapTests} actions without flush before transactions`, function(){
applyRandomTransactions(this.users, randomMapTransactions, numberOfYMapTests);
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