/* * Copyright (c) 2008-2017, Hazelcast, Inc. All Rights Reserved. * * 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.hazelcast.internal.ascii; import com.hazelcast.config.Config; import com.hazelcast.config.JoinConfig; import com.hazelcast.core.Hazelcast; import com.hazelcast.core.HazelcastInstance; import com.hazelcast.core.IMap; import com.hazelcast.internal.ascii.memcache.MemcacheCommandProcessor; import com.hazelcast.internal.ascii.memcache.MemcacheEntry; import com.hazelcast.spi.properties.GroupProperty; import com.hazelcast.test.AssertTask; import com.hazelcast.test.HazelcastParallelClassRunner; import com.hazelcast.test.HazelcastTestSupport; import com.hazelcast.test.annotation.QuickTest; import net.spy.memcached.ConnectionFactory; import net.spy.memcached.ConnectionFactoryBuilder; import net.spy.memcached.FailureMode; import net.spy.memcached.MemcachedClient; import net.spy.memcached.internal.OperationFuture; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.experimental.categories.Category; import org.junit.runner.RunWith; import java.net.InetSocketAddress; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Map; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; @RunWith(HazelcastParallelClassRunner.class) @Category(QuickTest.class) // intentionally not in ParallelTest category, // test is starting standalone HazelcastInstances. public class MemcacheTest extends HazelcastTestSupport { private Config config = new Config(); private HazelcastInstance instance; private MemcachedClient client; @Before public void setup() throws Exception { config.setProperty(GroupProperty.MEMCACHE_ENABLED.getName(), "true"); // Join is disabled intentionally. will start standalone HazelcastInstances. JoinConfig join = config.getNetworkConfig().getJoin(); join.getMulticastConfig().setEnabled(false); join.getTcpIpConfig().setEnabled(false); instance = Hazelcast.newHazelcastInstance(config); client = getMemcacheClient(instance); } @After public void tearDown() { try { if (client != null) { client.shutdown(); } } finally { if (instance != null) { instance.getLifecycleService().terminate(); } } } @Test public void testSetAndGet() throws Exception { String key = "key"; String value = "value"; OperationFuture<Boolean> future = client.set(key, 0, value); assertEquals(Boolean.TRUE, future.get()); assertEquals(value, client.get(key)); checkStats(1, 1, 1, 0, 0, 0, 0, 0, 0, 0); } @Test public void testAddAndGet() throws Exception { String key = "key"; String value = "value"; String key2 = "key2"; String value2 = "value2"; OperationFuture<Boolean> future = client.set(key, 0, value); assertEquals(Boolean.TRUE, future.get()); future = client.add(key, 0, value2); assertEquals(Boolean.FALSE, future.get()); assertEquals(value, client.get(key)); future = client.add(key2, 0, value2); assertEquals(Boolean.TRUE, future.get()); assertEquals(value2, client.get(key2)); checkStats(3, 2, 2, 0, 0, 0, 0, 0, 0, 0); } @Test public void testReplace() throws Exception { String key = "key"; String value = "value"; String value2 = "value2"; OperationFuture<Boolean> future = client.replace(key, 0, value2); assertEquals(Boolean.FALSE, future.get()); assertNull(client.get(key)); future = client.set(key, 0, value); assertEquals(Boolean.TRUE, future.get()); future = client.replace(key, 0, value2); assertEquals(Boolean.TRUE, future.get()); assertEquals(value2, client.get(key)); checkStats(3, 2, 1, 1, 0, 0, 0, 0, 0, 0); } @Test public void testDelete() throws Exception { String key = "key"; String value = "value"; OperationFuture<Boolean> future = client.delete(key); assertEquals(Boolean.FALSE, future.get()); future = client.set(key, 0, value); assertEquals(Boolean.TRUE, future.get()); future = client.delete(key); assertEquals(Boolean.TRUE, future.get()); assertNull(client.get(key)); checkStats(1, 1, 0, 1, 1, 1, 0, 0, 0, 0); } @Test public void testBulkGet() throws Exception { List<String> keys = new ArrayList<String>(); for (int i = 0; i < 10; i++) { keys.add("key" + i); } Map<String, Object> result = client.getBulk(keys); assertEquals(0, result.size()); String value = "value"; for (String key : keys) { OperationFuture<Boolean> future = client.set(key, 0, value); future.get(); } result = client.getBulk(keys); assertEquals(keys.size(), result.size()); for (String key : keys) { assertEquals(value, result.get(key)); } checkStats(keys.size(), keys.size() * 2, keys.size(), keys.size(), 0, 0, 0, 0, 0, 0); } @Test public void testSetGetDelete_WithDefaultIMap() throws Exception { testSetGetDelete_WithIMap(MemcacheCommandProcessor.DEFAULT_MAP_NAME, ""); } @Test public void testSetGetDelete_WithCustomIMap() throws Exception { String mapName = randomMapName(); testSetGetDelete_WithIMap(MemcacheCommandProcessor.MAP_NAME_PRECEDER + mapName, mapName + ":"); } private void testSetGetDelete_WithIMap(String mapName, String prefix) throws Exception { String key = "key"; String value = "value"; String value2 = "value2"; IMap<String, Object> map = instance.getMap(mapName); map.put(key, value); assertEquals(value, client.get(prefix + key)); client.set(prefix + key, 0, value2).get(); MemcacheEntry memcacheEntry = (MemcacheEntry) map.get(key); MemcacheEntry expectedEntry = new MemcacheEntry(prefix + key, value2.getBytes(), 0); assertEquals(expectedEntry, memcacheEntry); assertEquals(prefix + key, memcacheEntry.getKey()); client.delete(prefix + key).get(); assertNull(client.get(prefix + key)); assertNull(map.get(key)); } @Test public void testDeleteAll_withIMapPrefix() throws Exception { String mapName = randomMapName(); String prefix = mapName + ":"; IMap<String, Object> map = instance.getMap(MemcacheCommandProcessor.MAP_NAME_PRECEDER + mapName); for (int i = 0; i < 100; i++) { map.put(String.valueOf(i), i); } OperationFuture<Boolean> future = client.delete(prefix); future.get(); for (int i = 0; i < 100; i++) { assertNull(client.get(prefix + String.valueOf(i))); } assertTrue(map.isEmpty()); } @Test public void testIncrement() throws Exception { String key = "key"; long value = client.incr(key, 1); assertEquals(-1, value); assertNull(client.get(key)); OperationFuture<Boolean> future = client.set(key, 0, 1); future.get(); value = client.incr(key, 10); assertEquals(11, value); value = client.incr(key, -5); assertEquals(6, value); checkStats(1, 1, 0, 1, 0, 0, 2, 1, 0, 0); } @Test public void testDecrement() throws Exception { String key = "key"; long value = client.decr(key, 1); assertEquals(-1, value); assertNull(client.get(key)); OperationFuture<Boolean> future = client.set(key, 0, 5); future.get(); value = client.decr(key, 2); assertEquals(3, value); value = client.decr(key, -2); assertEquals(5, value); checkStats(1, 1, 0, 1, 0, 0, 0, 0, 2, 1); } @Test public void testAppend() throws Exception { String key = "key"; String value = "value"; String append = "123"; OperationFuture<Boolean> future = client.append(key, append); assertEquals(Boolean.FALSE, future.get()); future = client.set(key, 0, value); future.get(); future = client.append(key, append); assertEquals(Boolean.TRUE, future.get()); assertEquals(value + append, client.get(key)); } @Test public void testPrepend() throws Exception { String key = "key"; String value = "value"; String prepend = "123"; OperationFuture<Boolean> future = client.prepend(key, prepend); assertEquals(Boolean.FALSE, future.get()); future = client.set(key, 0, value); future.get(); future = client.prepend(key, prepend); assertEquals(Boolean.TRUE, future.get()); assertEquals(prepend + value, client.get(key)); } @Test public void testExpiration() throws Exception { final String key = "key"; client.set(key, 3, "value").get(); assertTrueEventually(new AssertTask() { @Override public void run() throws Exception { assertNull(client.get(key)); } }); } @Test public void testSetGet_withLargeValue() throws Exception { String key = "key"; int capacity = 10000; StringBuilder value = new StringBuilder(capacity); while (value.length() < capacity) { value.append(randomString()); } OperationFuture<Boolean> future = client.set(key, 0, value.toString()); future.get(); Object result = client.get(key); assertEquals(value.toString(), result); } @Test public void testBulkSetGet_withManyKeys() throws Exception { int numberOfKeys = 1000; Collection<String> keys = new HashSet<String>(numberOfKeys); for (int i = 0; i < numberOfKeys; i++) { String key = "key" + i; OperationFuture<Boolean> future = client.set(key, 0, key); future.get(); keys.add(key); } Map<String, Object> result = client.getBulk(keys); for (String key : keys) { assertEquals(key, result.get(key)); } } private void checkStats(int sets, int gets, int getHits, int getMisses, int deleteHits, int deleteMisses, int incHits, int incMisses, int decHits, int decMisses) { InetSocketAddress address = instance.getCluster().getLocalMember().getSocketAddress(); Map<String, String> stats = client.getStats().get(address); assertEquals(String.valueOf(sets), stats.get("cmd_set")); assertEquals(String.valueOf(gets), stats.get("cmd_get")); assertEquals(String.valueOf(getHits), stats.get("get_hits")); assertEquals(String.valueOf(getMisses), stats.get("get_misses")); assertEquals(String.valueOf(deleteHits), stats.get("delete_hits")); assertEquals(String.valueOf(deleteMisses), stats.get("delete_misses")); assertEquals(String.valueOf(incHits), stats.get("incr_hits")); assertEquals(String.valueOf(incMisses), stats.get("incr_misses")); assertEquals(String.valueOf(decHits), stats.get("decr_hits")); assertEquals(String.valueOf(decMisses), stats.get("decr_misses")); } private MemcachedClient getMemcacheClient(HazelcastInstance instance) throws Exception { InetSocketAddress address = instance.getCluster().getLocalMember().getSocketAddress(); ConnectionFactory factory = new ConnectionFactoryBuilder() .setOpTimeout(60 * 60 * 60) .setDaemon(true) .setFailureMode(FailureMode.Retry) .build(); return new MemcachedClient(factory, Collections.singletonList(address)); } }