/* This file is part of VoltDB.
* Copyright (C) 2008-2017 VoltDB Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*/
package callcenter;
import java.util.Random;
/**
* Randomly take events from an EventSource and delay and duplicate
* them. Uses a Zipfian distribution for delay and a fixed probablibilty
* of duplification.
*/
public class NetworkSadnessTransformer<T> implements EventSource<T> {
// Upstream source
final EventSource<T> source;
// random number generator with constant seed
final Random rand = new Random(1);
// uses a DelayQueue for scheduling events
final DelayedQueue<T> delayed = new DelayedQueue<>();
// for zipf generation
private final int size;
private final double skew;
private final double bottom;
NetworkSadnessTransformer(EventSource<T> source) {
this.source = source;
// zipf params and setup
size = 1;
skew = 1;
double bottomtemp = 0;
for (int i = 1; i < size; i++) {
bottomtemp += (1 / Math.pow(i, this.skew));
}
bottom = bottomtemp; // because final
}
// Based on code by Hyunsik Choi
// http://diveintodata.org/2009/09/zipf-distribution-generator-in-java/
public int nextZipfDelay() {
int value;
double friquency = 0;
double dice;
value = rand.nextInt(size);
friquency = (1.0d / Math.pow(value, this.skew)) / this.bottom;
dice = rand.nextDouble();
while(!(dice < friquency)) {
value = rand.nextInt(size);
friquency = (1.0d / Math.pow(value, this.skew)) / this.bottom;
dice = rand.nextDouble();
}
return value;
}
/**
* Possibly duplicate and delay by some random amount.
*/
void transformAndQueue(T event, long systemCurrentTimeMillis) {
// if you're super unlucky, this blows up the stack
if (rand.nextDouble() < 0.05) {
// duplicate this message (note recursion means maybe more than duped)
transformAndQueue(event, systemCurrentTimeMillis);
}
long delayms = nextZipfDelay();
delayed.add(systemCurrentTimeMillis + delayms, event);
}
/**
* Return the next event that is safe for delivery or null
* if there are no safe objects to deliver.
*
* Null response could mean no events, or could mean all events
* are scheduled for the future.
*
* @param systemCurrentTimeMillis The current time.
*/
@Override
public T next(long systemCurrentTimeMillis) {
// drain all the waiting messages from the source (up to 10k)
while (delayed.size() < 10000) {
T event = source.next(systemCurrentTimeMillis);
if (event == null) {
break;
}
transformAndQueue(event, systemCurrentTimeMillis);
}
return delayed.nextReady(systemCurrentTimeMillis);
}
/**
* Ignore any scheduled delays and return events in
* schedule order until empty.
*/
@Override
public T drain() {
T event = delayed.drain();
if (event != null) {
return event;
}
return source.drain();
}
}