Skip to content

Commit 76fcb0a

Browse files
committed
Default to parallel reads in serverless mode (50,000 docs/partition)
Instead of requiring explicit opensearch.input.max.docs.per.partition to enable parallel reads, serverless mode now automatically splits reads using a default of 50,000 documents per partition. Users can still override this value. Also removes incorrect documentation about Classic Serverless not supporting the Slice API. Signed-off-by: Sotaro Hikita <bering1814@gmail.com>
1 parent c782307 commit 76fcb0a

4 files changed

Lines changed: 30 additions & 14 deletions

File tree

USER_GUIDE.md

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -489,16 +489,14 @@ opensearch.aws.sigv4.region=<region>
489489
opensearch.aws.sigv4.service.name=aoss
490490
```
491491

492-
### Parallel Reads (NextGen Only)
492+
### Parallel Reads
493493

494-
By default, serverless mode reads each index serially in a single partition. The next-generation OpenSearch Serverless architecture supports PIT with sliced search, which enables parallel reads across multiple Spark tasks.
494+
Serverless mode automatically splits reads into parallel partitions using PIT + Slice. By default, the connector creates one partition per 50,000 documents in each index.
495495

496-
To enable parallel reads, set `opensearch.input.max.docs.per.partition`:
496+
To customize the partition size, set `opensearch.input.max.docs.per.partition`:
497497

498498
```
499499
opensearch.input.max.docs.per.partition=100000
500500
```
501501

502502
The connector will count the documents in each index, divide by this value to determine the number of slices, and create one Spark partition per slice. For example, an index with 1,000,000 documents and `max.docs.per.partition=100000` will produce 10 parallel read tasks.
503-
504-
This setting requires the next-generation OpenSearch Serverless architecture. Using it with Classic Serverless collections will result in a server-side error because Classic does not support the Slice API.

mr/src/main/java/org/opensearch/hadoop/cfg/ConfigurationOptions.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,7 @@ public interface ConfigurationOptions {
164164

165165
/** Input options **/
166166
String OPENSEARCH_MAX_DOCS_PER_PARTITION = "opensearch.input.max.docs.per.partition";
167+
String OPENSEARCH_MAX_DOCS_PER_PARTITION_SERVERLESS_DEFAULT = "50000";
167168

168169
String OPENSEARCH_INPUT_JSON = "opensearch.input.json";
169170
String OPENSEARCH_INPUT_JSON_DEFAULT = "no";

mr/src/main/java/org/opensearch/hadoop/rest/RestService.java

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -229,9 +229,8 @@ public static List<PartitionDefinition> findPartitions(Settings settings, Log lo
229229

230230
/**
231231
* Create partitions for OpenSearch Serverless mode, which doesn't support shard APIs.
232-
* When maxDocsPerPartition is set, creates multiple sliced partitions per index for parallel reads.
233-
* This requires PIT + Slice support (available on next-generation OpenSearch Serverless).
234-
* On Classic Serverless, using maxDocsPerPartition will result in a server-side error.
232+
* When maxDocsPerPartition is set, creates multiple sliced partitions per index for parallel reads
233+
* using PIT + Slice.
235234
*/
236235
static List<PartitionDefinition> findServerlessPartitions(RestRepository client, Settings settings, MappingSet mappingSet, Log log) {
237236
Resource readResource = new Resource(settings, true);
@@ -244,13 +243,16 @@ static List<PartitionDefinition> findServerlessPartitions(RestRepository client,
244243
String[] indices = readResource.index().split(",");
245244

246245
Integer maxDocsPerPartition = settings.getMaxDocsPerPartition();
246+
if (maxDocsPerPartition == null) {
247+
maxDocsPerPartition = Integer.parseInt(ConfigurationOptions.OPENSEARCH_MAX_DOCS_PER_PARTITION_SERVERLESS_DEFAULT);
248+
}
247249

248250
for (String indexName : indices) {
249251
indexName = indexName.trim();
250-
if (maxDocsPerPartition != null) {
251-
QueryBuilder query = QueryUtils.parseQueryAndFilters(settings);
252-
long numDocs = client.getRestClient().count(indexName, query);
253-
int numPartitions = (int) Math.max(1, numDocs / maxDocsPerPartition);
252+
QueryBuilder query = QueryUtils.parseQueryAndFilters(settings);
253+
long numDocs = client.getRestClient().count(indexName, query);
254+
int numPartitions = (int) Math.max(1, numDocs / maxDocsPerPartition);
255+
if (numPartitions > 1) {
254256
log.info(String.format("Serverless parallel read: index [%s] has [%d] docs, creating [%d] sliced partitions", indexName, numDocs, numPartitions));
255257
for (int i = 0; i < numPartitions; i++) {
256258
PartitionDefinition.Slice slice = new PartitionDefinition.Slice(i, numPartitions);

mr/src/test/java/org/opensearch/hadoop/rest/ServerlessModeTest.java

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,8 +57,16 @@ public void testFindServerlessPartitionsSingleIndex() {
5757
settings.setProperty(ConfigurationOptions.OPENSEARCH_RESOURCE_READ, "test-index");
5858
settings.setServerlessMode(true);
5959

60-
List<PartitionDefinition> partitions = RestService.findServerlessPartitions(null, settings, null, LOGGER);
60+
RestClient restClient = Mockito.mock(RestClient.class);
61+
Mockito.when(restClient.count(Mockito.eq("test-index"), Mockito.any()))
62+
.thenReturn(100L);
63+
64+
RestRepository repository = Mockito.mock(RestRepository.class);
65+
Mockito.when(repository.getRestClient()).thenReturn(restClient);
66+
67+
List<PartitionDefinition> partitions = RestService.findServerlessPartitions(repository, settings, null, LOGGER);
6168

69+
// 100 docs / 50000 default = 1 partition (no slicing needed)
6270
assertEquals(1, partitions.size());
6371
assertEquals("test-index", partitions.get(0).getIndex());
6472
assertEquals(0, partitions.get(0).getShardId());
@@ -70,7 +78,14 @@ public void testFindServerlessPartitionsMultipleIndices() {
7078
settings.setProperty(ConfigurationOptions.OPENSEARCH_RESOURCE_READ, "index1,index2,index3");
7179
settings.setServerlessMode(true);
7280

73-
List<PartitionDefinition> partitions = RestService.findServerlessPartitions(null, settings, null, LOGGER);
81+
RestClient restClient = Mockito.mock(RestClient.class);
82+
Mockito.when(restClient.count(Mockito.anyString(), Mockito.any()))
83+
.thenReturn(100L);
84+
85+
RestRepository repository = Mockito.mock(RestRepository.class);
86+
Mockito.when(repository.getRestClient()).thenReturn(restClient);
87+
88+
List<PartitionDefinition> partitions = RestService.findServerlessPartitions(repository, settings, null, LOGGER);
7489

7590
assertEquals(3, partitions.size());
7691
assertEquals("index1", partitions.get(0).getIndex());

0 commit comments

Comments
 (0)