Index: oak-blob/src/main/java/org/apache/jackrabbit/oak/spi/blob/FileBlobStore.java =================================================================== --- oak-blob/src/main/java/org/apache/jackrabbit/oak/spi/blob/FileBlobStore.java (revision 1578223) +++ oak-blob/src/main/java/org/apache/jackrabbit/oak/spi/blob/FileBlobStore.java (working copy) @@ -27,6 +27,7 @@ import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.Iterator; +import java.util.List; import javax.annotation.Nullable; @@ -219,20 +220,22 @@ } @Override - public boolean deleteChunk(String chunkId, long maxLastModifiedTime) throws Exception { - byte[] digest = StringUtils.convertHexToBytes(chunkId); - File f = getFile(digest, false); - if (!f.exists()) { - File old = getFile(digest, true); - f.getParentFile().mkdir(); - old.renameTo(f); - f = getFile(digest, false); + public boolean deleteChunks(List chunkIds, long maxLastModifiedTime) throws Exception { + for (String chunkId : chunkIds) { + byte[] digest = StringUtils.convertHexToBytes(chunkId); + File f = getFile(digest, false); + if (!f.exists()) { + File old = getFile(digest, true); + f.getParentFile().mkdir(); + old.renameTo(f); + f = getFile(digest, false); + } + if ((maxLastModifiedTime <= 0) + || FileUtils.isFileOlder(f, maxLastModifiedTime)) { + f.delete(); + } } - if ((maxLastModifiedTime <= 0) - || FileUtils.isFileOlder(f, maxLastModifiedTime)) { - return f.delete(); - } - return false; + return true; } @Override Index: oak-blob/src/main/java/org/apache/jackrabbit/oak/spi/blob/GarbageCollectableBlobStore.java =================================================================== --- oak-blob/src/main/java/org/apache/jackrabbit/oak/spi/blob/GarbageCollectableBlobStore.java (revision 1578223) +++ oak-blob/src/main/java/org/apache/jackrabbit/oak/spi/blob/GarbageCollectableBlobStore.java (working copy) @@ -18,6 +18,7 @@ import java.io.IOException; import java.util.Iterator; +import java.util.List; /** * A blob store that support garbage collection. @@ -83,16 +84,14 @@ Iterator getAllChunkIds(long maxLastModifiedTime) throws Exception; /** - * Delete the blob with the given id. - * - * @param chunkId the chunk id - * @param maxLastModifiedTime - * the max last modified time to consider for retrieval + * Deletes the blobs with the given ids. + * + * @param chunkIds the chunk ids + * @param maxLastModifiedTime the max last modified time to consider for retrieval * @return true, if successful - * @throws Exception - * the exception + * @throws Exception the exception */ - boolean deleteChunk(String chunkId, long maxLastModifiedTime) throws Exception; + boolean deleteChunks(List chunkIds, long maxLastModifiedTime) throws Exception; /** * Resolve chunks from the given Id. Index: oak-blob/src/main/java/org/apache/jackrabbit/oak/spi/blob/MemoryBlobStore.java =================================================================== --- oak-blob/src/main/java/org/apache/jackrabbit/oak/spi/blob/MemoryBlobStore.java (revision 1578223) +++ oak-blob/src/main/java/org/apache/jackrabbit/oak/spi/blob/MemoryBlobStore.java (working copy) @@ -19,6 +19,7 @@ import java.io.IOException; import java.util.HashMap; import java.util.Iterator; +import java.util.List; import com.google.common.collect.AbstractIterator; import com.google.common.collect.Maps; @@ -84,12 +85,14 @@ * Ignores the maxlastModifiedTime */ @Override - public boolean deleteChunk(String chunkId, long maxLastModifiedTime) throws Exception { - BlockId id = new BlockId(StringUtils.convertHexToBytes(chunkId), 0); - if (map.containsKey(id)) { - map.remove(id); - } else if (old.containsKey(id)) { - old.remove(id); + public boolean deleteChunks(List chunkIds, long maxLastModifiedTime) throws Exception { + for (String chunkId : chunkIds) { + BlockId id = new BlockId(StringUtils.convertHexToBytes(chunkId), 0); + if (map.containsKey(id)) { + map.remove(id); + } else if (old.containsKey(id)) { + old.remove(id); + } } return true; } Index: oak-blob/src/test/java/org/apache/jackrabbit/oak/spi/blob/AbstractBlobStoreTest.java =================================================================== --- oak-blob/src/test/java/org/apache/jackrabbit/oak/spi/blob/AbstractBlobStoreTest.java (revision 1578223) +++ oak-blob/src/test/java/org/apache/jackrabbit/oak/spi/blob/AbstractBlobStoreTest.java (working copy) @@ -31,6 +31,7 @@ import java.util.Set; import java.util.concurrent.atomic.AtomicBoolean; +import com.google.common.collect.Lists; import com.google.common.collect.Sets; import org.apache.jackrabbit.oak.commons.json.JsopBuilder; import org.apache.jackrabbit.oak.commons.json.JsopTokenizer; @@ -393,9 +394,7 @@ public void delete() throws Exception { Set ids = createArtifacts(); - for (String id : ids) { - store.deleteChunk(id, 0); - } + store.deleteChunks(Lists.newArrayList(ids), 0); Iterator iter = store.getAllChunkIds(0); Set ret = Sets.newHashSet(); Index: oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/blob/cloud/CloudBlobStore.java =================================================================== --- oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/blob/cloud/CloudBlobStore.java (revision 1578223) +++ oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/blob/cloud/CloudBlobStore.java (working copy) @@ -19,6 +19,7 @@ import java.io.IOException; import java.util.ArrayDeque; import java.util.Iterator; +import java.util.List; import java.util.Map; import java.util.NoSuchElementException; @@ -226,18 +227,20 @@ final org.jclouds.blobstore.BlobStore blobStore = context.getBlobStore(); return new CloudStoreIterator(blobStore, maxLastModifiedTime); -} + } @Override - public boolean deleteChunk(String chunkId, long maxLastModifiedTime) throws Exception { + public boolean deleteChunks(List chunkIds, long maxLastModifiedTime) throws Exception { Preconditions.checkNotNull(context); - final org.jclouds.blobstore.BlobStore blobStore = context.getBlobStore(); - StorageMetadata metadata = blobStore.blobMetadata(cloudContainer, chunkId); - if ((maxLastModifiedTime <= 0) - || (metadata.getLastModified().getTime() <= maxLastModifiedTime)) { - blobStore.removeBlob(cloudContainer, chunkId); - return true; + for (String chunkId : chunkIds) { + final org.jclouds.blobstore.BlobStore blobStore = context.getBlobStore(); + StorageMetadata metadata = blobStore.blobMetadata(cloudContainer, chunkId); + if ((maxLastModifiedTime <= 0) + || (metadata.getLastModified().getTime() <= maxLastModifiedTime)) { + blobStore.removeBlob(cloudContainer, chunkId); + return true; + } } return true; } Index: oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/blob/datastore/DataStoreBlobStore.java =================================================================== --- oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/blob/datastore/DataStoreBlobStore.java (revision 1578223) +++ oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/blob/datastore/DataStoreBlobStore.java (working copy) @@ -26,6 +26,7 @@ import java.io.InputStream; import java.io.SequenceInputStream; import java.util.Iterator; +import java.util.List; import javax.annotation.Nullable; import javax.jcr.RepositoryException; @@ -225,14 +226,16 @@ } @Override - public boolean deleteChunk(String chunkId, long maxLastModifiedTime) throws Exception { + public boolean deleteChunks(List chunkIds, long maxLastModifiedTime) throws Exception { if (delegate instanceof MultiDataStoreAware) { - DataIdentifier identifier = new DataIdentifier(chunkId); - DataRecord dataRecord = delegate.getRecord(identifier); - if ((maxLastModifiedTime <= 0) - || dataRecord.getLastModified() <= maxLastModifiedTime) { - ((MultiDataStoreAware) delegate).deleteRecord(identifier); - return true; + for (String chunkId : chunkIds) { + DataIdentifier identifier = new DataIdentifier(chunkId); + DataRecord dataRecord = delegate.getRecord(identifier); + if ((maxLastModifiedTime <= 0) + || dataRecord.getLastModified() <= maxLastModifiedTime) { + ((MultiDataStoreAware) delegate).deleteRecord(identifier); + return true; + } } } return false; Index: oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/blob/db/DbBlobStore.java =================================================================== --- oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/blob/db/DbBlobStore.java (revision 1578223) +++ oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/blob/db/DbBlobStore.java (working copy) @@ -24,6 +24,7 @@ import java.sql.Statement; import java.util.ArrayList; import java.util.Iterator; +import java.util.List; import org.apache.jackrabbit.oak.commons.StringUtils; import org.apache.jackrabbit.oak.plugins.blob.CachingBlobStore; @@ -220,32 +221,49 @@ } @Override - public boolean deleteChunk(String chunkId, long maxLastModifiedTime) throws Exception { + public boolean deleteChunks(List chunkIds, long maxLastModifiedTime) throws Exception { Connection conn = cp.getConnection(); try { + PreparedStatement prep = null; PreparedStatement prepData = null; + + StringBuilder inClause = new StringBuilder(); + int batch = chunkIds.size(); + for (int i = 0; i < batch; i++) { + inClause.append('?'); + if (i != batch -1) { + inClause.append(','); + } + } if (maxLastModifiedTime > 0) { prep = conn.prepareStatement( - "delete from datastore_meta where id = ? and lastMod <= ?"); - prep.setLong(2, maxLastModifiedTime); + "delete from datastore_meta where id in (" + + inClause.toString() + ") and lastMod <= ?"); + prep.setLong(batch + 1, maxLastModifiedTime); prepData = conn.prepareStatement( - "delete from datastore_data where id = ? and lastMod <= ?"); - prepData.setLong(2, maxLastModifiedTime); + "delete from datastore_data where id in (" + + inClause.toString() + ") and lastMod <= ?"); + prepData.setLong(batch + 1, maxLastModifiedTime); } else { prep = conn.prepareStatement( - "delete from datastore_meta where id = ?"); + "delete from datastore_meta where id in (" + + inClause.toString() + ")"); prepData = conn.prepareStatement( - "delete from datastore_data where id = ?"); + "delete from datastore_data where id in (" + + inClause.toString() + ")"); } - prep.setString(1, chunkId); + + for (int idx = 0; idx < batch; idx++) { + prep.setString(idx + 1, chunkIds.get(idx)); + prepData.setString(idx + 1, chunkIds.get(idx)); + } + prep.execute(); - prepData.setString(1, chunkId); prepData.execute(); - prep.close(); prepData.close(); } finally { @@ -256,6 +274,7 @@ return true; } + @Override public Iterator getAllChunkIds(long maxLastModifiedTime) throws Exception { final Connection conn = cp.getConnection(); Index: oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/blob/MarkSweepGarbageCollector.java =================================================================== --- oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/blob/MarkSweepGarbageCollector.java (revision 1578223) +++ oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/blob/MarkSweepGarbageCollector.java (working copy) @@ -392,17 +392,15 @@ @Override public void run() { - for (String id : ids) { - try { - boolean deleted = ((GarbageCollectableBlobStore) nodeStore.getBlobStore()) - .deleteChunk(id, maxLastModifiedTime); - if (!deleted) { - exceptionQueue.add(id); - } - } catch (Exception e) { - e.printStackTrace(); - exceptionQueue.add(id); + try { + boolean deleted = ((GarbageCollectableBlobStore) nodeStore.getBlobStore()) + .deleteChunks(ids, maxLastModifiedTime); + if (!deleted) { + exceptionQueue.addAll(ids); } + } catch (Exception e) { + e.printStackTrace(); + exceptionQueue.addAll(ids); } } } Index: oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/mongo/MongoBlobStore.java =================================================================== --- oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/mongo/MongoBlobStore.java (revision 1578223) +++ oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/mongo/MongoBlobStore.java (working copy) @@ -18,6 +18,7 @@ import java.io.IOException; import java.util.Iterator; +import java.util.List; import org.apache.jackrabbit.oak.commons.StringUtils; import org.apache.jackrabbit.oak.plugins.blob.CachingBlobStore; @@ -193,13 +194,21 @@ } return queryBuilder.get(); } - + @Override - public boolean deleteChunk(String chunkId, long maxLastModifiedTime) throws Exception { + public boolean deleteChunks(List chunkIds, long maxLastModifiedTime) throws Exception { DBCollection collection = getBlobCollection(); - WriteResult result = collection.remove(getBlobQuery(chunkId, maxLastModifiedTime)); + QueryBuilder queryBuilder = new QueryBuilder(); + if (chunkIds != null) { + queryBuilder = queryBuilder.and(MongoBlob.KEY_ID).in(chunkIds.toArray(new String[0])); + if (maxLastModifiedTime > 0) { + queryBuilder = queryBuilder.and(MongoBlob.KEY_LAST_MOD) + .lessThan(maxLastModifiedTime); + } + } - if (result.getN() == 1) { + WriteResult result = collection.remove(queryBuilder.get()); + if (result.getN() == chunkIds.size()) { return true; } Index: oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/rdb/RDBBlobStore.java =================================================================== --- oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/rdb/RDBBlobStore.java (revision 1578223) +++ oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/rdb/RDBBlobStore.java (working copy) @@ -26,6 +26,7 @@ import java.sql.Statement; import java.util.ArrayList; import java.util.Iterator; +import java.util.List; import javax.sql.DataSource; @@ -289,30 +290,47 @@ } @Override - public boolean deleteChunk(String chunkId, long maxLastModifiedTime) throws Exception { + public boolean deleteChunks(List chunkIds, long maxLastModifiedTime) throws Exception { try { PreparedStatement prep = null; PreparedStatement prepData = null; + StringBuilder inClause = new StringBuilder(); + int batch = chunkIds.size(); + for (int i = 0; i < batch; i++) { + inClause.append('?'); + if (i != batch - 1) { + inClause.append(','); + } + } + if (maxLastModifiedTime > 0) { prep = connection.prepareStatement( - "delete from datastore_meta where id = ? and lastMod <= ?"); - prep.setLong(2, maxLastModifiedTime); + "delete from datastore_meta where id in (" + + inClause.toString() + ") and lastMod <= ?"); + prep.setLong(batch + 1, maxLastModifiedTime); prepData = connection.prepareStatement( - "delete from datastore_data where id = ? and lastMod <= ?"); - prepData.setLong(2, maxLastModifiedTime); + "delete from datastore_data where id in (" + + inClause.toString() + ") and lastMod <= ?"); + prepData.setLong(batch + 1, maxLastModifiedTime); } else { prep = connection.prepareStatement( - "delete from datastore_meta where id = ?"); + "delete from datastore_meta where id in (" + + inClause.toString() + ")"); + prepData = connection.prepareStatement( - "delete from datastore_data where id = ?"); + "delete from datastore_data where id in (" + + inClause.toString() + ")"); } - prep.setString(1, chunkId); + + for (int idx = 0; idx < batch; idx++) { + prep.setString(idx + 1, chunkIds.get(idx)); + prepData.setString(idx + 1, chunkIds.get(idx)); + } + prep.execute(); - prepData.setString(1, chunkId); prepData.execute(); - prep.close(); prepData.close(); } finally {