Seek references by prefix in reftable
Reftable implementation of RefDatabase.getRefsByPrefix() should be more performant, as references are filtered directly by prefix; instead of fetching the whole subtree then filter by prefix. Change-Id: If4f5f8c08285ea1eaec9efb83c3d864cea7a1321 Signed-off-by: Minh Thai <mthai@google.com>
This commit is contained in:
parent
04b9f44367
commit
e04d96e3fa
|
@ -70,7 +70,7 @@ protected void run() throws Exception {
|
|||
BlockSource src = BlockSource.from(in);
|
||||
ReftableReader reader = new ReftableReader(src)) {
|
||||
try (RefCursor rc = ref != null
|
||||
? reader.seekRef(ref)
|
||||
? reader.seekPrefix(ref)
|
||||
: reader.allRefs()) {
|
||||
while (rc.next()) {
|
||||
write(rc.getRef());
|
||||
|
|
|
@ -80,7 +80,7 @@ public void noTables() throws IOException {
|
|||
try (RefCursor rc = mr.seekRef(HEAD)) {
|
||||
assertFalse(rc.next());
|
||||
}
|
||||
try (RefCursor rc = mr.seekRef(R_HEADS)) {
|
||||
try (RefCursor rc = mr.seekPrefix(R_HEADS)) {
|
||||
assertFalse(rc.next());
|
||||
}
|
||||
}
|
||||
|
@ -94,7 +94,7 @@ public void oneEmptyTable() throws IOException {
|
|||
try (RefCursor rc = mr.seekRef(HEAD)) {
|
||||
assertFalse(rc.next());
|
||||
}
|
||||
try (RefCursor rc = mr.seekRef(R_HEADS)) {
|
||||
try (RefCursor rc = mr.seekPrefix(R_HEADS)) {
|
||||
assertFalse(rc.next());
|
||||
}
|
||||
}
|
||||
|
@ -108,7 +108,7 @@ public void twoEmptyTables() throws IOException {
|
|||
try (RefCursor rc = mr.seekRef(HEAD)) {
|
||||
assertFalse(rc.next());
|
||||
}
|
||||
try (RefCursor rc = mr.seekRef(R_HEADS)) {
|
||||
try (RefCursor rc = mr.seekPrefix(R_HEADS)) {
|
||||
assertFalse(rc.next());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -101,7 +101,7 @@ public void emptyTable() throws IOException {
|
|||
try (RefCursor rc = t.seekRef(HEAD)) {
|
||||
assertFalse(rc.next());
|
||||
}
|
||||
try (RefCursor rc = t.seekRef(R_HEADS)) {
|
||||
try (RefCursor rc = t.seekPrefix(R_HEADS)) {
|
||||
assertFalse(rc.next());
|
||||
}
|
||||
try (LogCursor rc = t.allLogs()) {
|
||||
|
@ -317,10 +317,10 @@ public void seekNotFound() throws IOException {
|
|||
public void namespaceNotFound() throws IOException {
|
||||
Ref exp = ref(MASTER, 1);
|
||||
ReftableReader t = read(write(exp));
|
||||
try (RefCursor rc = t.seekRef("refs/changes/")) {
|
||||
try (RefCursor rc = t.seekPrefix("refs/changes/")) {
|
||||
assertFalse(rc.next());
|
||||
}
|
||||
try (RefCursor rc = t.seekRef("refs/tags/")) {
|
||||
try (RefCursor rc = t.seekPrefix("refs/tags/")) {
|
||||
assertFalse(rc.next());
|
||||
}
|
||||
}
|
||||
|
@ -332,12 +332,12 @@ public void namespaceHeads() throws IOException {
|
|||
Ref v1 = tag(V1_0, 3, 4);
|
||||
|
||||
ReftableReader t = read(write(master, next, v1));
|
||||
try (RefCursor rc = t.seekRef("refs/tags/")) {
|
||||
try (RefCursor rc = t.seekPrefix("refs/tags/")) {
|
||||
assertTrue(rc.next());
|
||||
assertEquals(V1_0, rc.getRef().getName());
|
||||
assertFalse(rc.next());
|
||||
}
|
||||
try (RefCursor rc = t.seekRef("refs/heads/")) {
|
||||
try (RefCursor rc = t.seekPrefix("refs/heads/")) {
|
||||
assertTrue(rc.next());
|
||||
assertEquals(MASTER, rc.getRef().getName());
|
||||
|
||||
|
@ -484,7 +484,7 @@ public void onlyReflog() throws IOException {
|
|||
try (RefCursor rc = t.allRefs()) {
|
||||
assertFalse(rc.next());
|
||||
}
|
||||
try (RefCursor rc = t.seekRef("refs/heads/")) {
|
||||
try (RefCursor rc = t.seekPrefix("refs/heads/")) {
|
||||
assertFalse(rc.next());
|
||||
}
|
||||
try (LogCursor lc = t.allLogs()) {
|
||||
|
|
|
@ -45,6 +45,9 @@
|
|||
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
|
||||
|
@ -238,7 +241,8 @@ public Map<String, Ref> getRefs(String prefix) throws IOException {
|
|||
try {
|
||||
Reftable table = reader();
|
||||
try (RefCursor rc = ALL.equals(prefix) ? table.allRefs()
|
||||
: table.seekRef(prefix)) {
|
||||
: (prefix.endsWith("/") ? table.seekPrefix(prefix) //$NON-NLS-1$
|
||||
: table.seekRef(prefix))) {
|
||||
while (rc.next()) {
|
||||
Ref ref = table.resolve(rc.getRef());
|
||||
if (ref != null && ref.getObjectId() != null) {
|
||||
|
@ -254,6 +258,29 @@ public Map<String, Ref> getRefs(String prefix) throws IOException {
|
|||
return new RefMap(prefix, all.toRefList(), none, none);
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public List<Ref> getRefsByPrefix(String prefix) throws IOException {
|
||||
List<Ref> all = new ArrayList<>();
|
||||
lock.lock();
|
||||
try {
|
||||
Reftable table = reader();
|
||||
try (RefCursor rc = ALL.equals(prefix) ? table.allRefs()
|
||||
: table.seekPrefix(prefix)) {
|
||||
while (rc.next()) {
|
||||
Ref ref = table.resolve(rc.getRef());
|
||||
if (ref != null && ref.getObjectId() != null) {
|
||||
all.add(ref);
|
||||
}
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
|
||||
return Collections.unmodifiableList(all);
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public Ref peel(Ref ref) throws IOException {
|
||||
|
|
|
@ -70,7 +70,6 @@
|
|||
import org.eclipse.jgit.internal.storage.dfs.DfsObjDatabase.PackSource;
|
||||
import org.eclipse.jgit.internal.storage.io.BlockSource;
|
||||
import org.eclipse.jgit.internal.storage.pack.PackExt;
|
||||
import org.eclipse.jgit.internal.storage.reftable.RefCursor;
|
||||
import org.eclipse.jgit.internal.storage.reftable.Reftable;
|
||||
import org.eclipse.jgit.internal.storage.reftable.ReftableCompactor;
|
||||
import org.eclipse.jgit.internal.storage.reftable.ReftableConfig;
|
||||
|
@ -240,11 +239,7 @@ private boolean checkConflicting(List<ReceiveCommand> pending)
|
|||
private boolean checkExpected(Reftable table, List<ReceiveCommand> pending)
|
||||
throws IOException {
|
||||
for (ReceiveCommand cmd : pending) {
|
||||
Ref ref;
|
||||
try (RefCursor rc = table.seekRef(cmd.getRefName())) {
|
||||
ref = rc.next() ? rc.getRef() : null;
|
||||
}
|
||||
if (!matchOld(cmd, ref)) {
|
||||
if (!matchOld(cmd, table.exactRef(cmd.getRefName()))) {
|
||||
cmd.setResult(LOCK_FAILURE);
|
||||
if (isAtomic()) {
|
||||
ReceiveCommand.abort(pending);
|
||||
|
|
|
@ -111,6 +111,16 @@ public RefCursor seekRef(String name) throws IOException {
|
|||
return m;
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public RefCursor seekPrefix(String prefix) throws IOException {
|
||||
MergedRefCursor m = new MergedRefCursor();
|
||||
for (int i = 0; i < tables.length; i++) {
|
||||
m.add(new RefQueueEntry(tables[i].seekPrefix(prefix), i));
|
||||
}
|
||||
return m;
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public RefCursor byObjectId(AnyObjectId name) throws IOException {
|
||||
|
|
|
@ -108,24 +108,35 @@ public void setIncludeDeletes(boolean deletes) {
|
|||
public abstract RefCursor allRefs() throws IOException;
|
||||
|
||||
/**
|
||||
* Seek either to a reference, or a reference subtree.
|
||||
* Seek to a reference.
|
||||
* <p>
|
||||
* If {@code refName} ends with {@code "/"} the method will seek to the
|
||||
* subtree of all references starting with {@code refName} as a prefix. If
|
||||
* no references start with this prefix, an empty cursor is returned.
|
||||
* <p>
|
||||
* Otherwise exactly {@code refName} will be looked for. If present, the
|
||||
* This method will seek to the reference {@code refName}. If present, the
|
||||
* returned cursor will iterate exactly one entry. If not found, an empty
|
||||
* cursor is returned.
|
||||
*
|
||||
* @param refName
|
||||
* reference name or subtree to find.
|
||||
* reference name.
|
||||
* @return cursor to iterate; empty cursor if no references match.
|
||||
* @throws java.io.IOException
|
||||
* if references cannot be read.
|
||||
*/
|
||||
public abstract RefCursor seekRef(String refName) throws IOException;
|
||||
|
||||
/**
|
||||
* Seek references with prefix.
|
||||
* <p>
|
||||
* The method will seek all the references starting with {@code prefix} as a
|
||||
* prefix. If no references start with this prefix, an empty cursor is
|
||||
* returned.
|
||||
*
|
||||
* @param prefix
|
||||
* prefix to find.
|
||||
* @return cursor to iterate; empty cursor if no references match.
|
||||
* @throws java.io.IOException
|
||||
* if references cannot be read.
|
||||
*/
|
||||
public abstract RefCursor seekPrefix(String prefix) throws IOException;
|
||||
|
||||
/**
|
||||
* Match references pointing to a specific object.
|
||||
*
|
||||
|
@ -206,8 +217,9 @@ public Ref exactRef(String refName) throws IOException {
|
|||
* if references cannot be read.
|
||||
*/
|
||||
public boolean hasRef(String refName) throws IOException {
|
||||
try (RefCursor rc = seekRef(refName)) {
|
||||
return rc.next();
|
||||
try (RefCursor rc = seekPrefix(refName)) {
|
||||
return rc.next() && (refName.endsWith("/") //$NON-NLS-1$
|
||||
|| refName.equals(rc.getRef().getName()));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -183,9 +183,18 @@ public RefCursor seekRef(String refName) throws IOException {
|
|||
initRefIndex();
|
||||
|
||||
byte[] key = refName.getBytes(CHARSET);
|
||||
boolean prefix = key[key.length - 1] == '/';
|
||||
RefCursorImpl i = new RefCursorImpl(refEnd, key, false);
|
||||
i.block = seek(REF_BLOCK_TYPE, key, refIndex, 0, refEnd);
|
||||
return i;
|
||||
}
|
||||
|
||||
RefCursorImpl i = new RefCursorImpl(refEnd, key, prefix);
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public RefCursor seekPrefix(String prefix) throws IOException {
|
||||
initRefIndex();
|
||||
|
||||
byte[] key = prefix.getBytes(CHARSET);
|
||||
RefCursorImpl i = new RefCursorImpl(refEnd, key, true);
|
||||
i.block = seek(REF_BLOCK_TYPE, key, refIndex, 0, refEnd);
|
||||
return i;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue