/* * 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. */ /** * Copyright (C) 2014 Spotify AB */ 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.google.common.util.concurrent.SettableFuture; import com.google.common.util.concurrent.Uninterruptibles; import net.spy.memcached.DefaultConnectionFactory; import net.spy.memcached.MemcachedClient; import net.spy.memcached.OperationFactory; import net.spy.memcached.internal.BulkFuture; import net.spy.memcached.internal.BulkGetCompletionListener; import net.spy.memcached.internal.BulkGetFuture; import net.spy.memcached.internal.OperationCompletionListener; import net.spy.memcached.internal.OperationFuture; import net.spy.memcached.ops.OperationStatus; import net.spy.memcached.ops.StatusCode; import net.spy.memcached.protocol.ascii.AsciiOperationFactory; import net.spy.memcached.protocol.binary.BinaryOperationFactory; import java.io.IOException; import java.net.InetSocketAddress; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.ExecutionException; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import static com.spotify.folsom.client.Utils.SAME_THREAD_EXECUTOR; import static java.util.concurrent.TimeUnit.DAYS; public class SimpleMemcacheClientBenchmark { public static final int MULTIGET_SIZE = 50; public static final int CONCURRENCY = 200; public static final boolean TEST_SPYMEMCACHED = false; public static final boolean ASCII_PROTOCOL = false; public static final int NUM_CLIENT_CONNECTIONS = 3; public static void main(final String[] args) throws ExecutionException, InterruptedException, IOException { // Set up client BinaryMemcacheClient<String> client; MemcachedClient spyClient; if (TEST_SPYMEMCACHED) { final DefaultConnectionFactory defaultConnectionFactory = new DefaultConnectionFactory() { @Override public OperationFactory getOperationFactory() { if (ASCII_PROTOCOL) { return new AsciiOperationFactory(); } else { return new BinaryOperationFactory(); } } }; spyClient = new MemcachedClient(defaultConnectionFactory, Collections.nCopies(NUM_CLIENT_CONNECTIONS, new InetSocketAddress("localhost", 11211))); } else { client = MemcacheClientBuilder.newStringClient() .withMaxOutstandingRequests(100000) .withAddress(HostAndPort.fromParts("127.0.0.1", 11211)) .withConnections(NUM_CLIENT_CONNECTIONS) .withRetry(false) .connectBinary(); System.out.println(client); } // Set up test data final List<List<String>> keys = Lists.newArrayList(); final List<List<String>> values = Lists.newArrayList(); final List<ListenableFuture<MemcacheStatus>> futures = Lists.newArrayList(); for (int i = 0; i < CONCURRENCY; i++) { final List<String> keys2 = Lists.newArrayList(); final List<String> values2 = Lists.newArrayList(); for (int j = 0; j < MULTIGET_SIZE; j++) { final String key = String.format("key-%020d-%010d", i, j); final String value = String.format("value-%0200d-%0200d", i, j); keys2.add(key); values2.add(value); if (TEST_SPYMEMCACHED) { final SettableFuture<MemcacheStatus> settable = SettableFuture.create(); spyClient.set(key, Integer.MAX_VALUE, value) .addListener(new OperationCompletionListener() { @Override public void onComplete(final OperationFuture<?> future) throws Exception { if (future.getStatus().getStatusCode() == StatusCode.SUCCESS) { settable.set(null); } else { settable.setException(new RuntimeException(future.getStatus().getMessage())); } } }); futures.add(settable); } else { futures.add(client.set(key, value, Integer.MAX_VALUE)); } } keys.add(keys2); values.add(values2); } for (final ListenableFuture<MemcacheStatus> future : futures) { future.get(); } // Run benchmark final ScheduledExecutorService backoffExecutor = Executors.newSingleThreadScheduledExecutor(); final ProgressMeter meter = new ProgressMeter("req"); for (int i = 0; i < CONCURRENCY; i++) { if (TEST_SPYMEMCACHED) { sendSpyMemcached(spyClient, keys.get(i), toMap(keys.get(i), values.get(i)), meter, backoffExecutor); } else { sendFolsom(client, keys.get(i), values.get(i), meter, backoffExecutor); } } while (true) { Uninterruptibles.sleepUninterruptibly(1, DAYS); } } private static Map<String, String> toMap(final List<String> keys, final List<String> values) { final HashMap<String, String> map = Maps.newHashMap(); for (int i = 0; i < keys.size(); i++) { map.put(keys.get(i), values.get(i)); } return map; } private static void sendFolsom(final BinaryMemcacheClient<String> client, final List<String> keys, final List<String> expected, final ProgressMeter meter, final ScheduledExecutorService backoffExecutor) { final ListenableFuture<List<String>> future = client.get(keys); final long start = System.nanoTime(); Futures.addCallback(future, new FutureCallback<List<String>>() { @Override public void onSuccess(final List<String> response) { final long end = System.nanoTime(); final long latency = end - start; meter.inc(keys.size(), latency); if (!expected.equals(response)) { throw new AssertionError("expected: " + expected + ", got: " + response); } sendFolsom(client, keys, expected, meter, backoffExecutor); } @Override public void onFailure(final Throwable throwable) { System.err.println(throwable.getMessage()); } }, SAME_THREAD_EXECUTOR); } private static void sendSpyMemcached(final MemcachedClient client, final List<String> keys, final Map<String, String> expectedMap, final ProgressMeter meter, final ScheduledExecutorService backoffExecutor) { final long start = System.nanoTime(); final BulkFuture<Map<String, Object>> future = client.asyncGetBulk(keys); future.addListener(new BulkGetCompletionListener() { @Override public void onComplete(final BulkGetFuture<?> getFuture) throws Exception { final OperationStatus status = getFuture.getStatus(); if (status.isSuccess()) { final Map<String, String> response = (Map<String, String>) getFuture.get(); final long end = System.nanoTime(); final long latency = end - start; meter.inc(keys.size(), latency); if (!expectedMap.equals(response)) { throw new AssertionError("expected: " + expectedMap + ", got: " + response); } sendSpyMemcached(client, keys, expectedMap, meter, backoffExecutor); } else { System.err.println("failure!"); } } }); } }