/* * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ package com.github.geophile.erdo.consolidate; import java.util.ArrayList; import java.util.List; import java.util.PriorityQueue; import java.util.logging.Level; import java.util.logging.Logger; import static com.github.geophile.erdo.consolidate.Consolidation.Element; import static java.lang.Math.abs; import static java.lang.Math.max; abstract class KeepSmallestConsolidationPlanner extends ConsolidationPlanner { // ConsolidationPlanner interface @Override public String type() { return "keepSmallest"; } @Override public boolean planConsolidation(Element newElement) { assert newElement == null : newElement; PriorityQueue<PartialConsolidation> pq = new PriorityQueue<PartialConsolidation>(); List<Element> consolidationCandidates = consolidationCandidates(); long candidateRecords = 0; for (Element element : consolidationCandidates) { pq.add(new Single(element)); candidateRecords += element.count(); } // Consolidate as much as possible while (pq.size() > 1) { PartialConsolidation first = pq.poll(); PartialConsolidation second = pq.poll(); if (consolidate(first, second)) { pq.add(new Double(first, second)); } else { // Keep the one with the most trees. Do consolidations of other, smaller // elements, and maybe the selected PartialConsolidation will continue to be // combined later. pq.add(mostTrees(first, second)); } } // Gather elements to be consolidated and see if there's actually anything to be done. consolidationElements = new ArrayList<Element>(); PartialConsolidation consolidation = pq.poll(); if (consolidation == null) { consolidationElements = null; } else { consolidation.collectElements(consolidationElements); int nElements = consolidationElements.size(); if (nElements < minMaps || consolidation.size() < minSize) { if (LOG.isLoggable(Level.FINER)) { consolidationSet.describe(LOG, Level.FINER, "skip keepSmallest consolidation"); LOG.log(Level.FINER, "skip keepSmallest consolidation: {0} element{1}, {2} records", new Object[]{nElements, nElements == 1 ? "" : "s", consolidation.records()}) ; } consolidationElements = null; } else { if (LOG.isLoggable(Level.FINE)) { consolidationSet.describe(LOG, Level.FINE, "keepSmallest planning"); LOG.log(Level.FINE, "keepSmallest planning {0} / {1}: " + "Consolidation candidates: {2}, consolidation: {3}", new Object[]{consolidation.records(), candidateRecords, consolidationCandidates, consolidationElements}); } } } return consolidationElements != null; } @Override public List<Element> elementsToConsolidate() { return consolidationElements; } // KeepSmallestConsolidationPlanner interface public static KeepSmallestConsolidationPlanner durable(ConsolidationSet consolidationSet, int minConsolidationMaps, int minConsoldiationSize) { return new Durable(consolidationSet, minConsolidationMaps, minConsoldiationSize); } public static KeepSmallestConsolidationPlanner nonDurable(ConsolidationSet consolidationSet, int minConsolidationMaps, int minConsoldiationSize) { return new NonDurable(consolidationSet, minConsolidationMaps, minConsoldiationSize); } // For use by subclasses protected abstract List<Element> consolidationCandidates(); protected KeepSmallestConsolidationPlanner(ConsolidationSet consolidationSet, boolean inputDurable, boolean outputDurable, int minConsolidationMaps, int minConsolidationSize) { super(consolidationSet, inputDurable, outputDurable); minMaps = max(minConsolidationMaps, 1); minSize = minConsolidationSize; } // For use by this class private PartialConsolidation mostTrees(PartialConsolidation x, PartialConsolidation y) { return x == null ? y : y == null ? x : x.trees() < y.trees() ? y : x; } private boolean consolidate(PartialConsolidation x, PartialConsolidation y) { long xSize = x.size(); long ySize = y.size(); return ((float) abs(xSize - ySize)) / max(xSize, ySize) <= THRESHOLD; } // Class state private static final float THRESHOLD = 0.50f; private static final Logger LOG = Logger.getLogger(KeepSmallestConsolidationPlanner.class.getName()); // Object state private final int minMaps; private final long minSize; private List<Element> consolidationElements; // Inner classes private static class Durable extends KeepSmallestConsolidationPlanner { @Override protected List<Element> consolidationCandidates() { return consolidationSet.durable().availableForConsolidation(); } public Durable(ConsolidationSet consolidationSet, int minConsolidationMaps, int minConsolidationSize) { super(consolidationSet, true, true, minConsolidationMaps, minConsolidationSize); } } private static class NonDurable extends KeepSmallestConsolidationPlanner { @Override protected List<Element> consolidationCandidates() { return consolidationSet.nonDurable().availableForConsolidation(); } public NonDurable(ConsolidationSet consolidationSet, int minConsolidationMaps, int minConsolidationSize) { super(consolidationSet, false, false, minConsolidationMaps, minConsolidationSize); } } private static abstract class PartialConsolidation implements Comparable<PartialConsolidation> { public String toString() { return Long.toString(records()); } public int compareTo(PartialConsolidation that) { long thisSize = this.size(); long thatSize = that.size(); return thisSize < thatSize ? -1 : thisSize > thatSize ? 1 : 0; } public int trees() { return trees; } public long records() { return records; } public long size() { return size; } public abstract void collectElements(List<Element> elements); protected PartialConsolidation(int trees, long records, long size) { this.trees = trees; this.records = records; this.size = size; } protected final int trees; protected final long records; protected final long size; } private static class Single extends PartialConsolidation { @Override public void collectElements(List<Element> elements) { elements.add(element); } public Single(Element element) { super(1, element.count(), element.sizeBytes()); this.element = element; } private final Element element; } private static class Double extends PartialConsolidation { @Override public void collectElements(List<Element> elements) { left.collectElements(elements); right.collectElements(elements); } public Double(PartialConsolidation left, PartialConsolidation right) { super(left.trees + right.trees, left.records + right.records, left.size + right.size); this.left = left; this.right = right; } private final PartialConsolidation left; private final PartialConsolidation right; } }