blob: 367dea04b9262707f6d2497fdb0f46e9f3a9afce [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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 org.apache.cassandra.simulator.utils;
import java.util.Arrays;
import org.apache.cassandra.simulator.RandomSource;
public enum KindOfSequence
{
UNIFORM, UNIFORM_STEP, RANDOMWALK;
public interface LinkLatency
{
long get(RandomSource random, int from, int to);
}
public interface NetworkDecision
{
boolean get(RandomSource random, int from, int to);
}
public interface Period
{
long get(RandomSource random);
}
public interface Decision
{
boolean get(RandomSource random);
}
static class UniformPeriod implements LinkLatency, Period
{
final LongRange nanos;
UniformPeriod(LongRange nanos)
{
this.nanos = nanos;
}
@Override
public long get(RandomSource random, int from, int to)
{
return nanos.select(random);
}
@Override
public long get(RandomSource random)
{
return nanos.select(random);
}
}
static class UniformStepPeriod implements LinkLatency, Period
{
final LongRange range;
long target;
long cur;
long step;
UniformStepPeriod(LongRange range, RandomSource random)
{
this.range = range;
cur = range.select(random);
next(random);
}
void next(RandomSource random)
{
target = range.select(random);
step = (target - cur) / random.uniform(16, 128);
if (step == 0) step = target > cur ? 1 : -1;
}
@Override
public long get(RandomSource random, int from, int to)
{
return get(random);
}
@Override
public long get(RandomSource random)
{
long result = cur;
cur += step;
if (step < 0 && cur <= target) next(random);
else if (step > 0 && cur >= target) next(random);
return result;
}
}
static class RandomWalkPeriod implements Period
{
final LongRange nanos;
final long maxStepSize;
long cur;
RandomWalkPeriod(LongRange nanos, RandomSource random)
{
this.nanos = nanos;
this.maxStepSize = maxStepSize(nanos, random);
this.cur = nanos.select(random);
}
@Override
public long get(RandomSource random)
{
long step = random.uniform(-maxStepSize, maxStepSize);
long cur = this.cur;
this.cur = step > 0 ? Math.min(nanos.max, cur + step)
: Math.max(nanos.min, cur + step);
return cur;
}
}
static class RandomWalkLinkLatency implements LinkLatency
{
final LongRange nanos;
final long maxStepSize;
final long[][] curs;
RandomWalkLinkLatency(int nodes, LongRange nanos, RandomSource random)
{
this.nanos = nanos;
this.maxStepSize = maxStepSize(nanos, random);
this.curs = new long[nodes][nodes];
for (long[] c : curs)
Arrays.fill(c, nanos.min);
}
@Override
public long get(RandomSource random, int from, int to)
{
--from;--to;
long cur = curs[from][to];
long step = random.uniform(-maxStepSize, maxStepSize);
curs[from][to] = step > 0 ? Math.min(nanos.max, cur + step)
: Math.max(nanos.min, cur + step);
return cur;
}
}
static class UniformStepLinkLatency implements LinkLatency
{
private static final int CUR = 0, TARGET = 1, STEP = 2;
final LongRange range;
final long[][][] state;
UniformStepLinkLatency(int nodes, LongRange range, RandomSource random)
{
this.range = range;
this.state = new long[nodes][nodes][3];
for (int i = 0 ; i < nodes ; ++i)
{
for (int j = 0 ; j < nodes ; ++j)
{
long[] state = this.state[i][j];
state[CUR] = range.select(random);
next(random, state);
}
}
}
void next(RandomSource random, long[] state)
{
state[TARGET] = range.select(random);
state[STEP] = (state[TARGET] - state[CUR]) / random.uniform(16, 128);
if (state[STEP] == 0) state[STEP] = state[TARGET] > state[CUR] ? 1 : -1;
}
@Override
public long get(RandomSource random, int from, int to)
{
--from;--to;
long[] state = this.state[from][to];
long cur = state[CUR];
state[CUR] += state[STEP];
if (state[STEP] < 0 && cur <= state[TARGET]) next(random, state);
else if (state[STEP] > 0 && cur >= state[TARGET]) next(random, state);
return cur;
}
}
static class FixedChance implements NetworkDecision, Decision
{
final float chance;
FixedChance(float chance)
{
this.chance = chance;
}
@Override
public boolean get(RandomSource random, int from, int to)
{
return random.decide(chance);
}
@Override
public boolean get(RandomSource random)
{
return random.decide(chance);
}
}
static class RandomWalkNetworkDecision implements NetworkDecision
{
final ChanceRange range;
final float maxStepSize;
final float[][] curs;
RandomWalkNetworkDecision(int nodes, ChanceRange range, RandomSource random)
{
this.range = range;
this.maxStepSize = maxStepSize(range, random);
this.curs = new float[nodes][nodes];
for (float[] c : curs)
Arrays.fill(c, range.select(random));
}
@Override
public boolean get(RandomSource random, int from, int to)
{
--from;--to;
float cur = curs[from][to];
float step = (2*random.uniformFloat() - 1f) * maxStepSize;
curs[from][to] = step > 0 ? Math.min(range.max, cur + step)
: Math.max(range.min, cur + step);
return random.decide(cur);
}
}
static class UniformStepNetworkDecision implements NetworkDecision
{
private static final int CUR = 0, TARGET = 1, STEP = 2;
final ChanceRange range;
final float[][][] state;
UniformStepNetworkDecision(int nodes, ChanceRange range, RandomSource random)
{
this.range = range;
this.state = new float[nodes][nodes][3];
for (int i = 0 ; i < nodes ; ++i)
{
for (int j = 0 ; j < nodes ; ++j)
{
float[] state = this.state[i][j];
state[CUR] = range.select(random);
next(random, state);
}
}
}
void next(RandomSource random, float[] state)
{
state[TARGET] = range.select(random);
state[STEP] = (state[TARGET] - state[CUR]) / random.uniform(16, 128);
if (state[STEP] == 0) state[STEP] = state[TARGET] > state[CUR] ? 1 : -1;
}
@Override
public boolean get(RandomSource random, int from, int to)
{
--from;--to;
float[] state = this.state[from][to];
float cur = state[CUR];
state[CUR] += state[STEP];
if (state[STEP] < 0 && cur <= state[TARGET]) next(random, state);
else if (state[STEP] > 0 && cur >= state[TARGET]) next(random, state);
return random.decide(cur);
}
}
static class RandomWalkDecision implements Decision
{
final ChanceRange range;
final float maxStepSize;
float cur;
RandomWalkDecision(ChanceRange range, RandomSource random)
{
this.range = range;
this.maxStepSize = maxStepSize(range, random);
this.cur = range.select(random);
}
@Override
public boolean get(RandomSource random)
{
float step = (2*random.uniformFloat() - 1f) * maxStepSize;
float cur = this.cur;
this.cur = step > 0 ? Math.min(range.max, cur + step)
: Math.max(range.min, cur + step);
return random.decide(cur);
}
}
static class UniformStepDecision implements Decision
{
final ChanceRange range;
float target;
float cur;
float step;
UniformStepDecision(ChanceRange range, RandomSource random)
{
this.range = range;
cur = range.select(random);
next(random);
}
void next(RandomSource random)
{
target = range.select(random);
step = (target - cur) / random.uniform(16, 128);
if (step == 0) step = target > cur ? 1 : -1;
}
@Override
public boolean get(RandomSource random)
{
float chance = cur;
cur += step;
if (step < 0 && cur <= target) next(random);
else if (step > 0 && cur >= target) next(random);
return random.decide(chance);
}
}
public LinkLatency linkLatency(int nodes, LongRange nanos, RandomSource random)
{
switch (this)
{
default:throw new AssertionError();
case UNIFORM: return new UniformPeriod(nanos);
case UNIFORM_STEP: return new UniformStepLinkLatency(nodes, nanos, random);
case RANDOMWALK: return new RandomWalkLinkLatency(nodes, nanos, random);
}
}
public Period period(LongRange nanos, RandomSource random)
{
switch (this)
{
default: throw new AssertionError();
case UNIFORM: return new UniformPeriod(nanos);
case UNIFORM_STEP: return new UniformStepPeriod(nanos, random);
case RANDOMWALK: return new RandomWalkPeriod(nanos, random);
}
}
public NetworkDecision networkDecision(int nodes, ChanceRange range, RandomSource random)
{
switch (this)
{
default:throw new AssertionError();
// TODO: support a uniform per node variant?
case UNIFORM: return new FixedChance(range.select(random));
case UNIFORM_STEP: return new UniformStepNetworkDecision(nodes, range, random);
case RANDOMWALK: return new RandomWalkNetworkDecision(nodes, range, random);
}
}
public Decision decision(ChanceRange range, RandomSource random)
{
switch (this)
{
default:throw new AssertionError();
case UNIFORM: return new FixedChance(range.select(random));
case UNIFORM_STEP: return new UniformStepDecision(range, random);
case RANDOMWALK: return new RandomWalkDecision(range, random);
}
}
public static float maxStepSize(ChanceRange range, RandomSource random)
{
return maxStepSize(range.min, range.max, random);
}
public static float maxStepSize(float min, float max, RandomSource random)
{
switch (random.uniform(0, 3))
{
case 0:
return Math.max(Float.MIN_VALUE, (max/32) - (min/32));
case 1:
return Math.max(Float.MIN_VALUE, (max/256) - (min/256));
case 2:
return Math.max(Float.MIN_VALUE, (max/2048) - (min/2048));
default:
return Math.max(Float.MIN_VALUE, (max/16384) - (min/16384));
}
}
private static long maxStepSize(LongRange range, RandomSource random)
{
switch (random.uniform(0, 3))
{
case 0:
return Math.max(1, (range.max/32) - (range.min/32));
case 1:
return Math.max(1, (range.max/256) - (range.min/256));
case 2:
return Math.max(1, (range.max/2048) - (range.min/2048));
default:
return Math.max(1, (range.max/16384) - (range.min/16384));
}
}
}