From 1d4b4bd357be41b31c815faa77a7331f1000ba21 Mon Sep 17 00:00:00 2001 From: "Robert J. Moore" <Robert.J.Moore@allanbank.com> Date: Wed, 7 Oct 2015 22:02:47 -0400 Subject: [PATCH] [core] Update the AcknowledgedCounterGenerator to use a power of two size for the window so a mask can be used instead of a modulo operation. This avoids issues when there is overflow of the id integer values to negative values. --- .../AcknowledgedCounterGenerator.java | 36 ++++++++--- .../AcknowledgedCounterGeneratorTest.java | 61 +++++++++++++++++++ 2 files changed, 89 insertions(+), 8 deletions(-) create mode 100644 core/src/test/java/com/yahoo/ycsb/generator/AcknowledgedCounterGeneratorTest.java diff --git a/core/src/main/java/com/yahoo/ycsb/generator/AcknowledgedCounterGenerator.java b/core/src/main/java/com/yahoo/ycsb/generator/AcknowledgedCounterGenerator.java index 443b47f5..69884c29 100644 --- a/core/src/main/java/com/yahoo/ycsb/generator/AcknowledgedCounterGenerator.java +++ b/core/src/main/java/com/yahoo/ycsb/generator/AcknowledgedCounterGenerator.java @@ -1,3 +1,19 @@ +/** + * Copyright (c) 2015 YCSB contributors. 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. See accompanying + * LICENSE file. + */ package com.yahoo.ycsb.generator; import java.util.concurrent.locks.ReentrantLock; @@ -8,7 +24,11 @@ import java.util.concurrent.locks.ReentrantLock; */ public class AcknowledgedCounterGenerator extends CounterGenerator { - private static final int WINDOW_SIZE = 1000000; + /** The size of the window of pending id ack's. 2^20 = {@value} */ + static final int WINDOW_SIZE = Integer.rotateLeft(1, 20); + + /** The mask to use to turn an id into a slot in {@link #window}. */ + private static final int WINDOW_MASK = WINDOW_SIZE - 1; private final ReentrantLock lock; private final boolean[] window; @@ -40,22 +60,22 @@ public class AcknowledgedCounterGenerator extends CounterGenerator */ public void acknowledge(int value) { - if (value > limit + WINDOW_SIZE) { + final int currentSlot = (value & WINDOW_MASK); + if (window[currentSlot] == true) { throw new RuntimeException("Too many unacknowledged insertion keys."); } - window[value % WINDOW_SIZE] = true; + window[currentSlot] = true; if (lock.tryLock()) { // move a contiguous sequence from the window // over to the "limit" variable - try { + // Only loop through the entire window at most once. + int beforeFirstSlot = (limit & WINDOW_MASK); int index; - - for (index = limit + 1; index <= value; ++index) { - int slot = index % WINDOW_SIZE; - + for (index = limit + 1; index != beforeFirstSlot; ++index) { + int slot = (index & WINDOW_MASK); if (!window[slot]) { break; } diff --git a/core/src/test/java/com/yahoo/ycsb/generator/AcknowledgedCounterGeneratorTest.java b/core/src/test/java/com/yahoo/ycsb/generator/AcknowledgedCounterGeneratorTest.java new file mode 100644 index 00000000..54e02322 --- /dev/null +++ b/core/src/test/java/com/yahoo/ycsb/generator/AcknowledgedCounterGeneratorTest.java @@ -0,0 +1,61 @@ +/** + * Copyright (c) 2015 YCSB contributors. 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. See accompanying + * LICENSE file. + */ +package com.yahoo.ycsb.generator; + +import java.util.Random; +import java.util.concurrent.ArrayBlockingQueue; +import java.util.concurrent.BlockingQueue; + +import org.testng.annotations.Test; + +/** + * Tests for the AcknowledgedCounterGenerator class. + */ +public class AcknowledgedCounterGeneratorTest { + + /** + * Test that advancing past {@link Integer#MAX_VALUE} works. + */ + @Test + public void testIncrementPastIntegerMaxValue() { + final long toTry = AcknowledgedCounterGenerator.WINDOW_SIZE * 3; + + AcknowledgedCounterGenerator generator = + new AcknowledgedCounterGenerator(Integer.MAX_VALUE - 1000); + + Random rand = new Random(System.currentTimeMillis()); + BlockingQueue<Integer> pending = new ArrayBlockingQueue<Integer>(1000); + for (long i = 0; i < toTry; ++i) { + int value = generator.nextInt(); + + while (!pending.offer(value)) { + + Integer first = pending.poll(); + + // Don't always advance by one. + if (rand.nextBoolean()) { + generator.acknowledge(first); + } else { + Integer second = pending.poll(); + pending.add(first); + generator.acknowledge(second); + } + } + } + + } +} -- GitLab