| /* |
| * 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.cassandra.io.sstable; |
| |
| import java.util.Iterator; |
| import java.util.concurrent.ThreadLocalRandom; |
| |
| import org.junit.Ignore; |
| import org.junit.Test; |
| import org.junit.runner.RunWith; |
| |
| import org.apache.cassandra.OrderedJUnit4ClassRunner; |
| import org.apache.cassandra.cql3.CQLTester; |
| import org.apache.cassandra.cql3.UntypedResultSet; |
| import org.apache.cassandra.metrics.CacheMetrics; |
| import org.apache.cassandra.service.CacheService; |
| |
| /** |
| * Test intended to manually measure GC pressure to write and read partitions of different size |
| * for CASSANDRA-11206. |
| */ |
| @RunWith(OrderedJUnit4ClassRunner.class) |
| @Ignore // all these tests take very, very long - so only run them manually |
| public class LargePartitionsTest extends CQLTester |
| { |
| |
| @FunctionalInterface |
| interface Measured |
| { |
| void measure() throws Throwable; |
| } |
| |
| private static void measured(String name, Measured measured) throws Throwable |
| { |
| long t0 = System.currentTimeMillis(); |
| measured.measure(); |
| long t = System.currentTimeMillis() - t0; |
| System.out.println("LargePartitionsTest-measured: " + name + " took " + t + " ms"); |
| } |
| |
| private static String randomText(int bytes) |
| { |
| char[] ch = new char[bytes]; |
| ThreadLocalRandom r = ThreadLocalRandom.current(); |
| for (int i = 0; i < bytes; i++) |
| ch[i] = (char) (32 + r.nextInt(95)); |
| return new String(ch); |
| } |
| |
| private static final int rowKBytes = 8; |
| |
| private void withPartitionSize(long partitionKBytes, long totalMBytes) throws Throwable |
| { |
| long totalKBytes = totalMBytes * 1024L; |
| |
| createTable("CREATE TABLE %s (pk text, ck text, val text, PRIMARY KEY (pk, ck))"); |
| |
| String name = "part=" + partitionKBytes + "k total=" + totalMBytes + 'M'; |
| |
| measured("INSERTs for " + name, () -> { |
| for (long writtenKBytes = 0L; writtenKBytes < totalKBytes; writtenKBytes += partitionKBytes) |
| { |
| String pk = Long.toBinaryString(writtenKBytes); |
| for (long kbytes = 0L; kbytes < partitionKBytes; kbytes += rowKBytes) |
| { |
| String ck = Long.toBinaryString(kbytes); |
| execute("INSERT INTO %s (pk, ck, val) VALUES (?,?,?)", pk, ck, randomText(rowKBytes * 1024)); |
| } |
| } |
| }); |
| |
| measured("flush for " + name, () -> flush(true)); |
| |
| CacheService.instance.keyCache.clear(); |
| |
| measured("compact for " + name, () -> { |
| keyCacheMetrics("before compaction"); |
| compact(); |
| keyCacheMetrics("after compaction"); |
| }); |
| |
| measured("SELECTs 1 for " + name, () -> selects(partitionKBytes, totalKBytes)); |
| |
| measured("SELECTs 2 for " + name, () -> selects(partitionKBytes, totalKBytes)); |
| |
| CacheService.instance.keyCache.clear(); |
| measured("Scan for " + name, () -> scan(partitionKBytes, totalKBytes)); |
| } |
| |
| private void selects(long partitionKBytes, long totalKBytes) throws Throwable |
| { |
| for (int i = 0; i < 50000; i++) |
| { |
| long pk = ThreadLocalRandom.current().nextLong(totalKBytes / partitionKBytes) * partitionKBytes; |
| long ck = ThreadLocalRandom.current().nextLong(partitionKBytes / rowKBytes) * rowKBytes; |
| execute("SELECT val FROM %s WHERE pk=? AND ck=?", |
| Long.toBinaryString(pk), |
| Long.toBinaryString(ck)).one(); |
| if (i % 1000 == 0) |
| keyCacheMetrics("after " + i + " selects"); |
| } |
| keyCacheMetrics("after all selects"); |
| } |
| |
| private void scan(long partitionKBytes, long totalKBytes) throws Throwable |
| { |
| long pk = ThreadLocalRandom.current().nextLong(totalKBytes / partitionKBytes) * partitionKBytes; |
| Iterator<UntypedResultSet.Row> iter = execute("SELECT val FROM %s WHERE pk=?", Long.toBinaryString(pk)).iterator(); |
| int i = 0; |
| while (iter.hasNext()) |
| { |
| iter.next(); |
| if (i++ % 1000 == 0) |
| keyCacheMetrics("after " + i + " iteration"); |
| } |
| keyCacheMetrics("after all iteration"); |
| } |
| |
| private static void keyCacheMetrics(String title) |
| { |
| CacheMetrics metrics = CacheService.instance.keyCache.getMetrics(); |
| System.out.println("Key cache metrics " + title + ": capacity:" + metrics.capacity.getValue() + |
| " size:"+metrics.size.getValue()+ |
| " entries:" + metrics.entries.getValue() + |
| " hit-rate:"+metrics.hitRate.getValue() + |
| " one-min-rate:"+metrics.oneMinuteHitRate.getValue()); |
| } |
| |
| @Test |
| public void prepare() throws Throwable |
| { |
| for (int i = 0; i < 4; i++) |
| { |
| withPartitionSize(8L, 32L); |
| } |
| } |
| |
| @Test |
| public void test_01_16k() throws Throwable |
| { |
| withPartitionSize(16L, 1024L); |
| } |
| |
| @Test |
| public void test_02_512k() throws Throwable |
| { |
| withPartitionSize(512L, 1024L); |
| } |
| |
| @Test |
| public void test_03_1M() throws Throwable |
| { |
| withPartitionSize(1024L, 1024L); |
| } |
| |
| @Test |
| public void test_04_4M() throws Throwable |
| { |
| withPartitionSize(4L * 1024L, 1024L); |
| } |
| |
| @Test |
| public void test_05_8M() throws Throwable |
| { |
| withPartitionSize(8L * 1024L, 1024L); |
| } |
| |
| @Test |
| public void test_06_16M() throws Throwable |
| { |
| withPartitionSize(16L * 1024L, 1024L); |
| } |
| |
| @Test |
| public void test_07_32M() throws Throwable |
| { |
| withPartitionSize(32L * 1024L, 1024L); |
| } |
| |
| @Test |
| public void test_08_64M() throws Throwable |
| { |
| withPartitionSize(64L * 1024L, 1024L); |
| } |
| |
| @Test |
| public void test_09_256M() throws Throwable |
| { |
| withPartitionSize(256L * 1024L, 4 * 1024L); |
| } |
| |
| @Test |
| public void test_10_512M() throws Throwable |
| { |
| withPartitionSize(512L * 1024L, 4 * 1024L); |
| } |
| |
| @Test |
| public void test_11_1G() throws Throwable |
| { |
| withPartitionSize(1024L * 1024L, 8 * 1024L); |
| } |
| |
| @Test |
| public void test_12_2G() throws Throwable |
| { |
| withPartitionSize(2L * 1024L * 1024L, 8 * 1024L); |
| } |
| |
| @Test |
| public void test_13_4G() throws Throwable |
| { |
| withPartitionSize(4L * 1024L * 1024L, 16 * 1024L); |
| } |
| |
| @Test |
| public void test_14_8G() throws Throwable |
| { |
| withPartitionSize(8L * 1024L * 1024L, 32 * 1024L); |
| } |
| } |