/* * Copyright 2014, The Sporting Exchange Limited * * 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.betfair.cougar.perf.socket; import com.betfair.cougar.api.ContainerContext; import com.betfair.cougar.api.RequestContext; import com.betfair.cougar.core.api.ev.ConnectedResponse; import com.betfair.cougar.core.api.ev.Subscription; import com.betfair.cougar.core.impl.ev.ConnectedResponseImpl; import com.betfair.cougar.core.impl.ev.DefaultSubscription; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.betfair.platform.virtualheap.Heap; import com.betfair.platform.virtualheap.MutableHeap; import com.betfair.cougar.perf.socket.v1_0.SocketPerfTestingService; import com.betfair.cougar.perf.socket.v1_0.co.HeapTesterCO; import com.betfair.cougar.perf.socket.v1_0.co.RpcControlCO; import com.betfair.cougar.perf.socket.v1_0.co.server.HeapTesterServerCO; import com.betfair.cougar.perf.socket.v1_0.co.server.RpcControlServerCO; import com.betfair.tornjak.monitor.MonitorRegistry; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.atomic.AtomicInteger; import java.util.logging.Level; public class SocketPerfTestingServiceImpl implements SocketPerfTestingService { private static CougarLogger LOG = CougarLoggingUtils.getLogger(SocketPerfTestingServiceImpl.class); private int numHeapsForTest = 1000; private int concurrency = numHeapsForTest/3; private int numUpdates = 1000000; private ExecutorService executorService; private Heap[] heaps; private HeapTesterCO[] heapProjections; private Heap rpcControlHeap; private RpcControlCO rpcControlProjection; private AtomicInteger activeSubscriptions = new AtomicInteger(); public SocketPerfTestingServiceImpl() { } public void setNumHeapsForTest(int numHeapsForTest) { this.numHeapsForTest = numHeapsForTest; } public void setConcurrency(int concurrency) { this.concurrency = concurrency; } public void setNumUpdates(int numUpdates) { this.numUpdates = numUpdates; } @Override public void startRpcTest(RequestContext ctx, Long length , Integer numClientThreads) { rpcControlHeap.beginUpdate(); rpcControlProjection.setNumThreads(numClientThreads != null ? numClientThreads : 1); rpcControlProjection.setRunning(true); rpcControlHeap.endUpdate(); try { Thread.sleep(length); } catch (InterruptedException e) { LOG.log(Level.SEVERE, "Test ended early due to interrupted exception"); } rpcControlHeap.beginUpdate(); rpcControlProjection.setRunning(false); rpcControlHeap.endUpdate(); } @Override public ConnectedResponse getRpcControl(RequestContext ctx) { return new ConnectedResponseImpl(rpcControlHeap, new DefaultSubscription()); } @Override public void noop(RequestContext ctx) { // literally do nothing } @Override public void startPushTest(RequestContext ctx) { long startTime = System.currentTimeMillis(); // slam 'em all in final CountDownLatch latch = new CountDownLatch(numHeapsForTest); for (int j=0; j<numHeapsForTest; j++) { final int heap = j; // LOG.log(Level.SEVERE, "Scheduling update "+update+" for "+heap); final Runnable r = new Runnable() { @Override public void run() { try { for (int i=0; i<numUpdates && activeSubscriptions.get() > 0; i++) { final int update = i; heaps[heap].beginUpdate(); heapProjections[heap].setValue(heapProjections[heap].getValue()+update); heaps[heap].endUpdate(); } heaps[heap].beginUpdate(); heaps[heap].terminateHeap(); heaps[heap].endUpdate(); // LOG.log(Level.SEVERE, "Send update "+update+" to "+heap); } catch (Exception e) { LOG.log(Level.SEVERE, "Error", e); } latch.countDown(); } }; executorService.submit(r); } long midTime = System.currentTimeMillis(); try { latch.await(); } catch (InterruptedException ie) { // ignore } long endTime = System.currentTimeMillis(); LOG.log(Level.SEVERE, "For test started at "+startTime+" took "+(midTime-startTime)+"ms to submit all updates"); LOG.log(Level.SEVERE, "For test started at "+startTime+" took "+(endTime-startTime)+"ms to emit all updates"); } @Override public Integer getNumHeapsForTest(RequestContext ctx) { return numHeapsForTest; } @Override public ConnectedResponse subscribeToHeap(RequestContext ctx, Integer heapNumber) { activeSubscriptions.incrementAndGet(); DefaultSubscription ds = new DefaultSubscription(); ds.addListener(new Subscription.SubscriptionListener() { @Override public void subscriptionClosed(Subscription subscription, Subscription.CloseReason reason) { activeSubscriptions.decrementAndGet(); } }); return new ConnectedResponseImpl(heaps[heapNumber], ds); } @Override public void init(ContainerContext cc) { heaps = new Heap[numHeapsForTest]; heapProjections = new HeapTesterCO[numHeapsForTest]; long totalExpected = 0; for (int i=0; i<numUpdates; i++) { totalExpected += i; } for (int i=0; i<numHeapsForTest; i++) { heaps[i] = new MutableHeap("push-perf-test-"+i); heaps[i].beginUpdate(); heapProjections[i] = HeapTesterServerCO.rootFrom(heaps[i]); heapProjections[i].setExpectedFinalValue(totalExpected); heapProjections[i].setValue(0L); heaps[i].endUpdate(); } executorService = Executors.newFixedThreadPool(concurrency); rpcControlHeap = new MutableHeap("rpcControl"); rpcControlHeap.beginUpdate(); rpcControlProjection = RpcControlServerCO.rootFrom(rpcControlHeap); rpcControlProjection.setNumThreads(1); rpcControlProjection.setRunning(false); rpcControlHeap.endUpdate(); LOG.log(Level.SEVERE, "Running test with following parameters:"); LOG.log(Level.SEVERE, "numHeapsForTest = "+numHeapsForTest); LOG.log(Level.SEVERE, "concurrency = "+concurrency); LOG.log(Level.SEVERE, "numUpdates = "+numUpdates); } }