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);
|
BlockSource src = BlockSource.from(in);
|
||||||
ReftableReader reader = new ReftableReader(src)) {
|
ReftableReader reader = new ReftableReader(src)) {
|
||||||
try (RefCursor rc = ref != null
|
try (RefCursor rc = ref != null
|
||||||
? reader.seekRef(ref)
|
? reader.seekPrefix(ref)
|
||||||
: reader.allRefs()) {
|
: reader.allRefs()) {
|
||||||
while (rc.next()) {
|
while (rc.next()) {
|
||||||
write(rc.getRef());
|
write(rc.getRef());
|
||||||
|
|
|
@ -80,7 +80,7 @@ public void noTables() throws IOException {
|
||||||
try (RefCursor rc = mr.seekRef(HEAD)) {
|
try (RefCursor rc = mr.seekRef(HEAD)) {
|
||||||
assertFalse(rc.next());
|
assertFalse(rc.next());
|
||||||
}
|
}
|
||||||
try (RefCursor rc = mr.seekRef(R_HEADS)) {
|
try (RefCursor rc = mr.seekPrefix(R_HEADS)) {
|
||||||
assertFalse(rc.next());
|
assertFalse(rc.next());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -94,7 +94,7 @@ public void oneEmptyTable() throws IOException {
|
||||||
try (RefCursor rc = mr.seekRef(HEAD)) {
|
try (RefCursor rc = mr.seekRef(HEAD)) {
|
||||||
assertFalse(rc.next());
|
assertFalse(rc.next());
|
||||||
}
|
}
|
||||||
try (RefCursor rc = mr.seekRef(R_HEADS)) {
|
try (RefCursor rc = mr.seekPrefix(R_HEADS)) {
|
||||||
assertFalse(rc.next());
|
assertFalse(rc.next());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -108,7 +108,7 @@ public void twoEmptyTables() throws IOException {
|
||||||
try (RefCursor rc = mr.seekRef(HEAD)) {
|
try (RefCursor rc = mr.seekRef(HEAD)) {
|
||||||
assertFalse(rc.next());
|
assertFalse(rc.next());
|
||||||
}
|
}
|
||||||
try (RefCursor rc = mr.seekRef(R_HEADS)) {
|
try (RefCursor rc = mr.seekPrefix(R_HEADS)) {
|
||||||
assertFalse(rc.next());
|
assertFalse(rc.next());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -101,7 +101,7 @@ public void emptyTable() throws IOException {
|
||||||
try (RefCursor rc = t.seekRef(HEAD)) {
|
try (RefCursor rc = t.seekRef(HEAD)) {
|
||||||
assertFalse(rc.next());
|
assertFalse(rc.next());
|
||||||
}
|
}
|
||||||
try (RefCursor rc = t.seekRef(R_HEADS)) {
|
try (RefCursor rc = t.seekPrefix(R_HEADS)) {
|
||||||
assertFalse(rc.next());
|
assertFalse(rc.next());
|
||||||
}
|
}
|
||||||
try (LogCursor rc = t.allLogs()) {
|
try (LogCursor rc = t.allLogs()) {
|
||||||
|
@ -317,10 +317,10 @@ public void seekNotFound() throws IOException {
|
||||||
public void namespaceNotFound() throws IOException {
|
public void namespaceNotFound() throws IOException {
|
||||||
Ref exp = ref(MASTER, 1);
|
Ref exp = ref(MASTER, 1);
|
||||||
ReftableReader t = read(write(exp));
|
ReftableReader t = read(write(exp));
|
||||||
try (RefCursor rc = t.seekRef("refs/changes/")) {
|
try (RefCursor rc = t.seekPrefix("refs/changes/")) {
|
||||||
assertFalse(rc.next());
|
assertFalse(rc.next());
|
||||||
}
|
}
|
||||||
try (RefCursor rc = t.seekRef("refs/tags/")) {
|
try (RefCursor rc = t.seekPrefix("refs/tags/")) {
|
||||||
assertFalse(rc.next());
|
assertFalse(rc.next());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -332,12 +332,12 @@ public void namespaceHeads() throws IOException {
|
||||||
Ref v1 = tag(V1_0, 3, 4);
|
Ref v1 = tag(V1_0, 3, 4);
|
||||||
|
|
||||||
ReftableReader t = read(write(master, next, v1));
|
ReftableReader t = read(write(master, next, v1));
|
||||||
try (RefCursor rc = t.seekRef("refs/tags/")) {
|
try (RefCursor rc = t.seekPrefix("refs/tags/")) {
|
||||||
assertTrue(rc.next());
|
assertTrue(rc.next());
|
||||||
assertEquals(V1_0, rc.getRef().getName());
|
assertEquals(V1_0, rc.getRef().getName());
|
||||||
assertFalse(rc.next());
|
assertFalse(rc.next());
|
||||||
}
|
}
|
||||||
try (RefCursor rc = t.seekRef("refs/heads/")) {
|
try (RefCursor rc = t.seekPrefix("refs/heads/")) {
|
||||||
assertTrue(rc.next());
|
assertTrue(rc.next());
|
||||||
assertEquals(MASTER, rc.getRef().getName());
|
assertEquals(MASTER, rc.getRef().getName());
|
||||||
|
|
||||||
|
@ -484,7 +484,7 @@ public void onlyReflog() throws IOException {
|
||||||
try (RefCursor rc = t.allRefs()) {
|
try (RefCursor rc = t.allRefs()) {
|
||||||
assertFalse(rc.next());
|
assertFalse(rc.next());
|
||||||
}
|
}
|
||||||
try (RefCursor rc = t.seekRef("refs/heads/")) {
|
try (RefCursor rc = t.seekPrefix("refs/heads/")) {
|
||||||
assertFalse(rc.next());
|
assertFalse(rc.next());
|
||||||
}
|
}
|
||||||
try (LogCursor lc = t.allLogs()) {
|
try (LogCursor lc = t.allLogs()) {
|
||||||
|
|
|
@ -45,6 +45,9 @@
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.concurrent.locks.ReentrantLock;
|
import java.util.concurrent.locks.ReentrantLock;
|
||||||
|
|
||||||
|
@ -238,7 +241,8 @@ public Map<String, Ref> getRefs(String prefix) throws IOException {
|
||||||
try {
|
try {
|
||||||
Reftable table = reader();
|
Reftable table = reader();
|
||||||
try (RefCursor rc = ALL.equals(prefix) ? table.allRefs()
|
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()) {
|
while (rc.next()) {
|
||||||
Ref ref = table.resolve(rc.getRef());
|
Ref ref = table.resolve(rc.getRef());
|
||||||
if (ref != null && ref.getObjectId() != null) {
|
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);
|
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} */
|
/** {@inheritDoc} */
|
||||||
@Override
|
@Override
|
||||||
public Ref peel(Ref ref) throws IOException {
|
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.dfs.DfsObjDatabase.PackSource;
|
||||||
import org.eclipse.jgit.internal.storage.io.BlockSource;
|
import org.eclipse.jgit.internal.storage.io.BlockSource;
|
||||||
import org.eclipse.jgit.internal.storage.pack.PackExt;
|
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.Reftable;
|
||||||
import org.eclipse.jgit.internal.storage.reftable.ReftableCompactor;
|
import org.eclipse.jgit.internal.storage.reftable.ReftableCompactor;
|
||||||
import org.eclipse.jgit.internal.storage.reftable.ReftableConfig;
|
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)
|
private boolean checkExpected(Reftable table, List<ReceiveCommand> pending)
|
||||||
throws IOException {
|
throws IOException {
|
||||||
for (ReceiveCommand cmd : pending) {
|
for (ReceiveCommand cmd : pending) {
|
||||||
Ref ref;
|
if (!matchOld(cmd, table.exactRef(cmd.getRefName()))) {
|
||||||
try (RefCursor rc = table.seekRef(cmd.getRefName())) {
|
|
||||||
ref = rc.next() ? rc.getRef() : null;
|
|
||||||
}
|
|
||||||
if (!matchOld(cmd, ref)) {
|
|
||||||
cmd.setResult(LOCK_FAILURE);
|
cmd.setResult(LOCK_FAILURE);
|
||||||
if (isAtomic()) {
|
if (isAtomic()) {
|
||||||
ReceiveCommand.abort(pending);
|
ReceiveCommand.abort(pending);
|
||||||
|
|
|
@ -111,6 +111,16 @@ public RefCursor seekRef(String name) throws IOException {
|
||||||
return m;
|
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} */
|
/** {@inheritDoc} */
|
||||||
@Override
|
@Override
|
||||||
public RefCursor byObjectId(AnyObjectId name) throws IOException {
|
public RefCursor byObjectId(AnyObjectId name) throws IOException {
|
||||||
|
|
|
@ -108,24 +108,35 @@ public void setIncludeDeletes(boolean deletes) {
|
||||||
public abstract RefCursor allRefs() throws IOException;
|
public abstract RefCursor allRefs() throws IOException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Seek either to a reference, or a reference subtree.
|
* Seek to a reference.
|
||||||
* <p>
|
* <p>
|
||||||
* If {@code refName} ends with {@code "/"} the method will seek to the
|
* This method will seek to the reference {@code refName}. If present, 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
|
|
||||||
* returned cursor will iterate exactly one entry. If not found, an empty
|
* returned cursor will iterate exactly one entry. If not found, an empty
|
||||||
* cursor is returned.
|
* cursor is returned.
|
||||||
*
|
*
|
||||||
* @param refName
|
* @param refName
|
||||||
* reference name or subtree to find.
|
* reference name.
|
||||||
* @return cursor to iterate; empty cursor if no references match.
|
* @return cursor to iterate; empty cursor if no references match.
|
||||||
* @throws java.io.IOException
|
* @throws java.io.IOException
|
||||||
* if references cannot be read.
|
* if references cannot be read.
|
||||||
*/
|
*/
|
||||||
public abstract RefCursor seekRef(String refName) throws IOException;
|
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.
|
* Match references pointing to a specific object.
|
||||||
*
|
*
|
||||||
|
@ -206,8 +217,9 @@ public Ref exactRef(String refName) throws IOException {
|
||||||
* if references cannot be read.
|
* if references cannot be read.
|
||||||
*/
|
*/
|
||||||
public boolean hasRef(String refName) throws IOException {
|
public boolean hasRef(String refName) throws IOException {
|
||||||
try (RefCursor rc = seekRef(refName)) {
|
try (RefCursor rc = seekPrefix(refName)) {
|
||||||
return rc.next();
|
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();
|
initRefIndex();
|
||||||
|
|
||||||
byte[] key = refName.getBytes(CHARSET);
|
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);
|
i.block = seek(REF_BLOCK_TYPE, key, refIndex, 0, refEnd);
|
||||||
return i;
|
return i;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue