Merge "Add staleness checker for groups in NoteDb"
This commit is contained in:
@@ -21,6 +21,7 @@ import com.google.gerrit.reviewdb.client.Account;
|
|||||||
import com.google.gerrit.reviewdb.client.AccountGroup;
|
import com.google.gerrit.reviewdb.client.AccountGroup;
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
import java.sql.Timestamp;
|
import java.sql.Timestamp;
|
||||||
|
import org.eclipse.jgit.lib.ObjectId;
|
||||||
|
|
||||||
@AutoValue
|
@AutoValue
|
||||||
public abstract class InternalGroup implements Serializable {
|
public abstract class InternalGroup implements Serializable {
|
||||||
@@ -30,6 +31,14 @@ public abstract class InternalGroup implements Serializable {
|
|||||||
AccountGroup accountGroup,
|
AccountGroup accountGroup,
|
||||||
ImmutableSet<Account.Id> members,
|
ImmutableSet<Account.Id> members,
|
||||||
ImmutableSet<AccountGroup.UUID> subgroups) {
|
ImmutableSet<AccountGroup.UUID> subgroups) {
|
||||||
|
return create(accountGroup, members, subgroups, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static InternalGroup create(
|
||||||
|
AccountGroup accountGroup,
|
||||||
|
ImmutableSet<Account.Id> members,
|
||||||
|
ImmutableSet<AccountGroup.UUID> subgroups,
|
||||||
|
ObjectId refState) {
|
||||||
return builder()
|
return builder()
|
||||||
.setId(accountGroup.getId())
|
.setId(accountGroup.getId())
|
||||||
.setNameKey(accountGroup.getNameKey())
|
.setNameKey(accountGroup.getNameKey())
|
||||||
@@ -40,6 +49,7 @@ public abstract class InternalGroup implements Serializable {
|
|||||||
.setCreatedOn(accountGroup.getCreatedOn())
|
.setCreatedOn(accountGroup.getCreatedOn())
|
||||||
.setMembers(members)
|
.setMembers(members)
|
||||||
.setSubgroups(subgroups)
|
.setSubgroups(subgroups)
|
||||||
|
.setRefState(refState)
|
||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -66,6 +76,11 @@ public abstract class InternalGroup implements Serializable {
|
|||||||
|
|
||||||
public abstract ImmutableSet<AccountGroup.UUID> getSubgroups();
|
public abstract ImmutableSet<AccountGroup.UUID> getSubgroups();
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public abstract ObjectId getRefState();
|
||||||
|
|
||||||
|
public abstract Builder toBuilder();
|
||||||
|
|
||||||
public static Builder builder() {
|
public static Builder builder() {
|
||||||
return new AutoValue_InternalGroup.Builder();
|
return new AutoValue_InternalGroup.Builder();
|
||||||
}
|
}
|
||||||
@@ -90,6 +105,8 @@ public abstract class InternalGroup implements Serializable {
|
|||||||
|
|
||||||
public abstract Builder setSubgroups(ImmutableSet<AccountGroup.UUID> subgroups);
|
public abstract Builder setSubgroups(ImmutableSet<AccountGroup.UUID> subgroups);
|
||||||
|
|
||||||
|
public abstract Builder setRefState(ObjectId refState);
|
||||||
|
|
||||||
public abstract InternalGroup build();
|
public abstract InternalGroup build();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ import com.google.common.collect.Streams;
|
|||||||
import com.google.gerrit.reviewdb.client.Account;
|
import com.google.gerrit.reviewdb.client.Account;
|
||||||
import com.google.gerrit.reviewdb.client.AccountGroup;
|
import com.google.gerrit.reviewdb.client.AccountGroup;
|
||||||
import com.google.gerrit.reviewdb.client.RefNames;
|
import com.google.gerrit.reviewdb.client.RefNames;
|
||||||
|
import com.google.gerrit.server.git.MetaDataUpdate;
|
||||||
import com.google.gerrit.server.git.VersionedMetaData;
|
import com.google.gerrit.server.git.VersionedMetaData;
|
||||||
import com.google.gerrit.server.group.InternalGroup;
|
import com.google.gerrit.server.group.InternalGroup;
|
||||||
import com.google.gwtorm.server.OrmDuplicateKeyException;
|
import com.google.gwtorm.server.OrmDuplicateKeyException;
|
||||||
@@ -40,6 +41,7 @@ import java.util.stream.Stream;
|
|||||||
import org.eclipse.jgit.errors.ConfigInvalidException;
|
import org.eclipse.jgit.errors.ConfigInvalidException;
|
||||||
import org.eclipse.jgit.lib.CommitBuilder;
|
import org.eclipse.jgit.lib.CommitBuilder;
|
||||||
import org.eclipse.jgit.lib.Config;
|
import org.eclipse.jgit.lib.Config;
|
||||||
|
import org.eclipse.jgit.lib.ObjectId;
|
||||||
import org.eclipse.jgit.lib.PersonIdent;
|
import org.eclipse.jgit.lib.PersonIdent;
|
||||||
import org.eclipse.jgit.lib.Repository;
|
import org.eclipse.jgit.lib.Repository;
|
||||||
import org.eclipse.jgit.revwalk.RevCommit;
|
import org.eclipse.jgit.revwalk.RevCommit;
|
||||||
@@ -123,7 +125,9 @@ public class GroupConfig extends VersionedMetaData {
|
|||||||
Config config = readConfig(GROUP_CONFIG_FILE);
|
Config config = readConfig(GROUP_CONFIG_FILE);
|
||||||
ImmutableSet<Account.Id> members = readMembers();
|
ImmutableSet<Account.Id> members = readMembers();
|
||||||
ImmutableSet<AccountGroup.UUID> subgroups = readSubgroups();
|
ImmutableSet<AccountGroup.UUID> subgroups = readSubgroups();
|
||||||
loadedGroup = Optional.of(createFrom(groupUuid, config, members, subgroups, createdOn));
|
loadedGroup =
|
||||||
|
Optional.of(
|
||||||
|
createFrom(groupUuid, config, members, subgroups, createdOn, revision.toObjectId()));
|
||||||
}
|
}
|
||||||
|
|
||||||
isLoaded = true;
|
isLoaded = true;
|
||||||
@@ -134,7 +138,8 @@ public class GroupConfig extends VersionedMetaData {
|
|||||||
Config config,
|
Config config,
|
||||||
ImmutableSet<Account.Id> members,
|
ImmutableSet<Account.Id> members,
|
||||||
ImmutableSet<AccountGroup.UUID> subgroups,
|
ImmutableSet<AccountGroup.UUID> subgroups,
|
||||||
Timestamp createdOn) {
|
Timestamp createdOn,
|
||||||
|
ObjectId refState) {
|
||||||
InternalGroup.Builder group = InternalGroup.builder();
|
InternalGroup.Builder group = InternalGroup.builder();
|
||||||
group.setGroupUUID(groupUuid);
|
group.setGroupUUID(groupUuid);
|
||||||
Arrays.stream(GroupConfigEntry.values())
|
Arrays.stream(GroupConfigEntry.values())
|
||||||
@@ -142,9 +147,17 @@ public class GroupConfig extends VersionedMetaData {
|
|||||||
group.setMembers(members);
|
group.setMembers(members);
|
||||||
group.setSubgroups(subgroups);
|
group.setSubgroups(subgroups);
|
||||||
group.setCreatedOn(createdOn);
|
group.setCreatedOn(createdOn);
|
||||||
|
group.setRefState(refState);
|
||||||
return group.build();
|
return group.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public RevCommit commit(MetaDataUpdate update) throws IOException {
|
||||||
|
RevCommit c = super.commit(update);
|
||||||
|
loadedGroup = Optional.of(loadedGroup.get().toBuilder().setRefState(c.toObjectId()).build());
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean onSave(CommitBuilder commit) throws IOException, ConfigInvalidException {
|
protected boolean onSave(CommitBuilder commit) throws IOException, ConfigInvalidException {
|
||||||
checkLoaded();
|
checkLoaded();
|
||||||
@@ -184,7 +197,8 @@ public class GroupConfig extends VersionedMetaData {
|
|||||||
config,
|
config,
|
||||||
updatedMembers.orElse(originalMembers),
|
updatedMembers.orElse(originalMembers),
|
||||||
updatedSubgroups.orElse(originalSubgroups),
|
updatedSubgroups.orElse(originalSubgroups),
|
||||||
createdOn));
|
createdOn,
|
||||||
|
null));
|
||||||
groupCreation = Optional.empty();
|
groupCreation = Optional.empty();
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
@@ -48,6 +48,7 @@ import com.google.gerrit.server.git.GitRepositoryManager;
|
|||||||
import com.google.gerrit.server.git.MetaDataUpdate;
|
import com.google.gerrit.server.git.MetaDataUpdate;
|
||||||
import com.google.gerrit.server.git.RenameGroupOp;
|
import com.google.gerrit.server.git.RenameGroupOp;
|
||||||
import com.google.gerrit.server.group.InternalGroup;
|
import com.google.gerrit.server.group.InternalGroup;
|
||||||
|
import com.google.gerrit.server.group.InternalGroup.Builder;
|
||||||
import com.google.gerrit.server.update.RefUpdateUtil;
|
import com.google.gerrit.server.update.RefUpdateUtil;
|
||||||
import com.google.gwtorm.server.OrmDuplicateKeyException;
|
import com.google.gwtorm.server.OrmDuplicateKeyException;
|
||||||
import com.google.gwtorm.server.OrmException;
|
import com.google.gwtorm.server.OrmException;
|
||||||
@@ -62,6 +63,7 @@ import java.util.concurrent.TimeUnit;
|
|||||||
import org.eclipse.jgit.errors.ConfigInvalidException;
|
import org.eclipse.jgit.errors.ConfigInvalidException;
|
||||||
import org.eclipse.jgit.lib.BatchRefUpdate;
|
import org.eclipse.jgit.lib.BatchRefUpdate;
|
||||||
import org.eclipse.jgit.lib.Config;
|
import org.eclipse.jgit.lib.Config;
|
||||||
|
import org.eclipse.jgit.lib.ObjectId;
|
||||||
import org.eclipse.jgit.lib.PersonIdent;
|
import org.eclipse.jgit.lib.PersonIdent;
|
||||||
import org.eclipse.jgit.lib.Repository;
|
import org.eclipse.jgit.lib.Repository;
|
||||||
import org.eclipse.jgit.revwalk.RevWalk;
|
import org.eclipse.jgit.revwalk.RevWalk;
|
||||||
@@ -264,7 +266,10 @@ public class GroupsUpdate {
|
|||||||
AccountGroup group = createAccountGroup(groupCreation);
|
AccountGroup group = createAccountGroup(groupCreation);
|
||||||
UpdateResult updateResult = updateGroupInReviewDb(db, group, groupUpdate);
|
UpdateResult updateResult = updateGroupInReviewDb(db, group, groupUpdate);
|
||||||
return InternalGroup.create(
|
return InternalGroup.create(
|
||||||
group, updateResult.getModifiedMembers(), updateResult.getModifiedSubgroups());
|
group,
|
||||||
|
updateResult.getModifiedMembers(),
|
||||||
|
updateResult.getModifiedSubgroups(),
|
||||||
|
updateResult.getRefState());
|
||||||
}
|
}
|
||||||
|
|
||||||
public static AccountGroup createAccountGroup(
|
public static AccountGroup createAccountGroup(
|
||||||
@@ -504,7 +509,8 @@ public class GroupsUpdate {
|
|||||||
.setGroupId(updatedGroup.getId())
|
.setGroupId(updatedGroup.getId())
|
||||||
.setGroupName(updatedGroup.getNameKey())
|
.setGroupName(updatedGroup.getNameKey())
|
||||||
.setModifiedMembers(modifiedMembers)
|
.setModifiedMembers(modifiedMembers)
|
||||||
.setModifiedSubgroups(modifiedSubgroups);
|
.setModifiedSubgroups(modifiedSubgroups)
|
||||||
|
.setRefState(updatedGroup.getRefState());
|
||||||
if (!Objects.equals(originalGroup.getNameKey(), updatedGroup.getNameKey())) {
|
if (!Objects.equals(originalGroup.getNameKey(), updatedGroup.getNameKey())) {
|
||||||
resultBuilder.setPreviousGroupName(originalGroup.getNameKey());
|
resultBuilder.setPreviousGroupName(originalGroup.getNameKey());
|
||||||
}
|
}
|
||||||
@@ -637,6 +643,9 @@ public class GroupsUpdate {
|
|||||||
|
|
||||||
abstract ImmutableSet<AccountGroup.UUID> getModifiedSubgroups();
|
abstract ImmutableSet<AccountGroup.UUID> getModifiedSubgroups();
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public abstract ObjectId getRefState();
|
||||||
|
|
||||||
static Builder builder() {
|
static Builder builder() {
|
||||||
return new AutoValue_GroupsUpdate_UpdateResult.Builder();
|
return new AutoValue_GroupsUpdate_UpdateResult.Builder();
|
||||||
}
|
}
|
||||||
@@ -655,6 +664,8 @@ public class GroupsUpdate {
|
|||||||
|
|
||||||
abstract Builder setModifiedSubgroups(Set<AccountGroup.UUID> modifiedSubgroups);
|
abstract Builder setModifiedSubgroups(Set<AccountGroup.UUID> modifiedSubgroups);
|
||||||
|
|
||||||
|
public abstract Builder setRefState(ObjectId refState);
|
||||||
|
|
||||||
abstract UpdateResult build();
|
abstract UpdateResult build();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ import static com.google.gerrit.server.index.change.ChangeField.CHANGE;
|
|||||||
import static com.google.gerrit.server.index.change.ChangeField.LEGACY_ID;
|
import static com.google.gerrit.server.index.change.ChangeField.LEGACY_ID;
|
||||||
import static com.google.gerrit.server.index.change.ChangeField.PROJECT;
|
import static com.google.gerrit.server.index.change.ChangeField.PROJECT;
|
||||||
|
|
||||||
|
import com.google.common.base.Function;
|
||||||
import com.google.common.collect.ImmutableMap;
|
import com.google.common.collect.ImmutableMap;
|
||||||
import com.google.common.collect.ImmutableSet;
|
import com.google.common.collect.ImmutableSet;
|
||||||
import com.google.common.collect.Sets;
|
import com.google.common.collect.Sets;
|
||||||
@@ -30,12 +31,27 @@ import com.google.gerrit.server.index.project.ProjectField;
|
|||||||
import com.google.gerrit.server.query.change.SingleGroupUser;
|
import com.google.gerrit.server.query.change.SingleGroupUser;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
import java.util.concurrent.ExecutionException;
|
||||||
import org.eclipse.jgit.errors.ConfigInvalidException;
|
import org.eclipse.jgit.errors.ConfigInvalidException;
|
||||||
|
|
||||||
public final class IndexUtils {
|
public final class IndexUtils {
|
||||||
public static final ImmutableMap<String, String> CUSTOM_CHAR_MAPPING =
|
public static final ImmutableMap<String, String> CUSTOM_CHAR_MAPPING =
|
||||||
ImmutableMap.of("_", " ", ".", " ");
|
ImmutableMap.of("_", " ", ".", " ");
|
||||||
|
|
||||||
|
public static final Function<Exception, IOException> MAPPER =
|
||||||
|
new Function<Exception, IOException>() {
|
||||||
|
@Override
|
||||||
|
public IOException apply(Exception in) {
|
||||||
|
if (in instanceof IOException) {
|
||||||
|
return (IOException) in;
|
||||||
|
} else if (in instanceof ExecutionException && in.getCause() instanceof IOException) {
|
||||||
|
return (IOException) in.getCause();
|
||||||
|
} else {
|
||||||
|
return new IOException(in);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
public static void setReady(SitePaths sitePaths, String name, int version, boolean ready)
|
public static void setReady(SitePaths sitePaths, String name, int version, boolean ready)
|
||||||
throws IOException {
|
throws IOException {
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -17,7 +17,6 @@ package com.google.gerrit.server.index.change;
|
|||||||
import static com.google.gerrit.server.extensions.events.EventUtil.logEventListenerError;
|
import static com.google.gerrit.server.extensions.events.EventUtil.logEventListenerError;
|
||||||
import static com.google.gerrit.server.git.QueueProvider.QueueType.BATCH;
|
import static com.google.gerrit.server.git.QueueProvider.QueueType.BATCH;
|
||||||
|
|
||||||
import com.google.common.base.Function;
|
|
||||||
import com.google.common.util.concurrent.Atomics;
|
import com.google.common.util.concurrent.Atomics;
|
||||||
import com.google.common.util.concurrent.Futures;
|
import com.google.common.util.concurrent.Futures;
|
||||||
import com.google.common.util.concurrent.ListenableFuture;
|
import com.google.common.util.concurrent.ListenableFuture;
|
||||||
@@ -31,6 +30,7 @@ import com.google.gerrit.reviewdb.server.ReviewDb;
|
|||||||
import com.google.gerrit.server.CurrentUser;
|
import com.google.gerrit.server.CurrentUser;
|
||||||
import com.google.gerrit.server.config.GerritServerConfig;
|
import com.google.gerrit.server.config.GerritServerConfig;
|
||||||
import com.google.gerrit.server.index.IndexExecutor;
|
import com.google.gerrit.server.index.IndexExecutor;
|
||||||
|
import com.google.gerrit.server.index.IndexUtils;
|
||||||
import com.google.gerrit.server.notedb.ChangeNotes;
|
import com.google.gerrit.server.notedb.ChangeNotes;
|
||||||
import com.google.gerrit.server.notedb.NotesMigration;
|
import com.google.gerrit.server.notedb.NotesMigration;
|
||||||
import com.google.gerrit.server.query.change.ChangeData;
|
import com.google.gerrit.server.query.change.ChangeData;
|
||||||
@@ -50,7 +50,6 @@ import java.util.Collection;
|
|||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.concurrent.Callable;
|
import java.util.concurrent.Callable;
|
||||||
import java.util.concurrent.ExecutionException;
|
|
||||||
import java.util.concurrent.Future;
|
import java.util.concurrent.Future;
|
||||||
import java.util.concurrent.atomic.AtomicReference;
|
import java.util.concurrent.atomic.AtomicReference;
|
||||||
import org.eclipse.jgit.lib.Config;
|
import org.eclipse.jgit.lib.Config;
|
||||||
@@ -79,23 +78,9 @@ public class ChangeIndexer {
|
|||||||
// ExecutionException, so we can reuse the same mapper as for a single
|
// ExecutionException, so we can reuse the same mapper as for a single
|
||||||
// future. Assume the actual contents of the exception are not useful to
|
// future. Assume the actual contents of the exception are not useful to
|
||||||
// callers. All exceptions are already logged by IndexTask.
|
// callers. All exceptions are already logged by IndexTask.
|
||||||
return Futures.makeChecked(Futures.allAsList(futures), MAPPER);
|
return Futures.makeChecked(Futures.allAsList(futures), IndexUtils.MAPPER);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final Function<Exception, IOException> MAPPER =
|
|
||||||
new Function<Exception, IOException>() {
|
|
||||||
@Override
|
|
||||||
public IOException apply(Exception in) {
|
|
||||||
if (in instanceof IOException) {
|
|
||||||
return (IOException) in;
|
|
||||||
} else if (in instanceof ExecutionException && in.getCause() instanceof IOException) {
|
|
||||||
return (IOException) in.getCause();
|
|
||||||
} else {
|
|
||||||
return new IOException(in);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
private final ChangeIndexCollection indexes;
|
private final ChangeIndexCollection indexes;
|
||||||
private final ChangeIndex index;
|
private final ChangeIndex index;
|
||||||
private final SchemaFactory<ReviewDb> schemaFactory;
|
private final SchemaFactory<ReviewDb> schemaFactory;
|
||||||
@@ -335,7 +320,8 @@ public class ChangeIndexer {
|
|||||||
@SuppressWarnings("deprecation")
|
@SuppressWarnings("deprecation")
|
||||||
private static <T> com.google.common.util.concurrent.CheckedFuture<T, IOException> submit(
|
private static <T> com.google.common.util.concurrent.CheckedFuture<T, IOException> submit(
|
||||||
Callable<T> task, ListeningExecutorService executor) {
|
Callable<T> task, ListeningExecutorService executor) {
|
||||||
return Futures.makeChecked(Futures.nonCancellationPropagating(executor.submit(task)), MAPPER);
|
return Futures.makeChecked(
|
||||||
|
Futures.nonCancellationPropagating(executor.submit(task)), IndexUtils.MAPPER);
|
||||||
}
|
}
|
||||||
|
|
||||||
private abstract class AbstractIndexTask<T> implements Callable<T> {
|
private abstract class AbstractIndexTask<T> implements Callable<T> {
|
||||||
|
|||||||
@@ -19,14 +19,18 @@ import static com.google.gerrit.index.FieldDef.exact;
|
|||||||
import static com.google.gerrit.index.FieldDef.fullText;
|
import static com.google.gerrit.index.FieldDef.fullText;
|
||||||
import static com.google.gerrit.index.FieldDef.integer;
|
import static com.google.gerrit.index.FieldDef.integer;
|
||||||
import static com.google.gerrit.index.FieldDef.prefix;
|
import static com.google.gerrit.index.FieldDef.prefix;
|
||||||
|
import static com.google.gerrit.index.FieldDef.storedOnly;
|
||||||
import static com.google.gerrit.index.FieldDef.timestamp;
|
import static com.google.gerrit.index.FieldDef.timestamp;
|
||||||
|
|
||||||
|
import com.google.common.base.MoreObjects;
|
||||||
import com.google.gerrit.index.FieldDef;
|
import com.google.gerrit.index.FieldDef;
|
||||||
import com.google.gerrit.index.SchemaUtil;
|
import com.google.gerrit.index.SchemaUtil;
|
||||||
import com.google.gerrit.reviewdb.client.Account;
|
import com.google.gerrit.reviewdb.client.Account;
|
||||||
import com.google.gerrit.reviewdb.client.AccountGroup;
|
import com.google.gerrit.reviewdb.client.AccountGroup;
|
||||||
import com.google.gerrit.server.group.InternalGroup;
|
import com.google.gerrit.server.group.InternalGroup;
|
||||||
import java.sql.Timestamp;
|
import java.sql.Timestamp;
|
||||||
|
import org.eclipse.jgit.lib.Constants;
|
||||||
|
import org.eclipse.jgit.lib.ObjectId;
|
||||||
|
|
||||||
/** Secondary index schemas for groups. */
|
/** Secondary index schemas for groups. */
|
||||||
public class GroupField {
|
public class GroupField {
|
||||||
@@ -72,4 +76,14 @@ public class GroupField {
|
|||||||
.buildRepeatable(
|
.buildRepeatable(
|
||||||
g ->
|
g ->
|
||||||
g.getSubgroups().stream().map(AccountGroup.UUID::get).collect(toImmutableList()));
|
g.getSubgroups().stream().map(AccountGroup.UUID::get).collect(toImmutableList()));
|
||||||
|
|
||||||
|
/** ObjectId of HEAD:refs/groups/<UUID>. */
|
||||||
|
public static final FieldDef<InternalGroup, byte[]> REF_STATE =
|
||||||
|
storedOnly("ref_state")
|
||||||
|
.build(
|
||||||
|
g -> {
|
||||||
|
byte[] a = new byte[Constants.OBJECT_ID_STRING_LENGTH];
|
||||||
|
MoreObjects.firstNonNull(g.getRefState(), ObjectId.zeroId()).copyTo(a, 0);
|
||||||
|
return a;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,20 +14,30 @@
|
|||||||
|
|
||||||
package com.google.gerrit.server.index.group;
|
package com.google.gerrit.server.index.group;
|
||||||
|
|
||||||
|
import static com.google.gerrit.server.git.QueueProvider.QueueType.BATCH;
|
||||||
|
|
||||||
import com.google.common.collect.ImmutableSet;
|
import com.google.common.collect.ImmutableSet;
|
||||||
|
import com.google.common.util.concurrent.Futures;
|
||||||
|
import com.google.common.util.concurrent.ListeningExecutorService;
|
||||||
import com.google.gerrit.common.Nullable;
|
import com.google.gerrit.common.Nullable;
|
||||||
import com.google.gerrit.extensions.events.GroupIndexedListener;
|
import com.google.gerrit.extensions.events.GroupIndexedListener;
|
||||||
import com.google.gerrit.extensions.registration.DynamicSet;
|
import com.google.gerrit.extensions.registration.DynamicSet;
|
||||||
import com.google.gerrit.index.Index;
|
import com.google.gerrit.index.Index;
|
||||||
import com.google.gerrit.reviewdb.client.AccountGroup;
|
import com.google.gerrit.reviewdb.client.AccountGroup;
|
||||||
import com.google.gerrit.server.account.GroupCache;
|
import com.google.gerrit.server.account.GroupCache;
|
||||||
|
import com.google.gerrit.server.config.GerritServerConfig;
|
||||||
import com.google.gerrit.server.group.InternalGroup;
|
import com.google.gerrit.server.group.InternalGroup;
|
||||||
|
import com.google.gerrit.server.index.IndexExecutor;
|
||||||
|
import com.google.gerrit.server.index.IndexUtils;
|
||||||
import com.google.inject.assistedinject.Assisted;
|
import com.google.inject.assistedinject.Assisted;
|
||||||
import com.google.inject.assistedinject.AssistedInject;
|
import com.google.inject.assistedinject.AssistedInject;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
import java.util.concurrent.Callable;
|
||||||
|
import java.util.concurrent.Future;
|
||||||
|
import org.eclipse.jgit.lib.Config;
|
||||||
|
|
||||||
public class GroupIndexerImpl implements GroupIndexer {
|
public class GroupIndexerImpl implements GroupIndexer {
|
||||||
public interface Factory {
|
public interface Factory {
|
||||||
@@ -39,16 +49,25 @@ public class GroupIndexerImpl implements GroupIndexer {
|
|||||||
private final GroupCache groupCache;
|
private final GroupCache groupCache;
|
||||||
private final DynamicSet<GroupIndexedListener> indexedListener;
|
private final DynamicSet<GroupIndexedListener> indexedListener;
|
||||||
private final GroupIndexCollection indexes;
|
private final GroupIndexCollection indexes;
|
||||||
|
private final StalenessChecker stalenessChecker;
|
||||||
|
private final boolean autoReindexIfStale;
|
||||||
private final GroupIndex index;
|
private final GroupIndex index;
|
||||||
|
private final ListeningExecutorService batchExecutor;
|
||||||
|
|
||||||
@AssistedInject
|
@AssistedInject
|
||||||
GroupIndexerImpl(
|
GroupIndexerImpl(
|
||||||
GroupCache groupCache,
|
GroupCache groupCache,
|
||||||
DynamicSet<GroupIndexedListener> indexedListener,
|
DynamicSet<GroupIndexedListener> indexedListener,
|
||||||
|
StalenessChecker stalenessChecker,
|
||||||
|
@GerritServerConfig Config config,
|
||||||
|
@IndexExecutor(BATCH) ListeningExecutorService batchExecutor,
|
||||||
@Assisted GroupIndexCollection indexes) {
|
@Assisted GroupIndexCollection indexes) {
|
||||||
this.groupCache = groupCache;
|
this.groupCache = groupCache;
|
||||||
this.indexedListener = indexedListener;
|
this.indexedListener = indexedListener;
|
||||||
this.indexes = indexes;
|
this.indexes = indexes;
|
||||||
|
this.stalenessChecker = stalenessChecker;
|
||||||
|
this.autoReindexIfStale = autoReindexIfStale(config);
|
||||||
|
this.batchExecutor = batchExecutor;
|
||||||
this.index = null;
|
this.index = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -56,10 +75,16 @@ public class GroupIndexerImpl implements GroupIndexer {
|
|||||||
GroupIndexerImpl(
|
GroupIndexerImpl(
|
||||||
GroupCache groupCache,
|
GroupCache groupCache,
|
||||||
DynamicSet<GroupIndexedListener> indexedListener,
|
DynamicSet<GroupIndexedListener> indexedListener,
|
||||||
|
StalenessChecker stalenessChecker,
|
||||||
|
@GerritServerConfig Config config,
|
||||||
|
@IndexExecutor(BATCH) ListeningExecutorService batchExecutor,
|
||||||
@Assisted GroupIndex index) {
|
@Assisted GroupIndex index) {
|
||||||
this.groupCache = groupCache;
|
this.groupCache = groupCache;
|
||||||
this.indexedListener = indexedListener;
|
this.indexedListener = indexedListener;
|
||||||
this.indexes = null;
|
this.indexes = null;
|
||||||
|
this.stalenessChecker = stalenessChecker;
|
||||||
|
this.autoReindexIfStale = autoReindexIfStale(config);
|
||||||
|
this.batchExecutor = batchExecutor;
|
||||||
this.index = index;
|
this.index = index;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -74,6 +99,44 @@ public class GroupIndexerImpl implements GroupIndexer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
fireGroupIndexedEvent(uuid.get());
|
fireGroupIndexedEvent(uuid.get());
|
||||||
|
autoReindexIfStale(uuid);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean autoReindexIfStale(Config cfg) {
|
||||||
|
return cfg.getBoolean("index", null, "autoReindexIfStale", true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void autoReindexIfStale(AccountGroup.UUID uuid) {
|
||||||
|
if (autoReindexIfStale) {
|
||||||
|
// Don't retry indefinitely; if this fails the change will be stale.
|
||||||
|
@SuppressWarnings("unused")
|
||||||
|
Future<?> possiblyIgnoredError = reindexIfStale(uuid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Asynchronously check if a group is stale, and reindex if it is.
|
||||||
|
*
|
||||||
|
* <p>Always run on the batch executor, even if this indexer instance is configured to use a
|
||||||
|
* different executor.
|
||||||
|
*
|
||||||
|
* @param uuid the unique identified of the group.
|
||||||
|
* @return future for reindexing the group; returns true if the change was stale.
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("deprecation")
|
||||||
|
public com.google.common.util.concurrent.CheckedFuture<Boolean, IOException> reindexIfStale(
|
||||||
|
AccountGroup.UUID uuid) {
|
||||||
|
Callable<Boolean> task =
|
||||||
|
() -> {
|
||||||
|
if (stalenessChecker.isStale(uuid)) {
|
||||||
|
index(uuid);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
return Futures.makeChecked(
|
||||||
|
Futures.nonCancellationPropagating(batchExecutor.submit(task)), IndexUtils.MAPPER);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void fireGroupIndexedEvent(String uuid) {
|
private void fireGroupIndexedEvent(String uuid) {
|
||||||
|
|||||||
@@ -34,8 +34,11 @@ public class GroupSchemaDefinitions extends SchemaDefinitions<InternalGroup> {
|
|||||||
|
|
||||||
@Deprecated static final Schema<InternalGroup> V3 = schema(V2, GroupField.CREATED_ON);
|
@Deprecated static final Schema<InternalGroup> V3 = schema(V2, GroupField.CREATED_ON);
|
||||||
|
|
||||||
|
@Deprecated
|
||||||
static final Schema<InternalGroup> V4 = schema(V3, GroupField.MEMBER, GroupField.SUBGROUP);
|
static final Schema<InternalGroup> V4 = schema(V3, GroupField.MEMBER, GroupField.SUBGROUP);
|
||||||
|
|
||||||
|
static final Schema<InternalGroup> V5 = schema(V4, GroupField.REF_STATE);
|
||||||
|
|
||||||
public static final GroupSchemaDefinitions INSTANCE = new GroupSchemaDefinitions();
|
public static final GroupSchemaDefinitions INSTANCE = new GroupSchemaDefinitions();
|
||||||
|
|
||||||
private GroupSchemaDefinitions() {
|
private GroupSchemaDefinitions() {
|
||||||
|
|||||||
@@ -15,6 +15,7 @@
|
|||||||
package com.google.gerrit.server.index.group;
|
package com.google.gerrit.server.index.group;
|
||||||
|
|
||||||
import com.google.gerrit.index.Index;
|
import com.google.gerrit.index.Index;
|
||||||
|
import com.google.gerrit.index.IndexConfig;
|
||||||
import com.google.gerrit.index.IndexedQuery;
|
import com.google.gerrit.index.IndexedQuery;
|
||||||
import com.google.gerrit.index.QueryOptions;
|
import com.google.gerrit.index.QueryOptions;
|
||||||
import com.google.gerrit.index.query.DataSource;
|
import com.google.gerrit.index.query.DataSource;
|
||||||
@@ -22,10 +23,22 @@ import com.google.gerrit.index.query.Predicate;
|
|||||||
import com.google.gerrit.index.query.QueryParseException;
|
import com.google.gerrit.index.query.QueryParseException;
|
||||||
import com.google.gerrit.reviewdb.client.AccountGroup;
|
import com.google.gerrit.reviewdb.client.AccountGroup;
|
||||||
import com.google.gerrit.server.group.InternalGroup;
|
import com.google.gerrit.server.group.InternalGroup;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
public class IndexedGroupQuery extends IndexedQuery<AccountGroup.UUID, InternalGroup>
|
public class IndexedGroupQuery extends IndexedQuery<AccountGroup.UUID, InternalGroup>
|
||||||
implements DataSource<InternalGroup> {
|
implements DataSource<InternalGroup> {
|
||||||
|
|
||||||
|
public static QueryOptions createOptions(
|
||||||
|
IndexConfig config, int start, int limit, Set<String> fields) {
|
||||||
|
// Always include GroupField.UUID since it is needed to load the group from NoteDb.
|
||||||
|
if (!fields.contains(GroupField.UUID.getName())) {
|
||||||
|
fields = new HashSet<>(fields);
|
||||||
|
fields.add(GroupField.UUID.getName());
|
||||||
|
}
|
||||||
|
return QueryOptions.create(config, start, limit, fields);
|
||||||
|
}
|
||||||
|
|
||||||
public IndexedGroupQuery(
|
public IndexedGroupQuery(
|
||||||
Index<AccountGroup.UUID, InternalGroup> index,
|
Index<AccountGroup.UUID, InternalGroup> index,
|
||||||
Predicate<InternalGroup> pred,
|
Predicate<InternalGroup> pred,
|
||||||
|
|||||||
@@ -0,0 +1,94 @@
|
|||||||
|
// Copyright (C) 2017 The Android Open Source Project
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package com.google.gerrit.server.index.group;
|
||||||
|
|
||||||
|
import com.google.common.collect.ImmutableSet;
|
||||||
|
import com.google.gerrit.index.IndexConfig;
|
||||||
|
import com.google.gerrit.index.query.FieldBundle;
|
||||||
|
import com.google.gerrit.reviewdb.client.AccountGroup;
|
||||||
|
import com.google.gerrit.reviewdb.client.RefNames;
|
||||||
|
import com.google.gerrit.server.config.AllUsersName;
|
||||||
|
import com.google.gerrit.server.config.GerritServerConfig;
|
||||||
|
import com.google.gerrit.server.git.GitRepositoryManager;
|
||||||
|
import com.google.gwtorm.server.OrmException;
|
||||||
|
import com.google.inject.Inject;
|
||||||
|
import com.google.inject.Singleton;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.Optional;
|
||||||
|
import org.eclipse.jgit.lib.Config;
|
||||||
|
import org.eclipse.jgit.lib.ObjectId;
|
||||||
|
import org.eclipse.jgit.lib.Ref;
|
||||||
|
import org.eclipse.jgit.lib.Repository;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if documents in the group index are stale.
|
||||||
|
*
|
||||||
|
* <p>An index document is considered stale if the stored SHA1 differs from the HEAD SHA1 of the
|
||||||
|
* groups branch.
|
||||||
|
*
|
||||||
|
* <p>Note: This only applies to NoteDb.
|
||||||
|
*/
|
||||||
|
@Singleton
|
||||||
|
public class StalenessChecker {
|
||||||
|
public static final ImmutableSet<String> FIELDS =
|
||||||
|
ImmutableSet.of(GroupField.UUID.getName(), GroupField.REF_STATE.getName());
|
||||||
|
|
||||||
|
private final GroupIndexCollection indexes;
|
||||||
|
private final GitRepositoryManager repoManager;
|
||||||
|
private final IndexConfig indexConfig;
|
||||||
|
private final AllUsersName allUsers;
|
||||||
|
private final Config config;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
StalenessChecker(
|
||||||
|
GroupIndexCollection indexes,
|
||||||
|
GitRepositoryManager repoManager,
|
||||||
|
IndexConfig indexConfig,
|
||||||
|
AllUsersName allUsers,
|
||||||
|
@GerritServerConfig Config config) {
|
||||||
|
this.indexes = indexes;
|
||||||
|
this.repoManager = repoManager;
|
||||||
|
this.indexConfig = indexConfig;
|
||||||
|
this.allUsers = allUsers;
|
||||||
|
this.config = config;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isStale(AccountGroup.UUID id) throws IOException, OrmException {
|
||||||
|
if (!config.getBoolean("user", "readGroupsFromNoteDb", false)) {
|
||||||
|
return false; // This class only treats staleness for groups in NoteDb.
|
||||||
|
}
|
||||||
|
|
||||||
|
GroupIndex i = indexes.getSearchIndex();
|
||||||
|
if (i == null) {
|
||||||
|
return false; // No index; caller couldn't do anything if it is stale.
|
||||||
|
}
|
||||||
|
if (!i.getSchema().hasField(GroupField.REF_STATE)) {
|
||||||
|
return false; // Index version not new enough for this check.
|
||||||
|
}
|
||||||
|
|
||||||
|
Optional<FieldBundle> result =
|
||||||
|
i.getRaw(id, IndexedGroupQuery.createOptions(indexConfig, 0, 1, FIELDS));
|
||||||
|
if (!result.isPresent()) {
|
||||||
|
// No document in the index
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
try (Repository repo = repoManager.openRepository(allUsers)) {
|
||||||
|
Ref ref = repo.exactRef(RefNames.refsGroups(id));
|
||||||
|
ObjectId head = ref == null ? ObjectId.zeroId() : ref.getObjectId();
|
||||||
|
return !head.equals(ObjectId.fromString(result.get().getValue(GroupField.REF_STATE), 0));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -46,6 +46,7 @@ public class GroupPredicates {
|
|||||||
return new GroupPredicate(GroupField.NAME, GroupQueryBuilder.FIELD_NAME, name);
|
return new GroupPredicate(GroupField.NAME, GroupQueryBuilder.FIELD_NAME, name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("deprecation")
|
||||||
public static Predicate<InternalGroup> owner(AccountGroup.UUID ownerUuid) {
|
public static Predicate<InternalGroup> owner(AccountGroup.UUID ownerUuid) {
|
||||||
return new GroupPredicate(
|
return new GroupPredicate(
|
||||||
GroupField.OWNER_UUID, GroupQueryBuilder.FIELD_OWNER, ownerUuid.get());
|
GroupField.OWNER_UUID, GroupQueryBuilder.FIELD_OWNER, ownerUuid.get());
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ public class GroupQueryBuilder extends QueryBuilder<InternalGroup> {
|
|||||||
public static final String FIELD_DESCRIPTION = "description";
|
public static final String FIELD_DESCRIPTION = "description";
|
||||||
public static final String FIELD_INNAME = "inname";
|
public static final String FIELD_INNAME = "inname";
|
||||||
public static final String FIELD_NAME = "name";
|
public static final String FIELD_NAME = "name";
|
||||||
public static final String FIELD_OWNER = "owner";
|
@Deprecated public static final String FIELD_OWNER = "owner";
|
||||||
public static final String FIELD_LIMIT = "limit";
|
public static final String FIELD_LIMIT = "limit";
|
||||||
|
|
||||||
private static final QueryBuilder.Definition<InternalGroup, GroupQueryBuilder> mydef =
|
private static final QueryBuilder.Definition<InternalGroup, GroupQueryBuilder> mydef =
|
||||||
@@ -109,12 +109,6 @@ public class GroupQueryBuilder extends QueryBuilder<InternalGroup> {
|
|||||||
return GroupPredicates.name(name);
|
return GroupPredicates.name(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Operator
|
|
||||||
public Predicate<InternalGroup> owner(String owner) throws QueryParseException {
|
|
||||||
AccountGroup.UUID groupUuid = parseGroup(owner);
|
|
||||||
return GroupPredicates.owner(groupUuid);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Operator
|
@Operator
|
||||||
public Predicate<InternalGroup> is(String value) throws QueryParseException {
|
public Predicate<InternalGroup> is(String value) throws QueryParseException {
|
||||||
if ("visibletoall".equalsIgnoreCase(value)) {
|
if ("visibletoall".equalsIgnoreCase(value)) {
|
||||||
@@ -133,11 +127,6 @@ public class GroupQueryBuilder extends QueryBuilder<InternalGroup> {
|
|||||||
if (!Strings.isNullOrEmpty(query)) {
|
if (!Strings.isNullOrEmpty(query)) {
|
||||||
preds.add(description(query));
|
preds.add(description(query));
|
||||||
}
|
}
|
||||||
try {
|
|
||||||
preds.add(owner(query));
|
|
||||||
} catch (QueryParseException e) {
|
|
||||||
// Skip.
|
|
||||||
}
|
|
||||||
return Predicate.or(preds);
|
return Predicate.or(preds);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -90,7 +90,7 @@ public class GroupRebuilderIT extends AbstractDaemonTest {
|
|||||||
InternalGroup reviewDbGroup = groups.getGroup(db, new AccountGroup.UUID(createdGroup.id)).get();
|
InternalGroup reviewDbGroup = groups.getGroup(db, new AccountGroup.UUID(createdGroup.id)).get();
|
||||||
deleteGroupRefs(reviewDbGroup);
|
deleteGroupRefs(reviewDbGroup);
|
||||||
|
|
||||||
assertThat(rebuild(reviewDbGroup)).isEqualTo(roundToSecond(reviewDbGroup));
|
assertThat(removeRefState(rebuild(reviewDbGroup))).isEqualTo(roundToSecond(reviewDbGroup));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -109,7 +109,7 @@ public class GroupRebuilderIT extends AbstractDaemonTest {
|
|||||||
deleteGroupRefs(reviewDbGroup);
|
deleteGroupRefs(reviewDbGroup);
|
||||||
|
|
||||||
InternalGroup noteDbGroup = rebuild(reviewDbGroup);
|
InternalGroup noteDbGroup = rebuild(reviewDbGroup);
|
||||||
assertThat(noteDbGroup).isEqualTo(roundToSecond(reviewDbGroup));
|
assertThat(removeRefState(noteDbGroup)).isEqualTo(roundToSecond(reviewDbGroup));
|
||||||
|
|
||||||
ImmutableList<CommitInfo> log = log(group1);
|
ImmutableList<CommitInfo> log = log(group1);
|
||||||
assertThat(log).hasSize(4);
|
assertThat(log).hasSize(4);
|
||||||
@@ -147,6 +147,10 @@ public class GroupRebuilderIT extends AbstractDaemonTest {
|
|||||||
assertThat(log.get(3)).committer().hasSameDateAs(log.get(3).author);
|
assertThat(log.get(3)).committer().hasSameDateAs(log.get(3).author);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static InternalGroup removeRefState(InternalGroup group) throws Exception {
|
||||||
|
return group.toBuilder().setRefState(null).build();
|
||||||
|
}
|
||||||
|
|
||||||
private void deleteGroupRefs(InternalGroup group) throws Exception {
|
private void deleteGroupRefs(InternalGroup group) throws Exception {
|
||||||
try (Repository repo = repoManager.openRepository(allUsers)) {
|
try (Repository repo = repoManager.openRepository(allUsers)) {
|
||||||
String refName = RefNames.refsGroups(group.getGroupUUID());
|
String refName = RefNames.refsGroups(group.getGroupUUID());
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ import static com.google.gerrit.server.group.SystemGroupBackend.ANONYMOUS_USERS;
|
|||||||
import static com.google.gerrit.server.group.SystemGroupBackend.REGISTERED_USERS;
|
import static com.google.gerrit.server.group.SystemGroupBackend.REGISTERED_USERS;
|
||||||
import static java.util.stream.Collectors.toList;
|
import static java.util.stream.Collectors.toList;
|
||||||
|
|
||||||
|
import com.google.common.cache.LoadingCache;
|
||||||
import com.google.common.collect.ImmutableList;
|
import com.google.common.collect.ImmutableList;
|
||||||
import com.google.common.collect.Iterables;
|
import com.google.common.collect.Iterables;
|
||||||
import com.google.gerrit.acceptance.AbstractDaemonTest;
|
import com.google.gerrit.acceptance.AbstractDaemonTest;
|
||||||
@@ -66,9 +67,11 @@ import com.google.gerrit.server.git.ProjectConfig;
|
|||||||
import com.google.gerrit.server.group.InternalGroup;
|
import com.google.gerrit.server.group.InternalGroup;
|
||||||
import com.google.gerrit.server.group.SystemGroupBackend;
|
import com.google.gerrit.server.group.SystemGroupBackend;
|
||||||
import com.google.gerrit.server.group.db.Groups;
|
import com.google.gerrit.server.group.db.Groups;
|
||||||
|
import com.google.gerrit.server.index.group.StalenessChecker;
|
||||||
import com.google.gerrit.server.util.MagicBranch;
|
import com.google.gerrit.server.util.MagicBranch;
|
||||||
import com.google.gerrit.testing.ConfigSuite;
|
import com.google.gerrit.testing.ConfigSuite;
|
||||||
import com.google.inject.Inject;
|
import com.google.inject.Inject;
|
||||||
|
import com.google.inject.name.Named;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.sql.Timestamp;
|
import java.sql.Timestamp;
|
||||||
import java.time.Instant;
|
import java.time.Instant;
|
||||||
@@ -79,6 +82,7 @@ import java.util.Collection;
|
|||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Optional;
|
||||||
import org.eclipse.jgit.internal.storage.dfs.InMemoryRepository;
|
import org.eclipse.jgit.internal.storage.dfs.InMemoryRepository;
|
||||||
import org.eclipse.jgit.junit.TestRepository;
|
import org.eclipse.jgit.junit.TestRepository;
|
||||||
import org.eclipse.jgit.lib.CommitBuilder;
|
import org.eclipse.jgit.lib.CommitBuilder;
|
||||||
@@ -89,6 +93,7 @@ import org.eclipse.jgit.lib.ObjectInserter;
|
|||||||
import org.eclipse.jgit.lib.PersonIdent;
|
import org.eclipse.jgit.lib.PersonIdent;
|
||||||
import org.eclipse.jgit.lib.RefUpdate;
|
import org.eclipse.jgit.lib.RefUpdate;
|
||||||
import org.eclipse.jgit.lib.Repository;
|
import org.eclipse.jgit.lib.Repository;
|
||||||
|
import org.eclipse.jgit.revwalk.RevCommit;
|
||||||
import org.eclipse.jgit.revwalk.RevWalk;
|
import org.eclipse.jgit.revwalk.RevWalk;
|
||||||
import org.eclipse.jgit.transport.PushResult;
|
import org.eclipse.jgit.transport.PushResult;
|
||||||
import org.eclipse.jgit.transport.RemoteRefUpdate;
|
import org.eclipse.jgit.transport.RemoteRefUpdate;
|
||||||
@@ -107,6 +112,11 @@ public class GroupsIT extends AbstractDaemonTest {
|
|||||||
@Inject private Groups groups;
|
@Inject private Groups groups;
|
||||||
@Inject private GroupIncludeCache groupIncludeCache;
|
@Inject private GroupIncludeCache groupIncludeCache;
|
||||||
@Inject private GroupBackend groupBackend;
|
@Inject private GroupBackend groupBackend;
|
||||||
|
@Inject private StalenessChecker stalenessChecker;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
@Named("groups_byuuid")
|
||||||
|
LoadingCache<String, Optional<InternalGroup>> groupsByUUIDCache;
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void systemGroupCanBeRetrievedFromIndex() throws Exception {
|
public void systemGroupCanBeRetrievedFromIndex() throws Exception {
|
||||||
@@ -974,6 +984,47 @@ public class GroupsIT extends AbstractDaemonTest {
|
|||||||
assertThat(rule.getMax()).isEqualTo(0);
|
assertThat(rule.getMax()).isEqualTo(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void stalenessChecker() throws Exception {
|
||||||
|
assume().that(groupsInNoteDb()).isTrue();
|
||||||
|
|
||||||
|
// Newly created group is not stale
|
||||||
|
GroupInfo groupInfo = gApi.groups().create(name("foo")).get();
|
||||||
|
AccountGroup.UUID groupUuid = new AccountGroup.UUID(groupInfo.id);
|
||||||
|
assertThat(stalenessChecker.isStale(groupUuid)).isFalse();
|
||||||
|
|
||||||
|
// Manual update makes index document stale
|
||||||
|
String groupRef = RefNames.refsGroups(groupUuid);
|
||||||
|
try (Repository repo = repoManager.openRepository(allUsers);
|
||||||
|
ObjectInserter oi = repo.newObjectInserter();
|
||||||
|
RevWalk rw = new RevWalk(repo)) {
|
||||||
|
RevCommit commit = rw.parseCommit(repo.exactRef(groupRef).getObjectId());
|
||||||
|
|
||||||
|
PersonIdent ident = new PersonIdent(serverIdent.get(), TimeUtil.nowTs());
|
||||||
|
CommitBuilder cb = new CommitBuilder();
|
||||||
|
cb.setTreeId(commit.getTree());
|
||||||
|
cb.setCommitter(ident);
|
||||||
|
cb.setAuthor(ident);
|
||||||
|
cb.setMessage(commit.getFullMessage());
|
||||||
|
ObjectId emptyCommit = oi.insert(cb);
|
||||||
|
oi.flush();
|
||||||
|
|
||||||
|
RefUpdate updateRef = repo.updateRef(groupRef);
|
||||||
|
updateRef.setExpectedOldObjectId(commit.toObjectId());
|
||||||
|
updateRef.setNewObjectId(emptyCommit);
|
||||||
|
assertThat(updateRef.forceUpdate()).isEqualTo(RefUpdate.Result.FORCED);
|
||||||
|
}
|
||||||
|
// Evict group from cache to be sure that we use the index state for staleness checks. This has
|
||||||
|
// to happen directly on the groupsByUUID cache because GroupsCacheImpl triggers a reindex for
|
||||||
|
// the group.
|
||||||
|
groupsByUUIDCache.invalidate(groupUuid);
|
||||||
|
assertThat(stalenessChecker.isStale(groupUuid)).isTrue();
|
||||||
|
|
||||||
|
// Reindex fixes staleness
|
||||||
|
gApi.groups().id(groupInfo.id).index();
|
||||||
|
assertThat(stalenessChecker.isStale(groupUuid)).isFalse();
|
||||||
|
}
|
||||||
|
|
||||||
private void pushToGroupBranchForReviewAndSubmit(Project.NameKey project, String expectedError)
|
private void pushToGroupBranchForReviewAndSubmit(Project.NameKey project, String expectedError)
|
||||||
throws Exception {
|
throws Exception {
|
||||||
grantLabel(
|
grantLabel(
|
||||||
|
|||||||
@@ -381,7 +381,7 @@ public class GroupRebuilderTest extends GerritBaseTests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private InternalGroup reload(AccountGroup g) throws Exception {
|
private InternalGroup reload(AccountGroup g) throws Exception {
|
||||||
return GroupConfig.loadForGroup(repo, g.getGroupUUID()).getLoadedGroup().get();
|
return removeRefState(GroupConfig.loadForGroup(repo, g.getGroupUUID()).getLoadedGroup().get());
|
||||||
}
|
}
|
||||||
|
|
||||||
private AccountGroup newGroup(String name) {
|
private AccountGroup newGroup(String name) {
|
||||||
@@ -473,4 +473,8 @@ public class GroupRebuilderTest extends GerritBaseTests {
|
|||||||
assertThat(commitInfo).committer().date().isEqualTo(commitInfo.author.date);
|
assertThat(commitInfo).committer().date().isEqualTo(commitInfo.author.date);
|
||||||
assertThat(commitInfo).committer().tz().isEqualTo(commitInfo.author.tz);
|
assertThat(commitInfo).committer().tz().isEqualTo(commitInfo.author.tz);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static InternalGroup removeRefState(InternalGroup group) throws Exception {
|
||||||
|
return group.toBuilder().setRefState(null).build();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -245,19 +245,6 @@ public abstract class AbstractQueryGroupsTest extends GerritServerTests {
|
|||||||
assertQuery("description:\"\"");
|
assertQuery("description:\"\"");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
public void byOwner() throws Exception {
|
|
||||||
GroupInfo ownerGroup = createGroup(name("owner-group"));
|
|
||||||
GroupInfo group = createGroupWithOwner(name("group"), ownerGroup);
|
|
||||||
createGroup(name("group2"));
|
|
||||||
|
|
||||||
assertQuery("owner:" + group.id);
|
|
||||||
|
|
||||||
// ownerGroup owns itself
|
|
||||||
assertQuery("owner:" + ownerGroup.id, group, ownerGroup);
|
|
||||||
assertQuery("owner:" + ownerGroup.name, group, ownerGroup);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void byIsVisibleToAll() throws Exception {
|
public void byIsVisibleToAll() throws Exception {
|
||||||
assertQuery("is:visibletoall");
|
assertQuery("is:visibletoall");
|
||||||
|
|||||||
Reference in New Issue
Block a user