import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
+import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/** Manages objects stored in {@link DfsPackFile} on a storage system. */
public abstract class DfsObjDatabase extends ObjectDatabase {
- private static final PackList NO_PACKS = new PackList(new DfsPackFile[0]) {
+ private static final PackList NO_PACKS = new PackList(
+ new DfsPackFile[0],
+ new DfsReftable[0]) {
@Override
boolean dirty() {
return true;
return getPackList().packs;
}
+ /**
+ * Scan and list all available reftable files in the repository.
+ *
+ * @return list of available reftables. The returned array is shared with
+ * the implementation and must not be modified by the caller.
+ * @throws IOException
+ * the pack list cannot be initialized.
+ */
+ public DfsReftable[] getReftables() throws IOException {
+ return getPackList().reftables;
+ }
+
/**
* Scan and list all available pack files in the repository.
*
return getCurrentPackList().packs;
}
+ /**
+ * List currently known reftable files in the repository, without scanning.
+ *
+ * @return list of available reftables. The returned array is shared with
+ * the implementation and must not be modified by the caller.
+ */
+ public DfsReftable[] getCurrentReftables() {
+ return getCurrentPackList().reftables;
+ }
+
/**
* List currently known pack files in the repository, without scanning.
*
DfsPackFile[] packs = new DfsPackFile[1 + o.packs.length];
packs[0] = newPack;
System.arraycopy(o.packs, 0, packs, 1, o.packs.length);
- n = new PackListImpl(packs);
+ n = new PackListImpl(packs, o.reftables);
} while (!packList.compareAndSet(o, n));
}
private PackList scanPacksImpl(PackList old) throws IOException {
DfsBlockCache cache = DfsBlockCache.getInstance();
- Map<DfsPackDescription, DfsPackFile> forReuse = reuseMap(old);
+ Map<DfsPackDescription, DfsPackFile> packs = packMap(old);
+ Map<DfsPackDescription, DfsReftable> reftables = reftableMap(old);
+
List<DfsPackDescription> scanned = listPacks();
Collections.sort(scanned);
- List<DfsPackFile> list = new ArrayList<>(scanned.size());
+ List<DfsPackFile> newPacks = new ArrayList<>(scanned.size());
+ List<DfsReftable> newReftables = new ArrayList<>(scanned.size());
boolean foundNew = false;
for (DfsPackDescription dsc : scanned) {
- DfsPackFile oldPack = forReuse.remove(dsc);
+ DfsPackFile oldPack = packs.remove(dsc);
if (oldPack != null) {
- list.add(oldPack);
+ newPacks.add(oldPack);
} else if (dsc.hasFileExt(PackExt.PACK)) {
- list.add(new DfsPackFile(cache, dsc));
+ newPacks.add(new DfsPackFile(cache, dsc));
+ foundNew = true;
+ }
+
+ DfsReftable oldReftable = reftables.remove(dsc);
+ if (oldReftable != null) {
+ newReftables.add(oldReftable);
+ } else if (dsc.hasFileExt(PackExt.REFTABLE)) {
+ newReftables.add(new DfsReftable(cache, dsc));
foundNew = true;
}
}
- for (DfsPackFile p : forReuse.values())
- p.close();
- if (list.isEmpty())
- return new PackListImpl(NO_PACKS.packs);
+ if (newPacks.isEmpty())
+ return new PackListImpl(NO_PACKS.packs, NO_PACKS.reftables);
if (!foundNew) {
old.clearDirty();
return old;
}
- return new PackListImpl(list.toArray(new DfsPackFile[list.size()]));
+ Collections.sort(newReftables, reftableComparator());
+ return new PackListImpl(
+ newPacks.toArray(new DfsPackFile[0]),
+ newReftables.toArray(new DfsReftable[0]));
}
- private static Map<DfsPackDescription, DfsPackFile> reuseMap(PackList old) {
+ private static Map<DfsPackDescription, DfsPackFile> packMap(PackList old) {
Map<DfsPackDescription, DfsPackFile> forReuse = new HashMap<>();
for (DfsPackFile p : old.packs) {
- if (p.invalid()) {
- // The pack instance is corrupted, and cannot be safely used
- // again. Do not include it in our reuse map.
- //
- p.close();
- continue;
+ if (!p.invalid()) {
+ forReuse.put(p.desc, p);
}
+ }
+ return forReuse;
+ }
- DfsPackFile prior = forReuse.put(p.getPackDescription(), p);
- if (prior != null) {
- // This should never occur. It should be impossible for us
- // to have two pack files with the same name, as all of them
- // came out of the same directory. If it does, we promised to
- // close any PackFiles we did not reuse, so close the second,
- // readers are likely to be actively using the first.
- //
- forReuse.put(prior.getPackDescription(), prior);
- p.close();
+ private static Map<DfsPackDescription, DfsReftable> reftableMap(PackList old) {
+ Map<DfsPackDescription, DfsReftable> forReuse = new HashMap<>();
+ for (DfsReftable p : old.reftables) {
+ if (!p.invalid()) {
+ forReuse.put(p.desc, p);
}
}
return forReuse;
}
+ /** @return comparator to sort {@link DfsReftable} by priority. */
+ protected Comparator<DfsReftable> reftableComparator() {
+ return (fa, fb) -> {
+ DfsPackDescription a = fa.getPackDescription();
+ DfsPackDescription b = fb.getPackDescription();
+
+ // GC, COMPACT reftables first by higher category.
+ int c = category(b) - category(a);
+ if (c != 0) {
+ return c;
+ }
+
+ // Lower maxUpdateIndex first.
+ c = Long.signum(a.getMaxUpdateIndex() - b.getMaxUpdateIndex());
+ if (c != 0) {
+ return c;
+ }
+
+ // Older reftable first.
+ return Long.signum(a.getLastModified() - b.getLastModified());
+ };
+ }
+
+ static int category(DfsPackDescription d) {
+ PackSource s = d.getPackSource();
+ return s != null ? s.category : 0;
+ }
+
/** Clears the cached list of packs, forcing them to be scanned again. */
protected void clearCache() {
packList.set(NO_PACKS);
@Override
public void close() {
- // PackList packs = packList.get();
packList.set(NO_PACKS);
-
- // TODO Close packs if they aren't cached.
- // for (DfsPackFile p : packs.packs)
- // p.close();
}
/** Snapshot of packs scanned in a single pass. */
/** All known packs, sorted. */
public final DfsPackFile[] packs;
+ /** All known reftables, sorted. */
+ public final DfsReftable[] reftables;
+
private long lastModified = -1;
- PackList(DfsPackFile[] packs) {
+ PackList(DfsPackFile[] packs, DfsReftable[] reftables) {
this.packs = packs;
+ this.reftables = reftables;
}
/** @return last modified time of all packs, in milliseconds. */
private static final class PackListImpl extends PackList {
private volatile boolean dirty;
- PackListImpl(DfsPackFile[] packs) {
- super(packs);
+ PackListImpl(DfsPackFile[] packs, DfsReftable[] reftables) {
+ super(packs, reftables);
}
@Override
package org.eclipse.jgit.internal.storage.dfs;
import static org.eclipse.jgit.internal.storage.pack.PackExt.PACK;
+import static org.eclipse.jgit.internal.storage.pack.PackExt.REFTABLE;
import java.util.Arrays;
import org.eclipse.jgit.internal.storage.dfs.DfsObjDatabase.PackSource;
import org.eclipse.jgit.internal.storage.pack.PackExt;
+import org.eclipse.jgit.internal.storage.reftable.ReftableWriter;
import org.eclipse.jgit.storage.pack.PackStatistics;
/**
private int[] blockSizeMap;
private long objectCount;
private long deltaCount;
- private PackStatistics stats;
+ private long minUpdateIndex;
+ private long maxUpdateIndex;
+
+ private PackStatistics packStats;
+ private ReftableWriter.Stats refStats;
private int extensions;
private int indexVersion;
private long estimatedPackSize;
return this;
}
+ /** @return minUpdateIndex for the reftable, if present. */
+ public long getMinUpdateIndex() {
+ return minUpdateIndex;
+ }
+
+ /**
+ * @param min
+ * minUpdateIndex for the reftable, or 0.
+ * @return {@code this}
+ */
+ public DfsPackDescription setMinUpdateIndex(long min) {
+ minUpdateIndex = min;
+ return this;
+ }
+
+ /** @return maxUpdateIndex for the reftable, if present. */
+ public long getMaxUpdateIndex() {
+ return maxUpdateIndex;
+ }
+
+ /**
+ * @param max
+ * maxUpdateIndex for the reftable, or 0.
+ * @return {@code this}
+ */
+ public DfsPackDescription setMaxUpdateIndex(long max) {
+ maxUpdateIndex = max;
+ return this;
+ }
+
/**
* @param ext
* the file extension.
* is being committed to the repository.
*/
public PackStatistics getPackStats() {
- return stats;
+ return packStats;
}
DfsPackDescription setPackStats(PackStatistics stats) {
- this.stats = stats;
+ this.packStats = stats;
setFileSize(PACK, stats.getTotalBytes());
setObjectCount(stats.getTotalObjects());
setDeltaCount(stats.getTotalDeltas());
return this;
}
+ /** @return stats from the sibling reftable, if created. */
+ public ReftableWriter.Stats getReftableStats() {
+ return refStats;
+ }
+
+ void setReftableStats(ReftableWriter.Stats stats) {
+ this.refStats = stats;
+ setMinUpdateIndex(stats.minUpdateIndex());
+ setMaxUpdateIndex(stats.maxUpdateIndex());
+ setFileSize(REFTABLE, stats.totalBytes());
+ setBlockSize(REFTABLE, stats.refBlockSize());
+ }
+
/**
* Discard the pack statistics, if it was populated.
*
* @return {@code this}
*/
public DfsPackDescription clearPackStats() {
- stats = null;
+ packStats = null;
+ refStats = null;
return this;
}