Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
53 commits
Select commit Hold shift + click to select a range
8a95da1
feat(store): Use pluggable DirectoryFactory in shard store validation
krishna-ggk Apr 16, 2026
c71c6b9
Remove duplicate import.
krishna-ggk Apr 21, 2026
fff4b09
Formatting fixes.
krishna-ggk Apr 24, 2026
e23b6c8
DissectMatch: fix 'occured' -> 'occurred' in Javadoc (#21244)
SAY-5 Apr 16, 2026
0b6f907
Move OpenSearch-specific Jenkins file from opensearch-build (#21113)
andrross Apr 16, 2026
823a9fc
dissect: fix 'occured' -> 'occurred' typo in DissectMatch#getResults …
SAY-5 Apr 16, 2026
3c960cd
Add version 3.6.1 on main (#21228)
andrross Apr 16, 2026
b8303b5
Add dependency on native rust lib build for composite plugin test tas…
mgodwan Apr 17, 2026
c7f4e2b
Implement Directory and IndexInput layers for WritableWarm tiered sto…
MayankHarsh03 Apr 17, 2026
1d77bdc
Add pipeline execution metrics to PollingIngestStats for pull-based i…
imRishN Apr 18, 2026
52db7c2
Update Jackson to 3.1.2 (#21271)
reta Apr 18, 2026
f97fadf
BitsetFilterCache refactor: Change it to a node level cache with a si…
sgup432 Apr 19, 2026
e40ca68
[GRPC] Add gRPC support for Min, Max, and Terms aggregations (#21205)
lucy66hw Apr 19, 2026
44ba0f2
Add server-side implementation for GetTieringStatus and ListTieringSt…
GeekGlider Apr 20, 2026
e431451
Make sure Netty4Http3ServerTransport uses configured HeaderVerifier a…
reta Apr 20, 2026
417435b
Adding per query memory tracking in Datafusion plugin (#21211)
himshikhagupta Apr 20, 2026
1f31a17
Validate segment count during catalog snapshot deserialization (#21236)
ask-kamal-nayan Apr 20, 2026
82b3780
Fix Rounding.isUTC() to recognize UTC timezone aliases (#21221)
Hailong-am Apr 20, 2026
51e62db
Improve RoutingNode invariant assertion efficiency (#21193)
andrross Apr 20, 2026
0e161b9
Bump org.jsoup:jsoup from 1.22.1 to 1.22.2 in /plugins/ingest-attachm…
dependabot[bot] Apr 20, 2026
e0d141c
Fix runner for cron Jenkins jobs (#21298)
andrross Apr 20, 2026
e40f495
Add Dynamic Properties Support (#20816)
aparajita31pandey Apr 21, 2026
a4c7d34
[GRPC] Support indices_boost_2 array format for gRPC (#21300)
lucy66hw Apr 21, 2026
199c0f7
Add tiered object storage crate for warm node routing (#21204)
nishchay21 Apr 21, 2026
d2431ca
Fix map_unmapped_fields_as_text lost after dynamic mapping update (#2…
andrross Apr 21, 2026
7421201
Fix YAML parser boolean handling after Jackson 3.x migration (#21294)
andrross Apr 21, 2026
aa5677a
Analytics Plugin Planner — DAG Construction, Plan Forking, and Fragme…
expani Apr 21, 2026
c206fb2
File Deletions Support through IndexFileDeleter in CatalogSnapshotMan…
rayshrey Apr 21, 2026
479c42d
[BUG] Fix hang in bulk request when index is deleted (#21305)
xiaoyuan0821 Apr 21, 2026
9bc1ac3
Document security setup for Arrow Flight RPC transport (#21314)
rishabhmaurya Apr 21, 2026
9c6266a
Fix flaky testThreeZoneOneReplicaWithForceZoneValueAndLoadAwareness (…
andrross Apr 22, 2026
5da6c3e
fix TaskResourceTrackingService LogManager (#21320)
guojialiang92 Apr 22, 2026
53ec49d
[DataFormatAwareEngine] Make flush idempotent of CatalogSnapshot (#21…
rayshrey Apr 22, 2026
2407979
Fix worker gone test failure in sandbox (#21270)
cwperks Apr 22, 2026
989f526
Add an event-driven scheduler and stage execution for analytics engin…
mch2 Apr 22, 2026
4368e20
Integrating parquet-data-format to search path (#21306)
vinaykpud Apr 23, 2026
bdfa896
Native Arrow transport path with zero-copy transfer (#21253)
rishabhmaurya Apr 23, 2026
2105e1d
Add server-side implementation for HotToWarm, WarmToHot, and CancelTi…
GeekGlider Apr 23, 2026
1b492b0
Add prefetch settings and stored fields prefetch for WritableWarm tie…
MayankHarsh03 Apr 23, 2026
4c86fb9
Null-guard accumulator in QueryPhaseResultConsumerTests to fix flaky …
AndreKurait Apr 24, 2026
f9c803c
Add Lucene engine impl for pluggable data formats (#21299)
mgodwan Apr 24, 2026
36535ce
Added substrait converter for the fragments (#21348)
vinaykpud Apr 24, 2026
931ca65
Fix O(n^2) removeAll in remote translog metadata cleanup (#21350)
gbbafna Apr 27, 2026
ddb221e
Fix Flaky Test Report for Netty4Http3IT (#21360)
reta Apr 27, 2026
ac1ae2f
Fix for RemoteIndexPrimaryRelocationIT Flaky test (#21249)
divyaruhil Apr 27, 2026
f3aa3bb
Update logic in FipsBuildParams.isInFipsApprovedOnlyMode to check for…
cwperks Apr 27, 2026
ccdda9c
Fix BlockTransferManagerTests to extend OpenSearchTestCase instead of…
MayankHarsh03 Apr 28, 2026
032f469
Adding CompositeMergeHandler and CompositeMergePolicy (#21128)
darjisagar7 Apr 28, 2026
e517a29
Fix incorrect defaults in FieldStorageResolver. (#21408)
mch2 Apr 28, 2026
f5a8b8d
feat(store): Use pluggable DirectoryFactory in shard store validation
Apr 16, 2026
c1ff310
Remove duplicate import.
Apr 21, 2026
f68b433
Formatting fixes.
Apr 24, 2026
85f08bf
Merge branch 'main' into pluggable-directory-store-validation
krishna-ggk Apr 28, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,16 @@
import org.opensearch.core.index.shard.ShardId;
import org.opensearch.core.xcontent.NamedXContentRegistry;
import org.opensearch.env.NodeEnvironment;
import org.opensearch.index.IndexModule;
import org.opensearch.index.IndexService;
import org.opensearch.index.IndexSettings;
import org.opensearch.index.shard.IndexShard;
import org.opensearch.index.shard.ShardPath;
import org.opensearch.index.shard.ShardStateMetadata;
import org.opensearch.index.store.Store;
import org.opensearch.indices.IndicesService;
import org.opensearch.indices.replication.checkpoint.ReplicationCheckpoint;
import org.opensearch.plugins.IndexStorePlugin;

import java.io.IOException;
import java.util.Objects;
Expand Down Expand Up @@ -84,7 +87,14 @@ public static GatewayStartedShard getShardInfoOnLocalNode(
if (shardPath == null) {
throw new IllegalStateException(shardId + " no shard path found");
}
Store.tryOpenIndex(shardPath.resolveIndex(), shardId, nodeEnv::shardLock, logger);
Store.tryOpenIndex(
shardPath.resolveIndex(),
shardId,
nodeEnv::shardLock,
logger,
resolveDirectoryFactory(shardId, indicesService, settings, clusterService),
resolveIndexSettings(shardId, indicesService, settings, clusterService)
);
} catch (Exception exception) {
final ShardPath finalShardPath = shardPath;
logger.trace(
Expand Down Expand Up @@ -114,6 +124,47 @@ public static GatewayStartedShard getShardInfoOnLocalNode(
return new GatewayStartedShard(null, false, null);
}

/**
* Resolves the {@link IndexStorePlugin.DirectoryFactory} for the given shard's index.
* Returns null if the index uses the default store type.
*/
private static IndexStorePlugin.DirectoryFactory resolveDirectoryFactory(
ShardId shardId,
IndicesService indicesService,
Settings settings,
ClusterService clusterService
) {
IndexSettings indexSettings = resolveIndexSettings(shardId, indicesService, settings, clusterService);
if (indexSettings == null) {
return null;
}
String storeType = IndexModule.INDEX_STORE_TYPE_SETTING.get(indexSettings.getSettings());
if (storeType.isEmpty()) {
return null;
}
return indicesService.getDirectoryFactories().get(storeType);
}

/**
* Resolves the {@link IndexSettings} for the given shard's index.
*/
private static IndexSettings resolveIndexSettings(
ShardId shardId,
IndicesService indicesService,
Settings settings,
ClusterService clusterService
) {
IndexService indexService = indicesService.indexService(shardId.getIndex());
if (indexService != null) {
return indexService.getIndexSettings();
}
IndexMetadata metadata = clusterService.state().metadata().index(shardId.getIndex());
if (metadata != null) {
Comment on lines +161 to +162

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Question: Will this be expensive to be invoked in recover path?

return new IndexSettings(metadata, settings);
}
return null;
}

/**
* This class encapsulates the metadata about a started shard that needs to be persisted or sent between nodes.
* This is used in {@link TransportNodesListGatewayStartedShardsBatch.NodeGatewayStartedShardsBatch} to construct the response for each node, instead of
Expand Down
57 changes: 55 additions & 2 deletions server/src/main/java/org/opensearch/index/store/Store.java
Original file line number Diff line number Diff line change
Expand Up @@ -618,10 +618,28 @@ public static MetadataSnapshot readMetadataSnapshot(
ShardId shardId,
NodeEnvironment.ShardLocker shardLocker,
Logger logger
) throws IOException {
return readMetadataSnapshot(indexLocation, shardId, shardLocker, logger, null, null);
}

/**
* Reads a MetadataSnapshot from the given index locations or returns an empty snapshot if it can't be read.
* When a {@link IndexStorePlugin.DirectoryFactory} and {@link IndexSettings} are provided, the directory
* is created using the factory (e.g. for encrypted indices). Otherwise falls back to a plain {@link NIOFSDirectory}.
*
* @throws IOException if the index we try to read is corrupted
*/
public static MetadataSnapshot readMetadataSnapshot(
Path indexLocation,
ShardId shardId,
NodeEnvironment.ShardLocker shardLocker,
Logger logger,
IndexStorePlugin.DirectoryFactory directoryFactory,
IndexSettings indexSettings
) throws IOException {
try (
ShardLock lock = shardLocker.lock(shardId, "read metadata snapshot", TimeUnit.SECONDS.toMillis(5));
Directory dir = new NIOFSDirectory(indexLocation)
Directory dir = openDirectory(indexLocation, directoryFactory, indexSettings, shardId)
) {
failIfCorrupted(dir);
return new MetadataSnapshot((IndexCommit) null, dir, logger);
Expand All @@ -642,16 +660,51 @@ public static MetadataSnapshot readMetadataSnapshot(
*/
public static void tryOpenIndex(Path indexLocation, ShardId shardId, NodeEnvironment.ShardLocker shardLocker, Logger logger)
throws IOException, ShardLockObtainFailedException {
tryOpenIndex(indexLocation, shardId, shardLocker, logger, null, null);
}

/**
* Tries to open an index for the given location. This includes reading the
* segment infos and possible corruption markers. If the index can not
* be opened, an exception is thrown.
* When a {@link IndexStorePlugin.DirectoryFactory} and {@link IndexSettings} are provided, the directory
* is created using the factory (e.g. for encrypted indices). Otherwise falls back to a plain {@link NIOFSDirectory}.
*/
public static void tryOpenIndex(
Path indexLocation,
ShardId shardId,
NodeEnvironment.ShardLocker shardLocker,
Logger logger,
IndexStorePlugin.DirectoryFactory directoryFactory,
IndexSettings indexSettings
) throws IOException, ShardLockObtainFailedException {
try (
ShardLock lock = shardLocker.lock(shardId, "open index", TimeUnit.SECONDS.toMillis(5));
Directory dir = new NIOFSDirectory(indexLocation)
Directory dir = openDirectory(indexLocation, directoryFactory, indexSettings, shardId)
) {
failIfCorrupted(dir);
SegmentInfos segInfo = Lucene.readSegmentInfos(dir);
logger.trace("{} loaded segment info [{}]", shardId, segInfo);
}
}

/**
* Opens a directory for the given index location. If a {@link IndexStorePlugin.DirectoryFactory} is provided,
* it is used to create the directory; otherwise a plain {@link NIOFSDirectory} is used.
*/
private static Directory openDirectory(
Path indexLocation,
IndexStorePlugin.DirectoryFactory directoryFactory,
IndexSettings indexSettings,
ShardId shardId
) throws IOException {
if (directoryFactory != null && indexSettings != null) {
ShardPath shardPath = new ShardPath(false, indexLocation.getParent(), indexLocation.getParent(), shardId);
return directoryFactory.newDirectory(indexSettings, shardPath);
}
return new NIOFSDirectory(indexLocation);
}

/**
* The returned IndexOutput validates the files checksum.
* <p>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1418,6 +1418,13 @@ public IndicesQueryCache getIndicesQueryCache() {
return indicesQueryCache;
}

/**
* Returns the registered directory factories keyed by store type name.
*/
public Map<String, IndexStorePlugin.DirectoryFactory> getDirectoryFactories() {
return directoryFactories;
}

/**
* Accumulate stats from the passed Object. Use this instead of
* {@link #addDocStatusStats(org.opensearch.index.shard.IndexingStats.Stats.DocStatusStats)} after
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import org.opensearch.core.common.io.stream.Writeable;
import org.opensearch.core.index.shard.ShardId;
import org.opensearch.env.NodeEnvironment;
import org.opensearch.index.IndexModule;
import org.opensearch.index.IndexService;
import org.opensearch.index.IndexSettings;
import org.opensearch.index.seqno.ReplicationTracker;
Expand All @@ -30,6 +31,7 @@
import org.opensearch.index.store.Store;
import org.opensearch.index.store.StoreFileMetadata;
import org.opensearch.indices.IndicesService;
import org.opensearch.plugins.IndexStorePlugin;

import java.io.IOException;
import java.util.Collections;
Expand Down Expand Up @@ -108,11 +110,20 @@ public static StoreFilesMetadata listShardMetadataInternal(
// 1) a shard is being constructed, which means the cluster-manager will not use a copy of this replica
// 2) A shard is shutting down and has not cleared it's content within lock timeout. In this case the cluster-manager may not
// reuse local resources.
final IndexStorePlugin.DirectoryFactory directoryFactory = resolveDirectoryFactory(
shardId,
indicesService,
settings,
clusterService
);
final IndexSettings indexSettings = resolveIndexSettings(shardId, indicesService, settings, clusterService);
final Store.MetadataSnapshot metadataSnapshot = Store.readMetadataSnapshot(
shardPath.resolveIndex(),
shardId,
nodeEnv::shardLock,
logger
logger,
directoryFactory,
indexSettings
);
// We use peer recovery retention leases from the primary for allocating replicas. We should always have retention leases when
// we refresh shard info after the primary has started. Hence, we can ignore retention leases if there is no active shard.
Expand All @@ -127,6 +138,47 @@ public static StoreFilesMetadata listShardMetadataInternal(
}
}

/**
* Resolves the {@link IndexStorePlugin.DirectoryFactory} for the given shard's index.
* Returns null if the index uses the default store type.
*/
private static IndexStorePlugin.DirectoryFactory resolveDirectoryFactory(
ShardId shardId,
IndicesService indicesService,
Settings settings,
ClusterService clusterService
) {
IndexSettings indexSettings = resolveIndexSettings(shardId, indicesService, settings, clusterService);
if (indexSettings == null) {
return null;
}
String storeType = IndexModule.INDEX_STORE_TYPE_SETTING.get(indexSettings.getSettings());
if (storeType.isEmpty()) {
return null;
}
return indicesService.getDirectoryFactories().get(storeType);
}

/**
* Resolves the {@link IndexSettings} for the given shard's index.
*/
private static IndexSettings resolveIndexSettings(
ShardId shardId,
IndicesService indicesService,
Settings settings,
ClusterService clusterService
) {
IndexService indexService = indicesService.indexService(shardId.getIndex());
if (indexService != null) {
return indexService.getIndexSettings();
}
IndexMetadata metadata = clusterService.state().metadata().index(shardId.getIndex());
if (metadata != null) {
return new IndexSettings(metadata, settings);
}
return null;
}

/**
* Metadata for store files
*
Expand Down
Loading
Loading