Author: reschke
Date: Tue Dec 8 14:44:53 2015
New Revision: 1718626
URL: http://svn.apache.org/viewvc?rev=1718626&view=rev
Log:
OAK-3729: RDBDocumentStore: implement RDB-specific VersionGC support for lookup
of deleted documents
Added:
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/rdb/RDBVersionGCSupport.java
(with props)
jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/rdb/RDBDocumentStoreTest.java
(with props)
Modified:
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentMK.java
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/rdb/RDBDocumentStore.java
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/rdb/RDBDocumentStoreJDBC.java
Modified:
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentMK.java
URL:
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentMK.java?rev=1718626&r1=1718625&r2=1718626&view=diff
==============================================================================
---
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentMK.java
(original)
+++
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentMK.java
Tue Dec 8 14:44:53 2015
@@ -60,6 +60,7 @@ import org.apache.jackrabbit.oak.plugins
import org.apache.jackrabbit.oak.plugins.document.rdb.RDBBlobStore;
import org.apache.jackrabbit.oak.plugins.document.rdb.RDBDocumentStore;
import org.apache.jackrabbit.oak.plugins.document.rdb.RDBOptions;
+import org.apache.jackrabbit.oak.plugins.document.rdb.RDBVersionGCSupport;
import org.apache.jackrabbit.oak.plugins.document.util.MongoConnection;
import org.apache.jackrabbit.oak.plugins.document.util.RevisionsKey;
import org.apache.jackrabbit.oak.plugins.document.util.StringValue;
@@ -881,6 +882,8 @@ public class DocumentMK {
DocumentStore store = getDocumentStore();
if (store instanceof MongoDocumentStore) {
return new MongoVersionGCSupport((MongoDocumentStore) store);
+ } else if (store instanceof RDBDocumentStore) {
+ return new RDBVersionGCSupport((RDBDocumentStore) store);
} else {
return new VersionGCSupport(store);
}
Modified:
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/rdb/RDBDocumentStore.java
URL:
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/rdb/RDBDocumentStore.java?rev=1718626&r1=1718625&r2=1718626&view=diff
==============================================================================
---
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/rdb/RDBDocumentStore.java
(original)
+++
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/rdb/RDBDocumentStore.java
Tue Dec 8 14:44:53 2015
@@ -234,7 +234,17 @@ public class RDBDocumentStore implements
@Override
public <T extends Document> List<T> query(Collection<T> collection, String
fromKey, String toKey, String indexedProperty,
long startValue, int limit) {
- return internalQuery(collection, fromKey, toKey, indexedProperty,
startValue, limit);
+ List<QueryCondition> conditions = Collections.emptyList();
+ if (indexedProperty != null) {
+ conditions = Collections.singletonList(new
QueryCondition(indexedProperty, ">=", startValue));
+ }
+ return internalQuery(collection, fromKey, toKey, conditions, limit);
+ }
+
+ @Nonnull
+ protected <T extends Document> List<T> query(Collection<T> collection,
String fromKey, String toKey,
+ List<QueryCondition> conditions, int limit) {
+ return internalQuery(collection, fromKey, toKey, conditions, limit);
}
@Override
@@ -1204,14 +1214,16 @@ public class RDBDocumentStore implements
private Map<Thread, QueryContext> qmap = new ConcurrentHashMap<Thread,
QueryContext>();
private <T extends Document> List<T> internalQuery(Collection<T>
collection, String fromKey, String toKey,
- String indexedProperty, long startValue, int limit) {
+ List<QueryCondition> conditions, int limit) {
Connection connection = null;
RDBTableMetaData tmd = getTable(collection);
- if (indexedProperty != null &&
(!INDEXEDPROPERTIES.contains(indexedProperty))) {
- String message = "indexed property " + indexedProperty + " not
supported, query was '>= '" + startValue
- + "'; supported properties are " + INDEXEDPROPERTIES;
- LOG.info(message);
- throw new DocumentStoreException(message);
+ for (QueryCondition cond : conditions) {
+ if (!INDEXEDPROPERTIES.contains(cond.getPropertyName())) {
+ String message = "indexed property " + cond.getPropertyName()
+ " not supported, query was '" + cond.getOperator()
+ + "'" + cond.getValue() + "'; supported properties are
" + INDEXEDPROPERTIES;
+ LOG.info(message);
+ throw new DocumentStoreException(message);
+ }
}
try {
long now = System.currentTimeMillis();
@@ -1223,7 +1235,7 @@ public class RDBDocumentStore implements
connection = this.ch.getROConnection();
String from = collection == Collection.NODES &&
NodeDocument.MIN_ID_VALUE.equals(fromKey) ? null : fromKey;
String to = collection == Collection.NODES &&
NodeDocument.MAX_ID_VALUE.equals(toKey) ? null : toKey;
- List<RDBRow> dbresult = db.query(connection, tmd, from, to,
indexedProperty, startValue, limit);
+ List<RDBRow> dbresult = db.query(connection, tmd, from, to,
conditions, limit);
connection.commit();
int size = dbresult.size();
@@ -1668,4 +1680,34 @@ public class RDBDocumentStore implements
protected NodeDocumentCache getNodeDocumentCache() {
return nodesCache;
}
+
+ // slightly extended query support
+ protected static class QueryCondition {
+
+ private final String propertyName, operator;
+ private final long value;
+
+ public QueryCondition(String propertyName, String operator, long
value) {
+ this.propertyName = propertyName;
+ this.operator = operator;
+ this.value = value;
+ }
+
+ public String getPropertyName() {
+ return propertyName;
+ }
+
+ public String getOperator() {
+ return operator;
+ }
+
+ public long getValue() {
+ return value;
+ }
+
+ @Override
+ public String toString() {
+ return String.format("%s %s %d", propertyName, operator, value);
+ }
+ }
}
Modified:
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/rdb/RDBDocumentStoreJDBC.java
URL:
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/rdb/RDBDocumentStoreJDBC.java?rev=1718626&r1=1718625&r2=1718626&view=diff
==============================================================================
---
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/rdb/RDBDocumentStoreJDBC.java
(original)
+++
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/rdb/RDBDocumentStoreJDBC.java
Tue Dec 8 14:44:53 2015
@@ -29,9 +29,13 @@ import java.sql.SQLException;
import java.sql.Statement;
import java.sql.Types;
import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
+import java.util.Set;
import javax.annotation.CheckForNull;
import javax.annotation.Nonnull;
@@ -41,6 +45,7 @@ import org.apache.jackrabbit.oak.plugins
import org.apache.jackrabbit.oak.plugins.document.NodeDocument;
import org.apache.jackrabbit.oak.plugins.document.UpdateOp.Condition;
import org.apache.jackrabbit.oak.plugins.document.UpdateOp.Key;
+import
org.apache.jackrabbit.oak.plugins.document.rdb.RDBDocumentStore.QueryCondition;
import
org.apache.jackrabbit.oak.plugins.document.rdb.RDBDocumentStore.RDBTableMetaData;
import
org.apache.jackrabbit.oak.plugins.document.rdb.RDBDocumentStoreDB.FETCHFIRSTSYNTAX;
import
org.apache.jackrabbit.oak.plugins.document.rdb.RDBDocumentStoreDB.PreparedStatementComponent;
@@ -318,9 +323,29 @@ public class RDBDocumentStoreJDBC {
}
}
+ private final static Map<String, String> INDEXED_PROP_MAPPING;
+ static {
+ Map<String, String> tmp = new HashMap<String, String>();
+ tmp.put(MODIFIED, "MODIFIED");
+ tmp.put(NodeDocument.HAS_BINARY_FLAG, "HASBINARY");
+ tmp.put(NodeDocument.DELETED_ONCE, "DELETEDONCE");
+ INDEXED_PROP_MAPPING = Collections.unmodifiableMap(tmp);
+ }
+
+ private final static Set<String> SUPPORTED_OPS;
+ static {
+ Set<String> tmp = new HashSet<String>();
+ tmp.add(">=");
+ tmp.add(">");
+ tmp.add("<=");
+ tmp.add("<");
+ tmp.add("=");
+ SUPPORTED_OPS = Collections.unmodifiableSet(tmp);
+ }
+
@Nonnull
- public List<RDBRow> query(Connection connection, RDBTableMetaData tmd,
String minId, String maxId, String indexedProperty,
- long startValue, int limit) throws SQLException {
+ public List<RDBRow> query(Connection connection, RDBTableMetaData tmd,
String minId, String maxId,
+ List<QueryCondition> conditions, int limit) throws SQLException {
long start = System.currentTimeMillis();
StringBuilder selectClause = new StringBuilder();
StringBuilder whereClause = new StringBuilder();
@@ -339,20 +364,16 @@ public class RDBDocumentStoreJDBC {
whereClause.append(whereSep).append("ID < ?");
whereSep = " and ";
}
-
- if (indexedProperty != null) {
- if (MODIFIED.equals(indexedProperty)) {
- whereClause.append(whereSep).append("MODIFIED >= ?");
- } else if (NodeDocument.HAS_BINARY_FLAG.equals(indexedProperty)) {
- if (startValue != NodeDocument.HAS_BINARY_VAL) {
- throw new DocumentStoreException("unsupported value for
property " + NodeDocument.HAS_BINARY_FLAG);
- }
- whereClause.append(whereSep).append("HASBINARY = 1");
- } else if (NodeDocument.DELETED_ONCE.equals(indexedProperty)) {
- if (startValue != 1) {
- throw new DocumentStoreException("unsupported value for
property " + NodeDocument.DELETED_ONCE);
- }
- whereClause.append(whereSep).append("DELETEDONCE = 1");
+ for (QueryCondition cond : conditions) {
+ String op = cond.getOperator();
+ if (!SUPPORTED_OPS.contains(op)) {
+ throw new DocumentStoreException("unsupported operator: " +
op);
+ }
+ String indexedProperty = cond.getPropertyName();
+ String column = INDEXED_PROP_MAPPING.get(indexedProperty);
+ if (column != null) {
+ whereClause.append(whereSep).append(column).append("
").append(op).append(" ?");
+ whereSep = " and ";
} else {
throw new DocumentStoreException("unsupported indexed
property: " + indexedProperty);
}
@@ -390,9 +411,8 @@ public class RDBDocumentStoreJDBC {
if (maxId != null) {
setIdInStatement(tmd, stmt, si++, maxId);
}
-
- if (MODIFIED.equals(indexedProperty)) {
- stmt.setLong(si++, startValue);
+ for (QueryCondition cond : conditions) {
+ stmt.setLong(si++, cond.getValue());
}
if (limit != Integer.MAX_VALUE) {
stmt.setFetchSize(limit);
@@ -423,14 +443,13 @@ public class RDBDocumentStoreJDBC {
long elapsed = System.currentTimeMillis() - start;
if (this.queryHitsLimit != 0 && result.size() > this.queryHitsLimit) {
String message = String.format(
- "Potentially excessive query with %d hits (limited to %d,
configured QUERYHITSLIMIT %d), elapsed time %dms, params minid '%s' maxid '%s'
indexedProperty %s startValue %d limit %d. Check calling method.",
- result.size(), limit, this.queryHitsLimit, elapsed, minId,
maxId, indexedProperty, startValue, limit);
+ "Potentially excessive query with %d hits (limited to %d,
configured QUERYHITSLIMIT %d), elapsed time %dms, params minid '%s' maxid '%s'
condition %s limit %d. Check calling method.",
+ result.size(), limit, this.queryHitsLimit, elapsed, minId,
maxId, conditions, limit);
LOG.info(message, new Exception("call stack"));
} else if (this.queryTimeLimit != 0 && elapsed > this.queryTimeLimit) {
String message = String.format(
- "Long running query with %d hits (limited to %d), elapsed
time %dms (configured QUERYTIMELIMIT %d), params minid '%s' maxid '%s'
indexedProperty %s startValue %d limit %d. Read %d chars from DATA and %d bytes
from BDATA. Check calling method.",
- result.size(), limit, elapsed, this.queryTimeLimit, minId,
maxId, indexedProperty, startValue, limit, dataTotal,
- bdataTotal);
+ "Long running query with %d hits (limited to %d), elapsed
time %dms (configured QUERYTIMELIMIT %d), params minid '%s' maxid '%s'
condition %s limit %d. Read %d chars from DATA and %d bytes from BDATA. Check
calling method.",
+ result.size(), limit, elapsed, this.queryTimeLimit, minId,
maxId, conditions, limit, dataTotal, bdataTotal);
LOG.info(message, new Exception("call stack"));
}
Added:
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/rdb/RDBVersionGCSupport.java
URL:
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/rdb/RDBVersionGCSupport.java?rev=1718626&view=auto
==============================================================================
---
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/rdb/RDBVersionGCSupport.java
(added)
+++
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/rdb/RDBVersionGCSupport.java
Tue Dec 8 14:44:53 2015
@@ -0,0 +1,87 @@
+/*
+ * 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.rdb;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import org.apache.jackrabbit.oak.plugins.document.Collection;
+import org.apache.jackrabbit.oak.plugins.document.NodeDocument;
+import org.apache.jackrabbit.oak.plugins.document.VersionGCSupport;
+import
org.apache.jackrabbit.oak.plugins.document.rdb.RDBDocumentStore.QueryCondition;
+
+import com.google.common.collect.AbstractIterator;
+
+/**
+ * RDB specific version of {@link VersionGCSupport} which uses an extended
query
+ * interface to fetch required {@link NodeDocuments}.
+ */
+public class RDBVersionGCSupport extends VersionGCSupport {
+ private RDBDocumentStore store;
+
+ public RDBVersionGCSupport(RDBDocumentStore store) {
+ super(store);
+ this.store = store;
+ }
+
+ @Override
+ public Iterable<NodeDocument> getPossiblyDeletedDocs(final long
lastModifiedTime) {
+ List<QueryCondition> conditions = new ArrayList<QueryCondition>();
+ conditions.add(new QueryCondition(NodeDocument.DELETED_ONCE, "=", 1));
+ conditions.add(new QueryCondition(NodeDocument.MODIFIED_IN_SECS, "<",
NodeDocument.getModifiedInSecs(lastModifiedTime)));
+ return getIterator(conditions);
+ }
+
+ private Iterable<NodeDocument> getIterator(final List<QueryCondition>
conditions) {
+ return new Iterable<NodeDocument>() {
+ @Override
+ public Iterator<NodeDocument> iterator() {
+ return new AbstractIterator<NodeDocument>() {
+
+ private static final int BATCH_SIZE = 100;
+ private String startId = NodeDocument.MIN_ID_VALUE;
+ private Iterator<NodeDocument> batch = nextBatch();
+
+ @Override
+ protected NodeDocument computeNext() {
+ // read next batch if necessary
+ if (!batch.hasNext()) {
+ batch = nextBatch();
+ }
+
+ NodeDocument doc;
+ if (batch.hasNext()) {
+ doc = batch.next();
+ // remember current id
+ startId = doc.getId();
+ } else {
+ doc = endOfData();
+ }
+ return doc;
+ }
+
+ private Iterator<NodeDocument> nextBatch() {
+ List<NodeDocument> result =
store.query(Collection.NODES, startId, NodeDocument.MAX_ID_VALUE, conditions,
+ BATCH_SIZE);
+ return result.iterator();
+ }
+ };
+ }
+ };
+ }
+}
Propchange:
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/rdb/RDBVersionGCSupport.java
------------------------------------------------------------------------------
svn:eol-style = native
Added:
jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/rdb/RDBDocumentStoreTest.java
URL:
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/rdb/RDBDocumentStoreTest.java?rev=1718626&view=auto
==============================================================================
---
jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/rdb/RDBDocumentStoreTest.java
(added)
+++
jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/rdb/RDBDocumentStoreTest.java
Tue Dec 8 14:44:53 2015
@@ -0,0 +1,68 @@
+/*
+ * 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.rdb;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assume.assumeTrue;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import org.apache.jackrabbit.oak.plugins.document.AbstractDocumentStoreTest;
+import org.apache.jackrabbit.oak.plugins.document.Collection;
+import org.apache.jackrabbit.oak.plugins.document.DocumentStoreFixture;
+import org.apache.jackrabbit.oak.plugins.document.NodeDocument;
+import org.apache.jackrabbit.oak.plugins.document.UpdateOp;
+import
org.apache.jackrabbit.oak.plugins.document.rdb.RDBDocumentStore.QueryCondition;
+import org.junit.Test;
+
+public class RDBDocumentStoreTest extends AbstractDocumentStoreTest {
+
+ public RDBDocumentStoreTest(DocumentStoreFixture dsf) {
+ super(dsf);
+ }
+
+ @Test
+ public void testRDBQuery() {
+ if (ds instanceof RDBDocumentStore) {
+ RDBDocumentStore rds = (RDBDocumentStore) ds;
+ // create ten documents
+ long now = System.currentTimeMillis();
+ String base = this.getClass().getName() + ".testRDBQuery-";
+ for (int i = 0; i < 10; i++) {
+ String id = base + i;
+ UpdateOp up = new UpdateOp(id, true);
+ up.set("_id", id);
+ up.set(NodeDocument.DELETED_ONCE, i % 2 == 1);
+ up.set(NodeDocument.MODIFIED_IN_SECS, now++);
+ boolean success = super.ds.create(Collection.NODES,
Collections.singletonList(up));
+ assertTrue("document with " + id + " not created", success);
+ removeMe.add(id);
+ }
+
+ List<QueryCondition> conditions = new ArrayList<QueryCondition>();
+ // matches every second
+ conditions.add(new QueryCondition(NodeDocument.DELETED_ONCE, "=",
0));
+ // matches first eight
+ conditions.add(new QueryCondition(NodeDocument.MODIFIED_IN_SECS,
"<", now - 2));
+ List<NodeDocument> result = rds.query(Collection.NODES, base, base
+ "A", conditions, 10);
+ assertEquals(4, result.size());
+ }
+ }
+}
Propchange:
jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/rdb/RDBDocumentStoreTest.java
------------------------------------------------------------------------------
svn:eol-style = native