/* * Copyright 2013 Rackspace * * Licensed 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 com.rackspacecloud.blueflood.cache; import com.rackspacecloud.blueflood.io.CassandraModel; import com.rackspacecloud.blueflood.io.CassandraUtilsIO; import com.rackspacecloud.blueflood.io.astyanax.ACassandraUtilsIO; import com.rackspacecloud.blueflood.io.datastax.DMetadataIO; import com.rackspacecloud.blueflood.io.datastax.DCassandraUtilsIO; import org.junit.runner.RunWith; import com.rackspacecloud.blueflood.io.astyanax.AMetadataIO; import com.rackspacecloud.blueflood.io.MetadataIO; import com.rackspacecloud.blueflood.types.Locator; import com.rackspacecloud.blueflood.io.IntegrationTestBase; import com.rackspacecloud.blueflood.types.MetricMetadata; import com.rackspacecloud.blueflood.utils.TimeValue; import org.junit.Assert; import org.junit.Test; import org.junit.runners.Parameterized; import com.rackspacecloud.blueflood.utils.InMemoryMetadataIO; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.util.*; import java.util.concurrent.TimeUnit; @RunWith(Parameterized.class) public class MetadataCacheIntegrationTest extends IntegrationTestBase { private final MetadataIO metadataIO; private final CassandraUtilsIO cassandraUtilsIO; public MetadataCacheIntegrationTest(MetadataIO mIO, CassandraUtilsIO tIO) { metadataIO = mIO; cassandraUtilsIO = tIO; } @Override public void setUp() throws Exception { super.setUp(); // equivalent of database truncate. if ( metadataIO instanceof InMemoryMetadataIO) { ((InMemoryMetadataIO) metadataIO ).backingTable.clear(); } } @Test public void testPut() throws Exception { Assert.assertEquals( 0, cassandraUtilsIO.getKeyCount( CassandraModel.CF_METRICS_METADATA_NAME ) ); MetadataCache cache = MetadataCache.createLoadingCacheInstance(new TimeValue(5, TimeUnit.MINUTES), 1); cache.setIO( metadataIO ); Locator loc1 = Locator.createLocatorFromPathComponents( getRandomTenantId(), "acOne", "ent", "chk", "mz", "met"); Locator loc2 = Locator.createLocatorFromPathComponents( getRandomTenantId(), "acTwo", "ent", "chk", "mz", "met"); cache.put(loc1, "metaA", "some string"); cache.put(loc1, "metaB", "fooz"); cache.put(loc1, "metaC", "some other string"); Assert.assertEquals( 1, cassandraUtilsIO.getKeyCount( CassandraModel.CF_METRICS_METADATA_NAME ) ); cache.put(loc2, "metaA", "hello"); Assert.assertEquals( 2, cassandraUtilsIO.getKeyCount( CassandraModel.CF_METRICS_METADATA_NAME ) ); } @Test public void testGetNull() throws Exception { Locator loc1 = Locator.createLocatorFromPathComponents( getRandomTenantId(), "acOne", "ent", "chk", "mz", "met"); MetadataCache cache1 = MetadataCache.createLoadingCacheInstance(new TimeValue(5, TimeUnit.MINUTES), 1); cache1.setIO( metadataIO ); Assert.assertNull(cache1.get(loc1, "foo")); Assert.assertNull(cache1.get(loc1, "foo")); } @Test public void testCollisions() throws Exception { Locator loc1 = Locator.createLocatorFromPathComponents( getRandomTenantId(), "ac76PeGPSR", "entZ4MYd1W", "chJ0fvB5Ao", "mzord", "truncated"); // put unit of bytes Locator loc2 = Locator.createLocatorFromPathComponents( getRandomTenantId(), "acTmPLSgfv", "enLctkAMeN", "chQwBe5YiE", "mzdfw", "cert_end_in"); // put type of I MetadataCache cache = MetadataCache.getInstance(); cache.setIO( metadataIO ); cache.put(loc1, MetricMetadata.UNIT.name().toLowerCase(), "foo"); } @Test public void testGet() throws Exception { Locator loc1 = Locator.createLocatorFromPathComponents( getRandomTenantId(), "acOne", "ent", "chk", "mz", "met"); MetadataCache cache1 = MetadataCache.createLoadingCacheInstance(new TimeValue(5, TimeUnit.MINUTES), 1); MetadataCache cache2 = MetadataCache.createLoadingCacheInstance(new TimeValue(5, TimeUnit.MINUTES), 1); cache1.setIO( metadataIO ); cache2.setIO( metadataIO ); // put in one, read in both. Class<String> expectedClass = String.class; String expected = "expected"; String key = "metaA"; cache1.put(loc1, key, expected); Assert.assertEquals(expected, cache1.get(loc1, key, expectedClass)); Assert.assertEquals(expected, cache2.get(loc1, key, expectedClass)); // update in one verify can only new value there. expected = "different expected"; Assert.assertFalse(expected.equals(cache1.get(loc1, key, expectedClass))); cache1.put(loc1, key, expected); Assert.assertEquals(expected, cache1.get(loc1, key, expectedClass)); // cache2 has old value that is unexpired. invalidate and read new value. Assert.assertFalse(expected.equals(cache2.get(loc1, key, expectedClass))); cache2.invalidate(loc1, key); Assert.assertEquals(expected, cache2.get(loc1, key, expectedClass)); // re-read on invalidate. cache1.invalidate(loc1, key); Assert.assertFalse(cache1.containsKey(loc1, key)); Assert.assertEquals(expected, cache1.get(loc1, key)); } @Test public void testPutsAreNotDuplicative() throws Exception { Locator loc1 = Locator.createLocatorFromPathComponents( getRandomTenantId(), "acOne", "ent", "chk", "mz", "met"); MetadataCache cache1 = MetadataCache.createLoadingCacheInstance(new TimeValue(5, TimeUnit.MINUTES), 1); cache1.setIO( metadataIO ); String key = "metaA"; String v1 = new String("Hello"); String v2 = new String("Hello"); Assert.assertTrue(v1 != v2); Assert.assertEquals(v1, v2); Assert.assertTrue(cache1.put(loc1, key, v1)); Assert.assertFalse(cache1.put(loc1, key, v2)); } @Test public void testExpiration() throws Exception { Locator loc1 = Locator.createLocatorFromPathComponents( getRandomTenantId(), "acOne", "ent.chk.mz.met"); MetadataCache cache1 = MetadataCache.createLoadingCacheInstance(new TimeValue(5, TimeUnit.MINUTES), 1); MetadataCache cache2 = MetadataCache.createLoadingCacheInstance(new TimeValue(3, TimeUnit.SECONDS), 1); cache1.setIO( metadataIO ); cache2.setIO( metadataIO ); // update in 1, should read out of both. Class<String> expectedClass = String.class; String expected = "Hello"; String key = "metaA"; cache1.put(loc1, key, expected); Assert.assertEquals(expected, cache1.get(loc1, key, expectedClass)); Assert.assertEquals(expected, cache2.get(loc1, key, expectedClass)); // update cache1, but not cache2. expected = "Hello2"; Assert.assertFalse(expected.equals(cache1.get(loc1, key, expectedClass))); cache1.put(loc1, key, expected); Assert.assertEquals(expected, cache1.get(loc1, key, expectedClass)); // verify that 2 has old value. Assert.assertFalse(expected.equals(cache2.get(loc1, key, expectedClass))); // wait for expiration, then verify that new value is picked up. Thread.sleep(4000); Assert.assertEquals(expected, cache2.get(loc1, key, expectedClass)); } @Test public void testTypedGet() throws Exception { MetadataCache cache = MetadataCache.createLoadingCacheInstance(new TimeValue(5, TimeUnit.MINUTES), 1); cache.setIO( metadataIO ); Locator loc1 = Locator.createLocatorFromPathComponents( getRandomTenantId(), "acOne", "ent", "chk", "mz", "met"); String expectedString = "expected"; cache.put(loc1, "str", expectedString); Assert.assertEquals(expectedString, cache.get(loc1, "str", String.class)); } @Test public void testIOReplacement() throws Exception { // create the replacement IO. final MetadataIO mapIO = new InMemoryMetadataIO(); final MetadataIO astIO = new AMetadataIO(); final MetadataCache cache = MetadataCache.createLoadingCacheInstance(); cache.setIO(astIO); // DO NOT SET USING LOCAL IO INSTANCE!!!! // put an get a value with the old IO Locator loc = Locator.createLocatorFromPathComponents( getRandomTenantId(), "io_replacment", "a", "b", "c"); Assert.assertNull(cache.get(loc, "foo")); cache.put(loc, "foo", "bar"); Assert.assertNotNull(cache.get(loc, "foo")); Assert.assertEquals("bar", cache.get(loc, "foo")); // replace the IO, ensure there is nothing there, do a put and get, verify they are different than from before. cache.setIO(mapIO); Assert.assertNull(cache.get(loc, "foo")); cache.put(loc, "foo", "baz"); Assert.assertNotNull(cache.get(loc, "foo")); Assert.assertEquals("baz", cache.get(loc, "foo")); // put the old IO back. this should result in the old value being read from the cache. cache.setIO(astIO); Assert.assertEquals("bar", cache.get(loc, "foo")); } @Test public void testPersistence() throws Exception { MetadataCache cache0 = MetadataCache.createLoadingCacheInstance(); cache0.setIO(new InMemoryMetadataIO()); Locator l0 = Locator.createLocatorFromPathComponents( getRandomTenantId(), "1", "a", "b"); Locator l1 = Locator.createLocatorFromPathComponents( getRandomTenantId(), "1", "c", "d"); cache0.put(l0, "foo" , "l0_foo"); cache0.put(l0, "bar", "l0_bar"); cache0.put(l1, "zee", "zzzzz"); File f = File.createTempFile("metadatacache_persistence", "txt"); f.deleteOnExit(); DataOutputStream out = new DataOutputStream(new FileOutputStream(f, false)); cache0.save(out); out.close(); MetadataCache cache1 = MetadataCache.createLoadingCacheInstance(); cache1.setIO(new InMemoryMetadataIO()); // verify nothing is in the cache. Assert.assertNull(cache1.get(l0, "foo")); Assert.assertNull(cache1.get(l0, "bar")); Assert.assertNull(cache1.get(l1, "zee")); // now load it. DataInputStream in = new DataInputStream(new FileInputStream(f)); cache1.load(in); Assert.assertEquals("l0_foo", cache1.get(l0, "foo")); Assert.assertEquals("l0_bar", cache1.get(l0, "bar")); Assert.assertEquals("zzzzz", cache1.get(l1, "zee")); } @Parameterized.Parameters public static Collection<Object[]> getIOs() { List<Object[]> ios = new ArrayList<Object[]>(); ios.add(new Object[] { new AMetadataIO(), new ACassandraUtilsIO() }); ios.add(new Object[] { new DMetadataIO(), new DCassandraUtilsIO() }); InMemoryMetadataIO memIO = new InMemoryMetadataIO(); ios.add(new Object[] { memIO, memIO }); return ios; } }