mirror of https://gitee.com/bigwinds/arangodb
1238 lines
40 KiB
JavaScript
1238 lines
40 KiB
JavaScript
/* global describe, it, before, beforeEach, afterEach */
|
|
|
|
// //////////////////////////////////////////////////////////////////////////////
|
|
// / @brief JavaScript cluster functionality
|
|
// /
|
|
// / @file
|
|
// /
|
|
// / DISCLAIMER
|
|
// /
|
|
// / Copyright 2017 ArangoDB 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 ArangoDB GmbH, Cologne, Germany
|
|
// /
|
|
// / @author Andreas Streichardt
|
|
// //////////////////////////////////////////////////////////////////////////////
|
|
|
|
const db = require('internal').db;
|
|
const cluster = require('@arangodb/cluster');
|
|
const expect = require('chai').expect;
|
|
const ArangoCollection = require('@arangodb/arango-collection').ArangoCollection;
|
|
|
|
describe('Cluster sync', function() {
|
|
before(function() {
|
|
require('@arangodb/sync-replication-debug').setup();
|
|
});
|
|
|
|
describe('Databaseplan to local', function() {
|
|
beforeEach(function() {
|
|
db._databases().forEach(database => {
|
|
if (database !== '_system') {
|
|
db._dropDatabase(database);
|
|
}
|
|
});
|
|
});
|
|
it('should create a planned database', function() {
|
|
let plan = {
|
|
"Databases": {
|
|
"test": {
|
|
"id": 1,
|
|
"name": "test"
|
|
}
|
|
}
|
|
};
|
|
let errors = cluster.executePlanForDatabases(plan.Databases);
|
|
let databases = db._databases();
|
|
expect(databases).to.have.lengthOf(2);
|
|
expect(databases).to.contain('test');
|
|
expect(errors).to.be.empty;
|
|
});
|
|
it('should leave everything in place if a planned database already exists', function() {
|
|
let plan = {
|
|
Databases: {
|
|
"test": {
|
|
"id": 1,
|
|
"name": "test"
|
|
}
|
|
}
|
|
};
|
|
db._createDatabase('test');
|
|
let errors = cluster.executePlanForDatabases(plan.Databases);
|
|
let databases = db._databases();
|
|
expect(databases).to.have.lengthOf(2);
|
|
expect(databases).to.contain('test');
|
|
expect(errors).to.be.empty;
|
|
});
|
|
it('should delete a database if it is not used anymore', function() {
|
|
db._createDatabase('peng');
|
|
let plan = {
|
|
Databases: {
|
|
}
|
|
};
|
|
cluster.executePlanForDatabases(plan.Databases);
|
|
let databases = db._databases();
|
|
expect(databases).to.have.lengthOf(1);
|
|
expect(databases).to.contain('_system');
|
|
});
|
|
});
|
|
describe('Collection plan to local', function() {
|
|
let numSystemCollections;
|
|
before(function() {
|
|
require('@arangodb/sync-replication-debug').setup();
|
|
});
|
|
|
|
beforeEach(function() {
|
|
db._databases().forEach(database => {
|
|
if (database !== '_system') {
|
|
db._dropDatabase(database);
|
|
}
|
|
});
|
|
db._createDatabase('test');
|
|
db._useDatabase('test');
|
|
numSystemCollections = db._collections().length;
|
|
});
|
|
afterEach(function() {
|
|
db._useDatabase('_system');
|
|
});
|
|
it('should create and load a collection if it does not exist', function() {
|
|
let plan = {
|
|
Collections: {
|
|
test: {
|
|
"100001": {
|
|
"deleted": false,
|
|
"doCompact": true,
|
|
"id": "100001",
|
|
"indexBuckets": 8,
|
|
"indexes": [
|
|
{
|
|
"fields": [
|
|
"_key"
|
|
],
|
|
"id": "0",
|
|
"sparse": false,
|
|
"type": "primary",
|
|
"unique": true
|
|
}
|
|
],
|
|
"isSystem": false,
|
|
"isVolatile": false,
|
|
"journalSize": 1048576,
|
|
"keyOptions": {
|
|
"allowUserKeys": true,
|
|
"type": "traditional"
|
|
},
|
|
"name": "test",
|
|
"numberOfShards": 1,
|
|
"replicationFactor": 2,
|
|
"shardKeys": [
|
|
"_key"
|
|
],
|
|
"shards": {
|
|
"s100001": [
|
|
"repltest",
|
|
]
|
|
},
|
|
"status": 3,
|
|
"type": 2,
|
|
"waitForSync": false
|
|
}
|
|
}
|
|
}
|
|
};
|
|
cluster.executePlanForCollections(plan.Collections);
|
|
db._useDatabase('test');
|
|
let collections = db._collections();
|
|
expect(collections.map(collection => collection.name())).to.contain('s100001');
|
|
expect(db._collection('s100001').status()).to.equal(ArangoCollection.STATUS_LOADED);
|
|
});
|
|
it('should create a collection if it does not exist (unloaded case)', function() {
|
|
let plan = {
|
|
Collections: {
|
|
test: {
|
|
"100001": {
|
|
"deleted": false,
|
|
"doCompact": true,
|
|
"id": "100001",
|
|
"indexBuckets": 8,
|
|
"indexes": [
|
|
{
|
|
"fields": [
|
|
"_key"
|
|
],
|
|
"id": "0",
|
|
"sparse": false,
|
|
"type": "primary",
|
|
"unique": true
|
|
}
|
|
],
|
|
"isSystem": false,
|
|
"isVolatile": false,
|
|
"journalSize": 1048576,
|
|
"keyOptions": {
|
|
"allowUserKeys": true,
|
|
"type": "traditional"
|
|
},
|
|
"name": "test",
|
|
"numberOfShards": 1,
|
|
"replicationFactor": 2,
|
|
"shardKeys": [
|
|
"_key"
|
|
],
|
|
"shards": {
|
|
"s100001": [
|
|
"repltest",
|
|
]
|
|
},
|
|
"status": 2,
|
|
"type": 2,
|
|
"waitForSync": false
|
|
}
|
|
}
|
|
}
|
|
};
|
|
cluster.executePlanForCollections(plan.Collections);
|
|
db._useDatabase('test');
|
|
let collections = db._collections();
|
|
expect(collections.map(collection => collection.name())).to.contain('s100001');
|
|
let count = 0;
|
|
while (db._collection('s100001').status() == ArangoCollection.STATUS_UNLOADING && count++ < 100) {
|
|
wait(0.1);
|
|
}
|
|
expect(db._collection('s100001').status()).to.equal(ArangoCollection.STATUS_UNLOADED);
|
|
});
|
|
it('should unload an existing collection', function() {
|
|
db._create('s100001');
|
|
expect(db._collection('s100001').status()).to.equal(ArangoCollection.STATUS_LOADED);
|
|
let plan = {
|
|
test: {
|
|
"100001": {
|
|
"deleted": false,
|
|
"doCompact": true,
|
|
"id": "100001",
|
|
"indexBuckets": 8,
|
|
"indexes": [
|
|
{
|
|
"fields": [
|
|
"_key"
|
|
],
|
|
"id": "0",
|
|
"sparse": false,
|
|
"type": "primary",
|
|
"unique": true
|
|
}
|
|
],
|
|
"isSystem": false,
|
|
"isVolatile": false,
|
|
"journalSize": 1048576,
|
|
"keyOptions": {
|
|
"allowUserKeys": true,
|
|
"type": "traditional"
|
|
},
|
|
"name": "test",
|
|
"numberOfShards": 1,
|
|
"replicationFactor": 2,
|
|
"shardKeys": [
|
|
"_key"
|
|
],
|
|
"shards": {
|
|
"s100001": [
|
|
"repltest",
|
|
]
|
|
},
|
|
"status": 2,
|
|
"type": 2,
|
|
"waitForSync": false
|
|
}
|
|
}
|
|
};
|
|
cluster.executePlanForCollections(plan);
|
|
db._useDatabase('test');
|
|
let count = 0;
|
|
while (db._collection('s100001').status() == ArangoCollection.STATUS_UNLOADING && count++ < 100) {
|
|
wait(0.1);
|
|
}
|
|
expect(db._collection('s100001').status()).to.equal(ArangoCollection.STATUS_UNLOADED);
|
|
});
|
|
it('should delete a stale collection', function() {
|
|
db._create('s100001');
|
|
let plan = {
|
|
Collections: {
|
|
test: {
|
|
}
|
|
}
|
|
};
|
|
cluster.executePlanForCollections(plan.Collections);
|
|
db._useDatabase('test');
|
|
let collections = db._collections();
|
|
expect(collections).to.have.lengthOf(numSystemCollections);
|
|
});
|
|
it('should ignore a collection for which it is not responsible', function() {
|
|
let plan = {
|
|
Collections: {
|
|
test: {
|
|
"100001": {
|
|
"deleted": false,
|
|
"doCompact": true,
|
|
"id": "100001",
|
|
"indexBuckets": 8,
|
|
"indexes": [
|
|
{
|
|
"fields": [
|
|
"_key"
|
|
],
|
|
"id": "0",
|
|
"sparse": false,
|
|
"type": "primary",
|
|
"unique": true
|
|
}
|
|
],
|
|
"isSystem": false,
|
|
"isVolatile": false,
|
|
"journalSize": 1048576,
|
|
"keyOptions": {
|
|
"allowUserKeys": true,
|
|
"type": "traditional"
|
|
},
|
|
"name": "test",
|
|
"numberOfShards": 1,
|
|
"replicationFactor": 2,
|
|
"shardKeys": [
|
|
"_key"
|
|
],
|
|
"shards": {
|
|
"s100001": [
|
|
"swag",
|
|
]
|
|
},
|
|
"status": 3,
|
|
"type": 2,
|
|
"waitForSync": false
|
|
}
|
|
}
|
|
}
|
|
};
|
|
cluster.executePlanForCollections(plan.Collections);
|
|
db._useDatabase('test');
|
|
let collections = db._collections();
|
|
expect(collections).to.have.lengthOf(numSystemCollections);
|
|
});
|
|
it('should delete a collection for which it lost responsibility', function() {
|
|
db._create('s100001');
|
|
let plan = {
|
|
Collections: {
|
|
test: {
|
|
"100001": {
|
|
"deleted": false,
|
|
"doCompact": true,
|
|
"id": "100001",
|
|
"indexBuckets": 8,
|
|
"indexes": [
|
|
{
|
|
"fields": [
|
|
"_key"
|
|
],
|
|
"id": "0",
|
|
"sparse": false,
|
|
"type": "primary",
|
|
"unique": true
|
|
}
|
|
],
|
|
"isSystem": false,
|
|
"isVolatile": false,
|
|
"journalSize": 1048576,
|
|
"keyOptions": {
|
|
"allowUserKeys": true,
|
|
"type": "traditional"
|
|
},
|
|
"name": "test",
|
|
"numberOfShards": 1,
|
|
"replicationFactor": 2,
|
|
"shardKeys": [
|
|
"_key"
|
|
],
|
|
"shards": {
|
|
"s100001": [
|
|
"debug-follower", // this is a different server than we are
|
|
]
|
|
},
|
|
"status": 2,
|
|
"type": 2,
|
|
"waitForSync": false
|
|
}
|
|
}
|
|
}
|
|
};
|
|
cluster.executePlanForCollections(plan.Collections);
|
|
db._useDatabase('test');
|
|
let collections = db._collections();
|
|
expect(collections).to.have.lengthOf(numSystemCollections);
|
|
});
|
|
it('should create an additional index if instructed to do so', function() {
|
|
db._create('s100001');
|
|
let plan = {
|
|
Collections: {
|
|
test: {
|
|
"100001": {
|
|
"deleted": false,
|
|
"doCompact": true,
|
|
"id": "100001",
|
|
"indexBuckets": 8,
|
|
"indexes": [
|
|
{
|
|
"fields": [
|
|
"_key"
|
|
],
|
|
"id": "0",
|
|
"sparse": false,
|
|
"type": "primary",
|
|
"unique": true
|
|
},
|
|
{
|
|
"error": false,
|
|
"errorMessage": "",
|
|
"errorNum": 0,
|
|
"fields": [
|
|
"user"
|
|
],
|
|
"id": "100005",
|
|
"sparse": true,
|
|
"type": "hash",
|
|
"unique": true
|
|
}
|
|
],
|
|
"isSystem": false,
|
|
"isVolatile": false,
|
|
"journalSize": 1048576,
|
|
"keyOptions": {
|
|
"allowUserKeys": true,
|
|
"type": "traditional"
|
|
},
|
|
"name": "test",
|
|
"numberOfShards": 1,
|
|
"replicationFactor": 2,
|
|
"shardKeys": [
|
|
"_key"
|
|
],
|
|
"shards": {
|
|
"s100001": [
|
|
"repltest",
|
|
]
|
|
},
|
|
"status": 2,
|
|
"type": 2,
|
|
"waitForSync": false
|
|
}
|
|
}
|
|
}
|
|
};
|
|
cluster.executePlanForCollections(plan.Collections);
|
|
db._useDatabase('test');
|
|
let indexes = db._collection('s100001').getIndexes();
|
|
expect(indexes).to.have.lengthOf(2);
|
|
});
|
|
it('should report failure when creating an index does not work', function() {
|
|
let c = db._create('s100001');
|
|
c.insert({"peng": "peng"});
|
|
c.insert({"peng": "peng"});
|
|
let plan = {
|
|
Collections: {
|
|
test: {
|
|
"100001": {
|
|
"deleted": false,
|
|
"doCompact": true,
|
|
"id": "100001",
|
|
"indexBuckets": 8,
|
|
"indexes": [
|
|
{
|
|
"fields": [
|
|
"_key"
|
|
],
|
|
"id": "0",
|
|
"sparse": false,
|
|
"type": "primary",
|
|
"unique": true
|
|
},
|
|
{
|
|
"error": false,
|
|
"errorMessage": "",
|
|
"errorNum": 0,
|
|
"fields": [
|
|
"peng"
|
|
],
|
|
"id": "100005",
|
|
"sparse": true,
|
|
"type": "hash",
|
|
"unique": true
|
|
}
|
|
],
|
|
"isSystem": false,
|
|
"isVolatile": false,
|
|
"journalSize": 1048576,
|
|
"keyOptions": {
|
|
"allowUserKeys": true,
|
|
"type": "traditional"
|
|
},
|
|
"name": "test",
|
|
"numberOfShards": 1,
|
|
"replicationFactor": 2,
|
|
"shardKeys": [
|
|
"_key"
|
|
],
|
|
"shards": {
|
|
"s100001": [
|
|
"repltest",
|
|
]
|
|
},
|
|
"status": 2,
|
|
"type": 2,
|
|
"waitForSync": false
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
let errors = cluster.executePlanForCollections(plan.Collections);
|
|
expect(errors).to.have.property('s100001')
|
|
.with.property('indexErrors')
|
|
.with.property('100005');
|
|
});
|
|
it('should remove an additional index if instructed to do so', function() {
|
|
db._create('s100001');
|
|
db._collection('s100001').ensureIndex({ type: "hash", fields: [ "name" ] });
|
|
let plan = {
|
|
Databases: {
|
|
"_system": {
|
|
"id": 1,
|
|
"name": "_system"
|
|
},
|
|
"test": {
|
|
"id": 2,
|
|
"name": "test"
|
|
}
|
|
},
|
|
Collections: {
|
|
test: {
|
|
"100001": {
|
|
"deleted": false,
|
|
"doCompact": true,
|
|
"id": "100001",
|
|
"indexBuckets": 8,
|
|
"indexes": [
|
|
{
|
|
"fields": [
|
|
"_key"
|
|
],
|
|
"id": "0",
|
|
"sparse": false,
|
|
"type": "primary",
|
|
"unique": true
|
|
}
|
|
],
|
|
"isSystem": false,
|
|
"isVolatile": false,
|
|
"journalSize": 1048576,
|
|
"keyOptions": {
|
|
"allowUserKeys": true,
|
|
"type": "traditional"
|
|
},
|
|
"name": "test",
|
|
"numberOfShards": 1,
|
|
"replicationFactor": 2,
|
|
"shardKeys": [
|
|
"_key"
|
|
],
|
|
"shards": {
|
|
"s100001": [
|
|
"repltest",
|
|
]
|
|
},
|
|
"status": 2,
|
|
"type": 2,
|
|
"waitForSync": false
|
|
}
|
|
}
|
|
}
|
|
};
|
|
cluster.executePlanForCollections(plan.Collections);
|
|
db._useDatabase('test');
|
|
let indexes = db._collection('s100001').getIndexes();
|
|
expect(indexes).to.have.lengthOf(1);
|
|
});
|
|
it('should report an error when collection creation failed', function() {
|
|
let plan = {
|
|
Collections: {
|
|
test: {
|
|
"100001": {
|
|
"deleted": false,
|
|
"doCompact": true,
|
|
"id": "100001",
|
|
"indexBuckets": 8,
|
|
"indexes": [
|
|
{
|
|
"fields": [
|
|
"_key"
|
|
],
|
|
"id": "0",
|
|
"sparse": false,
|
|
"type": "primary",
|
|
"unique": true
|
|
}
|
|
],
|
|
"isSystem": false,
|
|
"isVolatile": false,
|
|
"journalSize": 1048576,
|
|
"keyOptions": {
|
|
"allowUserKeys": true,
|
|
"type": "traditional"
|
|
},
|
|
"name": "test",
|
|
"numberOfShards": 1,
|
|
"replicationFactor": 2,
|
|
"shardKeys": [
|
|
"_key"
|
|
],
|
|
"shards": {
|
|
"Möter": [
|
|
"repltest",
|
|
]
|
|
},
|
|
"status": 2,
|
|
"type": 2,
|
|
"waitForSync": false
|
|
}
|
|
}
|
|
}
|
|
};
|
|
let errors = cluster.executePlanForCollections(plan.Collections);
|
|
expect(errors).to.be.an('object');
|
|
expect(errors).to.have.property('Möter');
|
|
});
|
|
it('should be leading a collection when ordered to be leader', function() {
|
|
let plan = {
|
|
test: {
|
|
"100001": {
|
|
"deleted": false,
|
|
"doCompact": true,
|
|
"id": "100001",
|
|
"indexBuckets": 8,
|
|
"indexes": [
|
|
{
|
|
"fields": [
|
|
"_key"
|
|
],
|
|
"id": "0",
|
|
"sparse": false,
|
|
"type": "primary",
|
|
"unique": true
|
|
}
|
|
],
|
|
"isSystem": false,
|
|
"isVolatile": false,
|
|
"journalSize": 1048576,
|
|
"keyOptions": {
|
|
"allowUserKeys": true,
|
|
"type": "traditional"
|
|
},
|
|
"name": "test",
|
|
"numberOfShards": 1,
|
|
"replicationFactor": 2,
|
|
"shardKeys": [
|
|
"_key"
|
|
],
|
|
"shards": {
|
|
"s100001": [
|
|
"repltest",
|
|
]
|
|
},
|
|
"status": 3,
|
|
"type": 2,
|
|
"waitForSync": false
|
|
}
|
|
}
|
|
};
|
|
let errors = cluster.executePlanForCollections(plan);
|
|
db._useDatabase('test');
|
|
expect(db._collection('s100001').isLeader()).to.equal(true);
|
|
});
|
|
it('should be following a leader when ordered to be follower', function() {
|
|
let plan = {
|
|
test: {
|
|
"100001": {
|
|
"deleted": false,
|
|
"doCompact": true,
|
|
"id": "100001",
|
|
"indexBuckets": 8,
|
|
"indexes": [
|
|
{
|
|
"fields": [
|
|
"_key"
|
|
],
|
|
"id": "0",
|
|
"sparse": false,
|
|
"type": "primary",
|
|
"unique": true
|
|
}
|
|
],
|
|
"isSystem": false,
|
|
"isVolatile": false,
|
|
"journalSize": 1048576,
|
|
"keyOptions": {
|
|
"allowUserKeys": true,
|
|
"type": "traditional"
|
|
},
|
|
"name": "test",
|
|
"numberOfShards": 1,
|
|
"replicationFactor": 2,
|
|
"shardKeys": [
|
|
"_key"
|
|
],
|
|
"shards": {
|
|
"s100001": [
|
|
"the leader-leader",
|
|
"repltest",
|
|
]
|
|
},
|
|
"status": 2,
|
|
"type": 2,
|
|
"waitForSync": false
|
|
}
|
|
}
|
|
};
|
|
let errors = cluster.executePlanForCollections(plan);
|
|
db._useDatabase('test');
|
|
expect(db._collection('s100001').isLeader()).to.equal(false);
|
|
});
|
|
it('should be able to switch from leader to follower', function() {
|
|
let plan = {
|
|
test: {
|
|
"100001": {
|
|
"deleted": false,
|
|
"doCompact": true,
|
|
"id": "100001",
|
|
"indexBuckets": 8,
|
|
"indexes": [
|
|
{
|
|
"fields": [
|
|
"_key"
|
|
],
|
|
"id": "0",
|
|
"sparse": false,
|
|
"type": "primary",
|
|
"unique": true
|
|
}
|
|
],
|
|
"isSystem": false,
|
|
"isVolatile": false,
|
|
"journalSize": 1048576,
|
|
"keyOptions": {
|
|
"allowUserKeys": true,
|
|
"type": "traditional"
|
|
},
|
|
"name": "test",
|
|
"numberOfShards": 1,
|
|
"replicationFactor": 2,
|
|
"shardKeys": [
|
|
"_key"
|
|
],
|
|
"shards": {
|
|
"s100001": [
|
|
"repltest",
|
|
]
|
|
},
|
|
"status": 2,
|
|
"type": 2,
|
|
"waitForSync": false
|
|
}
|
|
}
|
|
};
|
|
let errors = cluster.executePlanForCollections(plan);
|
|
plan.test['100001'].shards['s100001'].unshift('der-hund');
|
|
cluster.executePlanForCollections(plan);
|
|
db._useDatabase('test');
|
|
expect(db._collection('s100001').isLeader()).to.equal(false);
|
|
});
|
|
it('should be able to switch from follower to leader', function() {
|
|
let plan = {
|
|
test: {
|
|
"100001": {
|
|
"deleted": false,
|
|
"doCompact": true,
|
|
"id": "100001",
|
|
"indexBuckets": 8,
|
|
"indexes": [
|
|
{
|
|
"fields": [
|
|
"_key"
|
|
],
|
|
"id": "0",
|
|
"sparse": false,
|
|
"type": "primary",
|
|
"unique": true
|
|
}
|
|
],
|
|
"isSystem": false,
|
|
"isVolatile": false,
|
|
"journalSize": 1048576,
|
|
"keyOptions": {
|
|
"allowUserKeys": true,
|
|
"type": "traditional"
|
|
},
|
|
"name": "test",
|
|
"numberOfShards": 1,
|
|
"replicationFactor": 2,
|
|
"shardKeys": [
|
|
"_key"
|
|
],
|
|
"shards": {
|
|
"s100001": [
|
|
"old-leader",
|
|
"repltest",
|
|
]
|
|
},
|
|
"status": 2,
|
|
"type": 2,
|
|
"waitForSync": false
|
|
}
|
|
}
|
|
};
|
|
let errors = cluster.executePlanForCollections(plan);
|
|
plan.test['100001'].shards['s100001'] = ["repltest"];
|
|
cluster.executePlanForCollections(plan);
|
|
db._useDatabase('test');
|
|
expect(db._collection('s100001').isLeader()).to.equal(true);
|
|
});
|
|
it('should kill any unplanned server from current', function() {
|
|
let collection = db._create('s100001');
|
|
collection.assumeLeadership();
|
|
collection.addFollower('test');
|
|
collection.addFollower('test2');
|
|
let plan = {
|
|
test: {
|
|
"100001": {
|
|
"deleted": false,
|
|
"doCompact": true,
|
|
"id": "100001",
|
|
"indexBuckets": 8,
|
|
"indexes": [
|
|
{
|
|
"fields": [
|
|
"_key"
|
|
],
|
|
"id": "0",
|
|
"sparse": false,
|
|
"type": "primary",
|
|
"unique": true
|
|
}
|
|
],
|
|
"isSystem": false,
|
|
"isVolatile": false,
|
|
"journalSize": 1048576,
|
|
"keyOptions": {
|
|
"allowUserKeys": true,
|
|
"type": "traditional"
|
|
},
|
|
"name": "testi",
|
|
"numberOfShards": 1,
|
|
"replicationFactor": 2,
|
|
"shardKeys": [
|
|
"_key"
|
|
],
|
|
"shards": {
|
|
"s100001": [
|
|
"repltest",
|
|
"test2",
|
|
]
|
|
},
|
|
"status": 2,
|
|
"type": 2,
|
|
"waitForSync": false
|
|
}
|
|
}
|
|
};
|
|
cluster.executePlanForCollections(plan);
|
|
db._useDatabase('test');
|
|
expect(collection.getFollowers()).to.deep.equal(['test2']);
|
|
});
|
|
});
|
|
describe('Update current database', function() {
|
|
beforeEach(function() {
|
|
db._databases().forEach(database => {
|
|
if (database !== '_system') {
|
|
db._dropDatabase(database);
|
|
}
|
|
});
|
|
});
|
|
it('should report a new database', function() {
|
|
db._createDatabase('testi');
|
|
let current = {
|
|
_system: {
|
|
repltest: {
|
|
id: 1,
|
|
name: '_system',
|
|
},
|
|
}
|
|
};
|
|
let result = cluster.updateCurrentForDatabases({}, current);
|
|
expect(result).to.have.property('/arango/Current/Databases/testi/repltest');
|
|
expect(result['/arango/Current/Databases/testi/repltest']).to.have.property('op', 'set');
|
|
expect(result['/arango/Current/Databases/testi/repltest']).to.have.deep.property('new.name', 'testi');
|
|
});
|
|
it('should not do anything if there is nothing to do', function() {
|
|
let current = {
|
|
_system: {
|
|
repltest: {
|
|
id: 1,
|
|
name: '_system',
|
|
},
|
|
}
|
|
};
|
|
let result = cluster.updateCurrentForDatabases({}, current);
|
|
expect(Object.keys(result)).to.have.lengthOf(0);
|
|
});
|
|
it('should remove a database from the agency if it is not present locally', function() {
|
|
let current = {
|
|
_system: {
|
|
repltest: {
|
|
id: 1,
|
|
name: '_system',
|
|
},
|
|
},
|
|
testi: {
|
|
repltest: {
|
|
id: 2,
|
|
name: 'testi',
|
|
}
|
|
}
|
|
};
|
|
let result = cluster.updateCurrentForDatabases({}, current);
|
|
expect(result).to.have.property('/arango/Current/Databases/testi/repltest');
|
|
expect(Object.keys(result)).to.have.lengthOf(1);
|
|
expect(result['/arango/Current/Databases/testi/repltest']).to.have.property('op', 'delete');
|
|
});
|
|
it('should not delete other server database entries when removing', function() {
|
|
let current = {
|
|
_system: {
|
|
repltest: {
|
|
id: 1,
|
|
name: '_system',
|
|
},
|
|
},
|
|
testi: {
|
|
repltest: {
|
|
id: 2,
|
|
name: 'testi',
|
|
},
|
|
testreplicant: {
|
|
id: 2,
|
|
name: 'testi',
|
|
}
|
|
}
|
|
};
|
|
let result = cluster.updateCurrentForDatabases({}, current);
|
|
expect(result).to.have.property('/arango/Current/Databases/testi/repltest');
|
|
expect(Object.keys(result)).to.have.lengthOf(1);
|
|
});
|
|
it('should report any errors during database creation', function() {
|
|
let current = {
|
|
_system: {
|
|
repltest: {
|
|
id: 1,
|
|
name: '_system',
|
|
},
|
|
},
|
|
};
|
|
let result = cluster.updateCurrentForDatabases({testi: {"error": true, name: "testi"}}, current);
|
|
expect(result).to.have.property('/arango/Current/Databases/testi/repltest');
|
|
expect(Object.keys(result)).to.have.lengthOf(1);
|
|
expect(result['/arango/Current/Databases/testi/repltest']).to.have.property('op', 'set');
|
|
expect(result['/arango/Current/Databases/testi/repltest']).to.have.deep.property('new.error', true);
|
|
});
|
|
it('should override any previous error if creation finally worked', function() {
|
|
let current = {
|
|
_system: {
|
|
repltest: {
|
|
id: 1,
|
|
name: '_system',
|
|
},
|
|
},
|
|
testi: {
|
|
repltest: {
|
|
id: 2,
|
|
name: 'testi',
|
|
error: true,
|
|
}
|
|
},
|
|
};
|
|
db._createDatabase('testi');
|
|
let result = cluster.updateCurrentForDatabases({}, current);
|
|
expect(result).to.have.property('/arango/Current/Databases/testi/repltest');
|
|
expect(Object.keys(result)).to.have.lengthOf(1);
|
|
expect(result['/arango/Current/Databases/testi/repltest']).to.have.property('op', 'set');
|
|
expect(result['/arango/Current/Databases/testi/repltest']).to.have.deep.property('new.name', 'testi');
|
|
expect(result['/arango/Current/Databases/testi/repltest']).to.have.deep.property('new.error', false);
|
|
});
|
|
it('should not do anything if nothing happened', function() {
|
|
let current = {
|
|
_system: {
|
|
repltest: {
|
|
id: 1,
|
|
name: '_system',
|
|
},
|
|
},
|
|
};
|
|
let result = cluster.updateCurrentForDatabases({}, current);
|
|
expect(Object.keys(result)).to.have.lengthOf(0);
|
|
});
|
|
});
|
|
describe('Update current collection', function() {
|
|
beforeEach(function() {
|
|
db._useDatabase('_system');
|
|
db._databases().forEach(database => {
|
|
if (database !== '_system') {
|
|
db._dropDatabase(database);
|
|
}
|
|
});
|
|
db._createDatabase('testung');
|
|
db._useDatabase('testung');
|
|
});
|
|
it('should report a new collection in current', function() {
|
|
let props = { planId: '888111' };
|
|
let collection = db._create('testi', props);
|
|
collection.assumeLeadership();
|
|
let current = {
|
|
};
|
|
let result = cluster.updateCurrentForCollections({}, current);
|
|
expect(Object.keys(result)).to.have.length.of.at.least(1);
|
|
expect(result).to.have.property('/arango/Current/Collections/testung/888111/testi');
|
|
expect(result['/arango/Current/Collections/testung/888111/testi']).to.have.property('op', 'set');
|
|
expect(result['/arango/Current/Collections/testung/888111/testi']).to.have.deep.property('new.servers')
|
|
.that.is.an('array')
|
|
.that.deep.equals(['repltest']);
|
|
});
|
|
it('should not do anything if nothing changed', function() {
|
|
let current = {
|
|
};
|
|
let result = cluster.updateCurrentForCollections({}, current);
|
|
expect(Object.keys(result)).to.have.lengthOf(0);
|
|
});
|
|
it('should not report any collections for which we are not leader (will be handled in replication)', function() {
|
|
let props = { planId: '888111' };
|
|
let collection = db._create('testi', props);
|
|
let current = {
|
|
};
|
|
let result = cluster.updateCurrentForCollections({}, {}, current);
|
|
expect(Object.keys(result)).to.have.lengthOf(0);
|
|
});
|
|
it('should not delete any collections for which we are not a leader locally', function() {
|
|
let current = {
|
|
testung: {
|
|
888111: {
|
|
testi : { "error" : false, "errorMessage" : "", "errorNum" : 0, "indexes" : [ { "id" : "0", "type" : "primary", "fields" : [ "_key" ], "selectivityEstimate" : 1, "unique" : true, "sparse" : false } ], "servers" : [ "the-master", "repltest" ] }
|
|
},
|
|
}
|
|
};
|
|
let result = cluster.updateCurrentForCollections({}, {}, current);
|
|
expect(Object.keys(result)).to.have.lengthOf(0);
|
|
});
|
|
it('should resign leadership for which we are no more leader locally', function() {
|
|
let props = { planId: '888111' };
|
|
let collection = db._create('testi', props);
|
|
let current = {
|
|
testung: {
|
|
888111: {
|
|
testi : { "error" : false, "errorMessage" : "", "errorNum" : 0, "indexes" : [ { "id" : "0", "type" : "primary", "fields" : [ "_key" ], "selectivityEstimate" : 1, "unique" : true, "sparse" : false } ], "servers" : [ "repltest" ] }
|
|
},
|
|
}
|
|
};
|
|
let result = cluster.updateCurrentForCollections({}, {}, current);
|
|
expect(result).to.be.an('object');
|
|
expect(Object.keys(result)).to.have.lengthOf(1);
|
|
expect(result).to.have.property('/arango/Current/Collections/testung/888111/testi/servers')
|
|
.that.have.property('op', 'set');
|
|
expect(result).to.have.property('/arango/Current/Collections/testung/888111/testi/servers')
|
|
.that.has.property('new')
|
|
.with.deep.equal(["_repltest"]);
|
|
});
|
|
it('should report newly assumed leadership for which we were a follower previously and remove any leaders and followers (these have to reregister themselves separately)', function() {
|
|
let props = { planId: '888111' };
|
|
let collection = db._create('testi', props);
|
|
collection.assumeLeadership();
|
|
let current = {
|
|
testung: {
|
|
888111: {
|
|
testi : { "error" : false, "errorMessage" : "", "errorNum" : 0, "indexes" : [ { "id" : "0", "type" : "primary", "fields" : [ "_key" ], "selectivityEstimate" : 1, "unique" : true, "sparse" : false } ], "servers" : [ "bogus-old-leader", "repltest", "useless-follower" ] }
|
|
},
|
|
}
|
|
};
|
|
let result = cluster.updateCurrentForCollections({}, {}, current);
|
|
expect(result).to.be.an('object');
|
|
expect(Object.keys(result)).to.have.lengthOf(1);
|
|
expect(result).to.have.property('/arango/Current/Collections/testung/888111/testi')
|
|
.that.have.property('op', 'set');
|
|
expect(result).to.have.property('/arango/Current/Collections/testung/888111/testi')
|
|
.that.has.deep.property('new.servers')
|
|
.with.deep.equal(["repltest"]);
|
|
});
|
|
it('should delete any collections for which we are not a leader locally', function() {
|
|
let current = {
|
|
testung: {
|
|
888111: {
|
|
testi : { "error" : false, "errorMessage" : "", "errorNum" : 0, "indexes" : [ { "id" : "0", "type" : "primary", "fields" : [ "_key" ], "selectivityEstimate" : 1, "unique" : true, "sparse" : false } ], "servers" : [ "repltest" ] }
|
|
},
|
|
}
|
|
};
|
|
let result = cluster.updateCurrentForCollections({}, {}, current);
|
|
expect(result).to.be.an('object');
|
|
expect(Object.keys(result)).to.have.lengthOf(1);
|
|
expect(result).to.have.property('/arango/Current/Collections/testung/888111/testi')
|
|
.that.has.deep.property('op', 'delete');
|
|
});
|
|
it('should report newly created indices', function() {
|
|
let props = { planId: '888111' };
|
|
let collection = db._create('testi', props);
|
|
collection.assumeLeadership();
|
|
collection.ensureIndex({"type": "hash", "fields": ["hund"]});
|
|
let current = {
|
|
testung: {
|
|
888111: {
|
|
testi : { "error" : false, "errorMessage" : "", "errorNum" : 0, "indexes" : [ { "id" : "0", "type" : "primary", "fields" : [ "_key" ], "selectivityEstimate" : 1, "unique" : true, "sparse" : false } ], "servers" : [ "repltest" ] }
|
|
},
|
|
}
|
|
};
|
|
let result = cluster.updateCurrentForCollections({}, {}, current);
|
|
expect(result).to.be.an('object');
|
|
expect(Object.keys(result)).to.have.lengthOf(1);
|
|
expect(result).to.have.property('/arango/Current/Collections/testung/888111/testi')
|
|
.that.have.property('op', 'set');
|
|
expect(result).to.have.property('/arango/Current/Collections/testung/888111/testi')
|
|
.that.has.deep.property('new.indexes')
|
|
.with.lengthOf(2);
|
|
});
|
|
it('should report collection errors', function() {
|
|
let errors = {
|
|
'testi': {
|
|
collection: {
|
|
'name': 'testi',
|
|
'error': true,
|
|
'errorNum': 666,
|
|
'errorMessage': 'the number of the beast :S',
|
|
},
|
|
info: {
|
|
database: 'testung',
|
|
planId: '888111',
|
|
shardName: 'testi',
|
|
}
|
|
}
|
|
};
|
|
let result = cluster.updateCurrentForCollections(errors, {});
|
|
expect(result).to.be.an('object');
|
|
expect(Object.keys(result)).to.have.lengthOf(1);
|
|
expect(result).to.have.property('/arango/Current/Collections/testung/888111/testi')
|
|
.that.have.property('op', 'set');
|
|
expect(result).to.have.property('/arango/Current/Collections/testung/888111/testi')
|
|
.that.has.deep.property('new.error')
|
|
.equals(true);
|
|
});
|
|
it('should report index errors', function() {
|
|
let current = {
|
|
testung: {
|
|
888111: {
|
|
testi : { "error" : false, "errorMessage" : "", "errorNum" : 0, "indexes" : [ { "id" : "0", "type" : "primary", "fields" : [ "_key" ], "selectivityEstimate" : 1, "unique" : true, "sparse" : false } ], "servers" : [ "repltest" ] }
|
|
},
|
|
}
|
|
};
|
|
|
|
let errors = {
|
|
'testi': {
|
|
'indexErrors': {
|
|
1: {
|
|
'id': 1,
|
|
'error': true,
|
|
'errorNum': 666,
|
|
'errorMessage': 'the number of the beast :S',
|
|
}
|
|
},
|
|
}
|
|
};
|
|
let props = { planId: '888111' };
|
|
let collection = db._create('testi', props);
|
|
collection.assumeLeadership();
|
|
let result = cluster.updateCurrentForCollections(errors, current);
|
|
expect(result).to.be.an('object');
|
|
expect(Object.keys(result)).to.have.lengthOf(1);
|
|
expect(result).to.have.property('/arango/Current/Collections/testung/888111/testi')
|
|
.that.have.property('op', 'set');
|
|
expect(result).to.have.property('/arango/Current/Collections/testung/888111/testi')
|
|
.that.has.deep.property('new.indexes.1.error')
|
|
.equals(true);
|
|
});
|
|
it('should report deleted indexes', function() {
|
|
let current = {
|
|
testung: {
|
|
888111: {
|
|
testi : { "error" : false, "errorMessage" : "", "errorNum" : 0, "indexes" : [ { "id" : "0", "type" : "primary", "fields" : [ "_key" ], "selectivityEstimate" : 1, "unique" : true, "sparse" : false },
|
|
{
|
|
"fields": [
|
|
"test"
|
|
],
|
|
"id": "testi",
|
|
"selectivityEstimate": 1,
|
|
"sparse": false,
|
|
"type": "hash",
|
|
"unique": false
|
|
}
|
|
], "servers" : [ "repltest" ] }
|
|
},
|
|
}
|
|
};
|
|
|
|
let props = { planId: '888111' };
|
|
let collection = db._create('testi', props);
|
|
collection.assumeLeadership();
|
|
let result = cluster.updateCurrentForCollections({}, current);
|
|
expect(result).to.be.an('object');
|
|
expect(Object.keys(result)).to.have.lengthOf(1);
|
|
expect(result).to.have.property('/arango/Current/Collections/testung/888111/testi')
|
|
.that.have.property('op', 'set');
|
|
expect(result).to.have.property('/arango/Current/Collections/testung/888111/testi')
|
|
.that.has.deep.property('new.indexes')
|
|
.that.has.lengthOf(1);
|
|
});
|
|
it('should report added indexes', function() {
|
|
let current = {
|
|
testung: {
|
|
888111: {
|
|
testi : { "error" : false, "errorMessage" : "", "errorNum" : 0, "indexes" : [ { "id" : "0", "type" : "primary", "fields" : [ "_key" ], "selectivityEstimate" : 1, "unique" : true, "sparse" : false },
|
|
], "servers" : [ "repltest" ] }
|
|
},
|
|
}
|
|
};
|
|
|
|
let props = { planId: '888111' };
|
|
let collection = db._create('testi', props);
|
|
collection.assumeLeadership();
|
|
collection.ensureIndex({type: "hash", fields: ["test"]});
|
|
let result = cluster.updateCurrentForCollections({}, current);
|
|
expect(result).to.be.an('object');
|
|
expect(Object.keys(result)).to.have.lengthOf(1);
|
|
expect(result).to.have.property('/arango/Current/Collections/testung/888111/testi')
|
|
.that.have.property('op', 'set');
|
|
expect(result).to.have.property('/arango/Current/Collections/testung/888111/testi')
|
|
.that.has.deep.property('new.indexes')
|
|
.that.has.lengthOf(2);
|
|
});
|
|
});
|
|
});
|