/*
* Copyright (c) 2014-2015 Spotify AB
*
* 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.spotify.folsom;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.net.HostAndPort;
import com.google.common.util.concurrent.FutureCallback;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import com.thimbleware.jmemcached.protocol.MemcachedCommandHandler;
import org.junit.After;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
import org.slf4j.LoggerFactory;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicInteger;
import ch.qos.logback.classic.Level;
import ch.qos.logback.classic.LoggerContext;
import static org.junit.Assert.assertEquals;
public class MemcacheClientStressTest {
private static final String KEY = "mykey";
private static final byte[] VALUE = "myvalue".getBytes();
public static final int N = 1000;
private final EmbeddedServer daemon = new EmbeddedServer(false);
private ExecutorService workerExecutor;
private MemcacheClient<byte[]> client;
@Before
public void setUp() throws Exception {
final LoggerContext lc = (LoggerContext) LoggerFactory.getILoggerFactory();
lc.getLogger(MemcachedCommandHandler.class).setLevel(Level.ERROR);
workerExecutor = Executors.newFixedThreadPool(100);
client = MemcacheClientBuilder.newByteArrayClient()
.withAddress(HostAndPort.fromParts("127.0.0.1", daemon.getPort()))
.connectAscii();
ConnectFuture.connectFuture(client).get();
}
public static void main(final String[] args) throws Exception {
final MemcacheClientStressTest test = new MemcacheClientStressTest();
test.setUp();
try {
test.stressTest();
} finally {
test.tearDown();
}
}
@Test
@Ignore
public void stressTest() throws Exception {
client.set(KEY, VALUE, 100000).get();
while (true) {
final List<ListenableFuture<byte[]>> futures = Lists.newArrayList();
final AtomicInteger successes = new AtomicInteger();
final ConcurrentMap<String, AtomicInteger> failures = Maps.newConcurrentMap();
for (int i = 0; i < N; i++) {
addRequest(client, futures, successes, failures);
}
client.shutdown();
client = MemcacheClientBuilder.newByteArrayClient()
.withAddress(HostAndPort.fromParts("127.0.0.1", daemon.getPort()))
.connectBinary();
Futures.successfulAsList(futures).get();
System.out.println("success: " + successes.get());
for (final Map.Entry<String, AtomicInteger> entry : failures.entrySet()) {
System.out.println("failure: " + entry.getKey() + " = " + entry.getValue().get());
}
int totalFails = 0;
for (final AtomicInteger integer : failures.values()) {
totalFails += integer.get();
}
assertEquals(N, successes.get() + totalFails);
}
}
private void addRequest(final MemcacheClient<byte[]> client,
final List<ListenableFuture<byte[]>> futures,
final AtomicInteger successes,
final ConcurrentMap<String, AtomicInteger> failures) {
final ListenableFuture<byte[]> future = client.get(KEY);
Futures.addCallback(future, new FutureCallback<byte[]>() {
@Override
public void onSuccess(final byte[] result) {
successes.incrementAndGet();
}
@Override
public void onFailure(final Throwable t) {
final AtomicInteger newCounter = new AtomicInteger();
String message = t.getMessage();
if (message == null) {
message = "";
}
final AtomicInteger old = failures.putIfAbsent(message, newCounter);
if (old == null) {
newCounter.incrementAndGet();
} else {
old.incrementAndGet();
}
}
});
futures.add(future);
}
@After
public void tearDown() throws ExecutionException, InterruptedException {
client.shutdown();
daemon.stop();
workerExecutor.shutdown();
}
}