diff --git a/CHANGES.txt b/CHANGES.txt index fd843dd915..12bedfa328 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,4 +1,5 @@ 3.10 + * Add sequence distribution type to cassandra stress (CASSANDRA-12490) * "SELECT * FROM foo LIMIT ;" does not error out (CASSANDRA-12154) * Define executeLocally() at the ReadQuery Level (CASSANDRA-12474) * Extend read/write failure messages with a map of replica addresses diff --git a/tools/cqlstress-counter-example.yaml b/tools/cqlstress-counter-example.yaml index f8f70ea8f2..9084e84358 100644 --- a/tools/cqlstress-counter-example.yaml +++ b/tools/cqlstress-counter-example.yaml @@ -48,6 +48,7 @@ table_definition: | # GAUSSIAN(min..max,mean,stdev) A gaussian/normal distribution, with explicitly defined mean and stdev # UNIFORM(min..max) A uniform distribution over the range [min, max] # FIXED(val) A fixed distribution, always returning the same value +# SEQ(min..max) A fixed sequence, returning values from min to max sequentially, wrapping if necessary. # Aliases: extr, gauss, normal, norm, weibull # # If preceded by ~, the distribution is inverted diff --git a/tools/cqlstress-example.yaml b/tools/cqlstress-example.yaml index 835a4cb1ff..b2b915de30 100644 --- a/tools/cqlstress-example.yaml +++ b/tools/cqlstress-example.yaml @@ -59,6 +59,7 @@ table_definition: | # GAUSSIAN(min..max,mean,stdev) A gaussian/normal distribution, with explicitly defined mean and stdev # UNIFORM(min..max) A uniform distribution over the range [min, max] # FIXED(val) A fixed distribution, always returning the same value +# SEQ(min..max) A fixed sequence, returning values from min to max sequentially, wrapping if necessary. # Aliases: extr, gauss, normal, norm, weibull # # If preceded by ~, the distribution is inverted diff --git a/tools/cqlstress-insanity-example.yaml b/tools/cqlstress-insanity-example.yaml index a286625657..35bcd4a0fc 100644 --- a/tools/cqlstress-insanity-example.yaml +++ b/tools/cqlstress-insanity-example.yaml @@ -58,6 +58,7 @@ table_definition: | # GAUSSIAN(min..max,mean,stdev) A gaussian/normal distribution, with explicitly defined mean and stdev # UNIFORM(min..max) A uniform distribution over the range [min, max] # FIXED(val) A fixed distribution, always returning the same value +# SEQ(min..max) A fixed sequence, returning values from min to max sequentially, wrapping if necessary. # Aliases: extr, gauss, normal, norm, weibull # # If preceded by ~, the distribution is inverted diff --git a/tools/stress/src/org/apache/cassandra/stress/generate/DistributionSequence.java b/tools/stress/src/org/apache/cassandra/stress/generate/DistributionSequence.java new file mode 100644 index 0000000000..424c6611fd --- /dev/null +++ b/tools/stress/src/org/apache/cassandra/stress/generate/DistributionSequence.java @@ -0,0 +1,69 @@ +/* + * + * 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.stress.generate; + +import java.util.concurrent.atomic.AtomicLong; + +public class DistributionSequence extends Distribution +{ + + private final long start; + private final long totalCount; + private final AtomicLong next = new AtomicLong(); + + public DistributionSequence(long start, long end) + { + if (start > end) + throw new IllegalStateException(); + this.start = start; + this.totalCount = 1 + end - start; + } + + private long nextWithWrap() + { + long next = this.next.getAndIncrement(); + return start + (next % totalCount); + } + + @Override + public long next() + { + return nextWithWrap(); + } + + @Override + public double nextDouble() + { + return nextWithWrap(); + } + + @Override + public long inverseCumProb(double cumProb) + { + return (long) (start + (totalCount-1) * cumProb); + } + + @Override + public void setSeed(long seed){} + +} + diff --git a/tools/stress/src/org/apache/cassandra/stress/settings/OptionDistribution.java b/tools/stress/src/org/apache/cassandra/stress/settings/OptionDistribution.java index 6c18fbb880..f613985cd3 100644 --- a/tools/stress/src/org/apache/cassandra/stress/settings/OptionDistribution.java +++ b/tools/stress/src/org/apache/cassandra/stress/settings/OptionDistribution.java @@ -124,6 +124,7 @@ public class OptionDistribution extends Option GroupedOptions.formatMultiLine("GAUSSIAN(min..max,mean,stdev)", "A gaussian/normal distribution, with explicitly defined mean and stdev"), GroupedOptions.formatMultiLine("UNIFORM(min..max)", "A uniform distribution over the range [min, max]"), GroupedOptions.formatMultiLine("FIXED(val)", "A fixed distribution, always returning the same value"), + GroupedOptions.formatMultiLine("SEQ(min..max)", "A fixed sequence, returning values from min to max sequentially, wrapping if necessary."), "Preceding the name with ~ will invert the distribution, e.g. ~exp(1..10) will yield 10 most, instead of least, often", "Aliases: extr, qextr, gauss, normal, norm, weibull" ); @@ -166,6 +167,7 @@ public class OptionDistribution extends Option lookup.put("norm", lookup.get("gaussian")); lookup.put("uniform", new UniformImpl()); lookup.put("fixed", new FixedImpl()); + lookup.put("seq", new SequenceImpl()); LOOKUP = lookup; } @@ -339,18 +341,49 @@ public class OptionDistribution extends Option public DistributionFactory getFactory(List params) { if (params.size() != 1) - throw new IllegalArgumentException("Invalid parameter list for uniform distribution: " + params); + throw new IllegalArgumentException("Invalid parameter list for fixed distribution: " + params); try { final long key = parseLong(params.get(0)); return new FixedFactory(key); } catch (Exception ignore) { - throw new IllegalArgumentException("Invalid parameter list for uniform distribution: " + params); + throw new IllegalArgumentException("Invalid parameter list for fixed distribution: " + params); } } } + private static final class SequenceImpl implements Impl + { + + @Override + public DistributionFactory getFactory(List params) + { + if (params.size() != 1) + throw new IllegalArgumentException("Invalid parameter list for sequence distribution: " + params); + final long min; + final long max; + try + { + String[] bounds = params.get(0).split("\\.\\.+"); + min = parseLong(bounds[0]); + max = parseLong(bounds[1]); + } catch (Exception ignore) + { + throw new IllegalArgumentException("Invalid parameter list for sequence distribution: " + params); + } + if (min == max) + throw new IllegalArgumentException("Invalid parameter list for sequence distribution (min==max): " + params); + + if (min > max) + throw new IllegalArgumentException("Invalid parameter list for sequence distribution (min>max): " + params); + + return new SequenceFactory(min, max); + + } + } + + private static final class InverseFactory implements DistributionFactory { final DistributionFactory wrapped; @@ -492,6 +525,29 @@ public class OptionDistribution extends Option } + private static final class SequenceFactory implements DistributionFactory + { + final long start; + final long end; + + private SequenceFactory(long start, long end) + { + this.start=start; + this.end = end; + } + + @Override + public Distribution get() + { + return new DistributionSequence(start, end); + } + + @Override + public String getConfigAsString(){return String.format("Sequence: start=%d,end=%d", start, end);} + + } + + @Override public int hashCode() { diff --git a/tools/stress/test/unit/org/apache/cassandra/stress/generate/DistributionSequenceTest.java b/tools/stress/test/unit/org/apache/cassandra/stress/generate/DistributionSequenceTest.java new file mode 100644 index 0000000000..6f184d7643 --- /dev/null +++ b/tools/stress/test/unit/org/apache/cassandra/stress/generate/DistributionSequenceTest.java @@ -0,0 +1,99 @@ +/* + * 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.stress.generate; + +import org.junit.Test; + +import org.apache.cassandra.stress.settings.OptionDistribution; + +import static org.junit.Assert.*; + +public class DistributionSequenceTest +{ + @Test + public void simpleSequence() throws Exception + { + Distribution dist = OptionDistribution.get("seq(1..10)").get(); + assertTrue(dist instanceof DistributionSequence); + + assertEquals(1, dist.minValue()); + assertEquals(10, dist.maxValue()); + assertEquals(5, dist.average()); + + assertEquals(1, dist.inverseCumProb(0d)); + assertEquals(10, dist.inverseCumProb(1d)); + + long min = dist.next(); + assertEquals(1,min); + + long last = min; + for (int i=0; i<9; i++) + { + long next = dist.next(); + assertEquals(next, last+1); //increase by one each step + last = next; + } + + assertEquals(1, dist.next()); // wrapping + } + + + @Test + public void negValueSequence() throws Exception + { + Distribution dist = OptionDistribution.get("seq(-1000..-10)").get(); + assertTrue(dist instanceof DistributionSequence); + + assertEquals(-1000, dist.minValue()); + assertEquals( -10, dist.maxValue()); + assertEquals(-504, dist.average()); + + assertEquals(-1000, dist.inverseCumProb(0d)); + assertEquals(-10, dist.inverseCumProb(1d)); + + long min = dist.next(); + assertEquals(-1000, min); + + long last = min; + long next = dist.next(); + while (last