This is an automated email from the ASF dual-hosted git repository. boglesby pushed a commit to branch feature/GEODE-5145 in repository https://gitbox.apache.org/repos/asf/geode.git
commit d67979da7c38c38b9509b152421f4eee1468fa57 Author: Barry Oglesby <[email protected]> AuthorDate: Wed May 2 09:43:33 2018 -0700 GEODE-5145: Added support for destroying an index during region creation --- .../apache/geode/internal/cache/LocalRegion.java | 4 + .../apache/geode/management/cli/GfshCommand.java | 8 + .../geode/management/internal/cli/CliUtil.java | 27 ++ .../lucene/LuceneIndexDestroyedException.java | 2 +- .../lucene/internal/DestroyLuceneIndexMessage.java | 21 +- .../internal/LuceneIndexCreationProfile.java | 4 + .../internal/LuceneIndexForPartitionedRegion.java | 31 +- .../cache/lucene/internal/LuceneIndexImpl.java | 4 + .../lucene/internal/LuceneRegionListener.java | 8 +- .../cache/lucene/internal/LuceneServiceImpl.java | 31 +- .../lucene/internal/cli/LuceneCliStrings.java | 2 + .../lucene/internal/cli/LuceneIndexCommands.java | 84 ++--- .../cli/functions/LuceneDestroyIndexFunction.java | 31 +- .../cache/lucene/LuceneIndexDestroyDUnitTest.java | 45 +++ .../cli/DestroyLuceneIndexCommandsDUnitTest.java | 407 +++++++++++++++++++++ .../cli/LuceneIndexCommandsIntegrationTest.java | 8 + .../internal/cli/LuceneIndexCommandsJUnitTest.java | 102 ++---- .../LuceneDestroyIndexFunctionJUnitTest.java | 3 +- 18 files changed, 669 insertions(+), 153 deletions(-) diff --git a/geode-core/src/main/java/org/apache/geode/internal/cache/LocalRegion.java b/geode-core/src/main/java/org/apache/geode/internal/cache/LocalRegion.java index 1642a8a..5df28c4 100644 --- a/geode-core/src/main/java/org/apache/geode/internal/cache/LocalRegion.java +++ b/geode-core/src/main/java/org/apache/geode/internal/cache/LocalRegion.java @@ -10498,6 +10498,10 @@ public class LocalRegion extends AbstractRegion implements LoaderHelperFactory, this.cacheServiceProfiles.put(profile.getId(), profile); } + public void removeCacheServiceProfile(String profileId) { + this.cacheServiceProfiles.remove(profileId); + } + @Override public LoaderHelper createLoaderHelper(Object key, Object callbackArgument, boolean netSearchAllowed, boolean netLoadAllowed, SearchLoadAndWriteProcessor searcher) { diff --git a/geode-core/src/main/java/org/apache/geode/management/cli/GfshCommand.java b/geode-core/src/main/java/org/apache/geode/management/cli/GfshCommand.java index ab81ab2..1841eac 100644 --- a/geode-core/src/main/java/org/apache/geode/management/cli/GfshCommand.java +++ b/geode-core/src/main/java/org/apache/geode/management/cli/GfshCommand.java @@ -30,6 +30,7 @@ import org.apache.geode.cache.execute.ResultCollector; import org.apache.geode.distributed.ConfigurationPersistenceService; import org.apache.geode.distributed.DistributedMember; import org.apache.geode.distributed.internal.InternalLocator; +import org.apache.geode.internal.Version; import org.apache.geode.internal.cache.InternalCache; import org.apache.geode.management.internal.cli.CliUtil; import org.apache.geode.management.internal.cli.exceptions.EntityNotFoundException; @@ -124,6 +125,13 @@ public abstract class GfshCommand implements CommandMarker { return CliUtil.getAllNormalMembers(cache); } + /** + * Get All members >= a specific version, excluding locators + */ + public Set<DistributedMember> getNormalMembersWithSameOrNewerVersion(Version version) { + return CliUtil.getNormalMembersWithSameOrNewerVersion(cache, version); + } + public Execution getMembersFunctionExecutor(final Set<DistributedMember> members) { return FunctionService.onMembers(members); } diff --git a/geode-core/src/main/java/org/apache/geode/management/internal/cli/CliUtil.java b/geode-core/src/main/java/org/apache/geode/management/internal/cli/CliUtil.java index 715bab1..8634145 100755 --- a/geode-core/src/main/java/org/apache/geode/management/internal/cli/CliUtil.java +++ b/geode-core/src/main/java/org/apache/geode/management/internal/cli/CliUtil.java @@ -49,7 +49,9 @@ import org.apache.geode.cache.execute.FunctionService; import org.apache.geode.cache.execute.ResultCollector; import org.apache.geode.distributed.DistributedMember; import org.apache.geode.distributed.internal.InternalDistributedSystem; +import org.apache.geode.distributed.internal.membership.InternalDistributedMember; import org.apache.geode.internal.ClassPathLoader; +import org.apache.geode.internal.Version; import org.apache.geode.internal.cache.InternalCache; import org.apache.geode.internal.cache.execute.AbstractExecution; import org.apache.geode.internal.lang.StringUtils; @@ -184,6 +186,31 @@ public class CliUtil { } /** + * Returns a set of all the members of the distributed system of a specific version excluding + * locators. + */ + @SuppressWarnings("unchecked") + public static Set<DistributedMember> getNormalMembersWithSameOrNewerVersion(InternalCache cache, + Version version) { + return getAllNormalMembers(cache).stream().filter( + member -> ((InternalDistributedMember) member).getVersionObject().compareTo(version) >= 0) + .collect(Collectors.toSet()); + } + + /** + * Returns a set of all the members of the distributed system older than a specific version + * excluding locators. + */ + @SuppressWarnings("unchecked") + public static Set<DistributedMember> getNormalMembersWithSameOrOlderVersion(InternalCache cache, + Version version) { + return getAllNormalMembers(cache).stream() + .filter(member -> ((InternalDistributedMember) member).getVersionObject().equals(version)) + .collect(Collectors.toSet()); + // dm.removeMembersWithSameOrNewerVersion(oldMembers, version); + } + + /** * Returns a set of all the members of the distributed system including locators. */ @SuppressWarnings("unchecked") diff --git a/geode-lucene/src/main/java/org/apache/geode/cache/lucene/LuceneIndexDestroyedException.java b/geode-lucene/src/main/java/org/apache/geode/cache/lucene/LuceneIndexDestroyedException.java index 7b38f24..a0365e6 100644 --- a/geode-lucene/src/main/java/org/apache/geode/cache/lucene/LuceneIndexDestroyedException.java +++ b/geode-lucene/src/main/java/org/apache/geode/cache/lucene/LuceneIndexDestroyedException.java @@ -27,7 +27,7 @@ public class LuceneIndexDestroyedException extends GemFireException { private final String regionPath; public LuceneIndexDestroyedException(String indexName, String regionPath) { - super(); + super("Lucene index " + indexName + " on region " + regionPath + " has been destroyed"); this.indexName = indexName; this.regionPath = regionPath; } diff --git a/geode-lucene/src/main/java/org/apache/geode/cache/lucene/internal/DestroyLuceneIndexMessage.java b/geode-lucene/src/main/java/org/apache/geode/cache/lucene/internal/DestroyLuceneIndexMessage.java index abf7336..93c620d 100644 --- a/geode-lucene/src/main/java/org/apache/geode/cache/lucene/internal/DestroyLuceneIndexMessage.java +++ b/geode-lucene/src/main/java/org/apache/geode/cache/lucene/internal/DestroyLuceneIndexMessage.java @@ -22,6 +22,7 @@ import java.util.Collection; import org.apache.logging.log4j.Logger; import org.apache.geode.DataSerializer; +import org.apache.geode.cache.Region; import org.apache.geode.cache.lucene.LuceneServiceProvider; import org.apache.geode.distributed.internal.ClusterDistributionManager; import org.apache.geode.distributed.internal.MessageWithReply; @@ -29,6 +30,7 @@ import org.apache.geode.distributed.internal.PooledDistributionMessage; import org.apache.geode.distributed.internal.ReplyException; import org.apache.geode.distributed.internal.ReplyMessage; import org.apache.geode.internal.cache.InternalCache; +import org.apache.geode.internal.i18n.LocalizedStrings; import org.apache.geode.internal.logging.LogService; public class DestroyLuceneIndexMessage extends PooledDistributionMessage @@ -67,10 +69,21 @@ public class DestroyLuceneIndexMessage extends PooledDistributionMessage try { InternalCache cache = dm.getCache(); LuceneServiceImpl impl = (LuceneServiceImpl) LuceneServiceProvider.get(cache); - impl.destroyIndex(this.indexName, this.regionPath, false); - if (logger.isDebugEnabled()) { - logger.debug("DestroyLuceneIndexMessage: Destroyed regionPath=" + this.regionPath - + "; indexName=" + this.indexName); + try { + impl.destroyIndex(this.indexName, this.regionPath, false); + if (logger.isDebugEnabled()) { + logger.debug("DestroyLuceneIndexMessage: Destroyed regionPath=" + this.regionPath + + "; indexName=" + this.indexName); + } + } catch (IllegalArgumentException e) { + // If the IllegalArgumentException is index not found, then its ok; otherwise rethrow it. + String fullRegionPath = + regionPath.startsWith(Region.SEPARATOR) ? regionPath : Region.SEPARATOR + regionPath; + String indexNotFoundMessage = LocalizedStrings.LuceneService_INDEX_0_NOT_FOUND_IN_REGION_1 + .toLocalizedString(this.indexName, fullRegionPath); + if (!e.getLocalizedMessage().equals(indexNotFoundMessage)) { + throw e; + } } } catch (Throwable e) { replyException = new ReplyException(e); diff --git a/geode-lucene/src/main/java/org/apache/geode/cache/lucene/internal/LuceneIndexCreationProfile.java b/geode-lucene/src/main/java/org/apache/geode/cache/lucene/internal/LuceneIndexCreationProfile.java index 3d9b00d..c3b08b3 100644 --- a/geode-lucene/src/main/java/org/apache/geode/cache/lucene/internal/LuceneIndexCreationProfile.java +++ b/geode-lucene/src/main/java/org/apache/geode/cache/lucene/internal/LuceneIndexCreationProfile.java @@ -96,6 +96,10 @@ public class LuceneIndexCreationProfile implements CacheServiceProfile, Versione @Override public String getId() { + return generateId(indexName, regionPath); + } + + public static String generateId(String indexName, String regionPath) { return "lucene_" + LuceneServiceImpl.getUniqueIndexName(indexName, regionPath); } diff --git a/geode-lucene/src/main/java/org/apache/geode/cache/lucene/internal/LuceneIndexForPartitionedRegion.java b/geode-lucene/src/main/java/org/apache/geode/cache/lucene/internal/LuceneIndexForPartitionedRegion.java index 778b8fd..2343a3c 100644 --- a/geode-lucene/src/main/java/org/apache/geode/cache/lucene/internal/LuceneIndexForPartitionedRegion.java +++ b/geode-lucene/src/main/java/org/apache/geode/cache/lucene/internal/LuceneIndexForPartitionedRegion.java @@ -26,6 +26,7 @@ import org.apache.geode.cache.PartitionAttributesFactory; import org.apache.geode.cache.PartitionResolver; import org.apache.geode.cache.Region; import org.apache.geode.cache.RegionAttributes; +import org.apache.geode.cache.RegionDestroyedException; import org.apache.geode.cache.RegionShortcut; import org.apache.geode.cache.execute.FunctionService; import org.apache.geode.cache.execute.ResultCollector; @@ -44,6 +45,7 @@ import org.apache.geode.distributed.internal.membership.InternalDistributedMembe import org.apache.geode.internal.cache.BucketRegion; import org.apache.geode.internal.cache.InternalCache; import org.apache.geode.internal.cache.PartitionedRegion; +import org.apache.geode.internal.i18n.LocalizedStrings; public class LuceneIndexForPartitionedRegion extends LuceneIndexImpl { protected Region fileAndChunkRegion; @@ -208,9 +210,15 @@ public class LuceneIndexForPartitionedRegion extends LuceneIndexImpl { // localDestroyRegion can't be used because locally destroying regions is not supported on // colocated regions if (initiator) { - fileAndChunkRegion.destroyRegion(); - if (logger.isDebugEnabled()) { - logger.debug("Destroyed fileAndChunkRegion=" + fileAndChunkRegion.getName()); + try { + fileAndChunkRegion.destroyRegion(); + if (logger.isDebugEnabled()) { + logger.debug("Destroyed fileAndChunkRegion=" + fileAndChunkRegion.getName()); + } + } catch (RegionDestroyedException e) { + if (logger.isDebugEnabled()) { + logger.debug("Already destroyed fileAndChunkRegion=" + fileAndChunkRegion.getName()); + } } } @@ -228,9 +236,8 @@ public class LuceneIndexForPartitionedRegion extends LuceneIndexImpl { } private void destroyOnRemoteMembers() { - PartitionedRegion pr = (PartitionedRegion) getDataRegion(); - DistributionManager dm = pr.getDistributionManager(); - Set<InternalDistributedMember> recipients = pr.getRegionAdvisor().adviseAllPRNodes(); + DistributionManager dm = getDataRegion().getDistributionManager(); + Set<InternalDistributedMember> recipients = dm.getOtherNormalDistributionManagerIds(); if (!recipients.isEmpty()) { if (logger.isDebugEnabled()) { logger.debug("LuceneIndexForPartitionedRegion: About to send destroy message recipients=" @@ -246,7 +253,17 @@ public class LuceneIndexForPartitionedRegion extends LuceneIndexImpl { try { processor.waitForReplies(); } catch (ReplyException e) { - if (!(e.getCause() instanceof CancelException)) { + Throwable cause = e.getCause(); + if (cause instanceof IllegalArgumentException) { + // If the IllegalArgumentException is index not found, then its ok; otherwise rethrow it. + String fullRegionPath = + regionPath.startsWith(Region.SEPARATOR) ? regionPath : Region.SEPARATOR + regionPath; + String indexNotFoundMessage = LocalizedStrings.LuceneService_INDEX_0_NOT_FOUND_IN_REGION_1 + .toLocalizedString(indexName, fullRegionPath); + if (!cause.getLocalizedMessage().equals(indexNotFoundMessage)) { + throw e; + } + } else if (!(cause instanceof CancelException)) { throw e; } } catch (InterruptedException e) { diff --git a/geode-lucene/src/main/java/org/apache/geode/cache/lucene/internal/LuceneIndexImpl.java b/geode-lucene/src/main/java/org/apache/geode/cache/lucene/internal/LuceneIndexImpl.java index e58c21f..f06fd91 100644 --- a/geode-lucene/src/main/java/org/apache/geode/cache/lucene/internal/LuceneIndexImpl.java +++ b/geode-lucene/src/main/java/org/apache/geode/cache/lucene/internal/LuceneIndexImpl.java @@ -226,6 +226,10 @@ public abstract class LuceneIndexImpl implements InternalLuceneIndex { getDataRegion().getExtensionPoint().removeExtension(extensionToDelete); } + // Remove cache service profile + dataRegion + .removeCacheServiceProfile(LuceneIndexCreationProfile.generateId(indexName, regionPath)); + // Destroy the async event queue destroyAsyncEventQueue(initiator); diff --git a/geode-lucene/src/main/java/org/apache/geode/cache/lucene/internal/LuceneRegionListener.java b/geode-lucene/src/main/java/org/apache/geode/cache/lucene/internal/LuceneRegionListener.java index 88d7de0..7977670 100644 --- a/geode-lucene/src/main/java/org/apache/geode/cache/lucene/internal/LuceneRegionListener.java +++ b/geode-lucene/src/main/java/org/apache/geode/cache/lucene/internal/LuceneRegionListener.java @@ -22,6 +22,7 @@ import org.apache.lucene.analysis.Analyzer; import org.apache.geode.cache.AttributesFactory; import org.apache.geode.cache.Region; import org.apache.geode.cache.RegionAttributes; +import org.apache.geode.cache.lucene.LuceneIndexDestroyedException; import org.apache.geode.cache.lucene.LuceneSerializer; import org.apache.geode.internal.cache.InternalCache; import org.apache.geode.internal.cache.InternalRegionArguments; @@ -107,7 +108,12 @@ public class LuceneRegionListener implements RegionListener { public void afterCreate(Region region) { if (region.getFullPath().equals(this.regionPath) && this.afterCreateInvoked.compareAndSet(false, true)) { - this.service.afterDataRegionCreated(this.luceneIndex); + try { + this.service.afterDataRegionCreated(this.luceneIndex); + } catch (LuceneIndexDestroyedException e) { + // @todo log a warning here? + return; + } this.service.createLuceneIndexOnDataRegion((PartitionedRegion) region, luceneIndex); } } diff --git a/geode-lucene/src/main/java/org/apache/geode/cache/lucene/internal/LuceneServiceImpl.java b/geode-lucene/src/main/java/org/apache/geode/cache/lucene/internal/LuceneServiceImpl.java index 6ef8c60..9dbcb44 100644 --- a/geode-lucene/src/main/java/org/apache/geode/cache/lucene/internal/LuceneServiceImpl.java +++ b/geode-lucene/src/main/java/org/apache/geode/cache/lucene/internal/LuceneServiceImpl.java @@ -20,11 +20,11 @@ import static org.apache.geode.internal.DataSerializableFixedID.CREATE_REGION_ME import java.util.ArrayList; import java.util.Collection; import java.util.Collections; -import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; @@ -46,6 +46,7 @@ import org.apache.geode.cache.execute.Execution; import org.apache.geode.cache.execute.FunctionService; import org.apache.geode.cache.execute.ResultCollector; import org.apache.geode.cache.lucene.LuceneIndex; +import org.apache.geode.cache.lucene.LuceneIndexDestroyedException; import org.apache.geode.cache.lucene.LuceneIndexExistsException; import org.apache.geode.cache.lucene.LuceneQueryFactory; import org.apache.geode.cache.lucene.LuceneSerializer; @@ -94,8 +95,8 @@ public class LuceneServiceImpl implements InternalLuceneService { private static final Logger logger = LogService.getLogger(); private InternalCache cache; - private final HashMap<String, LuceneIndex> indexMap = new HashMap<String, LuceneIndex>(); - private final HashMap<String, LuceneIndexCreationProfile> definedIndexMap = new HashMap<>(); + private final Map<String, LuceneIndex> indexMap = new ConcurrentHashMap<>(); + private final Map<String, LuceneIndexCreationProfile> definedIndexMap = new ConcurrentHashMap<>(); private IndexListener managementListener; public static boolean LUCENE_REINDEX = Boolean.getBoolean(DistributionConfig.GEMFIRE_PREFIX + "luceneReindex"); @@ -265,7 +266,12 @@ public class LuceneServiceImpl implements InternalLuceneService { LuceneIndexImpl luceneIndex = beforeDataRegionCreated(indexName, regionPath, region.getAttributes(), analyzer, fieldAnalyzers, aeqId, serializer, fields); - afterDataRegionCreated(luceneIndex); + try { + afterDataRegionCreated(luceneIndex); + } catch (LuceneIndexDestroyedException e) { + // @todo log a warning here? + return; + } createLuceneIndexOnDataRegion(region, luceneIndex); } @@ -557,12 +563,21 @@ public class LuceneServiceImpl implements InternalLuceneService { // nothing to do there. } - public void registerIndex(LuceneIndex index) { + private boolean hasIndexBeenDestroyed(String uniqueIndexName) { + return !definedIndexMap.containsKey(uniqueIndexName); + } + + private void registerIndex(LuceneIndex index) { String regionAndIndex = getUniqueIndexName(index.getName(), index.getRegionPath()); - if (!indexMap.containsKey(regionAndIndex)) { - indexMap.put(regionAndIndex, index); + if (hasIndexBeenDestroyed(regionAndIndex)) { + ((InternalLuceneIndex) index).destroy(true); + throw new LuceneIndexDestroyedException(index.getName(), index.getRegionPath()); + } else { + if (!indexMap.containsKey(regionAndIndex)) { + indexMap.put(regionAndIndex, index); + } + definedIndexMap.remove(regionAndIndex); } - definedIndexMap.remove(regionAndIndex); } public void unregisterIndex(final String region) { diff --git a/geode-lucene/src/main/java/org/apache/geode/cache/lucene/internal/cli/LuceneCliStrings.java b/geode-lucene/src/main/java/org/apache/geode/cache/lucene/internal/cli/LuceneCliStrings.java index 81b6ecc..67d72a9 100644 --- a/geode-lucene/src/main/java/org/apache/geode/cache/lucene/internal/cli/LuceneCliStrings.java +++ b/geode-lucene/src/main/java/org/apache/geode/cache/lucene/internal/cli/LuceneCliStrings.java @@ -97,6 +97,8 @@ public class LuceneCliStrings { "Index cannot be empty."; public static final String LUCENE_DESTROY_INDEX__MSG__COULDNOT_FIND_MEMBERS_FOR_REGION_0 = "Could not find any members defining region {0}."; + public static final String LUCENE_DESTROY_INDEX__MSG__COULD_NOT_FIND__MEMBERS_GREATER_THAN_VERSION_0 = + "Could not find any members greater than or equal to version {0}."; public static final String LUCENE_DESTROY_INDEX__MSG__SUCCESSFULLY_DESTROYED_INDEXES_FROM_REGION_0 = "Successfully destroyed all lucene indexes from region {0}"; public static final String LUCENE_DESTROY_INDEX__MSG__SUCCESSFULLY_DESTROYED_INDEX_0_FROM_REGION_1 = diff --git a/geode-lucene/src/main/java/org/apache/geode/cache/lucene/internal/cli/LuceneIndexCommands.java b/geode-lucene/src/main/java/org/apache/geode/cache/lucene/internal/cli/LuceneIndexCommands.java index 43b7d2c..064c42a 100755 --- a/geode-lucene/src/main/java/org/apache/geode/cache/lucene/internal/cli/LuceneIndexCommands.java +++ b/geode-lucene/src/main/java/org/apache/geode/cache/lucene/internal/cli/LuceneIndexCommands.java @@ -14,10 +14,8 @@ */ package org.apache.geode.cache.lucene.internal.cli; -import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; -import java.util.HashSet; import java.util.LinkedHashSet; import java.util.List; import java.util.Objects; @@ -40,6 +38,7 @@ import org.apache.geode.cache.lucene.internal.cli.functions.LuceneSearchIndexFun import org.apache.geode.cache.lucene.internal.security.LucenePermission; import org.apache.geode.distributed.DistributedMember; import org.apache.geode.distributed.internal.InternalConfigurationPersistenceService; +import org.apache.geode.internal.Version; import org.apache.geode.internal.cache.InternalCache; import org.apache.geode.internal.cache.execute.AbstractExecution; import org.apache.geode.management.cli.CliMetaData; @@ -288,11 +287,27 @@ public class LuceneIndexCommands extends InternalGfshCommand { } authorize(Resource.CLUSTER, Operation.MANAGE, LucenePermission.TARGET); - Result result; - List<CliFunctionResult> accumulatedResults = new ArrayList<>(); - final XmlEntity xmlEntity = - executeDestroyIndexFunction(accumulatedResults, indexName, regionPath); - result = getDestroyIndexResult(accumulatedResults, indexName, regionPath); + + // Get members >= 1.6 + Set<DistributedMember> validVersionMembers = + getNormalMembersWithSameOrNewerVersion(Version.GEODE_160); + if (validVersionMembers.isEmpty()) { + return ResultBuilder.createInfoResult(CliStrings.format( + LuceneCliStrings.LUCENE_DESTROY_INDEX__MSG__COULD_NOT_FIND__MEMBERS_GREATER_THAN_VERSION_0, + Version.GEODE_160)); + } + + // Execute the destroy index function + LuceneDestroyIndexInfo indexInfo = new LuceneDestroyIndexInfo(indexName, regionPath); + ResultCollector<?, ?> rc = + executeFunction(destroyIndexFunction, indexInfo, validVersionMembers); + + // Get the result + List<CliFunctionResult> cliFunctionResults = (List<CliFunctionResult>) rc.getResult(); + Result result = getDestroyIndexResult(cliFunctionResults, indexName, regionPath); + + // Get and process the xml entity + XmlEntity xmlEntity = findXmlEntity(cliFunctionResults); if (xmlEntity != null) { persistClusterConfiguration(result, () -> { // Delete the xml entity to remove the index(es) in all groups @@ -304,61 +319,6 @@ public class LuceneIndexCommands extends InternalGfshCommand { return result; } - private XmlEntity executeDestroyIndexFunction(List<CliFunctionResult> accumulatedResults, - String indexName, String regionPath) { - // Destroy has three cases: - // - // - no members define the region - // In this case, send the request to all members to handle the case where the index has been - // created, but not the region - // - // - all members define the region - // In this case, send the request to one of the region members to destroy the index on all - // member - // - // - some members define the region; some don't - // In this case, send the request to one of the region members to destroy the index in all the - // region members. Then send the function to the remaining members to handle the case where - // the index has been created, but not the region - XmlEntity xmlEntity = null; - Set<DistributedMember> regionMembers = findMembersForRegion(regionPath); - Set<DistributedMember> normalMembers = getAllNormalMembers(); - LuceneDestroyIndexInfo indexInfo = new LuceneDestroyIndexInfo(indexName, regionPath); - ResultCollector<?, ?> rc; - if (regionMembers.isEmpty()) { - // Attempt to destroy the proxy index on all members - indexInfo.setDefinedDestroyOnly(true); - rc = executeFunction(destroyIndexFunction, indexInfo, normalMembers); - accumulatedResults.addAll((List<CliFunctionResult>) rc.getResult()); - } else { - // Attempt to destroy the index on a region member - indexInfo.setDefinedDestroyOnly(false); - Set<DistributedMember> singleMember = new HashSet<>(); - singleMember.add(regionMembers.iterator().next()); - rc = executeFunction(destroyIndexFunction, indexInfo, singleMember); - List<CliFunctionResult> cliFunctionResults = (List<CliFunctionResult>) rc.getResult(); - CliFunctionResult cliFunctionResult = cliFunctionResults.get(0); - xmlEntity = cliFunctionResult.getXmlEntity(); - for (DistributedMember regionMember : regionMembers) { - accumulatedResults.add(new CliFunctionResult(regionMember.getId(), - cliFunctionResult.isSuccessful(), cliFunctionResult.getMessage())); - } - // If that succeeds, destroy the proxy index(es) on all other members if necessary - if (cliFunctionResult.isSuccessful()) { - normalMembers.removeAll(regionMembers); - if (!normalMembers.isEmpty()) { - indexInfo.setDefinedDestroyOnly(true); - rc = executeFunction(destroyIndexFunction, indexInfo, normalMembers); - accumulatedResults.addAll((List<CliFunctionResult>) rc.getResult()); - } - } else { - // @todo Should dummy results be added to the accumulatedResults for the non-region - // members in the failed case - } - } - return xmlEntity; - } - private Result getDestroyIndexResult(List<CliFunctionResult> cliFunctionResults, String indexName, String regionPath) { final TabularResultData tabularResult = ResultBuilder.createTabularResultData(); diff --git a/geode-lucene/src/main/java/org/apache/geode/cache/lucene/internal/cli/functions/LuceneDestroyIndexFunction.java b/geode-lucene/src/main/java/org/apache/geode/cache/lucene/internal/cli/functions/LuceneDestroyIndexFunction.java index 0583b52..3271556 100644 --- a/geode-lucene/src/main/java/org/apache/geode/cache/lucene/internal/cli/functions/LuceneDestroyIndexFunction.java +++ b/geode-lucene/src/main/java/org/apache/geode/cache/lucene/internal/cli/functions/LuceneDestroyIndexFunction.java @@ -41,8 +41,35 @@ public class LuceneDestroyIndexFunction implements InternalFunction { ((LuceneServiceImpl) service).destroyDefinedIndexes(regionPath); result = new CliFunctionResult(memberId); } else { - service.destroyIndexes(regionPath); - result = new CliFunctionResult(memberId, getXmlEntity(indexName, regionPath)); + // Destroy all created indexes + CliFunctionResult destroyIndexesResult = null; + Exception destroyIndexesException = null; + try { + service.destroyIndexes(regionPath); + destroyIndexesResult = + new CliFunctionResult(memberId, getXmlEntity(indexName, regionPath)); + } catch (Exception e) { + destroyIndexesException = e; + } + + // Destroy all defined indexes + CliFunctionResult destroyDefinedIndexesResult = null; + Exception destroyDefinedIndexesException = null; + try { + ((LuceneServiceImpl) service).destroyDefinedIndexes(regionPath); + destroyDefinedIndexesResult = new CliFunctionResult(memberId); + } catch (Exception e) { + destroyDefinedIndexesException = e; + } + + // If there are two exceptions, throw one of them. Note: They should be the same 'No + // Lucene indexes were found' exception. Otherwise return the appropriate result. + if (destroyIndexesException != null && destroyDefinedIndexesException != null) { + throw destroyIndexesException; + } else { + result = + destroyIndexesResult == null ? destroyDefinedIndexesResult : destroyIndexesResult; + } } } else { if (indexInfo.isDefinedDestroyOnly()) { diff --git a/geode-lucene/src/test/java/org/apache/geode/cache/lucene/LuceneIndexDestroyDUnitTest.java b/geode-lucene/src/test/java/org/apache/geode/cache/lucene/LuceneIndexDestroyDUnitTest.java index 83541a0..b0a27ea 100644 --- a/geode-lucene/src/test/java/org/apache/geode/cache/lucene/LuceneIndexDestroyDUnitTest.java +++ b/geode-lucene/src/test/java/org/apache/geode/cache/lucene/LuceneIndexDestroyDUnitTest.java @@ -141,6 +141,43 @@ public class LuceneIndexDestroyDUnitTest extends LuceneDUnitTest { } @Test + @Parameters(method = "parametersForIndexDestroys") + public void verifyDestroySingleIndexWithDefinedIndexes(boolean destroyDataRegion, + RegionTestableType regionType) { + // Create index in both members + dataStore1.invoke(createIndex()); + dataStore2.invoke(createIndex()); + + // Verify index defined + dataStore1.invoke(() -> verifyDefinedIndexCreated()); + dataStore2.invoke(() -> verifyDefinedIndexCreated()); + + // Create region in one member + dataStore1.invoke(() -> initDataStore(regionType)); + + // Verify index created in one member and defined in the other + dataStore1.invoke(() -> verifyIndexCreated()); + dataStore2.invoke(() -> verifyDefinedIndexCreated()); + + // Attempt to destroy data region (should fail) + if (destroyDataRegion) { + dataStore1.invoke(() -> destroyDataRegion(false, INDEX_NAME)); + } + + // Destroy index (only needs to be done on one member) + dataStore1.invoke(() -> destroyIndex()); + + // Verify index destroyed in one member and defined index destroyed in the other + dataStore1.invoke(() -> verifyIndexDestroyed()); + dataStore2.invoke(() -> verifyDefinedIndexDestroyed()); + + // Attempt to destroy data region (should succeed) + if (destroyDataRegion) { + dataStore1.invoke(() -> destroyDataRegion(true)); + } + } + + @Test @Parameters(method = "getListOfRegionTestTypes") public void verifyDestroySingleIndexWhileDoingPuts(RegionTestableType regionType) throws Exception { @@ -517,12 +554,20 @@ public class LuceneIndexDestroyDUnitTest extends LuceneDUnitTest { assertNotNull(luceneService.getIndex(INDEX2_NAME, REGION_NAME)); } + private void verifyDefinedIndexCreated() { + verifyDefinedIndexCreated(INDEX_NAME, REGION_NAME); + } + private void verifyDefinedIndexCreated(String indexName, String regionName) { LuceneServiceImpl luceneService = (LuceneServiceImpl) LuceneServiceProvider.get(getCache()); assertNotNull(luceneService.getDefinedIndex(indexName, regionName)); assertEquals(1, getCache().getRegionListeners().size()); } + private void verifyDefinedIndexDestroyed() { + verifyDefinedIndexDestroyed(INDEX_NAME, REGION_NAME); + } + private void verifyDefinedIndexDestroyed(String indexName, String regionName) { LuceneServiceImpl luceneService = (LuceneServiceImpl) LuceneServiceProvider.get(getCache()); assertNull(luceneService.getDefinedIndex(indexName, regionName)); diff --git a/geode-lucene/src/test/java/org/apache/geode/cache/lucene/internal/cli/DestroyLuceneIndexCommandsDUnitTest.java b/geode-lucene/src/test/java/org/apache/geode/cache/lucene/internal/cli/DestroyLuceneIndexCommandsDUnitTest.java new file mode 100644 index 0000000..4380c26 --- /dev/null +++ b/geode-lucene/src/test/java/org/apache/geode/cache/lucene/internal/cli/DestroyLuceneIndexCommandsDUnitTest.java @@ -0,0 +1,407 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more contributor license + * agreements. See the NOTICE file distributed with this work for additional information regarding + * copyright ownership. The ASF licenses this file to You 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 org.apache.geode.cache.lucene.internal.cli; + +import static org.apache.geode.cache.lucene.test.LuceneTestUtilities.INDEX_NAME; +import static org.apache.geode.cache.lucene.test.LuceneTestUtilities.REGION_NAME; +import static org.apache.geode.distributed.ConfigurationProperties.SERIALIZABLE_OBJECT_FILTER; +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.spy; + +import java.io.Serializable; +import java.util.Properties; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +import junitparams.JUnitParamsRunner; +import junitparams.Parameters; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.experimental.categories.Category; +import org.junit.runner.RunWith; +import org.mockito.stubbing.Answer; + +import org.apache.geode.cache.RegionShortcut; +import org.apache.geode.cache.lucene.LuceneService; +import org.apache.geode.cache.lucene.LuceneServiceProvider; +import org.apache.geode.cache.lucene.internal.LuceneServiceImpl; +import org.apache.geode.internal.i18n.LocalizedStrings; +import org.apache.geode.management.internal.cli.i18n.CliStrings; +import org.apache.geode.test.dunit.AsyncInvocation; +import org.apache.geode.test.dunit.rules.ClusterStartupRule; +import org.apache.geode.test.dunit.rules.MemberVM; +import org.apache.geode.test.junit.assertions.CommandResultAssert; +import org.apache.geode.test.junit.categories.DistributedTest; +import org.apache.geode.test.junit.categories.LuceneTest; +import org.apache.geode.test.junit.rules.GfshCommandRule; + +@Category({DistributedTest.class, LuceneTest.class}) +@RunWith(JUnitParamsRunner.class) +public class DestroyLuceneIndexCommandsDUnitTest implements Serializable { + + @Rule + public transient GfshCommandRule gfsh = new GfshCommandRule(); + + @Rule + public ClusterStartupRule cluster = new ClusterStartupRule(); + + private MemberVM locator; + + private MemberVM server1; + + private MemberVM server2; + + private static CountDownLatch indexCreationInProgress; + + private static CountDownLatch indexDestroyComplete; + + @Before + public void before() throws Exception { + Properties props = new Properties(); + props.setProperty(SERIALIZABLE_OBJECT_FILTER, + "org.apache.geode.cache.lucene.internal.cli.DestroyLuceneIndexCommandsDUnitTest"); + locator = cluster.startLocatorVM(0, props); + server1 = cluster.startServerVM(1, props, locator.getPort()); + server2 = cluster.startServerVM(2, props, locator.getPort()); + gfsh.connectAndVerify(locator); + } + + @Test + @Parameters({"true", "false"}) + public void testDestroyIndex(boolean createRegion) throws Exception { + // Create index and region if necessary + server1.invoke(() -> createIndex(1)); + server2.invoke(() -> createIndex(1)); + if (createRegion) { + server1.invoke(() -> createRegion()); + server2.invoke(() -> createRegion()); + } + + // Execute command to destroy index + CommandResultAssert commandResultAssert = + gfsh.executeAndAssertThat("destroy lucene index --name=index0 --region=region"); + + // Assert command was successful and contains the correct rows and output + String expectedStatus = CliStrings.format( + LuceneCliStrings.LUCENE_DESTROY_INDEX__MSG__SUCCESSFULLY_DESTROYED_INDEX_0_FROM_REGION_1, + new Object[] {"index0", "/region"}); + validateCommandResult(commandResultAssert, expectedStatus); + + // Verify defined and created indexes are empty in both members + server1.invoke(() -> verifyDefinedAndCreatedIndexSizes(0, 0)); + server2.invoke(() -> verifyDefinedAndCreatedIndexSizes(0, 0)); + } + + @Test + public void testDestroyIndexWithRegionInOneMember() throws Exception { + // Create index in both members + server1.invoke(() -> createIndex(1)); + server2.invoke(() -> createIndex(1)); + + // Create region in one member + server1.invoke(() -> createRegion()); + + // Execute command to destroy index + CommandResultAssert commandResultAssert = + gfsh.executeAndAssertThat("destroy lucene index --name=index0 --region=region"); + + // Assert command was successful and contains the correct rows and output + String expectedStatus = CliStrings.format( + LuceneCliStrings.LUCENE_DESTROY_INDEX__MSG__SUCCESSFULLY_DESTROYED_INDEX_0_FROM_REGION_1, + new Object[] {"index0", "/region"}); + validateCommandResult(commandResultAssert, expectedStatus); + + // Verify defined and created indexes are empty in both members + server1.invoke(() -> verifyDefinedAndCreatedIndexSizes(0, 0)); + server2.invoke(() -> verifyDefinedAndCreatedIndexSizes(0, 0)); + } + + @Test + public void testDestroyNonExistentIndex() throws Exception { + // Execute command to destroy index + CommandResultAssert commandResultAssert = + gfsh.executeAndAssertThat("destroy lucene index --name=index0 --region=region"); + + // Assert command was successful and contains the correct rows and output + String expectedStatus = LocalizedStrings.LuceneService_INDEX_0_NOT_FOUND_IN_REGION_1 + .toLocalizedString("index0", "/region"); + validateCommandResult(commandResultAssert, expectedStatus); + + // Verify defined and created indexes are empty in both members + server1.invoke(() -> verifyDefinedAndCreatedIndexSizes(0, 0)); + server2.invoke(() -> verifyDefinedAndCreatedIndexSizes(0, 0)); + } + + @Test + public void testDestroyIndexWithRegionCreationInProgress() throws Exception { + server1.invoke(() -> initializeCountDownLatches()); + server2.invoke(() -> initializeCountDownLatches()); + + server1.invoke(() -> createIndexesOnSpy(1)); + server2.invoke(() -> createIndexesOnSpy(1)); + + // Asynchronously create region. This will cause the invokeBeforeAfterDataRegionCreated Answer + // to be invoked which will wait for the index to be destroyed before invoking the real + // afterDataRegionCreated method and completing region creation. The registerIndex method will + // realize the defined index has been destroyed and destroy the real one. + AsyncInvocation server1RegionCreationInvocation = server1.invokeAsync(() -> createRegion()); + AsyncInvocation server2RegionCreationInvocation = server2.invokeAsync(() -> createRegion()); + + // Wait for index creation to be in progress + server1.invoke(() -> waitForIndexCreationInProgress()); + server2.invoke(() -> waitForIndexCreationInProgress()); + + // Execute command to destroy index + CommandResultAssert commandResultAssert = + gfsh.executeAndAssertThat("destroy lucene index --name=index0 --region=region"); + + // Assert command was successful and contains the correct rows and output + String expectedStatus = CliStrings.format( + LuceneCliStrings.LUCENE_DESTROY_INDEX__MSG__SUCCESSFULLY_DESTROYED_INDEX_0_FROM_REGION_1, + new Object[] {"index0", "/region"}); + validateCommandResult(commandResultAssert, expectedStatus); + + // Notify region creation to continue creating the region + server1.invoke(() -> notifyIndexDestroyComplete()); + server2.invoke(() -> notifyIndexDestroyComplete()); + + server1RegionCreationInvocation.await(30, TimeUnit.SECONDS); + server2RegionCreationInvocation.await(30, TimeUnit.SECONDS); + + // Verify defined and created indexes are empty in both members + server1.invoke(() -> verifyDefinedAndCreatedIndexSizes(0, 0)); + server2.invoke(() -> verifyDefinedAndCreatedIndexSizes(0, 0)); + } + + @Test + @Parameters({"true", "false"}) + public void testDestroyIndexesWithOneIndex(boolean createRegion) throws Exception { + // Create index and region if necessary + server1.invoke(() -> createIndex(1)); + server2.invoke(() -> createIndex(1)); + if (createRegion) { + server1.invoke(() -> createRegion()); + server2.invoke(() -> createRegion()); + } + + // Execute command to destroy indexes + CommandResultAssert commandResultAssert = + gfsh.executeAndAssertThat("destroy lucene index --region=region"); + + // Assert command was successful and contains the correct rows and output + String expectedStatus = CliStrings.format( + LuceneCliStrings.LUCENE_DESTROY_INDEX__MSG__SUCCESSFULLY_DESTROYED_INDEXES_FROM_REGION_0, + new Object[] {"/region"}); + validateCommandResult(commandResultAssert, expectedStatus); + + // Verify defined and created indexes are empty in both members + server1.invoke(() -> verifyDefinedAndCreatedIndexSizes(0, 0)); + server2.invoke(() -> verifyDefinedAndCreatedIndexSizes(0, 0)); + } + + @Test + public void testDestroyIndexesWithOneIndexAndRegionInOneMember() throws Exception { + // Create index in both members + server1.invoke(() -> createIndex(1)); + server2.invoke(() -> createIndex(1)); + + // Create region in one member + server1.invoke(() -> createRegion()); + + // Execute command to destroy indexes + CommandResultAssert commandResultAssert = + gfsh.executeAndAssertThat("destroy lucene index --region=region"); + + // Assert command was successful and contains the correct rows and output + String expectedStatus = CliStrings.format( + LuceneCliStrings.LUCENE_DESTROY_INDEX__MSG__SUCCESSFULLY_DESTROYED_INDEXES_FROM_REGION_0, + new Object[] {"/region"}); + validateCommandResult(commandResultAssert, expectedStatus); + + // Verify defined and created indexes are empty in both members + server1.invoke(() -> verifyDefinedAndCreatedIndexSizes(0, 0)); + server2.invoke(() -> verifyDefinedAndCreatedIndexSizes(0, 0)); + } + + @Test + @Parameters({"true", "false"}) + public void testDestroyIndexesWithTwoIndexes(boolean createRegion) throws Exception { + // Create index and region if necessary + server1.invoke(() -> createIndex(2)); + server2.invoke(() -> createIndex(2)); + if (createRegion) { + server1.invoke(() -> createRegion()); + server2.invoke(() -> createRegion()); + } + + // Execute command to destroy indexes + CommandResultAssert commandResultAssert = + gfsh.executeAndAssertThat("destroy lucene index --region=region"); + + // Assert command was successful and contains the correct rows and output + String expectedStatus = CliStrings.format( + LuceneCliStrings.LUCENE_DESTROY_INDEX__MSG__SUCCESSFULLY_DESTROYED_INDEXES_FROM_REGION_0, + new Object[] {"/region"}); + validateCommandResult(commandResultAssert, expectedStatus); + + // Verify defined and created indexes are empty in both members + server1.invoke(() -> verifyDefinedAndCreatedIndexSizes(0, 0)); + server2.invoke(() -> verifyDefinedAndCreatedIndexSizes(0, 0)); + } + + @Test + public void testDestroyIndexesWithTwoIndexesAndRegionInOneMember() throws Exception { + // Create index in both members + server1.invoke(() -> createIndex(2)); + server2.invoke(() -> createIndex(2)); + + // Create region in one member + server1.invoke(() -> createRegion()); + + // Execute command to destroy indexes + CommandResultAssert commandResultAssert = + gfsh.executeAndAssertThat("destroy lucene index --region=region"); + + // Assert command was successful and contains the correct rows and output + String expectedStatus = CliStrings.format( + LuceneCliStrings.LUCENE_DESTROY_INDEX__MSG__SUCCESSFULLY_DESTROYED_INDEXES_FROM_REGION_0, + new Object[] {"/region"}); + validateCommandResult(commandResultAssert, expectedStatus); + + // Verify defined and created indexes are empty in both members + server1.invoke(() -> verifyDefinedAndCreatedIndexSizes(0, 0)); + server2.invoke(() -> verifyDefinedAndCreatedIndexSizes(0, 0)); + } + + @Test + public void testDestroyNonExistentIndexes() throws Exception { + // Execute command to destroy indexes + CommandResultAssert commandResultAssert = + gfsh.executeAndAssertThat("destroy lucene index --region=region"); + + // Assert command was successful and contains the correct rows and output + String expectedStatus = LocalizedStrings.LuceneService_NO_INDEXES_WERE_FOUND_IN_REGION_0 + .toLocalizedString("/region"); + validateCommandResult(commandResultAssert, expectedStatus); + + // Verify defined and created indexes are empty in both members + server1.invoke(() -> verifyDefinedAndCreatedIndexSizes(0, 0)); + server2.invoke(() -> verifyDefinedAndCreatedIndexSizes(0, 0)); + } + + @Test + public void testDestroyIndexesWithRegionCreationInProgress() throws Exception { + server1.invoke(() -> initializeCountDownLatches()); + server2.invoke(() -> initializeCountDownLatches()); + + server1.invoke(() -> createIndexesOnSpy(2)); + server2.invoke(() -> createIndexesOnSpy(2)); + + // Asynchronously create region. This will cause the invokeBeforeAfterDataRegionCreated Answer + // to be invoked which will wait for the indexes to be destroyed before invoking the real + // afterDataRegionCreated method and completing region creation. The registerIndex method will + // realize the defined index has been destroyed and destroy the real one. + AsyncInvocation server1RegionCreationInvocation = server1.invokeAsync(() -> createRegion()); + AsyncInvocation server2RegionCreationInvocation = server2.invokeAsync(() -> createRegion()); + + // Wait for index creation to be in progress + server1.invoke(() -> waitForIndexCreationInProgress()); + server2.invoke(() -> waitForIndexCreationInProgress()); + + // Execute command to destroy index + CommandResultAssert commandResultAssert = + gfsh.executeAndAssertThat("destroy lucene index --region=region"); + + // Assert command was successful and contains the correct rows and output + String expectedStatus = CliStrings.format( + LuceneCliStrings.LUCENE_DESTROY_INDEX__MSG__SUCCESSFULLY_DESTROYED_INDEXES_FROM_REGION_0, + new Object[] {"/region"}); + validateCommandResult(commandResultAssert, expectedStatus); + + // Notify region creation to continue creating the region + server1.invoke(() -> notifyIndexDestroyComplete()); + server2.invoke(() -> notifyIndexDestroyComplete()); + + server1RegionCreationInvocation.await(30, TimeUnit.SECONDS); + server2RegionCreationInvocation.await(30, TimeUnit.SECONDS); + + // Verify defined and created indexes are empty in both members + server1.invoke(() -> verifyDefinedAndCreatedIndexSizes(0, 0)); + server2.invoke(() -> verifyDefinedAndCreatedIndexSizes(0, 0)); + } + + private void validateCommandResult(CommandResultAssert commandResultAssert, + String expectedStatus) { + commandResultAssert.statusIsSuccess(); + commandResultAssert.tableHasRowCount("Member", 2); + commandResultAssert.tableHasColumnOnlyWithValues("Status", expectedStatus); + } + + private void verifyDefinedAndCreatedIndexSizes(int definedIndexesSize, int createdIndexesSize) { + LuceneServiceImpl luceneService = + (LuceneServiceImpl) LuceneServiceProvider.get(ClusterStartupRule.getCache()); + assertThat(luceneService.getIndexes(REGION_NAME)).isEmpty(); + assertThat(luceneService.getDefinedIndexes(REGION_NAME)).isEmpty(); + } + + private void createIndex(int numIndexes) { + LuceneService luceneService = LuceneServiceProvider.get(ClusterStartupRule.getCache()); + for (int i = 0; i < numIndexes; i++) { + luceneService.createIndexFactory().setFields("text" + i).create(INDEX_NAME + i, REGION_NAME); + } + } + + private void createRegion() { + ClusterStartupRule.getCache().createRegionFactory(RegionShortcut.PARTITION).create(REGION_NAME); + } + + private void initializeCountDownLatches() { + indexCreationInProgress = new CountDownLatch(1); + indexDestroyComplete = new CountDownLatch(1); + } + + private void createIndexesOnSpy(int numIndexes) { + LuceneServiceImpl luceneServiceSpy = + (LuceneServiceImpl) spy(LuceneServiceProvider.get(ClusterStartupRule.getCache())); + for (int i = 0; i < numIndexes; i++) { + luceneServiceSpy.createIndexFactory().setFields("text" + i).create(INDEX_NAME + i, + REGION_NAME); + } + + Answer invokeBeforeAfterDataRegionCreated = invocation -> { + // Confirm index creation is in progress + indexCreationInProgress.countDown(); + + // Wait for destroy index invocation to complete + indexDestroyComplete.await(); + + return invocation.callRealMethod(); + }; + + doAnswer(invokeBeforeAfterDataRegionCreated).when(luceneServiceSpy) + .afterDataRegionCreated(any()); + } + + private void waitForIndexCreationInProgress() throws Exception { + indexCreationInProgress.await(); + } + + private void notifyIndexDestroyComplete() throws Exception { + indexDestroyComplete.countDown(); + } +} diff --git a/geode-lucene/src/test/java/org/apache/geode/cache/lucene/internal/cli/LuceneIndexCommandsIntegrationTest.java b/geode-lucene/src/test/java/org/apache/geode/cache/lucene/internal/cli/LuceneIndexCommandsIntegrationTest.java index 2e2dca2..088f1fa 100644 --- a/geode-lucene/src/test/java/org/apache/geode/cache/lucene/internal/cli/LuceneIndexCommandsIntegrationTest.java +++ b/geode-lucene/src/test/java/org/apache/geode/cache/lucene/internal/cli/LuceneIndexCommandsIntegrationTest.java @@ -628,12 +628,20 @@ public class LuceneIndexCommandsIntegrationTest { createIndexWithoutRegion(); } + // Verify destroy all indexes is successful String expectedOutput = CliStrings.format( LuceneCliStrings.LUCENE_DESTROY_INDEX__MSG__SUCCESSFULLY_DESTROYED_INDEXES_FROM_REGION_0, new Object[] {"/region"}); gfsh.executeAndAssertThat("destroy lucene index --region=region").statusIsSuccess() .containsOutput(expectedOutput); + + // Verify destroy all indexes again reports no indexes exist + expectedOutput = LocalizedStrings.LuceneService_NO_INDEXES_WERE_FOUND_IN_REGION_0 + .toLocalizedString(new Object[] {"/region"}); + + gfsh.executeAndAssertThat("destroy lucene index --region=region").statusIsSuccess() + .containsOutput(expectedOutput); } @Test diff --git a/geode-lucene/src/test/java/org/apache/geode/cache/lucene/internal/cli/LuceneIndexCommandsJUnitTest.java b/geode-lucene/src/test/java/org/apache/geode/cache/lucene/internal/cli/LuceneIndexCommandsJUnitTest.java index 9f6bdc0..46a7e5e 100644 --- a/geode-lucene/src/test/java/org/apache/geode/cache/lucene/internal/cli/LuceneIndexCommandsJUnitTest.java +++ b/geode-lucene/src/test/java/org/apache/geode/cache/lucene/internal/cli/LuceneIndexCommandsJUnitTest.java @@ -14,6 +14,8 @@ */ package org.apache.geode.cache.lucene.internal.cli; +import static org.apache.commons.lang.SystemUtils.LINE_SEPARATOR; +import static org.apache.geode.management.internal.cli.result.ResultData.TYPE_TABULAR; import static org.junit.Assert.assertEquals; import static org.mockito.Matchers.isA; import static org.mockito.Mockito.any; @@ -59,6 +61,7 @@ import org.apache.geode.cache.lucene.internal.cli.functions.LuceneListIndexFunct import org.apache.geode.cache.lucene.internal.repository.serializer.HeterogeneousLuceneSerializer; import org.apache.geode.cache.lucene.internal.repository.serializer.PrimitiveSerializer; import org.apache.geode.distributed.DistributedMember; +import org.apache.geode.internal.Version; import org.apache.geode.internal.cache.InternalCache; import org.apache.geode.internal.cache.execute.AbstractExecution; import org.apache.geode.internal.security.SecurityService; @@ -427,34 +430,15 @@ public class LuceneIndexCommandsJUnitTest { } @Test - @Parameters({"true", "false"}) - public void testDestroySingleIndexNoRegionMembers(boolean expectedToSucceed) throws Exception { + public void testDestroySingleIndexNoRegionMembers() throws Exception { LuceneIndexCommands commands = createTestLuceneIndexCommandsForDestroyIndex(); - String indexName = "index"; - String regionPath = "regionPath"; - - final ResultCollector mockResultCollector = mock(ResultCollector.class); final List<CliFunctionResult> cliFunctionResults = new ArrayList<>(); - String expectedStatus; - if (expectedToSucceed) { - expectedStatus = CliStrings.format( - LuceneCliStrings.LUCENE_DESTROY_INDEX__MSG__SUCCESSFULLY_DESTROYED_INDEX_0_FROM_REGION_1, - new Object[] {indexName, regionPath}); - cliFunctionResults.add(new CliFunctionResult("member0")); - } else { - Exception e = new IllegalStateException("failed"); - expectedStatus = e.getMessage(); - cliFunctionResults.add(new CliFunctionResult("member0", e, e.getMessage())); - } - - doReturn(mockResultCollector).when(commands).executeFunction( - isA(LuceneDestroyIndexFunction.class), any(LuceneDestroyIndexInfo.class), any(Set.class)); - doReturn(cliFunctionResults).when(mockResultCollector).getResult(); - - doReturn(Collections.emptySet()).when(commands).getAllNormalMembers(); - doReturn(Collections.emptySet()).when(commands).findMembersForRegion(any()); - - CommandResult result = (CommandResult) commands.destroyIndex(indexName, regionPath); + String expectedStatus = CliStrings.format( + LuceneCliStrings.LUCENE_DESTROY_INDEX__MSG__COULD_NOT_FIND__MEMBERS_GREATER_THAN_VERSION_0, + new Object[] {Version.GEODE_160}) + LINE_SEPARATOR; + cliFunctionResults.add(new CliFunctionResult("member0")); + doReturn(Collections.emptySet()).when(commands).getNormalMembersWithSameOrNewerVersion(any()); + CommandResult result = (CommandResult) commands.destroyIndex("index", "regionPath"); verifyDestroyIndexCommandResult(result, cliFunctionResults, expectedStatus); } @@ -488,42 +472,22 @@ public class LuceneIndexCommandsJUnitTest { isA(LuceneDestroyIndexFunction.class), any(LuceneDestroyIndexInfo.class), any(Set.class)); doReturn(cliFunctionResults).when(mockResultCollector).getResult(); - doReturn(members).when(commands).getAllNormalMembers(); - doReturn(members).when(commands).findMembersForRegion(any()); + doReturn(members).when(commands).getNormalMembersWithSameOrNewerVersion(any()); CommandResult result = (CommandResult) commands.destroyIndex(indexName, regionPath); verifyDestroyIndexCommandResult(result, cliFunctionResults, expectedStatus); } @Test - @Parameters({"true", "false"}) - public void testDestroyAllIndexesNoRegionMembers(boolean expectedToSucceed) throws Exception { + public void testDestroyAllIndexesNoRegionMembers() throws Exception { LuceneIndexCommands commands = createTestLuceneIndexCommandsForDestroyIndex(); - String indexName = null; - String regionPath = "regionPath"; - - final ResultCollector mockResultCollector = mock(ResultCollector.class); + doReturn(Collections.emptySet()).when(commands).getNormalMembersWithSameOrNewerVersion(any()); final List<CliFunctionResult> cliFunctionResults = new ArrayList<>(); - String expectedStatus; - if (expectedToSucceed) { - expectedStatus = CliStrings.format( - LuceneCliStrings.LUCENE_DESTROY_INDEX__MSG__SUCCESSFULLY_DESTROYED_INDEXES_FROM_REGION_0, - new Object[] {regionPath}); - cliFunctionResults.add(new CliFunctionResult("member0")); - } else { - Exception e = new IllegalStateException("failed"); - expectedStatus = e.getMessage(); - cliFunctionResults.add(new CliFunctionResult("member0", e, e.getMessage())); - } - - doReturn(mockResultCollector).when(commands).executeFunction( - isA(LuceneDestroyIndexFunction.class), any(LuceneDestroyIndexInfo.class), any(Set.class)); - doReturn(cliFunctionResults).when(mockResultCollector).getResult(); - - doReturn(Collections.emptySet()).when(commands).getAllNormalMembers(); - doReturn(Collections.emptySet()).when(commands).findMembersForRegion(any()); - - CommandResult result = (CommandResult) commands.destroyIndex(indexName, regionPath); + String expectedStatus = CliStrings.format( + LuceneCliStrings.LUCENE_DESTROY_INDEX__MSG__COULD_NOT_FIND__MEMBERS_GREATER_THAN_VERSION_0, + new Object[] {Version.GEODE_160}) + LINE_SEPARATOR; + cliFunctionResults.add(new CliFunctionResult("member0")); + CommandResult result = (CommandResult) commands.destroyIndex(null, "regionPath"); verifyDestroyIndexCommandResult(result, cliFunctionResults, expectedStatus); } @@ -557,8 +521,7 @@ public class LuceneIndexCommandsJUnitTest { isA(LuceneDestroyIndexFunction.class), any(LuceneDestroyIndexInfo.class), any(Set.class)); doReturn(cliFunctionResults).when(mockResultCollector).getResult(); - doReturn(Collections.emptySet()).when(commands).getAllNormalMembers(); - doReturn(Collections.emptySet()).when(commands).findMembersForRegion(any()); + doReturn(members).when(commands).getNormalMembersWithSameOrNewerVersion(any()); CommandResult result = (CommandResult) commands.destroyIndex(indexName, regionPath); verifyDestroyIndexCommandResult(result, cliFunctionResults, expectedStatus); @@ -580,17 +543,22 @@ public class LuceneIndexCommandsJUnitTest { private void verifyDestroyIndexCommandResult(CommandResult result, List<CliFunctionResult> cliFunctionResults, String expectedStatus) { assertEquals(Status.OK, result.getStatus()); - TabularResultData data = (TabularResultData) result.getResultData(); - List<String> members = data.retrieveAllValues("Member"); - assertEquals(cliFunctionResults.size(), members.size()); - // Verify each member - for (int i = 0; i < members.size(); i++) { - assertEquals("member" + i, members.get(i)); - } - // Verify each status - List<String> status = data.retrieveAllValues("Status"); - for (String statu : status) { - assertEquals(expectedStatus, statu); + if (result.getType().equals(TYPE_TABULAR)) { + TabularResultData data = (TabularResultData) result.getResultData(); + List<String> members = data.retrieveAllValues("Member"); + assertEquals(cliFunctionResults.size(), members.size()); + // Verify each member + for (int i = 0; i < members.size(); i++) { + assertEquals("member" + i, members.get(i)); + } + // Verify each status + List<String> status = data.retrieveAllValues("Status"); + for (String statu : status) { + assertEquals(expectedStatus, statu); + } + } else { + // Info result. Verify next lines are equal. + assertEquals(result.nextLine(), expectedStatus); } } diff --git a/geode-lucene/src/test/java/org/apache/geode/cache/lucene/internal/cli/functions/LuceneDestroyIndexFunctionJUnitTest.java b/geode-lucene/src/test/java/org/apache/geode/cache/lucene/internal/cli/functions/LuceneDestroyIndexFunctionJUnitTest.java index 6b083f4..3928ee5 100644 --- a/geode-lucene/src/test/java/org/apache/geode/cache/lucene/internal/cli/functions/LuceneDestroyIndexFunctionJUnitTest.java +++ b/geode-lucene/src/test/java/org/apache/geode/cache/lucene/internal/cli/functions/LuceneDestroyIndexFunctionJUnitTest.java @@ -134,8 +134,8 @@ public class LuceneDestroyIndexFunctionJUnitTest { function = spy(function); function.execute(this.context); verify(this.service).destroyIndexes(eq(regionPath)); + verify(this.service).destroyDefinedIndexes(eq(regionPath)); verify(function).getXmlEntity(eq(null), eq(regionPath)); - verify(this.service, never()).destroyDefinedIndexes(eq(regionPath)); verify(this.service, never()).destroyIndex(any(), eq(regionPath)); verifyFunctionResult(true); } @@ -148,6 +148,7 @@ public class LuceneDestroyIndexFunctionJUnitTest { when(this.context.getArguments()).thenReturn(indexInfo); LuceneDestroyIndexFunction function = new LuceneDestroyIndexFunction(); doThrow(new IllegalStateException()).when(this.service).destroyIndexes(eq(regionPath)); + doThrow(new IllegalStateException()).when(this.service).destroyDefinedIndexes(eq(regionPath)); function.execute(this.context); verifyFunctionResult(false); }
