blob: aeafe0e9e48799487f61b53550613d8e1b0db4a7 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.
*/
package org.apache.jackrabbit.oak.plugins.document;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import com.google.common.collect.Lists;
import org.apache.jackrabbit.oak.plugins.document.memory.MemoryDocumentStore;
import org.apache.jackrabbit.oak.plugins.document.mongo.MongoDocumentStore;
import org.apache.jackrabbit.oak.plugins.document.util.MongoConnection;
import org.apache.jackrabbit.oak.plugins.document.util.Utils;
import org.junit.After;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Rule;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.mongodb.BasicDBObject;
import com.mongodb.client.MongoCollection;
import com.mongodb.client.model.IndexOptions;
/**
* Tests the document store.
*/
public class MongoDocumentStoreTest {
private static final Logger LOG = LoggerFactory.getLogger(MongoDocumentStoreTest.class);
@Rule
public MongoConnectionFactory connectionFactory = new MongoConnectionFactory();
// private static final boolean MONGO_DB = true;
// private static final int NODE_COUNT = 2000;
private static final boolean MONGO_DB = false;
private static final int NODE_COUNT = 10;
DocumentStore openDocumentStore() {
if (MONGO_DB) {
MongoConnection c = connectionFactory.getConnection();
assertNotNull(c);
return new MongoDocumentStore(c.getMongoClient(), c.getDatabase(), new DocumentMK.Builder());
}
return new MemoryDocumentStore();
}
void dropCollections() {
if (MONGO_DB) {
MongoUtils.dropCollections(connectionFactory.getConnection().getDatabase());
}
}
@Before
@After
public void cleanUp() {
dropCollections();
}
@Test
public void addGetAndRemove() throws Exception {
DocumentStore docStore = openDocumentStore();
UpdateOp updateOp = new UpdateOp("/", true);
Revision r1 = new Revision(0, 0, 0);
updateOp.setMapEntry("property1", r1, "value1");
updateOp.increment("property2", 1);
updateOp.set("property3", "value3");
docStore.createOrUpdate(Collection.NODES, updateOp);
NodeDocument doc = docStore.find(Collection.NODES, "/");
assertNotNull(doc);
Map<?, ?> property1 = (Map<?, ?>) doc.get("property1");
assertNotNull(property1);
String value1 = (String) property1.get(r1);
assertEquals("value1", value1);
Long value2 = (Long) doc.get("property2");
assertEquals(Long.valueOf(1), value2);
String value3 = (String) doc.get("property3");
assertEquals("value3", value3);
docStore.remove(Collection.NODES, "/");
doc = docStore.find(Collection.NODES, "/");
assertTrue(doc == null);
}
@Test
public void batchRemove() throws Exception {
DocumentStore docStore = openDocumentStore();
int nUpdates = 10;
Revision r1 = new Revision(0, 0, 0);
List<String> ids = Lists.newArrayList();
List<UpdateOp> updateOps = new ArrayList<UpdateOp>();
for (int i = 0; i < nUpdates; i++) {
String path = "/node" + i;
UpdateOp updateOp = new UpdateOp(path, true);
updateOp.setMapEntry("property1", r1, "value1");
updateOp.increment("property2", 1);
updateOp.set("property3", "value3");
updateOps.add(updateOp);
ids.add(updateOp.getId());
}
docStore.create(Collection.NODES, updateOps);
for(String id : ids){
assertNotNull(docStore.find(Collection.NODES, id));
}
docStore.remove(Collection.NODES, ids);
for(String id : ids){
assertNull(docStore.find(Collection.NODES, id));
}
}
@Test
public void batchAdd() throws Exception {
DocumentStore docStore = openDocumentStore();
Revision r1 = new Revision(0, 0, 0);
int nUpdates = 10;
List<UpdateOp> updateOps = new ArrayList<UpdateOp>();
for (int i = 0; i < nUpdates; i++) {
String path = "/node" + i;
UpdateOp updateOp = new UpdateOp(path, true);
updateOp.setMapEntry("property1", r1, "value1");
updateOp.increment("property2", 1);
updateOp.set("property3", "value3");
updateOps.add(updateOp);
}
docStore.create(Collection.NODES, updateOps);
}
@Test
public void addLotsOfNodes() throws Exception {
char[] nPrefix = new char[]{'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'};
int nNodes = NODE_COUNT;
for (int nThreads = 1; nThreads < 32; nThreads = nThreads * 2) {
DocumentStore docStore = openDocumentStore();
dropCollections();
log("Adding and updating " + nNodes + " nodes in each " + nThreads + " threads");
long start = System.currentTimeMillis();
ExecutorService executor = Executors.newFixedThreadPool(nThreads);
for (int j = 0; j < nThreads; j++) {
executor.submit(new AddAndUpdateNodesTask(docStore, "node" + nPrefix[j], nNodes));
}
executor.shutdown();
executor.awaitTermination(1, TimeUnit.MINUTES);
long end = System.currentTimeMillis();
log("Done: " + (end - start) + "ms");
}
}
@Test
public void containsMapEntry() {
Revision r = new Revision(0, 0, 0);
Revision unknown = new Revision(0, 1, 0);
DocumentStore docStore = openDocumentStore();
UpdateOp op = new UpdateOp("/node", true);
op.setMapEntry("map", r, "value");
docStore.createOrUpdate(Collection.NODES, op);
op = new UpdateOp("/node", false);
op.set("prop", "value");
op.containsMapEntry("map", unknown, true);
// update if unknown-key exists -> must not succeed
assertNull(docStore.findAndUpdate(Collection.NODES, op));
op = new UpdateOp("/node", false);
op.set("prop", "value");
op.containsMapEntry("map", r, true);
// update if key exists -> must succeed
NodeDocument doc = docStore.findAndUpdate(Collection.NODES, op);
assertNotNull(doc);
doc = docStore.find(Collection.NODES, "/node");
assertNotNull(doc);
assertNotNull(doc.get("prop"));
assertEquals("value", doc.get("prop"));
op = new UpdateOp("/node", false);
op.set("prop", "other");
op.containsMapEntry("map", r, false);
// update if key does not exist -> must not succeed
assertNull(docStore.findAndUpdate(Collection.NODES, op));
// value must still be the same
doc = docStore.find(Collection.NODES, "/node");
assertNotNull(doc);
assertNotNull(doc.get("prop"));
assertEquals("value", doc.get("prop"));
}
@Test
public void queryWithLimit() throws Exception {
DocumentStore docStore = openDocumentStore();
DocumentNodeStore store = new DocumentMK.Builder()
.setDocumentStore(docStore).setAsyncDelay(0).getNodeStore();
Revision rev = Revision.newRevision(0);
List<UpdateOp> inserts = new ArrayList<UpdateOp>();
for (int i = 0; i < DocumentMK.MANY_CHILDREN_THRESHOLD * 2; i++) {
DocumentNodeState n = new DocumentNodeState(store, "/node-" + i,
new RevisionVector(rev));
inserts.add(n.asOperation(rev));
}
docStore.create(Collection.NODES, inserts);
List<NodeDocument> docs = docStore.query(Collection.NODES,
Utils.getKeyLowerLimit("/"), Utils.getKeyUpperLimit("/"),
DocumentMK.MANY_CHILDREN_THRESHOLD);
assertEquals(DocumentMK.MANY_CHILDREN_THRESHOLD, docs.size());
store.dispose();
}
@Test
@Ignore
public void batchInsert() throws Exception {
doInsert(NODE_COUNT, true);
doInsert(NODE_COUNT, false);
}
private void doInsert(int n, boolean batch) {
dropCollections();
MongoConnection c = connectionFactory.getConnection();
assertNotNull(c);
MongoCollection<BasicDBObject> collection = c.getDatabase()
.getCollection("batchInsertTest", BasicDBObject.class);
BasicDBObject index = new BasicDBObject();
index.put("_path", 1L);
IndexOptions options = new IndexOptions().unique(true);
collection.createIndex(index, options);
log("Inserting " + n + " batch? " + batch);
long start = System.currentTimeMillis();
if (batch) {
List<BasicDBObject> docs = new ArrayList<>();
for (int i = 0; i < n; i++) {
docs.add(new BasicDBObject("_path", "/a" + i));
}
collection.insertMany(docs);
} else {
for (int i = 0; i < n; i++) {
collection.insertOne(new BasicDBObject("_path", "/a" + i));
}
}
long end = System.currentTimeMillis();
log("Done: " + (end - start) + "ms");
dropCollections();
}
private static void log(String s) {
LOG.info(s);
}
/**
* Task to create / update nodes.
*/
private static class AddAndUpdateNodesTask implements Runnable {
private final DocumentStore docStore;
private final String nodeName;
private final int nNodes;
public AddAndUpdateNodesTask(DocumentStore docStore, String nodeName, int nNodes) {
this.docStore = docStore;
this.nodeName = nodeName;
this.nNodes = nNodes;
}
@Override
public void run() {
addNodes();
updateNodes();
}
private void addNodes() {
Revision r1 = new Revision(0, 0, 0);
for (int i = 0; i < nNodes; i++) {
String path = "/" + nodeName + i;
UpdateOp updateOp = new UpdateOp(path, true);
updateOp.setMapEntry("property1", r1, "value1");
updateOp.set("property3", "value3");
docStore.createOrUpdate(Collection.NODES, updateOp);
}
}
private void updateNodes() {
Revision r2 = new Revision(0, 1, 0);
for (int i = 0; i < nNodes; i++) {
String path = "/" + nodeName + i;
UpdateOp updateOp = new UpdateOp(path, false);
updateOp.setMapEntry("property1", r2, "value2");
updateOp.set("property4", "value4");
docStore.createOrUpdate(Collection.NODES, updateOp);
}
}
}
}