blob: 88e8020c32ce0ce990120ce651bb2796b8b0ac3b [file] [log] [blame]
package org.apache.cassandra.stress.util;
/*
*
* 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 java.util.Arrays;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ThreadLocalRandom;
// a timer - this timer must be used by a single thread, and co-ordinates with other timers by
public final class Timer
{
private ThreadLocalRandom rnd;
// in progress snap start
private long sampleStartNanos;
// each entry is present with probability 1/p(opCount) or 1/(p(opCount)-1)
private final long[] sample;
private int opCount;
// aggregate info
private long errorCount;
private long partitionCount;
private long rowCount;
private long total;
private long max;
private long maxStart;
private long upToDateAsOf;
private long lastSnap = System.nanoTime();
// communication with summary/logging thread
private volatile CountDownLatch reportRequest;
volatile TimingInterval report;
private volatile TimingInterval finalReport;
public Timer(int sampleCount)
{
int powerOf2 = 32 - Integer.numberOfLeadingZeros(sampleCount - 1);
this.sample = new long[1 << powerOf2];
}
public void init()
{
rnd = ThreadLocalRandom.current();
}
public void start(){
// decide if we're logging this event
sampleStartNanos = System.nanoTime();
}
private int p(int index)
{
return 1 + (index / sample.length);
}
public boolean running()
{
return finalReport == null;
}
public void stop(long partitionCount, long rowCount, boolean error)
{
maybeReport();
long now = System.nanoTime();
long time = now - sampleStartNanos;
if (rnd.nextInt(p(opCount)) == 0)
sample[index(opCount)] = time;
if (time > max)
{
maxStart = sampleStartNanos;
max = time;
}
total += time;
opCount += 1;
this.partitionCount += partitionCount;
this.rowCount += rowCount;
if (error)
this.errorCount++;
upToDateAsOf = now;
}
private int index(int count)
{
return count & (sample.length - 1);
}
private TimingInterval buildReport()
{
final List<SampleOfLongs> sampleLatencies = Arrays.asList
( new SampleOfLongs(Arrays.copyOf(sample, index(opCount)), p(opCount)),
new SampleOfLongs(Arrays.copyOfRange(sample, index(opCount), Math.min(opCount, sample.length)), p(opCount) - 1)
);
final TimingInterval report = new TimingInterval(lastSnap, upToDateAsOf, max, maxStart, max, partitionCount,
rowCount, total, opCount, errorCount, SampleOfLongs.merge(rnd, sampleLatencies, Integer.MAX_VALUE));
// reset counters
opCount = 0;
partitionCount = 0;
rowCount = 0;
total = 0;
max = 0;
errorCount = 0;
lastSnap = upToDateAsOf;
return report;
}
// checks to see if a report has been requested, and if so produces the report, signals and clears the request
private void maybeReport()
{
if (reportRequest != null)
{
synchronized (this)
{
report = buildReport();
reportRequest.countDown();
reportRequest = null;
}
}
}
// checks to see if the timer is dead; if not requests a report, and otherwise fulfills the request itself
synchronized void requestReport(CountDownLatch signal)
{
if (finalReport != null)
{
report = finalReport;
finalReport = new TimingInterval(0);
signal.countDown();
}
else
reportRequest = signal;
}
// closes the timer; if a request is outstanding, it furnishes the request, otherwise it populates finalReport
public synchronized void close()
{
if (reportRequest == null)
finalReport = buildReport();
else
{
finalReport = new TimingInterval(0);
report = buildReport();
reportRequest.countDown();
reportRequest = null;
}
}
}