Merge "Add staleness checker for groups in NoteDb"

This commit is contained in:
Dave Borowitz
2017-11-15 11:15:48 +00:00
committed by Gerrit Code Review
16 changed files with 318 additions and 51 deletions

View File

@@ -21,6 +21,7 @@ import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.reviewdb.client.AccountGroup;
import java.io.Serializable;
import java.sql.Timestamp;
import org.eclipse.jgit.lib.ObjectId;
@AutoValue
public abstract class InternalGroup implements Serializable {
@@ -30,6 +31,14 @@ public abstract class InternalGroup implements Serializable {
AccountGroup accountGroup,
ImmutableSet<Account.Id> members,
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()
.setId(accountGroup.getId())
.setNameKey(accountGroup.getNameKey())
@@ -40,6 +49,7 @@ public abstract class InternalGroup implements Serializable {
.setCreatedOn(accountGroup.getCreatedOn())
.setMembers(members)
.setSubgroups(subgroups)
.setRefState(refState)
.build();
}
@@ -66,6 +76,11 @@ public abstract class InternalGroup implements Serializable {
public abstract ImmutableSet<AccountGroup.UUID> getSubgroups();
@Nullable
public abstract ObjectId getRefState();
public abstract Builder toBuilder();
public static Builder 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 setRefState(ObjectId refState);
public abstract InternalGroup build();
}
}

View File

@@ -25,6 +25,7 @@ import com.google.common.collect.Streams;
import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.reviewdb.client.AccountGroup;
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.group.InternalGroup;
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.lib.CommitBuilder;
import org.eclipse.jgit.lib.Config;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.PersonIdent;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.revwalk.RevCommit;
@@ -123,7 +125,9 @@ public class GroupConfig extends VersionedMetaData {
Config config = readConfig(GROUP_CONFIG_FILE);
ImmutableSet<Account.Id> members = readMembers();
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;
@@ -134,7 +138,8 @@ public class GroupConfig extends VersionedMetaData {
Config config,
ImmutableSet<Account.Id> members,
ImmutableSet<AccountGroup.UUID> subgroups,
Timestamp createdOn) {
Timestamp createdOn,
ObjectId refState) {
InternalGroup.Builder group = InternalGroup.builder();
group.setGroupUUID(groupUuid);
Arrays.stream(GroupConfigEntry.values())
@@ -142,9 +147,17 @@ public class GroupConfig extends VersionedMetaData {
group.setMembers(members);
group.setSubgroups(subgroups);
group.setCreatedOn(createdOn);
group.setRefState(refState);
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
protected boolean onSave(CommitBuilder commit) throws IOException, ConfigInvalidException {
checkLoaded();
@@ -184,7 +197,8 @@ public class GroupConfig extends VersionedMetaData {
config,
updatedMembers.orElse(originalMembers),
updatedSubgroups.orElse(originalSubgroups),
createdOn));
createdOn,
null));
groupCreation = Optional.empty();
return true;

View File

@@ -48,6 +48,7 @@ import com.google.gerrit.server.git.GitRepositoryManager;
import com.google.gerrit.server.git.MetaDataUpdate;
import com.google.gerrit.server.git.RenameGroupOp;
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.gwtorm.server.OrmDuplicateKeyException;
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.lib.BatchRefUpdate;
import org.eclipse.jgit.lib.Config;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.PersonIdent;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.revwalk.RevWalk;
@@ -264,7 +266,10 @@ public class GroupsUpdate {
AccountGroup group = createAccountGroup(groupCreation);
UpdateResult updateResult = updateGroupInReviewDb(db, group, groupUpdate);
return InternalGroup.create(
group, updateResult.getModifiedMembers(), updateResult.getModifiedSubgroups());
group,
updateResult.getModifiedMembers(),
updateResult.getModifiedSubgroups(),
updateResult.getRefState());
}
public static AccountGroup createAccountGroup(
@@ -504,7 +509,8 @@ public class GroupsUpdate {
.setGroupId(updatedGroup.getId())
.setGroupName(updatedGroup.getNameKey())
.setModifiedMembers(modifiedMembers)
.setModifiedSubgroups(modifiedSubgroups);
.setModifiedSubgroups(modifiedSubgroups)
.setRefState(updatedGroup.getRefState());
if (!Objects.equals(originalGroup.getNameKey(), updatedGroup.getNameKey())) {
resultBuilder.setPreviousGroupName(originalGroup.getNameKey());
}
@@ -637,6 +643,9 @@ public class GroupsUpdate {
abstract ImmutableSet<AccountGroup.UUID> getModifiedSubgroups();
@Nullable
public abstract ObjectId getRefState();
static Builder builder() {
return new AutoValue_GroupsUpdate_UpdateResult.Builder();
}
@@ -655,6 +664,8 @@ public class GroupsUpdate {
abstract Builder setModifiedSubgroups(Set<AccountGroup.UUID> modifiedSubgroups);
public abstract Builder setRefState(ObjectId refState);
abstract UpdateResult build();
}
}

View File

@@ -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.PROJECT;
import com.google.common.base.Function;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
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 java.io.IOException;
import java.util.Set;
import java.util.concurrent.ExecutionException;
import org.eclipse.jgit.errors.ConfigInvalidException;
public final class IndexUtils {
public static final ImmutableMap<String, String> CUSTOM_CHAR_MAPPING =
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)
throws IOException {
try {

View File

@@ -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.git.QueueProvider.QueueType.BATCH;
import com.google.common.base.Function;
import com.google.common.util.concurrent.Atomics;
import com.google.common.util.concurrent.Futures;
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.config.GerritServerConfig;
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.NotesMigration;
import com.google.gerrit.server.query.change.ChangeData;
@@ -50,7 +50,6 @@ import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.atomic.AtomicReference;
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
// future. Assume the actual contents of the exception are not useful to
// 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 ChangeIndex index;
private final SchemaFactory<ReviewDb> schemaFactory;
@@ -335,7 +320,8 @@ public class ChangeIndexer {
@SuppressWarnings("deprecation")
private static <T> com.google.common.util.concurrent.CheckedFuture<T, IOException> submit(
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> {

View File

@@ -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.integer;
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 com.google.common.base.MoreObjects;
import com.google.gerrit.index.FieldDef;
import com.google.gerrit.index.SchemaUtil;
import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.reviewdb.client.AccountGroup;
import com.google.gerrit.server.group.InternalGroup;
import java.sql.Timestamp;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.ObjectId;
/** Secondary index schemas for groups. */
public class GroupField {
@@ -72,4 +76,14 @@ public class GroupField {
.buildRepeatable(
g ->
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;
});
}

View File

@@ -14,20 +14,30 @@
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.util.concurrent.Futures;
import com.google.common.util.concurrent.ListeningExecutorService;
import com.google.gerrit.common.Nullable;
import com.google.gerrit.extensions.events.GroupIndexedListener;
import com.google.gerrit.extensions.registration.DynamicSet;
import com.google.gerrit.index.Index;
import com.google.gerrit.reviewdb.client.AccountGroup;
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.index.IndexExecutor;
import com.google.gerrit.server.index.IndexUtils;
import com.google.inject.assistedinject.Assisted;
import com.google.inject.assistedinject.AssistedInject;
import java.io.IOException;
import java.util.Collection;
import java.util.Collections;
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 interface Factory {
@@ -39,16 +49,25 @@ public class GroupIndexerImpl implements GroupIndexer {
private final GroupCache groupCache;
private final DynamicSet<GroupIndexedListener> indexedListener;
private final GroupIndexCollection indexes;
private final StalenessChecker stalenessChecker;
private final boolean autoReindexIfStale;
private final GroupIndex index;
private final ListeningExecutorService batchExecutor;
@AssistedInject
GroupIndexerImpl(
GroupCache groupCache,
DynamicSet<GroupIndexedListener> indexedListener,
StalenessChecker stalenessChecker,
@GerritServerConfig Config config,
@IndexExecutor(BATCH) ListeningExecutorService batchExecutor,
@Assisted GroupIndexCollection indexes) {
this.groupCache = groupCache;
this.indexedListener = indexedListener;
this.indexes = indexes;
this.stalenessChecker = stalenessChecker;
this.autoReindexIfStale = autoReindexIfStale(config);
this.batchExecutor = batchExecutor;
this.index = null;
}
@@ -56,10 +75,16 @@ public class GroupIndexerImpl implements GroupIndexer {
GroupIndexerImpl(
GroupCache groupCache,
DynamicSet<GroupIndexedListener> indexedListener,
StalenessChecker stalenessChecker,
@GerritServerConfig Config config,
@IndexExecutor(BATCH) ListeningExecutorService batchExecutor,
@Assisted GroupIndex index) {
this.groupCache = groupCache;
this.indexedListener = indexedListener;
this.indexes = null;
this.stalenessChecker = stalenessChecker;
this.autoReindexIfStale = autoReindexIfStale(config);
this.batchExecutor = batchExecutor;
this.index = index;
}
@@ -74,6 +99,44 @@ public class GroupIndexerImpl implements GroupIndexer {
}
}
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) {

View File

@@ -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> V4 = schema(V3, GroupField.MEMBER, GroupField.SUBGROUP);
static final Schema<InternalGroup> V5 = schema(V4, GroupField.REF_STATE);
public static final GroupSchemaDefinitions INSTANCE = new GroupSchemaDefinitions();
private GroupSchemaDefinitions() {

View File

@@ -15,6 +15,7 @@
package com.google.gerrit.server.index.group;
import com.google.gerrit.index.Index;
import com.google.gerrit.index.IndexConfig;
import com.google.gerrit.index.IndexedQuery;
import com.google.gerrit.index.QueryOptions;
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.reviewdb.client.AccountGroup;
import com.google.gerrit.server.group.InternalGroup;
import java.util.HashSet;
import java.util.Set;
public class IndexedGroupQuery extends IndexedQuery<AccountGroup.UUID, 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(
Index<AccountGroup.UUID, InternalGroup> index,
Predicate<InternalGroup> pred,

View File

@@ -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));
}
}
}

View File

@@ -46,6 +46,7 @@ public class GroupPredicates {
return new GroupPredicate(GroupField.NAME, GroupQueryBuilder.FIELD_NAME, name);
}
@SuppressWarnings("deprecation")
public static Predicate<InternalGroup> owner(AccountGroup.UUID ownerUuid) {
return new GroupPredicate(
GroupField.OWNER_UUID, GroupQueryBuilder.FIELD_OWNER, ownerUuid.get());

View File

@@ -49,7 +49,7 @@ public class GroupQueryBuilder extends QueryBuilder<InternalGroup> {
public static final String FIELD_DESCRIPTION = "description";
public static final String FIELD_INNAME = "inname";
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";
private static final QueryBuilder.Definition<InternalGroup, GroupQueryBuilder> mydef =
@@ -109,12 +109,6 @@ public class GroupQueryBuilder extends QueryBuilder<InternalGroup> {
return GroupPredicates.name(name);
}
@Operator
public Predicate<InternalGroup> owner(String owner) throws QueryParseException {
AccountGroup.UUID groupUuid = parseGroup(owner);
return GroupPredicates.owner(groupUuid);
}
@Operator
public Predicate<InternalGroup> is(String value) throws QueryParseException {
if ("visibletoall".equalsIgnoreCase(value)) {
@@ -133,11 +127,6 @@ public class GroupQueryBuilder extends QueryBuilder<InternalGroup> {
if (!Strings.isNullOrEmpty(query)) {
preds.add(description(query));
}
try {
preds.add(owner(query));
} catch (QueryParseException e) {
// Skip.
}
return Predicate.or(preds);
}

View File

@@ -90,7 +90,7 @@ public class GroupRebuilderIT extends AbstractDaemonTest {
InternalGroup reviewDbGroup = groups.getGroup(db, new AccountGroup.UUID(createdGroup.id)).get();
deleteGroupRefs(reviewDbGroup);
assertThat(rebuild(reviewDbGroup)).isEqualTo(roundToSecond(reviewDbGroup));
assertThat(removeRefState(rebuild(reviewDbGroup))).isEqualTo(roundToSecond(reviewDbGroup));
}
@Test
@@ -109,7 +109,7 @@ public class GroupRebuilderIT extends AbstractDaemonTest {
deleteGroupRefs(reviewDbGroup);
InternalGroup noteDbGroup = rebuild(reviewDbGroup);
assertThat(noteDbGroup).isEqualTo(roundToSecond(reviewDbGroup));
assertThat(removeRefState(noteDbGroup)).isEqualTo(roundToSecond(reviewDbGroup));
ImmutableList<CommitInfo> log = log(group1);
assertThat(log).hasSize(4);
@@ -147,6 +147,10 @@ public class GroupRebuilderIT extends AbstractDaemonTest {
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 {
try (Repository repo = repoManager.openRepository(allUsers)) {
String refName = RefNames.refsGroups(group.getGroupUUID());

View File

@@ -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 java.util.stream.Collectors.toList;
import com.google.common.cache.LoadingCache;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterables;
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.SystemGroupBackend;
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.testing.ConfigSuite;
import com.google.inject.Inject;
import com.google.inject.name.Named;
import java.io.IOException;
import java.sql.Timestamp;
import java.time.Instant;
@@ -79,6 +82,7 @@ import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import org.eclipse.jgit.internal.storage.dfs.InMemoryRepository;
import org.eclipse.jgit.junit.TestRepository;
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.RefUpdate;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.revwalk.RevWalk;
import org.eclipse.jgit.transport.PushResult;
import org.eclipse.jgit.transport.RemoteRefUpdate;
@@ -107,6 +112,11 @@ public class GroupsIT extends AbstractDaemonTest {
@Inject private Groups groups;
@Inject private GroupIncludeCache groupIncludeCache;
@Inject private GroupBackend groupBackend;
@Inject private StalenessChecker stalenessChecker;
@Inject
@Named("groups_byuuid")
LoadingCache<String, Optional<InternalGroup>> groupsByUUIDCache;
@Test
public void systemGroupCanBeRetrievedFromIndex() throws Exception {
@@ -974,6 +984,47 @@ public class GroupsIT extends AbstractDaemonTest {
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)
throws Exception {
grantLabel(

View File

@@ -381,7 +381,7 @@ public class GroupRebuilderTest extends GerritBaseTests {
}
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) {
@@ -473,4 +473,8 @@ public class GroupRebuilderTest extends GerritBaseTests {
assertThat(commitInfo).committer().date().isEqualTo(commitInfo.author.date);
assertThat(commitInfo).committer().tz().isEqualTo(commitInfo.author.tz);
}
private static InternalGroup removeRefState(InternalGroup group) throws Exception {
return group.toBuilder().setRefState(null).build();
}
}

View File

@@ -245,19 +245,6 @@ public abstract class AbstractQueryGroupsTest extends GerritServerTests {
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
public void byIsVisibleToAll() throws Exception {
assertQuery("is:visibletoall");