/** * Replication Benchmarker * https://github.com/score-team/replication-benchmarker/ * Copyright (C) 2013 LORIA / Inria / SCORE Team * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package crdt.simulator; import crdt.simulator.tracestorage.TraceStore; import collect.VectorClock; import crdt.CRDT; import crdt.CRDTMessage; import crdt.Factory; import crdt.PreconditionException; import crdt.simulator.sizecalculator.SizeCalculator; import crdt.simulator.sizecalculator.StandardSizeCalculator; import crdt.simulator.sizecalculator.VladiumCalculator; import java.io.IOException; import java.util.*; import java.util.Map.Entry; import jbenchmarker.core.LocalOperation; import jbenchmarker.vladium.IObjectProfileNode; import jbenchmarker.vladium.ObjectProfiler; /** * * @author urso */ public class CausalSimulator extends Simulator { int tour = 0; boolean overhead = false; HashSet<CRDTMessage> setOp; private Map<Integer, List<TraceOperation>> history; private Map<Integer, List<CRDTMessage>> genHistory; private long localSum = 0L, nbLocal = 0L, remoteSum = 0L, nbRemote = 0L; private int nbrTrace = 0; TraceStore writer = null; private HashMap<TraceOperation, Integer> orderTrace; private boolean detail = false; final private SizeCalculator serializer; private int passiveReplica = 1; private boolean debugInformation = true; public int nbRedo, nbMClean, delConcur, insConcur, insDelConcur; public CausalSimulator(Factory<? extends CRDT> rf) { super(rf); this.serializer = new VladiumCalculator(); } public CausalSimulator(Factory<? extends CRDT> rf, boolean detail, int nbrTrace, SizeCalculator sizeCalc) { super(rf); this.detail = detail; this.nbrTrace = nbrTrace; this.serializer = sizeCalc; } public CausalSimulator(Factory<? extends CRDT> rf, boolean detail, int nbrTrace) { this(rf, detail, nbrTrace, new VladiumCalculator()); } /* * Passive replica is a replicat added in simulation witch recieve all operations * */ public int getPassiveReplica() { return passiveReplica; } public void setPassiveReplica(int passiveReplica) { this.passiveReplica = passiveReplica; } public boolean isDebugInformation() { return debugInformation; } public void setDebugInformation(boolean debugInformation) { this.debugInformation = debugInformation; } /** * Lists of remote messages. * * @return a map replica id -> messages received. */ public Map<Integer, List<CRDTMessage>> getGenHistory() { return genHistory; } /** * Lists of local operation. * * @return a map replica id -> operation generated. */ public Map<Integer, List<TraceOperation>> getHistory() { return history; } /** * The whole time taken by appying local operations. * * @return time in nanoseconds. */ public long getLocalTimeSum() { return localSum; } /** * The number of local operations. * * @return number. */ public long getNbLocalOp() { return nbLocal; } /** * The number of remote operations. * * @return number. */ public long getNbRemote() { return nbRemote; } /** * The whole time taken by appying remote operations. * * @return time in nanoseconds. */ public long getRemoteSum() { return remoteSum; } /** * The average time taken by appying remote operations. * * @return time in nanoseconds. */ public double getRemoteAvg() { return (remoteSum / ((double) getNbLocalOp())) / (genHistory.size() - 1); } //Viewer view =null; /*new DiagSequence(20);*/ /** * Runs a causally ordered trace. Throws exception if not causally ordered * trace or pb with classes. */ public void clearStat() { localSum = 0L; nbLocal = 0L; remoteSum = 0L; nbRemote = 0L; } public TraceStore getWriter() { return writer; } public void setWriter(TraceStore writer) { this.writer = writer; } /** * Runs a trace of operations. Iterates trough trace and apply each * operation. Instanciate replica when needed using the replica factory. For * each operation apply (localy or remotely) store execution time. * Optionally, computes the memory usage (costly operation) * * @param trace a trace, i.e. a enumeration of TraceOperation * @throws Exception if the Trace is incorrect (non causal, etc..) * @see Trace, TraceOperation, crdt.CRDT */ @Override public void run(Trace trace) throws IncorrectTraceException, PreconditionException, IOException { long tmp; final Map<Integer, VectorClock> clocks = new HashMap<Integer, VectorClock>(); final VectorClock globalClock = new VectorClock(); final List<TraceOperation> concurrentOps = new LinkedList<TraceOperation>(); final Enumeration<TraceOperation> it = trace.enumeration(); orderTrace = new HashMap(); int numTrace = 0; // Passive replica that only receive operations for(int i=1;i<=passiveReplica;i++) { this.newReplica(-i); clocks.put(-i, new VectorClock()); } setOp = new HashSet(); history = new HashMap<Integer, List<TraceOperation>>(); genHistory = new HashMap<Integer, List<CRDTMessage>>(); while (it.hasMoreElements()) { tour++; final TraceOperation opt = it.nextElement(); final int r = opt.getReplica(); if (r == -1) { throw new IncorrectTraceException("Incorrect replica value (-1) : " + opt); } CRDT localReplica = this.getReplicas().get(r); if (localReplica == null) { localReplica = this.newReplica(r); clocks.put(r, new VectorClock()); genHistory.put(r, new ArrayList<CRDTMessage>()); history.put(r, new ArrayList<TraceOperation>()); } VectorClock vc = clocks.get(r); if (!vc.readyFor(r, opt.getVectorClock())) { // applyRemote concurrent operations Iterator<Integer> i = opt.getVectorClock().keySet().iterator(); concurrentOps.clear(); while (i.hasNext()) { int e = i.next(); if (e != r) { for (int j = opt.getVectorClock().get(e); j > vc.getSafe(e); j--) { insertCausalOrder(concurrentOps, history.get(e).get(j - 1)); } } } play(localReplica, vc, concurrentOps); } LocalOperation op = opt.getOperation(); op = op.adaptTo(localReplica); if (writer != null) { writer.storeOp(opt); } history.get(r).add(opt); if (detail) { orderTrace.put(opt, numTrace++); } if (!vc.readyFor(r, opt.getVectorClock())) { // Check causal readiness throw new IncorrectTraceException("replica " + r + " with vc " + vc + " not ready for " + opt.getVectorClock()); } //System.out.println("Before Generation --- Replica: "+opt.getReplica()+", VC : "+opt.getVectorClock()+"LocalOperation : "+op+"\n Observe : "+localReplica.lookup()); tmp = System.nanoTime(); final CRDTMessage m = localReplica.applyLocal(op); long after = System.nanoTime(); localSum += (after - tmp); if (detail) { genTime.add(after - tmp); //stat(opt, after - tmp, 0); genSize.add(m.size()); remoteTime.add(0L); } //System.out.println("After Generation --- Replica: "+opt.getReplica()+", VC : "+opt.getVectorClock()+"LocalOperation : "+op+"\n Observe : "+localReplica.lookup()); nbLocal++; final CRDTMessage msg = m.clone(); genHistory.get(r).add(msg); clocks.get(r).inc(r); globalClock.inc(r); ifSerializ(); nbRedo = localReplica.nbrRedo; nbMClean = localReplica.nbrCleanMerge; delConcur= localReplica.nbrDelDelConcur; insConcur= localReplica.nbrInsConcur; insDelConcur = localReplica.nbrInsDelConcur; } ifSerializ(); // Final : applyRemote all pending remote CRDTMessage (not the best complexity) for (CRDT r : replicas.values()) { int n = r.getReplicaNumber(); concurrentOps.clear(); VectorClock vc = clocks.get(n); for (Entry<Integer, Integer> e : globalClock.entrySet()) { for (int j = vc.getSafe(e.getKey()); j < e.getValue(); ++j) { insertCausalOrder(concurrentOps, history.get(e.getKey()).get(j)); } } play(r, vc, concurrentOps); } } private static void insertCausalOrder(List<TraceOperation> concurrentOps, TraceOperation opt) { final ListIterator<TraceOperation> it = concurrentOps.listIterator(); boolean cont = true; while (it.hasNext() && cont) { TraceOperation t = it.next(); if (t.getVectorClock().greaterThan(opt.getVectorClock())) { cont = false; it.previous(); } } it.add(opt); } /** * * @param r * @param vc * @param concurrentOps * @throws IOException * @throws IncorrectTraceException */ private void play(CRDT r, VectorClock vc, List<TraceOperation> concurrentOps) throws IOException, IncorrectTraceException { for (TraceOperation t : concurrentOps) { int e = t.getReplica(); CRDTMessage op = genHistory.get(e).get(t.getVectorClock().get(e) - 1); CRDTMessage optime = op.clone(); if (!vc.readyFor(e, t.getVectorClock())) { throw new IncorrectTraceException("replica " + r.getReplicaNumber() + " with vc " + vc + " not ready for " + t.getVectorClock()); } //System.out.println("Before Integration --- Replica: "+r.getReplicaNumber()+"With VC : "+vc+", Receive : "+t.toString()+" From replica : "+e+"\n Observe : "+r.lookup()); long before = System.nanoTime(); r.applyRemote(optime); long after = System.nanoTime(); //System.out.println("After Integration --- Replica: "+r.getReplicaNumber()+"With VC : "+vc+", Receive : "+t.toString()+" From replica : "+e+"\n Observe : "+r.lookup()); remoteSum += (after - before); if (detail) { int num = orderTrace.get(t); remoteTime.set(num, remoteTime.get(num) + after - before); //stat(t, after - tmp, 1); } nbRemote++; vc.inc(e); } } /** * Reset all replicas */ public void reset() { replicas.clear(); } private void causalCheck(TraceOperation opt, Map<Integer, VectorClock> vcs) throws IncorrectTraceException { int r = opt.getReplica(); if (opt.getVectorClock().getSafe(r) == 0) { throw new IncorrectTraceException("Zero/no entry in VC for replica" + opt); } VectorClock clock = vcs.get(r); if (clock.getSafe(r) > opt.getVectorClock().get(r) - 1) { throw new IncorrectTraceException("Already seen clock operation " + opt); } if (clock.getSafe(r) < opt.getVectorClock().getSafe(r) - 1) { throw new IncorrectTraceException("Missing operation before " + opt); } for (int i : opt.getVectorClock().keySet()) { if ((i != r) && (opt.getVectorClock().get(i) > 0)) { if (vcs.get(i) == null) { throw new IncorrectTraceException("Missing replica " + i + " for " + opt); } if (vcs.get(i).getSafe(i) < opt.getVectorClock().get(i)) { throw new IncorrectTraceException("Missing causal operation before " + opt + " on replica " + i); } } } } private void ifSerializ() throws IOException { if (nbrTrace > 0 && tour == nbrTrace && serializer != null) { long sumMemory = 0; long sumTimeView=0; for (int rep : this.getReplicas().keySet()) { CRDT crdt=this.getReplicas().get(rep); long before = System.nanoTime(); crdt.lookup(); long after=System.nanoTime(); sumTimeView += after-before; IObjectProfileNode profile = ObjectProfiler.profile (crdt); sumMemory += serializer.serializ(crdt); profile.size (); } viewTime.add(sumTimeView / this.getReplicas().keySet().size()); memUsed.add(sumMemory / this.getReplicas().keySet().size()); tour = 0; } } public List<Double> getAvgPerRemoteMessage(){ List<Double> l = new LinkedList(); double div=(double)(this.genHistory.size()-1); for (int i = 0; i < remoteTime.size(); ++i) { l.add((double)remoteTime.get(i) / div); } return l; } public List<Long> getAvgLongPerRemoteMessage(){ List<Long> l = new LinkedList(); long div=this.genHistory.size()-1; for (int i = 0; i < remoteTime.size(); ++i) { l.add(remoteTime.get(i) / div); } return l; } /** * Return list of integration by remote operation * One message can contain many operations * @return list of long */ public List<Long> getAvgLongPerOperation() { List<Long> l = new ArrayList(); for (int i = 0; i < remoteTime.size(); ++i) { int gs = genSize.get(i); long t = remoteTime.get(i) / gs; for (int j = 0; j < gs; ++j) { l.add(t); } } return l; } }