/* * 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.flink.contrib.streaming.state.benchmark; import org.apache.flink.core.memory.MemoryUtils; import org.apache.flink.testutils.junit.RetryOnFailure; import org.apache.flink.testutils.junit.RetryRule; import org.apache.flink.util.TestLogger; import org.junit.Rule; import org.junit.Test; import org.junit.rules.TemporaryFolder; import org.rocksdb.CompactionStyle; import org.rocksdb.NativeLibraryLoader; import org.rocksdb.Options; import org.rocksdb.RocksDB; import org.rocksdb.RocksIterator; import org.rocksdb.StringAppendOperator; import org.rocksdb.WriteOptions; import sun.misc.Unsafe; import java.io.File; import java.nio.charset.StandardCharsets; import java.util.Arrays; /** * Test that validates that the performance of RocksDB is as expected. * This test guards against the bug filed as 'FLINK-5756' */ public class RocksDBPerformanceTest extends TestLogger { @Rule public final TemporaryFolder TMP = new TemporaryFolder(); @Rule public final RetryRule retry = new RetryRule(); @Test(timeout = 2000) @RetryOnFailure(times = 3) public void testRocksDbMergePerformance() throws Exception { final File rocksDir = TMP.newFolder(); // ensure the RocksDB library is loaded to a distinct location each retry NativeLibraryLoader.getInstance().loadLibrary(rocksDir.getAbsolutePath()); final String key = "key"; final String value = "abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ7890654321"; final byte[] keyBytes = key.getBytes(StandardCharsets.UTF_8); final byte[] valueBytes = value.getBytes(StandardCharsets.UTF_8); final int num = 50000; try ( final Options options = new Options() .setCompactionStyle(CompactionStyle.LEVEL) .setLevelCompactionDynamicLevelBytes(true) .setIncreaseParallelism(4) .setUseFsync(false) .setMaxOpenFiles(-1) .setDisableDataSync(true) .setCreateIfMissing(true) .setMergeOperator(new StringAppendOperator()); final WriteOptions write_options = new WriteOptions() .setSync(false) .setDisableWAL(true); final RocksDB rocksDB = RocksDB.open(options, rocksDir.getAbsolutePath())) { // ----- insert ----- log.info("begin insert"); final long beginInsert = System.nanoTime(); for (int i = 0; i < num; i++) { rocksDB.merge(write_options, keyBytes, valueBytes); } final long endInsert = System.nanoTime(); log.info("end insert - duration: {} ms", (endInsert - beginInsert) / 1_000_000); // ----- read (attempt 1) ----- final byte[] resultHolder = new byte[num * (valueBytes.length + 2)]; final long beginGet1 = System.nanoTime(); rocksDB.get(keyBytes, resultHolder); final long endGet1 = System.nanoTime(); log.info("end get - duration: {} ms", (endGet1 - beginGet1) / 1_000_000); // ----- read (attempt 2) ----- final long beginGet2 = System.nanoTime(); rocksDB.get(keyBytes, resultHolder); final long endGet2 = System.nanoTime(); log.info("end get - duration: {} ms", (endGet2 - beginGet2) / 1_000_000); // ----- compact ----- log.info("compacting..."); final long beginCompact = System.nanoTime(); rocksDB.compactRange(); final long endCompact = System.nanoTime(); log.info("end compaction - duration: {} ms", (endCompact - beginCompact) / 1_000_000); // ----- read (attempt 3) ----- final long beginGet3 = System.nanoTime(); rocksDB.get(keyBytes, resultHolder); final long endGet3 = System.nanoTime(); log.info("end get - duration: {} ms", (endGet3 - beginGet3) / 1_000_000); } } @Test(timeout = 2000) @RetryOnFailure(times = 3) public void testRocksDbRangeGetPerformance() throws Exception { final File rocksDir = TMP.newFolder(); // ensure the RocksDB library is loaded to a distinct location each retry NativeLibraryLoader.getInstance().loadLibrary(rocksDir.getAbsolutePath()); final String key = "key"; final String value = "abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ7890654321"; final byte[] keyBytes = key.getBytes(StandardCharsets.UTF_8); final byte[] valueBytes = value.getBytes(StandardCharsets.UTF_8); final int num = 50000; try ( final Options options = new Options() .setCompactionStyle(CompactionStyle.LEVEL) .setLevelCompactionDynamicLevelBytes(true) .setIncreaseParallelism(4) .setUseFsync(false) .setMaxOpenFiles(-1) .setDisableDataSync(true) .setCreateIfMissing(true) .setMergeOperator(new StringAppendOperator()); final WriteOptions write_options = new WriteOptions() .setSync(false) .setDisableWAL(true); final RocksDB rocksDB = RocksDB.open(options, rocksDir.getAbsolutePath())) { final byte[] keyTemplate = Arrays.copyOf(keyBytes, keyBytes.length + 4); final Unsafe unsafe = MemoryUtils.UNSAFE; final long offset = unsafe.arrayBaseOffset(byte[].class) + keyTemplate.length - 4; log.info("begin insert"); final long beginInsert = System.nanoTime(); for (int i = 0; i < num; i++) { unsafe.putInt(keyTemplate, offset, i); rocksDB.put(write_options, keyTemplate, valueBytes); } final long endInsert = System.nanoTime(); log.info("end insert - duration: {} ms", (endInsert - beginInsert) / 1_000_000); @SuppressWarnings("MismatchedReadAndWriteOfArray") final byte[] resultHolder = new byte[num * valueBytes.length]; final long beginGet = System.nanoTime(); int pos = 0; try (final RocksIterator iterator = rocksDB.newIterator()) { // seek to start unsafe.putInt(keyTemplate, offset, 0); iterator.seek(keyTemplate); // iterate while (iterator.isValid() && samePrefix(keyBytes, iterator.key())) { byte[] currValue = iterator.value(); System.arraycopy(currValue, 0, resultHolder, pos, currValue.length); pos += currValue.length; iterator.next(); } } final long endGet = System.nanoTime(); log.info("end get - duration: {} ms", (endGet - beginGet) / 1_000_000); } } private static boolean samePrefix(byte[] prefix, byte[] key) { for (int i = 0; i < prefix.length; i++) { if (prefix[i] != key [i]) { return false; } } return true; } }