ArrivalWindow should use primitives

patch by sankalp kohli; reviewed by jasobrown for CASSANDRA-9496
This commit is contained in:
Jason Brown 2015-06-17 14:33:44 -07:00
parent ec52e77ecd
commit ad8047abdf
5 changed files with 111 additions and 144 deletions

View File

@ -1,4 +1,5 @@
2.0.16:
* ArrivalWindow should use primitives (CASSANDRA-9496)
* Periodically submit background compaction tasks (CASSANDRA-9592)
* Set HAS_MORE_PAGES flag to false when PagingState is null (CASSANDRA-9571)
* Backport indexed value validation fix from CASSANDRA-9057 (CASSANDRA-9564)

View File

@ -27,14 +27,12 @@ import java.util.concurrent.TimeUnit;
import javax.management.MBeanServer;
import javax.management.ObjectName;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.apache.cassandra.config.DatabaseDescriptor;
import org.apache.cassandra.io.FSWriteError;
import org.apache.cassandra.io.util.FileUtils;
import org.apache.cassandra.utils.BoundedStatsDeque;
import org.apache.cassandra.utils.FBUtilities;
/**
@ -289,11 +287,60 @@ public class FailureDetector implements IFailureDetector, FailureDetectorMBean
}
}
/*
This class is not thread safe.
*/
class ArrayBackedBoundedStats
{
private final long[] arrivalIntervals;
private long sum = 0;
private int index = 0;
private boolean isFilled = false;
private volatile double mean = 0;
public ArrayBackedBoundedStats(final int size)
{
arrivalIntervals = new long[size];
}
public void add(long interval)
{
if(index == arrivalIntervals.length)
{
isFilled = true;
index = 0;
}
if(isFilled)
sum = sum - arrivalIntervals[index];
arrivalIntervals[index++] = interval;
sum += interval;
mean = (double)sum / size();
}
private int size()
{
return isFilled ? arrivalIntervals.length : index;
}
public double mean()
{
return mean;
}
public long[] getArrivalIntervals()
{
return arrivalIntervals;
}
}
class ArrivalWindow
{
private static final Logger logger = LoggerFactory.getLogger(ArrivalWindow.class);
private long tLast = 0L;
private final BoundedStatsDeque arrivalIntervals;
private final ArrayBackedBoundedStats arrivalIntervals;
// this is useless except to provide backwards compatibility in phi_convict_threshold,
// because everyone seems pretty accustomed to the default of 8, and users who have
@ -309,7 +356,7 @@ class ArrivalWindow
ArrivalWindow(int size)
{
arrivalIntervals = new BoundedStatsDeque(size);
arrivalIntervals = new ArrayBackedBoundedStats(size);
}
private static long getMaxInterval()
@ -355,14 +402,14 @@ class ArrivalWindow
// see CASSANDRA-2597 for an explanation of the math at work here.
double phi(long tnow)
{
assert arrivalIntervals.size() > 0 && tLast > 0; // should not be called before any samples arrive
assert arrivalIntervals.mean() > 0 && tLast > 0; // should not be called before any samples arrive
long t = tnow - tLast;
return t / mean();
}
public String toString()
{
return StringUtils.join(arrivalIntervals.iterator(), " ");
return Arrays.toString(arrivalIntervals.getArrivalIntervals());
}
}

View File

@ -1,72 +0,0 @@
/*
* 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.utils;
import java.util.Iterator;
import java.util.NoSuchElementException;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.AtomicReference;
import com.google.common.util.concurrent.AtomicDouble;
/**
* bounded threadsafe deque
*/
public class BoundedStatsDeque implements Iterable<Long>
{
private final LinkedBlockingDeque<Long> deque;
private final AtomicLong sum;
public BoundedStatsDeque(int size)
{
deque = new LinkedBlockingDeque<>(size);
sum = new AtomicLong(0);
}
public Iterator<Long> iterator()
{
return deque.iterator();
}
public int size()
{
return deque.size();
}
public void add(long i)
{
if (!deque.offer(i))
{
Long removed = deque.remove();
sum.addAndGet(-removed);
deque.offer(i);
}
sum.addAndGet(i);
}
public long sum()
{
return sum.get();
}
public double mean()
{
return size() > 0 ? ((double) sum()) / size() : 0;
}
}

View File

@ -0,0 +1,57 @@
package org.apache.cassandra.gms;
import java.util.Arrays;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
public class ArrayBackedBoundedStatsTest {
@Test
public void test()
{
int size = 4;
ArrayBackedBoundedStats bsd = new ArrayBackedBoundedStats(size);
//check the values for an empty result
assertEquals(0, bsd.mean(), 0.001d);
bsd.add(1L); //this one falls out, over limit
bsd.add(2L);
bsd.add(3L);
bsd.add(4L);
bsd.add(5L);
//verify that everything is in there
long [] expected = new long[] {2,3,4,5};
assertArrivalIntervals(bsd, expected);
//check results
assertEquals(3.5, bsd.mean(), 0.001d);
}
private void assertArrivalIntervals(ArrayBackedBoundedStats bsd, long [] expected)
{
Arrays.sort(expected);
Arrays.sort(bsd.getArrivalIntervals());
assertTrue(Arrays.equals(bsd.getArrivalIntervals(), expected));
}
@Test
public void testMultipleRounds() throws Exception
{
int size = 5;
ArrayBackedBoundedStats bsd = new ArrayBackedBoundedStats(size);
for(long i=0; i <= 1000;i++)
{
bsd.add(i);
}
long [] expected = new long[] {1000,999,998,997, 996};
assertArrivalIntervals(bsd, expected);
}
}

View File

@ -1,66 +0,0 @@
package org.apache.cassandra.utils;
/*
*
* 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.
*
*/
import static org.junit.Assert.*;
import java.util.Iterator;
import org.junit.Test;
public class BoundedStatsDequeTest
{
@Test
public void test()
{
int size = 4;
BoundedStatsDeque bsd = new BoundedStatsDeque(size);
//check the values for an empty result
assertEquals(0, bsd.size());
assertEquals(0, bsd.sum(), 0.001d);
assertEquals(0, bsd.mean(), 0.001d);
bsd.add(1L); //this one falls out, over limit
bsd.add(2L);
bsd.add(3L);
bsd.add(4L);
bsd.add(5L);
//verify that everything is in there
Iterator<Long> iter = bsd.iterator();
assertTrue(iter.hasNext());
assertEquals(2L, iter.next(), 0);
assertTrue(iter.hasNext());
assertEquals(3L, iter.next(), 0);
assertTrue(iter.hasNext());
assertEquals(4L, iter.next(), 0);
assertTrue(iter.hasNext());
assertEquals(5L, iter.next(), 0);
assertFalse(iter.hasNext());
//check results
assertEquals(size, bsd.size());
assertEquals(14, bsd.sum(), 0.001d);
assertEquals(3.5, bsd.mean(), 0.001d);
}
}