Skip to content

Commit 9549df8

Browse files
Refactor FeatureFlags (#17611)
* Refactor FeatureFlags.java. - Remove internal immutable `settings` in favor of `ConcurrentHashMap`. - Move functionality to internal `FeatureFlagsImpl` class. Expose public api of `FeatureFlagsImpl` in FeatureFlags. Expose test api of `FeatureFlagsImpl` in FeatureFlags.TestUtils. - Read and set JVM system properties once on `initializeFeatureFlags`. Remove JVM system properties check from `isEnabled`. - Add `FlagLock` in `TestUtils` to maintain a lock for each feature flag. - Add helper functions to set & access feature flags in a thread safe way. `TestUtils.with(<feature flag>, () -> {})` to execute crtical sections. `New FlagLock(<feature flag>)` for fine grained control. Signed-off-by: Finn Carroll <[email protected]> * Add @LockFeatureFlag annotation - Add annotation in OpenSearchTestCase to enable and lock a flag for the duration of a single test case. Signed-off-by: Finn Carroll <[email protected]> * Update FeatureFlagTests - Add cases for public api. - Add cases for thread safe helpers @LockFeatureFlag FlagLock TestUtils.with Signed-off-by: Finn Carroll <[email protected]> * Remove FeatureFlagSetter Signed-off-by: Finn Carroll <[email protected]> * Find and replace feature flag test case usages with appropriate helper Replace all usages of `FeatureFlagSetter` in tests. Replace all usages of JVM system properties for feature flags in tests. Replace all usages of `initializeFeatureFlags` with `TestUtils.set` in tests. Signed-off-by: Finn Carroll <[email protected]> * Add changelog entry Signed-off-by: Finn Carroll <[email protected]> * Fix lingering test failures - Add missing LockFeatureFlag annotations. - Cannot use annotation in tests which expect exception thrown. - SEARCHABLE_SNAPSHOT_EXTENDED_COMPATIBILITY has no setting? Adding. - Flight server tests need flag enabled on setup. Signed-off-by: Finn Carroll <[email protected]> * Remove concurrency primitives JUnit does not run tests in parallel on the same JVM so these are not necessary. Additionally rename to `FlagWriteLock` for clarity. Signed-off-by: Finn Carroll <[email protected]> * Fix flight service IT. Address ff contant nit. Signed-off-by: Finn Carroll <[email protected]> * Nit. Signed-off-by: Finn Carroll <[email protected]> * Add blurb to DEVELOPER_GUIDE.md regarding ff test utils. Signed-off-by: Finn Carroll <[email protected]> --------- Signed-off-by: Finn Carroll <[email protected]>
1 parent 405586c commit 9549df8

File tree

36 files changed

+530
-340
lines changed

36 files changed

+530
-340
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6565

6666
### Fixed
6767
- Fix bytes parameter on `_cat/recovery` ([#17598](https://github.com/opensearch-project/OpenSearch/pull/17598))
68+
- Fix slow performance of FeatureFlag checks ([#17611](https://github.com/opensearch-project/OpenSearch/pull/17611))
6869

6970
### Security
7071

DEVELOPER_GUIDE.md

+2-1
Original file line numberDiff line numberDiff line change
@@ -594,7 +594,8 @@ Rapidly developing new features often benefit from several release cycles before
594594
uses an Experimental Development process leveraging [Feature Flags](https://featureflags.io/feature-flags/). This allows a feature to be developed using the same process as
595595
a LTS feature but with additional guard rails and communication mechanisms to signal to the users and development community the feature is not yet stable, may change in a future
596596
release, or be removed altogether. Any Developer or User APIs implemented along with the experimental feature should be marked with `@ExperimentalApi` (or documented as
597-
`@opensearch.experimental`) annotation to signal the implementation is not subject to LTS and does not follow backwards compatibility guidelines.
597+
`@opensearch.experimental`) annotation to signal the implementation is not subject to LTS and does not follow backwards compatibility guidelines. When writing tests for
598+
functionality gated behind a feature flag please refer to `FeatureFlags.TestUtils` and the `@LockFeatureFlag` annotation.
598599

599600
#### API Compatibility Checks
600601

modules/mapper-extras/src/test/java/org/opensearch/index/mapper/ScaledFloatFieldMapperTests.java

+1-13
Original file line numberDiff line numberDiff line change
@@ -36,15 +36,12 @@
3636
import org.apache.lucene.index.IndexableField;
3737
import org.opensearch.cluster.metadata.IndexMetadata;
3838
import org.opensearch.common.settings.Settings;
39-
import org.opensearch.common.util.FeatureFlags;
4039
import org.opensearch.common.xcontent.XContentFactory;
4140
import org.opensearch.core.common.bytes.BytesReference;
4241
import org.opensearch.core.xcontent.MediaTypeRegistry;
4342
import org.opensearch.core.xcontent.XContentBuilder;
4443
import org.opensearch.index.compositeindex.datacube.startree.StarTreeIndexSettings;
4544
import org.opensearch.plugins.Plugin;
46-
import org.junit.AfterClass;
47-
import org.junit.BeforeClass;
4845

4946
import java.io.IOException;
5047
import java.util.Arrays;
@@ -98,16 +95,7 @@ public void testExistsQueryDocValuesDisabled() throws IOException {
9895
assertParseMinimalWarnings();
9996
}
10097

101-
@BeforeClass
102-
public static void createMapper() {
103-
FeatureFlags.initializeFeatureFlags(Settings.builder().put(STAR_TREE_INDEX, "true").build());
104-
}
105-
106-
@AfterClass
107-
public static void clearMapper() {
108-
FeatureFlags.initializeFeatureFlags(Settings.EMPTY);
109-
}
110-
98+
@LockFeatureFlag(STAR_TREE_INDEX)
11199
public void testScaledFloatWithStarTree() throws Exception {
112100

113101
double scalingFactorField1 = randomDouble() * 100;

plugins/arrow-flight-rpc/src/internalClusterTest/java/org/opensearch/arrow/flight/ArrowFlightServerIT.java

+3-8
Original file line numberDiff line numberDiff line change
@@ -14,26 +14,20 @@
1414
import org.opensearch.arrow.flight.bootstrap.FlightService;
1515
import org.opensearch.arrow.flight.bootstrap.FlightStreamPlugin;
1616
import org.opensearch.cluster.node.DiscoveryNode;
17-
import org.opensearch.common.util.FeatureFlags;
1817
import org.opensearch.plugins.Plugin;
19-
import org.opensearch.test.FeatureFlagSetter;
2018
import org.opensearch.test.OpenSearchIntegTestCase;
21-
import org.junit.BeforeClass;
2219

2320
import java.util.Collection;
2421
import java.util.Collections;
2522
import java.util.concurrent.TimeUnit;
2623

24+
import static org.opensearch.common.util.FeatureFlags.ARROW_STREAMS;
25+
2726
@OpenSearchIntegTestCase.ClusterScope(scope = OpenSearchIntegTestCase.Scope.SUITE, numDataNodes = 5)
2827
public class ArrowFlightServerIT extends OpenSearchIntegTestCase {
2928

3029
private FlightClientManager flightClientManager;
3130

32-
@BeforeClass
33-
public static void setupFeatureFlags() {
34-
FeatureFlagSetter.set(FeatureFlags.ARROW_STREAMS_SETTING.getKey());
35-
}
36-
3731
@Override
3832
protected Collection<Class<? extends Plugin>> nodePlugins() {
3933
return Collections.singleton(FlightStreamPlugin.class);
@@ -48,6 +42,7 @@ public void setUp() throws Exception {
4842
flightClientManager = flightService.getFlightClientManager();
4943
}
5044

45+
@LockFeatureFlag(ARROW_STREAMS)
5146
public void testArrowFlightEndpoint() throws Exception {
5247
for (DiscoveryNode node : getClusterState().nodes()) {
5348
try (FlightClient flightClient = flightClientManager.getFlightClient(node.getId()).get()) {

plugins/arrow-flight-rpc/src/test/java/org/opensearch/arrow/flight/FlightStreamPluginTests.java

+3-7
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,7 @@
1919
import org.opensearch.common.network.NetworkService;
2020
import org.opensearch.common.settings.Setting;
2121
import org.opensearch.common.settings.Settings;
22-
import org.opensearch.common.util.FeatureFlags;
2322
import org.opensearch.plugins.SecureTransportSettingsProvider;
24-
import org.opensearch.test.FeatureFlagSetter;
2523
import org.opensearch.test.OpenSearchTestCase;
2624
import org.opensearch.threadpool.ExecutorBuilder;
2725
import org.opensearch.threadpool.ThreadPool;
@@ -31,19 +29,18 @@
3129
import java.util.List;
3230
import java.util.function.Supplier;
3331

34-
import static org.opensearch.common.util.FeatureFlags.ARROW_STREAMS_SETTING;
32+
import static org.opensearch.common.util.FeatureFlags.ARROW_STREAMS;
3533
import static org.opensearch.plugins.NetworkPlugin.AuxTransport.AUX_TRANSPORT_TYPES_KEY;
3634
import static org.mockito.Mockito.mock;
3735
import static org.mockito.Mockito.when;
3836

3937
public class FlightStreamPluginTests extends OpenSearchTestCase {
40-
private Settings settings;
38+
private final Settings settings = Settings.EMPTY;
4139
private ClusterService clusterService;
4240

4341
@Override
4442
public void setUp() throws Exception {
4543
super.setUp();
46-
settings = Settings.builder().put(ARROW_STREAMS_SETTING.getKey(), true).build();
4744
clusterService = mock(ClusterService.class);
4845
ClusterState clusterState = mock(ClusterState.class);
4946
DiscoveryNodes nodes = mock(DiscoveryNodes.class);
@@ -52,9 +49,8 @@ public void setUp() throws Exception {
5249
when(nodes.getLocalNodeId()).thenReturn("test-node");
5350
}
5451

52+
@LockFeatureFlag(ARROW_STREAMS)
5553
public void testPluginEnabled() throws IOException {
56-
FeatureFlags.initializeFeatureFlags(settings);
57-
FeatureFlagSetter.set(ARROW_STREAMS_SETTING.getKey());
5854
FlightStreamPlugin plugin = new FlightStreamPlugin(settings);
5955
Collection<Object> components = plugin.createComponents(
6056
null,

plugins/arrow-flight-rpc/src/test/java/org/opensearch/arrow/flight/bootstrap/FlightClientManagerTests.java

+4-2
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,6 @@
2828
import org.opensearch.core.action.ActionListener;
2929
import org.opensearch.core.common.transport.BoundTransportAddress;
3030
import org.opensearch.core.common.transport.TransportAddress;
31-
import org.opensearch.test.FeatureFlagSetter;
3231
import org.opensearch.test.OpenSearchTestCase;
3332
import org.opensearch.threadpool.ThreadPool;
3433
import org.opensearch.transport.client.Client;
@@ -55,6 +54,7 @@
5554
import io.netty.util.NettyRuntime;
5655

5756
import static org.opensearch.arrow.flight.bootstrap.FlightClientManager.LOCATION_TIMEOUT_MS;
57+
import static org.opensearch.common.util.FeatureFlags.ARROW_STREAMS;
5858
import static org.mockito.ArgumentMatchers.any;
5959
import static org.mockito.ArgumentMatchers.eq;
6060
import static org.mockito.Mockito.doAnswer;
@@ -64,6 +64,7 @@
6464

6565
@SuppressWarnings("unchecked")
6666
public class FlightClientManagerTests extends OpenSearchTestCase {
67+
private static FeatureFlags.TestUtils.FlagWriteLock ffLock = null;
6768

6869
private static BufferAllocator allocator;
6970
private static EventLoopGroup elg;
@@ -78,6 +79,7 @@ public class FlightClientManagerTests extends OpenSearchTestCase {
7879

7980
@BeforeClass
8081
public static void setupClass() throws Exception {
82+
ffLock = new FeatureFlags.TestUtils.FlagWriteLock(ARROW_STREAMS);
8183
ServerConfig.init(Settings.EMPTY);
8284
allocator = new RootAllocator();
8385
elg = ServerConfig.createELG("test-grpc-worker-elg", NettyRuntime.availableProcessors() * 2);
@@ -89,7 +91,6 @@ public void setUp() throws Exception {
8991
super.setUp();
9092
locationUpdaterExecutor = Executors.newScheduledThreadPool(1);
9193

92-
FeatureFlagSetter.set(FeatureFlags.ARROW_STREAMS_SETTING.getKey());
9394
clusterService = mock(ClusterService.class);
9495
client = mock(Client.class);
9596
state = getDefaultState();
@@ -176,6 +177,7 @@ private DiscoveryNode createNode(String nodeId, String host, int port) throws Ex
176177
@AfterClass
177178
public static void tearClass() {
178179
allocator.close();
180+
ffLock.close();
179181
}
180182

181183
public void testGetFlightClientForExistingNode() {

plugins/arrow-flight-rpc/src/test/java/org/opensearch/arrow/flight/bootstrap/FlightServiceTests.java

+4-2
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@
1919
import org.opensearch.common.settings.Settings;
2020
import org.opensearch.common.util.FeatureFlags;
2121
import org.opensearch.core.common.transport.TransportAddress;
22-
import org.opensearch.test.FeatureFlagSetter;
2322
import org.opensearch.test.OpenSearchTestCase;
2423
import org.opensearch.threadpool.ThreadPool;
2524
import org.opensearch.transport.client.Client;
@@ -32,10 +31,12 @@
3231
import java.util.concurrent.ExecutorService;
3332
import java.util.concurrent.atomic.AtomicInteger;
3433

34+
import static org.opensearch.common.util.FeatureFlags.ARROW_STREAMS;
3535
import static org.mockito.Mockito.mock;
3636
import static org.mockito.Mockito.when;
3737

3838
public class FlightServiceTests extends OpenSearchTestCase {
39+
FeatureFlags.TestUtils.FlagWriteLock ffLock = null;
3940

4041
private Settings settings;
4142
private ClusterService clusterService;
@@ -47,7 +48,7 @@ public class FlightServiceTests extends OpenSearchTestCase {
4748
@Override
4849
public void setUp() throws Exception {
4950
super.setUp();
50-
FeatureFlagSetter.set(FeatureFlags.ARROW_STREAMS_SETTING.getKey());
51+
ffLock = new FeatureFlags.TestUtils.FlagWriteLock(ARROW_STREAMS);
5152
int availablePort = getBasePort(9500) + port.addAndGet(1);
5253
settings = Settings.EMPTY;
5354
localNode = createNode(availablePort);
@@ -147,6 +148,7 @@ public void testLifecycleStateTransitions() throws Exception {
147148
@Override
148149
public void tearDown() throws Exception {
149150
super.tearDown();
151+
ffLock.close();
150152
}
151153

152154
private DiscoveryNode createNode(int port) throws Exception {

0 commit comments

Comments
 (0)