1
0
Fork 0
arangodb/js/server/tests/replication/fuzz/replication-fuzz.js

484 lines
16 KiB
JavaScript

/*jshint globalstrict:false, strict:false, unused: false */
/*global assertEqual, assertTrue, arango, ARGUMENTS */
////////////////////////////////////////////////////////////////////////////////
/// @brief test the replication
///
/// @file
///
/// DISCLAIMER
///
/// Copyright 2010-2012 triagens GmbH, Cologne, Germany
///
/// Licensed under the Apache License, Version 2.0 (the "License");
/// you may not use this file except in compliance with the License.
/// You may obtain a copy of the License at
///
/// http://www.apache.org/licenses/LICENSE-2.0
///
/// Unless required by applicable law or agreed to in writing, software
/// distributed under the License is distributed on an "AS IS" BASIS,
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
/// See the License for the specific language governing permissions and
/// limitations under the License.
///
/// Copyright holder is triAGENS GmbH, Cologne, Germany
///
/// @author Jan Steemann
/// @author Copyright 2017, triAGENS GmbH, Cologne, Germany
////////////////////////////////////////////////////////////////////////////////
var jsunity = require("jsunity");
var arangodb = require("@arangodb");
var db = arangodb.db;
var replication = require("@arangodb/replication");
let compareTicks = replication.compareTicks;
var console = require("console");
var internal = require("internal");
var masterEndpoint = arango.getEndpoint();
var slaveEndpoint = ARGUMENTS[0];
////////////////////////////////////////////////////////////////////////////////
/// @brief test suite
////////////////////////////////////////////////////////////////////////////////
function ReplicationSuite() {
'use strict';
var cn = "UnitTestsReplication";
var connectToMaster = function() {
arango.reconnect(masterEndpoint, db._name(), "root", "");
db._flushCache();
};
var connectToSlave = function() {
arango.reconnect(slaveEndpoint, db._name(), "root", "");
db._flushCache();
};
var collectionChecksum = function(name) {
return db._collection(name).checksum(false, true).checksum;
};
var collectionCount = function(name) {
return db._collection(name).count();
};
var compare = function(masterFunc, masterFunc2, slaveFuncFinal) {
var state = {};
assertEqual(cn, db._name());
db._flushCache();
masterFunc(state);
connectToSlave();
assertEqual(cn, db._name());
var syncResult = replication.sync({
endpoint: masterEndpoint,
username: "root",
password: "",
verbose: true,
includeSystem: false,
keepBarrier: true,
requireFromPresent: true,
incremental: true
});
assertTrue(syncResult.hasOwnProperty('lastLogTick'));
connectToMaster();
masterFunc2(state);
// use lastLogTick as of now
state.lastLogTick = replication.logger.state().state.lastLogTick;
let applierConfiguration = {
endpoint: masterEndpoint,
username: "root",
password: ""
};
connectToSlave();
assertEqual(cn, db._name());
replication.applier.properties(applierConfiguration);
replication.applier.start(syncResult.lastLogTick, syncResult.barrierId);
var printed = false;
while (true) {
var slaveState = replication.applier.state();
if (slaveState.state.lastError.errorNum > 0) {
console.log("slave has errored:", JSON.stringify(slaveState.state.lastError));
break;
}
if (!slaveState.state.running) {
console.log("slave is not running");
break;
}
if (compareTicks(slaveState.state.lastAppliedContinuousTick, state.lastLogTick) >= 0 ||
compareTicks(slaveState.state.lastProcessedContinuousTick, state.lastLogTick) >= 0) { // ||
// compareTicks(slaveState.state.lastAvailableContinuousTick, syncResult.lastLogTick) > 0) {
console.log("slave has caught up. state.lastLogTick:", state.lastLogTick, "slaveState.lastAppliedContinuousTick:", slaveState.state.lastAppliedContinuousTick, "slaveState.lastProcessedContinuousTick:", slaveState.state.lastProcessedContinuousTick);
break;
}
if (!printed) {
console.log("waiting for slave to catch up");
printed = true;
}
internal.wait(0.5, false);
}
db._flushCache();
slaveFuncFinal(state);
};
return {
setUp: function() {
db._useDatabase("_system");
connectToMaster();
try {
db._dropDatabase(cn);
} catch (err) {}
db._createDatabase(cn);
db._useDatabase(cn);
db._useDatabase("_system");
connectToSlave();
try {
db._dropDatabase(cn);
} catch (err) {}
db._createDatabase(cn);
},
tearDown: function() {
db._useDatabase("_system");
connectToMaster();
db._useDatabase(cn);
connectToSlave();
replication.applier.stop();
replication.applier.forget();
db._useDatabase("_system");
db._dropDatabase(cn);
},
testFuzz: function() {
db._useDatabase(cn);
connectToMaster();
compare(
function(state) {
},
function(state) {
let pickDatabase = function() {
db._useDatabase('_system');
let dbs;
while (true) {
dbs = db._databases().filter(function(db) { return db !== '_system'; });
if (dbs.length !== 0) {
break;
}
createDatabase();
}
let d = dbs[Math.floor(Math.random() * dbs.length)];
db._useDatabase(d);
};
let pickCollection = function() {
let collections;
while (true) {
collections = db._collections().filter(function(c) { return c.name()[0] !== '_' && c.type() === 2; });
if (collections.length !== 0) {
break;
}
return createCollection();
}
return collections[Math.floor(Math.random() * collections.length)];
};
let pickEdgeCollection = function() {
let collections;
while (true) {
collections = db._collections().filter(function(c) { return c.name()[0] !== '_' && c.type() === 3; });
if (collections.length !== 0) {
break;
}
return createEdgeCollection();
}
return collections[Math.floor(Math.random() * collections.length)];
};
let insert = function() {
let collection = pickCollection();
collection.insert({ value: Date.now() });
};
let remove = function() {
let collection = pickCollection();
if (collection.count() === 0) {
let k = collection.insert({});
collection.remove(k);
return;
}
collection.remove(collection.any());
};
let replace = function() {
let collection = pickCollection();
if (collection.count() === 0) {
let k = collection.insert({});
collection.replace(k, { value2: Date.now() });
return;
}
collection.replace(collection.any(), { value2: Date.now() });
};
let update = function() {
let collection = pickCollection();
if (collection.count() === 0) {
let k = collection.insert({});
collection.update(k, { value2: Date.now() });
return;
}
collection.update(collection.any(), { value2: Date.now() });
};
let insertEdge = function() {
let collection = pickEdgeCollection();
collection.insert({ _from: "test/v1", _to: "test/v2", value: Date.now() });
};
let insertOrReplace = function() {
let collection = pickCollection();
db._executeTransaction({
collections: { write: [collection.name()] },
action: function(params) {
let collection = params.cn, db = require("internal").db;
let key = "test" + Math.floor(Math.random() * 10000);
try {
db[collection].insert({ _key: key, value: Date.now() });
} catch (err) {
db[collection].replace(key, { value2: Date.now() });
}
},
params: { cn: collection.name() }
});
};
let insertOrUpdate = function() {
let collection = pickCollection();
db._executeTransaction({
collections: { write: [collection.name()] },
action: function(params) {
let collection = params.cn, db = require("internal").db;
let key = "test" + Math.floor(Math.random() * 10000);
try {
db[collection].insert({ _key: key, value: Date.now() });
} catch (err) {
db[collection].update(key, { value2: Date.now() });
}
},
params: { cn: collection.name() }
});
};
let insertMulti = function() {
let collection = pickCollection();
db._executeTransaction({
collections: { write: [collection.name()] },
action: function(params) {
let collection = params.cn, db = require("internal").db;
db[collection].insert({ value1: Date.now() });
db[collection].insert({ value2: Date.now() });
},
params: { cn: collection.name() }
});
};
let removeMulti = function() {
let collection = pickCollection();
db._executeTransaction({
collections: { write: [collection.name()] },
action: function(params) {
let collection = params.cn, db = require("internal").db;
if (db[collection].count() < 2) {
let k1 = db[collection].insert({});
let k2 = db[collection].insert({});
db[collection].remove(k1);
db[collection].remove(k2);
return;
}
db[collection].remove(db[collection].any());
db[collection].remove(db[collection].any());
},
params: { cn: collection.name() }
});
};
let removeInsert = function() {
let collection = pickCollection();
db._executeTransaction({
collections: { write: [collection.name()] },
action: function(params) {
let collection = params.cn, db = require("internal").db;
if (db[collection].count() === 0) {
db[collection].insert({ value: Date.now() });
}
db[collection].remove(db[collection].any());
db[collection].insert({ value: Date.now() });
},
params: { cn: collection.name() }
});
};
let insertRemove = function() {
let collection = pickCollection();
db._executeTransaction({
collections: { write: [collection.name()] },
action: function(params) {
let collection = params.cn, db = require("internal").db;
let k = db[collection].insert({ value: Date.now() });
db[collection].remove(k);
},
params: { cn: collection.name() }
});
};
let createCollection = function() {
let name = "test" + internal.genRandomAlphaNumbers(16) + Date.now();
return db._create(name);
};
let createEdgeCollection = function() {
let name = "edge" + internal.genRandomAlphaNumbers(16) + Date.now();
return db._createEdgeCollection(name);
};
let dropCollection = function() {
let collection = pickCollection();
if (db._name() === cn) {
// don't drop collections from our test database
return false;
}
db._drop(collection.name());
};
let renameCollection = function() {
let name = internal.genRandomAlphaNumbers(16) + Date.now();
let collection = pickCollection();
collection.rename("fuchs" + name);
};
let changeCollection = function() {
let collection = pickCollection();
collection.properties({ waitForSync: false });
};
let createIndex = function () {
let name = internal.genRandomAlphaNumbers(16) + Date.now();
let collection = pickCollection();
collection.ensureIndex({
type: Math.random() >= 0.5 ? "hash" : "skiplist",
fields: [ name ],
sparse: Math.random() > 0.5
});
};
let dropIndex = function () {
let collection = pickCollection();
let indexes = collection.getIndexes();
if (indexes.length > 1) {
collection.dropIndex(indexes[1]);
}
};
let createDatabase = function() {
db._useDatabase('_system');
let name = "test" + internal.genRandomAlphaNumbers(16) + Date.now();
return db._createDatabase(name);
};
let dropDatabase = function () {
pickDatabase();
let name = db._name();
if (name === cn) {
// we do not want to delete our test database
return;
}
db._useDatabase('_system');
db._dropDatabase(name);
};
let ops = [
{ name: "insert", func: insert },
{ name: "remove", func: remove },
{ name: "replace", func: replace },
{ name: "update", func: update },
{ name: "insertEdge", func: insertEdge },
{ name: "insertOrReplace", func: insertOrReplace },
{ name: "insertOrUpdate", func: insertOrUpdate },
{ name: "insertMulti", func: insertMulti },
{ name: "removeMulti", func: removeMulti },
{ name: "removeInsert", func: removeInsert },
{ name: "insertRemove", func: insertRemove },
{ name: "createCollection", func: createCollection },
{ name: "dropCollection", func: dropCollection },
{ name: "renameCollection", func: renameCollection },
{ name: "changeCollection", func: changeCollection },
{ name: "createIndex", func: createIndex },
{ name: "dropIndex", func: dropIndex },
{ name: "createDatabase", func: createDatabase },
{ name: "dropDatabase", func: dropDatabase },
];
for (let i = 0; i < 3000; ++i) {
pickDatabase();
let op = ops[Math.floor(Math.random() * ops.length)];
op.func();
}
db._useDatabase(cn);
let total = "";
db._collections().filter(function(c) { return c.name()[0] !== '_'; }).forEach(function(c) {
total += c.name() + "-" + c.count() + "-" + collectionChecksum(c.name());
});
state.state = total;
},
function(state) {
db._useDatabase(cn);
let total = "";
db._collections().filter(function(c) { return c.name()[0] !== '_'; }).forEach(function(c) {
total += c.name() + "-" + c.count() + "-" + collectionChecksum(c.name());
});
assertTrue(total.length > 0);
assertEqual(total, state.state);
}
);
}
};
}
////////////////////////////////////////////////////////////////////////////////
/// @brief executes the test suite
////////////////////////////////////////////////////////////////////////////////
jsunity.run(ReplicationSuite);
return jsunity.done();