Browse Source
A compaction of reftables is just copying the results of a MergedReftable into a ReftableWriter. Wrap this up into a utility. Change-Id: I6f5677d923e9628993a2d8b4b007a9b8662c9045stable-4.9
Shawn Pearce
7 years ago
2 changed files with 250 additions and 0 deletions
@ -0,0 +1,223 @@ |
|||||||
|
/* |
||||||
|
* Copyright (C) 2017, Google Inc. |
||||||
|
* and other copyright owners as documented in the project's IP log. |
||||||
|
* |
||||||
|
* This program and the accompanying materials are made available |
||||||
|
* under the terms of the Eclipse Distribution License v1.0 which |
||||||
|
* accompanies this distribution, is reproduced below, and is |
||||||
|
* available at http://www.eclipse.org/org/documents/edl-v10.php
|
||||||
|
* |
||||||
|
* All rights reserved. |
||||||
|
* |
||||||
|
* Redistribution and use in source and binary forms, with or |
||||||
|
* without modification, are permitted provided that the following |
||||||
|
* conditions are met: |
||||||
|
* |
||||||
|
* - Redistributions of source code must retain the above copyright |
||||||
|
* notice, this list of conditions and the following disclaimer. |
||||||
|
* |
||||||
|
* - Redistributions in binary form must reproduce the above |
||||||
|
* copyright notice, this list of conditions and the following |
||||||
|
* disclaimer in the documentation and/or other materials provided |
||||||
|
* with the distribution. |
||||||
|
* |
||||||
|
* - Neither the name of the Eclipse Foundation, Inc. nor the |
||||||
|
* names of its contributors may be used to endorse or promote |
||||||
|
* products derived from this software without specific prior |
||||||
|
* written permission. |
||||||
|
* |
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND |
||||||
|
* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, |
||||||
|
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES |
||||||
|
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
||||||
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR |
||||||
|
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||||||
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT |
||||||
|
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
||||||
|
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER |
||||||
|
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, |
||||||
|
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
||||||
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF |
||||||
|
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||||
|
*/ |
||||||
|
|
||||||
|
package org.eclipse.jgit.internal.storage.reftable; |
||||||
|
|
||||||
|
import java.io.IOException; |
||||||
|
import java.io.OutputStream; |
||||||
|
import java.util.ArrayDeque; |
||||||
|
import java.util.ArrayList; |
||||||
|
import java.util.List; |
||||||
|
|
||||||
|
import org.eclipse.jgit.lib.PersonIdent; |
||||||
|
import org.eclipse.jgit.internal.storage.reftable.ReftableWriter.Stats; |
||||||
|
import org.eclipse.jgit.lib.ReflogEntry; |
||||||
|
|
||||||
|
/** |
||||||
|
* Merges reftables and compacts them into a single output. |
||||||
|
* <p> |
||||||
|
* For a partial compaction callers should {@link #setIncludeDeletes(boolean)} |
||||||
|
* to {@code true} to ensure the new reftable continues to use a delete marker |
||||||
|
* to shadow any lower reftable that may have the reference present. |
||||||
|
* <p> |
||||||
|
* By default all log entries within the range defined by |
||||||
|
* {@link #setMinUpdateIndex(long)} and {@link #setMaxUpdateIndex(long)} are |
||||||
|
* copied, even if no references in the output file match the log records. |
||||||
|
* Callers may truncate the log to a more recent time horizon with |
||||||
|
* {@link #setOldestReflogTimeMillis(long)}, or disable the log altogether with |
||||||
|
* {@code setOldestReflogTimeMillis(Long.MAX_VALUE)}. |
||||||
|
*/ |
||||||
|
public class ReftableCompactor { |
||||||
|
private final ReftableWriter writer = new ReftableWriter(); |
||||||
|
private final ArrayDeque<Reftable> tables = new ArrayDeque<>(); |
||||||
|
|
||||||
|
private boolean includeDeletes; |
||||||
|
private long minUpdateIndex; |
||||||
|
private long maxUpdateIndex; |
||||||
|
private long oldestReflogTimeMillis; |
||||||
|
private Stats stats; |
||||||
|
|
||||||
|
/** |
||||||
|
* @param cfg |
||||||
|
* configuration for the reftable. |
||||||
|
* @return {@code this} |
||||||
|
*/ |
||||||
|
public ReftableCompactor setConfig(ReftableConfig cfg) { |
||||||
|
writer.setConfig(cfg); |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* @param deletes |
||||||
|
* {@code true} to include deletions in the output, which may be |
||||||
|
* necessary for partial compaction. |
||||||
|
* @return {@code this} |
||||||
|
*/ |
||||||
|
public ReftableCompactor setIncludeDeletes(boolean deletes) { |
||||||
|
includeDeletes = deletes; |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* @param min |
||||||
|
* the minimum update index for log entries that appear in the |
||||||
|
* compacted reftable. This should be 1 higher than the prior |
||||||
|
* reftable's {@code maxUpdateIndex} if this table will be used |
||||||
|
* in a stack. |
||||||
|
* @return {@code this} |
||||||
|
*/ |
||||||
|
public ReftableCompactor setMinUpdateIndex(long min) { |
||||||
|
minUpdateIndex = min; |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* @param max |
||||||
|
* the maximum update index for log entries that appear in the |
||||||
|
* compacted reftable. This should be at least 1 higher than the |
||||||
|
* prior reftable's {@code maxUpdateIndex} if this table will be |
||||||
|
* used in a stack. |
||||||
|
* @return {@code this} |
||||||
|
*/ |
||||||
|
public ReftableCompactor setMaxUpdateIndex(long max) { |
||||||
|
maxUpdateIndex = max; |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* @param timeMillis |
||||||
|
* oldest log time to preserve. Entries whose timestamps are |
||||||
|
* {@code >= timeMillis} will be copied into the output file. Log |
||||||
|
* entries that predate {@code timeMillis} will be discarded. |
||||||
|
* Specified in Java standard milliseconds since the epoch. |
||||||
|
* @return {@code this} |
||||||
|
*/ |
||||||
|
public ReftableCompactor setOldestReflogTimeMillis(long timeMillis) { |
||||||
|
oldestReflogTimeMillis = timeMillis; |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Add all of the tables, in the specified order. |
||||||
|
* |
||||||
|
* @param readers |
||||||
|
* tables to compact. Tables should be ordered oldest first/most |
||||||
|
* recent last so that the more recent tables can shadow the |
||||||
|
* older results. Caller is responsible for closing the readers. |
||||||
|
*/ |
||||||
|
public void addAll(List<? extends Reftable> readers) { |
||||||
|
tables.addAll(readers); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Write a compaction to {@code out}. |
||||||
|
* |
||||||
|
* @param out |
||||||
|
* stream to write the compacted tables to. Caller is responsible |
||||||
|
* for closing {@code out}. |
||||||
|
* @throws IOException |
||||||
|
* if tables cannot be read, or cannot be written. |
||||||
|
*/ |
||||||
|
public void compact(OutputStream out) throws IOException { |
||||||
|
MergedReftable mr = new MergedReftable(new ArrayList<>(tables)); |
||||||
|
mr.setIncludeDeletes(includeDeletes); |
||||||
|
|
||||||
|
writer.setMinUpdateIndex(minUpdateIndex); |
||||||
|
writer.setMaxUpdateIndex(maxUpdateIndex); |
||||||
|
writer.begin(out); |
||||||
|
mergeRefs(mr); |
||||||
|
mergeLogs(mr); |
||||||
|
writer.finish(); |
||||||
|
stats = writer.getStats(); |
||||||
|
} |
||||||
|
|
||||||
|
/** @return statistics of the last written reftable. */ |
||||||
|
public Stats getStats() { |
||||||
|
return stats; |
||||||
|
} |
||||||
|
|
||||||
|
private void mergeRefs(MergedReftable mr) throws IOException { |
||||||
|
try (RefCursor rc = mr.allRefs()) { |
||||||
|
while (rc.next()) { |
||||||
|
writer.writeRef(rc.getRef()); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private void mergeLogs(MergedReftable mr) throws IOException { |
||||||
|
if (oldestReflogTimeMillis == Long.MAX_VALUE) { |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
try (LogCursor lc = mr.allLogs()) { |
||||||
|
while (lc.next()) { |
||||||
|
long updateIndex = lc.getUpdateIndex(); |
||||||
|
if (updateIndex < minUpdateIndex |
||||||
|
|| updateIndex > maxUpdateIndex) { |
||||||
|
// Cannot merge log records outside the header's range.
|
||||||
|
continue; |
||||||
|
} |
||||||
|
|
||||||
|
String refName = lc.getRefName(); |
||||||
|
ReflogEntry log = lc.getReflogEntry(); |
||||||
|
if (log == null) { |
||||||
|
if (includeDeletes) { |
||||||
|
writer.deleteLog(refName, updateIndex); |
||||||
|
} |
||||||
|
continue; |
||||||
|
} |
||||||
|
|
||||||
|
PersonIdent who = log.getWho(); |
||||||
|
if (who.getWhen().getTime() >= oldestReflogTimeMillis) { |
||||||
|
writer.writeLog( |
||||||
|
refName, |
||||||
|
updateIndex, |
||||||
|
who, |
||||||
|
log.getOldId(), |
||||||
|
log.getNewId(), |
||||||
|
log.getComment()); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
Loading…
Reference in new issue