diff --git oak-segment-tar/src/test/java/org/apache/jackrabbit/oak/segment/tool/CompactTest.java oak-segment-tar/src/test/java/org/apache/jackrabbit/oak/segment/tool/CompactTest.java new file mode 100644 index 0000000..9509447 --- /dev/null +++ oak-segment-tar/src/test/java/org/apache/jackrabbit/oak/segment/tool/CompactTest.java @@ -0,0 +1,126 @@ +/* + * 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.segment.tool; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +import java.io.ByteArrayInputStream; +import java.io.File; +import java.io.IOException; +import java.util.Random; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; + +import org.apache.jackrabbit.oak.api.Blob; +import org.apache.jackrabbit.oak.segment.RecordId; +import org.apache.jackrabbit.oak.segment.SegmentIdProvider; +import org.apache.jackrabbit.oak.segment.SegmentNodeStore; +import org.apache.jackrabbit.oak.segment.SegmentNodeStoreBuilders; +import org.apache.jackrabbit.oak.segment.file.FileStore; +import org.apache.jackrabbit.oak.segment.file.FileStoreBuilder; +import org.apache.jackrabbit.oak.segment.file.InvalidFileStoreVersionException; +import org.apache.jackrabbit.oak.segment.file.JournalEntry; +import org.apache.jackrabbit.oak.segment.file.JournalReader; +import org.apache.jackrabbit.oak.spi.commit.CommitInfo; +import org.apache.jackrabbit.oak.spi.commit.EmptyHook; +import org.apache.jackrabbit.oak.spi.state.NodeBuilder; +import org.apache.jackrabbit.oak.spi.state.NodeStore; +import org.apache.jackrabbit.oak.stats.DefaultStatisticsProvider; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; + +public class CompactTest { + + @Rule + public final TemporaryFolder temporaryFolder = new TemporaryFolder(new File("target")); + + @Before + public void setup() throws Exception { + addTestData(); + } + + @Test + public void testOfflineCompaction() throws IOException, InvalidFileStoreVersionException { + ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor(); + long beforeCompactionSize = 0; + + try (FileStore fileStore = FileStoreBuilder.fileStoreBuilder(temporaryFolder.getRoot()).withMaxFileSize(256) + .withSegmentCacheSize(64).withStatisticsProvider(new DefaultStatisticsProvider(executor)).build()) { + beforeCompactionSize = fileStore.getStats().getApproximateSize(); + } + + Runnable offlineCompaction = Compact.builder().withPath(temporaryFolder.getRoot()).build(); + offlineCompaction.run(); + + File journal = new File(temporaryFolder.getRoot(), "journal.log"); + JournalEntry entry = null; + + try (JournalReader journalReader = new JournalReader(journal)) { + int count = 0; + while (journalReader.hasNext()) { + entry = journalReader.next(); + count++; + } + + assertEquals(1, count); + } + + assertNotNull(entry); + + try (FileStore fileStore = FileStoreBuilder.fileStoreBuilder(temporaryFolder.getRoot()).withMaxFileSize(256) + .withSegmentCacheSize(64).withStatisticsProvider(new DefaultStatisticsProvider(executor)).build()) { + SegmentIdProvider idProvider = fileStore.getSegmentIdProvider(); + RecordId head = RecordId.fromString(idProvider, entry.getRevision()); + assertNotNull(head); + + long afterCompactionSize = fileStore.getStats().getApproximateSize(); + + assertTrue(afterCompactionSize < beforeCompactionSize); + } + } + + private void addTestData() throws Exception { + try (FileStore fileStore = FileStoreBuilder.fileStoreBuilder(temporaryFolder.getRoot()).withMaxFileSize(256) + .withSegmentCacheSize(64).build()) { + + SegmentNodeStore nodeStore = SegmentNodeStoreBuilders.builder(fileStore).build(); + + // 1MB blob + int blobSize = 1 * 1024 * 1024; + + NodeBuilder builder = nodeStore.getRoot().builder(); + builder.setProperty("blob1", createBlob(nodeStore, blobSize)); + nodeStore.merge(builder, EmptyHook.INSTANCE, CommitInfo.EMPTY); + fileStore.flush(); + + builder = nodeStore.getRoot().builder(); + builder.removeProperty("blob1"); + nodeStore.merge(builder, EmptyHook.INSTANCE, CommitInfo.EMPTY); + fileStore.flush(); + } + } + + private static Blob createBlob(NodeStore nodeStore, int size) throws IOException { + byte[] data = new byte[size]; + new Random().nextBytes(data); + return nodeStore.createBlob(new ByteArrayInputStream(data)); + } +}