/**
* Copyright (C) 2009-2013 FoundationDB, LLC
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.foundationdb.server.store.statistics;
import com.foundationdb.ais.model.Index;
import com.foundationdb.server.store.statistics.histograms.Bucket;
import com.foundationdb.server.store.statistics.histograms.Sampler;
import com.foundationdb.util.Flywheel;
import java.util.ArrayList;
import java.util.List;
abstract class IndexStatisticsGenerator<K extends Comparable<? super K>, V>
{
private final Flywheel<K> keysFlywheel;
private final int columnCount;
private final int singleColumnPosition; // -1 for multi-column
private Sampler<K> keySampler;
protected final Index index;
protected final long timestamp;
protected int rowCount;
protected IndexStatisticsGenerator(Flywheel<K> keysFlywheel,
Index index,
int columnCount,
int singleColumnPosition) {
this.keysFlywheel = keysFlywheel;
this.index = index;
this.columnCount = columnCount;
this.singleColumnPosition = singleColumnPosition;
this.timestamp = System.currentTimeMillis();
this.rowCount = 0;
}
//
// IndexStatisticsGenerator, derived
//
protected abstract byte[] copyOfKeyBytes(K key);
protected abstract Sampler<K> createKeySampler(int bucketCount, long distinctCount);
protected final Flywheel<K> getKeysFlywheel() {
return keysFlywheel;
}
protected final void loadKey(K key) {
List<? extends K> recycles = keySampler.visit(key);
rowCount++;
for(K recycle : recycles) {
keysFlywheel.recycle(recycle);
}
}
//
// IndexStatisticsGenerator, public
//
public abstract void visit(K key, V value);
public final int columnCount() {
return columnCount;
}
public final int rowCount() {
return rowCount;
}
public void init(int bucketCount, long distinctCount) {
this.keySampler = createKeySampler(bucketCount, distinctCount);
keySampler.init();
}
public void finish(int bucketCount) {
keySampler.finish();
}
public final void getIndexStatistics(IndexStatistics indexStatistics) {
indexStatistics.setAnalysisTimestamp(timestamp);
List<List<Bucket<K>>> segmentBuckets = keySampler.toBuckets();
assert segmentBuckets.size() == columnCount
: String.format("expected %s segments, saw %s: %s", columnCount, segmentBuckets.size(), segmentBuckets);
for (int colCountSegment = 0; colCountSegment < columnCount; colCountSegment++) {
List<Bucket<K>> segmentSamples = segmentBuckets.get(colCountSegment);
int samplesCount = segmentSamples.size();
List<HistogramEntry> entries = new ArrayList<>(samplesCount);
for (Bucket<K> sample : segmentSamples) {
K key = sample.value();
byte[] keyBytes = copyOfKeyBytes(key);
HistogramEntry entry = new HistogramEntry(
key.toString(),
keyBytes,
sample.getEqualsCount(),
sample.getLessThanCount(),
sample.getLessThanDistinctsCount()
);
entries.add(entry);
}
if (singleColumnPosition < 0) {
indexStatistics.addHistogram(new Histogram(0, colCountSegment + 1, entries));
} else if (singleColumnPosition > 0) {
indexStatistics.addHistogram(new Histogram(singleColumnPosition, 1, entries));
} else {
// Single-column histogram for leading column is handled as a multi-column
// histogram with column count = 1.
assert false : "unnecesary sampler " + this;
}
}
}
}