-
Notifications
You must be signed in to change notification settings - Fork 70
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add feature flag for QueryPhaseSearcher #214
Changes from 3 commits
1fa7e78
4985c33
3fabc54
55780e6
5142d3f
c1356cf
2617ca4
a63b983
a6c52bb
86b51f5
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
@@ -0,0 +1,37 @@ | ||||||
/* | ||||||
* Copyright OpenSearch Contributors | ||||||
* SPDX-License-Identifier: Apache-2.0 | ||||||
*/ | ||||||
|
||||||
package org.opensearch.neuralsearch.util; | ||||||
|
||||||
import org.opensearch.common.util.FeatureFlags; | ||||||
import org.opensearch.core.common.Strings; | ||||||
import org.opensearch.transport.TransportSettings; | ||||||
|
||||||
/** | ||||||
* Abstracts feature flags operations specific to neural-search plugin | ||||||
*/ | ||||||
public class PluginFeatureFlags { | ||||||
|
||||||
/** | ||||||
* Used to test feature flags whose values are expected to be booleans. | ||||||
* This method returns true if the value is "true" (case-insensitive), | ||||||
* and false otherwise. | ||||||
* Checks alternative flag names as they may be different for plugins | ||||||
*/ | ||||||
public static boolean isEnabled(String featureFlagName) { | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ack |
||||||
return FeatureFlags.isEnabled(featureFlagName) || FeatureFlags.isEnabled(transportFeatureName(featureFlagName)); | ||||||
} | ||||||
|
||||||
/** | ||||||
* Get the feature name that is used for transport specific features. It's used by core for all features | ||||||
* defined at plugin level (https://github.com/opensearch-project/OpenSearch/blob/main/server/src/main/java/org/opensearch/plugins/PluginsService.java#L277) | ||||||
*/ | ||||||
private static String transportFeatureName(final String name) { | ||||||
if (Strings.isNullOrEmpty(name)) { | ||||||
return name; | ||||||
} | ||||||
return String.join(".", TransportSettings.FEATURE_PREFIX, name); | ||||||
} | ||||||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -6,11 +6,13 @@ | |
package org.opensearch.neuralsearch.plugin; | ||
|
||
import static org.mockito.Mockito.mock; | ||
import static org.opensearch.neuralsearch.plugin.NeuralSearch.NEURAL_SEARCH_HYBRID_SEARCH_ENABLED; | ||
|
||
import java.util.List; | ||
import java.util.Map; | ||
import java.util.Optional; | ||
|
||
import org.opensearch.common.SuppressForbidden; | ||
import org.opensearch.ingest.Processor; | ||
import org.opensearch.neuralsearch.processor.TextEmbeddingProcessor; | ||
import org.opensearch.neuralsearch.query.HybridQueryBuilder; | ||
|
@@ -32,13 +34,23 @@ public void testQuerySpecs() { | |
assertTrue(querySpecs.stream().anyMatch(spec -> HybridQueryBuilder.NAME.equals(spec.getName().getPreferredName()))); | ||
} | ||
|
||
@SuppressForbidden(reason = "manipulates system properties for testing") | ||
public void testQueryPhaseSearcher() { | ||
NeuralSearch plugin = new NeuralSearch(); | ||
Optional<QueryPhaseSearcher> queryPhaseSearcher = plugin.getQueryPhaseSearcher(); | ||
|
||
assertNotNull(queryPhaseSearcher); | ||
assertFalse(queryPhaseSearcher.isEmpty()); | ||
assertTrue(queryPhaseSearcher.get() instanceof HybridQueryPhaseSearcher); | ||
assertTrue(queryPhaseSearcher.isEmpty()); | ||
|
||
System.setProperty(NEURAL_SEARCH_HYBRID_SEARCH_ENABLED, "true"); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. should we set this property in base class? and why test are extending OpenSearchTestCase, there is should be a base Neural Search test class There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I was thinking that for some unit tests we may want to disable it, that would be complex if it's set in base class. I'll make setting in a method and will call it in a setup phase for this test class. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. sure |
||
|
||
Optional<QueryPhaseSearcher> queryPhaseSearcherWithFeatureFlagDisabled = plugin.getQueryPhaseSearcher(); | ||
|
||
assertNotNull(queryPhaseSearcherWithFeatureFlagDisabled); | ||
assertFalse(queryPhaseSearcherWithFeatureFlagDisabled.isEmpty()); | ||
assertTrue(queryPhaseSearcherWithFeatureFlagDisabled.get() instanceof HybridQueryPhaseSearcher); | ||
|
||
System.setProperty(NEURAL_SEARCH_HYBRID_SEARCH_ENABLED, ""); | ||
} | ||
|
||
public void testProcessors() { | ||
|
@@ -48,4 +60,12 @@ public void testProcessors() { | |
assertNotNull(processors); | ||
assertNotNull(processors.get(TextEmbeddingProcessor.TYPE)); | ||
} | ||
|
||
public void testFeature() { | ||
NeuralSearch plugin = new NeuralSearch(); | ||
Optional<String> feature = plugin.getFeature(); | ||
assertNotNull(feature); | ||
assertFalse(feature.isEmpty()); | ||
assertEquals(NEURAL_SEARCH_HYBRID_SEARCH_ENABLED, feature.get()); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
/* | ||
* Copyright OpenSearch Contributors | ||
* SPDX-License-Identifier: Apache-2.0 | ||
*/ | ||
|
||
package org.opensearch.neuralsearch.util; | ||
|
||
import static org.hamcrest.Matchers.allOf; | ||
import static org.hamcrest.Matchers.containsString; | ||
|
||
import org.opensearch.common.settings.Settings; | ||
import org.opensearch.common.util.FeatureFlags; | ||
import org.opensearch.test.OpenSearchTestCase; | ||
import org.opensearch.transport.TransportSettings; | ||
|
||
public class PluginFeatureFlagsTests extends OpenSearchTestCase { | ||
|
||
public void testIsEnabled_whenNamePassed_thenSuccessful() { | ||
String settingName = "my.cool.setting"; | ||
Settings settings = Settings.builder().put(settingName, true).build(); | ||
FeatureFlags.initializeFeatureFlags(settings); | ||
assertTrue(PluginFeatureFlags.isEnabled(settingName)); | ||
} | ||
|
||
public void testTransportFeaturePrefix_whenNamePassedWithPrefix_thenSuccessful() { | ||
String settingNameWithoutPrefix = "my.cool.setting"; | ||
String settingNameWithPrefix = new StringBuilder().append(TransportSettings.FEATURE_PREFIX) | ||
.append('.') | ||
.append(settingNameWithoutPrefix) | ||
.toString(); | ||
Settings settings = Settings.builder().put(settingNameWithPrefix, true).build(); | ||
FeatureFlags.initializeFeatureFlags(settings); | ||
assertTrue(PluginFeatureFlags.isEnabled(settingNameWithoutPrefix)); | ||
} | ||
|
||
public void testIsEnabled_whenNonExistentFeature_thenFail() { | ||
String settingName = "my.very_cool.setting"; | ||
Settings settings = Settings.builder().put(settingName, true).build(); | ||
FeatureFlags.initializeFeatureFlags(settings); | ||
assertFalse(PluginFeatureFlags.isEnabled("some_random_feature")); | ||
} | ||
|
||
public void testIsEnabled_whenFeatureIsNotBoolean_thenFail() { | ||
String settingName = "my.cool.setting"; | ||
Settings settings = Settings.builder().put(settingName, 1234).build(); | ||
FeatureFlags.initializeFeatureFlags(settings); | ||
IllegalArgumentException exception = expectThrows(IllegalArgumentException.class, () -> PluginFeatureFlags.isEnabled(settingName)); | ||
assertThat( | ||
exception.getMessage(), | ||
allOf(containsString("Failed to parse value"), containsString("only [true] or [false] are allowed")) | ||
); | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we need to try "transport" feature prefix, as this is the way core adding features that are set in plugins.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Then why both? Shouldn't we only check with "transport" feature prefix?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wanted to encapsulate that logic so client can do only one call. Otherwise on a client we need to do two calls:
featureFlags.isEnabled(flag) || customFeatureFlags.isEnabled(flag)
. Depending on scenario one feature flag can be set in one form or another (with or without prefix)There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I still think it is client's responsibility to pass correct key. In this case
transport.features.neural_search_hybrid_search_enabled
.There is no such case that
neural_search_hybrid_search_enabled
can be enabled by settingneural_search_hybrid_search_enabled
as true but only by settingtransport.features.neural_search_hybrid_search_enabled
as true I believe?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see your point, it can be that flag is set in two places and as we read it with OR and one that set in plugin is always TRUE then we cannot set it to FALSE ever. I'll rework to only use one name or another, in fact it should be always with the prefix except the definition.