/** * 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.db; import java.net.InetAddress; import java.util.Collection; import org.junit.AfterClass; import org.junit.Test; import org.apache.cassandra.SchemaLoader; import org.apache.cassandra.Util; import org.apache.cassandra.db.composites.*; import org.apache.cassandra.db.compaction.CompactionManager; import org.apache.cassandra.dht.BytesToken; import org.apache.cassandra.locator.TokenMetadata; import org.apache.cassandra.service.CacheService; import org.apache.cassandra.service.StorageService; import org.apache.cassandra.utils.ByteBufferUtil; import static org.junit.Assert.assertEquals; public class RowCacheTest extends SchemaLoader { private String KEYSPACE = "RowCacheSpace"; private String COLUMN_FAMILY = "CachedCF"; @AfterClass public static void cleanup() { cleanupSavedCaches(); } @Test public void testRowCache() throws Exception { CompactionManager.instance.disableAutoCompaction(); Keyspace keyspace = Keyspace.open(KEYSPACE); ColumnFamilyStore cachedStore = keyspace.getColumnFamilyStore(COLUMN_FAMILY); // empty the row cache CacheService.instance.invalidateRowCache(); // set global row cache size to 1 MB CacheService.instance.setRowCacheCapacityInMB(1); // inserting 100 rows into both column families insertData(KEYSPACE, COLUMN_FAMILY, 0, 100); // now reading rows one by one and checking if row change grows for (int i = 0; i < 100; i++) { DecoratedKey key = Util.dk("key" + i); cachedStore.getColumnFamily(key, Composites.EMPTY, Composites.EMPTY, false, 1, System.currentTimeMillis()); assert CacheService.instance.rowCache.size() == i + 1; assert cachedStore.containsCachedRow(key); // current key should be stored in the cache // checking if cell is read correctly after cache ColumnFamily cf = cachedStore.getColumnFamily(key, Composites.EMPTY, Composites.EMPTY, false, 1, System.currentTimeMillis()); Collection<Cell> cells = cf.getSortedColumns(); Cell cell = cells.iterator().next(); assert cells.size() == 1; assert cell.name().toByteBuffer().equals(ByteBufferUtil.bytes("col" + i)); assert cell.value().equals(ByteBufferUtil.bytes("val" + i)); } // insert 10 more keys insertData(KEYSPACE, COLUMN_FAMILY, 100, 10); for (int i = 100; i < 110; i++) { DecoratedKey key = Util.dk("key" + i); cachedStore.getColumnFamily(key, Composites.EMPTY, Composites.EMPTY, false, 1, System.currentTimeMillis()); assert cachedStore.containsCachedRow(key); // cache should be populated with the latest rows read (old ones should be popped) // checking if cell is read correctly after cache ColumnFamily cf = cachedStore.getColumnFamily(key, Composites.EMPTY, Composites.EMPTY, false, 1, System.currentTimeMillis()); Collection<Cell> cells = cf.getSortedColumns(); Cell cell = cells.iterator().next(); assert cells.size() == 1; assert cell.name().toByteBuffer().equals(ByteBufferUtil.bytes("col" + i)); assert cell.value().equals(ByteBufferUtil.bytes("val" + i)); } // clear 100 rows from the cache int keysLeft = 109; for (int i = 109; i >= 10; i--) { cachedStore.invalidateCachedRow(Util.dk("key" + i)); assert CacheService.instance.rowCache.size() == keysLeft; keysLeft--; } CacheService.instance.setRowCacheCapacityInMB(0); } @Test public void testRowCacheLoad() throws Exception { CacheService.instance.setRowCacheCapacityInMB(1); rowCacheLoad(100, Integer.MAX_VALUE, 0); CacheService.instance.setRowCacheCapacityInMB(0); } @Test public void testRowCacheCleanup() throws Exception { StorageService.instance.initServer(0); CacheService.instance.setRowCacheCapacityInMB(1); rowCacheLoad(100, Integer.MAX_VALUE, 1000); ColumnFamilyStore store = Keyspace.open(KEYSPACE).getColumnFamilyStore(COLUMN_FAMILY); assertEquals(CacheService.instance.rowCache.getKeySet().size(), 100); store.cleanupCache(); assertEquals(CacheService.instance.rowCache.getKeySet().size(), 100); TokenMetadata tmd = StorageService.instance.getTokenMetadata(); byte[] tk1, tk2; tk1 = "key1000".getBytes(); tk2 = "key1050".getBytes(); tmd.updateNormalToken(new BytesToken(tk1), InetAddress.getByName("127.0.0.1")); tmd.updateNormalToken(new BytesToken(tk2), InetAddress.getByName("127.0.0.2")); store.cleanupCache(); assertEquals(CacheService.instance.rowCache.getKeySet().size(), 50); CacheService.instance.setRowCacheCapacityInMB(0); } @Test public void testRowCachePartialLoad() throws Exception { CacheService.instance.setRowCacheCapacityInMB(1); rowCacheLoad(100, 50, 0); CacheService.instance.setRowCacheCapacityInMB(0); } public void rowCacheLoad(int totalKeys, int keysToSave, int offset) throws Exception { CompactionManager.instance.disableAutoCompaction(); ColumnFamilyStore store = Keyspace.open(KEYSPACE).getColumnFamilyStore(COLUMN_FAMILY); // empty the cache CacheService.instance.invalidateRowCache(); assert CacheService.instance.rowCache.size() == 0; // insert data and fill the cache insertData(KEYSPACE, COLUMN_FAMILY, offset, totalKeys); readData(KEYSPACE, COLUMN_FAMILY, offset, totalKeys); assert CacheService.instance.rowCache.size() == totalKeys; // force the cache to disk CacheService.instance.rowCache.submitWrite(keysToSave).get(); // empty the cache again to make sure values came from disk CacheService.instance.invalidateRowCache(); assert CacheService.instance.rowCache.size() == 0; assert CacheService.instance.rowCache.loadSaved(store) == (keysToSave == Integer.MAX_VALUE ? totalKeys : keysToSave); } }