/* * Copyright (c) 2008-2012, Hazel Bilisim Ltd. 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.examples; import com.hazelcast.config.Config; import com.hazelcast.core.Hazelcast; import com.hazelcast.core.HazelcastInstance; import com.hazelcast.core.IQueue; import com.hazelcast.core.Transaction; import java.util.*; import java.util.concurrent.*; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicLong; import java.util.logging.Level; import java.util.logging.Logger; public class LongRunningTransactionTest { private static final int STATS_SECONDS = 10; private List<TheNode> nodes = new CopyOnWriteArrayList<TheNode>(); private int nodeIdGen = 0; private final Logger logger = Logger.getLogger(LongRunningTransactionTest.class.getName()); private int starts, stops, restarts = 0; private final AtomicInteger ids = new AtomicInteger(); private final Timer producer = new Timer(); private final BlockingQueue<Integer> processedIds = new LinkedBlockingQueue<Integer>(); private final Random random = new Random(); public static void main(String[] args) { LongRunningTransactionTest t = new LongRunningTransactionTest(); t.run(); } public void run() { Runtime.getRuntime().addShutdownHook(new Thread() { public void run() { log("Shutting down " + nodes.size()); while (nodes.size() > 0) { removeNode(); } } }); while (true) { if (nodes.size() > 4) { removeNode(); } else if (nodes.size() == 0) { addNode(); addNode(); addNode(); startIdProducer(); startProcessWatcher(); } else if (nodes.size() < 2) { addNode(); } else { int action = random(3); switch (action) { case 0: removeNode(); break; case 1: addNode(); break; case 2: restartNode(); break; } } try { int nextSeconds = random(60, 260); log("Next Action after " + nextSeconds + " seconds."); log("members:" + nodes.size() + ", starts: " + starts + ", stops:" + stops + ", restart:" + restarts); log("transaction count: " + ids.get()); //noinspection BusyWait Thread.sleep(nextSeconds * 1000); } catch (InterruptedException e) { } } } private void startIdProducer() { producer.scheduleAtFixedRate(new TimerTask() { @Override public void run() { IQueue<Integer> q = Hazelcast.getQueue("default"); if (q.size() < 5000) { for (int i = 0; i < 5000; i++) { q.offer(ids.incrementAndGet()); } } } }, 0, 1000); } private void startProcessWatcher() { Executors.newSingleThreadExecutor().execute(new Runnable() { public void run() { Set<Integer> outOfOrderIds = new TreeSet<Integer>(); int lastId = 0; while (true) { try { Integer id = processedIds.take(); if (id % 5000 == 0) { log("ID " + id + " mapSize " + Hazelcast.getMap("default").size() + " OO " + outOfOrderIds.size() + ", lastId:" + lastId); } if (id == (lastId + 1)) { lastId = id; if (outOfOrderIds.size() > 0) { if (outOfOrderIds.size() > 20) { log("Consuming outOfOrders " + outOfOrderIds.size()); } if (outOfOrderIds.size() > 1000) { //noinspection BusyWait Thread.sleep(1000); System.exit(0); } Set<Integer> outOfOrderIdsNow = outOfOrderIds; outOfOrderIds = new TreeSet<Integer>(); for (Integer idOutOfOrder : outOfOrderIdsNow) { if (idOutOfOrder == (lastId + 1)) { lastId = idOutOfOrder; } else { outOfOrderIds.add(idOutOfOrder); } } } } else { // log(lastId + " lastId but id= " + id); outOfOrderIds.add(id); } } catch (Exception e) { e.printStackTrace(); } } } }); } void log(Object obj) { logger.log(Level.INFO, "LRT-" + obj); } void addNode() { starts++; int entryCount = random(10000); int threadCount = random(10, 50); TheNode node = new TheNode(nodeIdGen++, entryCount, threadCount); nodes.add(node); node.start(); log("Started " + node); } void restartNode() { restarts++; log("Restarting..."); removeNode(); try { Thread.sleep(random(10) * 1000); } catch (InterruptedException e) { } addNode(); } void removeNode() { stops++; TheNode node = nodes.remove(random(nodes.size())); node.stop(); log("Stopped " + node); } int random(int length) { return ((int) (Math.random() * 10000000) % length); } int random(int from, int to) { double diff = (to - from); return (int) (diff * Math.random() + from); } class TheNode { final int entryCount; final int threadCount; final int nodeId; final long createTime; final ExecutorService es; final ExecutorService esStats; final HazelcastInstance hazelcast; volatile boolean running = true; TheNode(int nodeId, int entryCount, int threadCount) { this.entryCount = entryCount; this.threadCount = threadCount; this.nodeId = nodeId; es = Executors.newFixedThreadPool(threadCount); hazelcast = Hazelcast.newHazelcastInstance(new Config()); esStats = Executors.newSingleThreadExecutor(); createTime = System.currentTimeMillis(); } public void stop() { try { running = false; es.shutdown(); es.awaitTermination(10, TimeUnit.SECONDS); esStats.shutdown(); hazelcast.shutdown(); } catch (Throwable t) { t.printStackTrace(); } } public void start() { final Stats stats = new Stats(); for (int i = 0; i < threadCount; i++) { es.execute(new Runnable() { public void run() { IQueue<Integer> q = hazelcast.getQueue("default"); Map<Integer, Integer> map1 = hazelcast.getMap("default"); Map<Integer, Integer> map2 = hazelcast.getMap("default"); Map<Integer, Integer> map3 = hazelcast.getMap("default"); Map<Integer, Integer> map4 = hazelcast.getMap("default"); while (running) { try { Transaction txn = hazelcast.getTransaction(); txn.begin(); try { int key = random(1000); while (key < 5) { key = random(1000); } Integer id = q.take(); Integer id1 = map1.put(1, id); Integer id2 = map2.put(2, id); Integer id3 = map3.put(3, id); Integer id4 = map4.put(key, id); //noinspection BusyWait Thread.sleep(random.nextInt(5)); txn.commit(); processedIds.put(id); } catch (Throwable e) { e.printStackTrace(); txn.rollback(); } } catch (Throwable ignored) { } } } }); } esStats.execute(new Runnable() { public void run() { while (running) { try { //noinspection BusyWait Thread.sleep(STATS_SECONDS * 1000); int clusterSize = hazelcast.getCluster().getMembers().size(); Stats currentStats = stats.getAndReset(); log("Cluster size: " + clusterSize + ", Operations per Second: " + (currentStats.total() / STATS_SECONDS)); } catch (Exception e) { e.printStackTrace(); } } } }); } @Override public String toString() { return "TheNode [" + nodeId + "] entryCount=" + entryCount + ", threadCount=" + threadCount + ", liveSeconds=" + ((System.currentTimeMillis() - createTime) / 1000) + ", running=" + running + '}'; } } class Stats { public AtomicLong mapPuts = new AtomicLong(); public AtomicLong mapTakes = new AtomicLong(); public Stats getAndReset() { long mapPutsNow = mapPuts.getAndSet(0); long mapRemovesNow = mapTakes.getAndSet(0); Stats newOne = new Stats(); newOne.mapPuts.set(mapPutsNow); newOne.mapTakes.set(mapRemovesNow); return newOne; } public long total() { return mapPuts.get() + mapTakes.get(); } public String toString() { return "total= " + total() + ", puts:" + mapPuts.get() + ", remove:" + mapTakes.get(); } } }