From dc191a17d7e1848a34cb4f02cab9179244a4ba1a Mon Sep 17 00:00:00 2001 From: rohanshah18 Date: Thu, 6 Nov 2025 11:12:11 -0500 Subject: [PATCH 1/3] add support for DRN --- README.md | 131 +++++++++ .../io/pinecone/helpers/TestUtilities.java | 75 +++++ .../java/io/pinecone/clients/Pinecone.java | 266 ++++++++++++++++++ 3 files changed, 472 insertions(+) diff --git a/README.md b/README.md index 63a596ad..0d158189 100644 --- a/README.md +++ b/README.md @@ -186,6 +186,105 @@ tags.put("env", "test"); IndexModel indexModel = pinecone.createServerlessIndex(indexName, similarityMetric, dimension, cloud, region, "enabled", tags); ``` +### Create a serverless index with dedicated read capacity + +The following example creates a serverless index with dedicated read capacity nodes for better performance and cost predictability. For more information, see [Dedicated Read Nodes](https://docs.pinecone.io/guides/index-data/dedicated-read-nodes). + +```java +import io.pinecone.clients.Pinecone; +import org.openapitools.db_control.client.model.IndexModel; +import org.openapitools.db_control.client.model.ReadCapacity; +import org.openapitools.db_control.client.model.ReadCapacityDedicatedSpec; +import org.openapitools.db_control.client.model.ReadCapacityDedicatedConfig; +import org.openapitools.db_control.client.model.ScalingConfigManual; +import java.util.HashMap; +... + +Pinecone pinecone = new Pinecone.Builder("PINECONE_API_KEY").build(); + +String indexName = "example-index"; +String similarityMetric = "cosine"; +int dimension = 1538; +String cloud = "aws"; +String region = "us-west-2"; +HashMap tags = new HashMap<>(); +tags.put("env", "test"); + +// Configure dedicated read capacity with manual scaling +ScalingConfigManual manual = new ScalingConfigManual().shards(2).replicas(2); +ReadCapacityDedicatedConfig dedicated = new ReadCapacityDedicatedConfig() + .nodeType("t1") + .scaling("Manual") + .manual(manual); +ReadCapacity readCapacity = new ReadCapacity( + new ReadCapacityDedicatedSpec().mode("Dedicated").dedicated(dedicated)); + +IndexModel indexModel = pinecone.createServerlessIndex(indexName, similarityMetric, dimension, + cloud, region, "enabled", tags, readCapacity, null); +``` + +### Create a serverless index with OnDemand read capacity + +The following example explicitly creates a serverless index with OnDemand read capacity (the default mode). OnDemand provides pay-per-use pricing with automatic scaling. + +```java +import io.pinecone.clients.Pinecone; +import org.openapitools.db_control.client.model.IndexModel; +import org.openapitools.db_control.client.model.ReadCapacity; +import org.openapitools.db_control.client.model.ReadCapacityOnDemandSpec; +import java.util.HashMap; +... + +Pinecone pinecone = new Pinecone.Builder("PINECONE_API_KEY").build(); + +String indexName = "example-index"; +String similarityMetric = "cosine"; +int dimension = 1538; +String cloud = "aws"; +String region = "us-west-2"; +HashMap tags = new HashMap<>(); +tags.put("env", "test"); + +// Configure OnDemand read capacity (optional - this is the default) +ReadCapacity readCapacity = new ReadCapacity(new ReadCapacityOnDemandSpec().mode("OnDemand")); + +IndexModel indexModel = pinecone.createServerlessIndex(indexName, similarityMetric, dimension, + cloud, region, "enabled", tags, readCapacity, null); +``` + +### Create a serverless index with metadata schema + +The following example creates a serverless index with metadata schema configuration to limit metadata indexing to specific fields for improved performance. + +```java +import io.pinecone.clients.Pinecone; +import org.openapitools.db_control.client.model.IndexModel; +import org.openapitools.db_control.client.model.BackupModelSchema; +import org.openapitools.db_control.client.model.BackupModelSchemaFieldsValue; +import java.util.HashMap; +... + +Pinecone pinecone = new Pinecone.Builder("PINECONE_API_KEY").build(); + +String indexName = "example-index"; +String similarityMetric = "cosine"; +int dimension = 1538; +String cloud = "aws"; +String region = "us-west-2"; +HashMap tags = new HashMap<>(); +tags.put("env", "test"); + +// Configure metadata schema to only index specific fields +HashMap fields = new HashMap<>(); +fields.put("genre", new BackupModelSchemaFieldsValue().filterable(true)); +fields.put("year", new BackupModelSchemaFieldsValue().filterable(true)); +fields.put("description", new BackupModelSchemaFieldsValue().filterable(true)); +BackupModelSchema schema = new BackupModelSchema().fields(fields); + +IndexModel indexModel = pinecone.createServerlessIndex(indexName, similarityMetric, dimension, + cloud, region, "enabled", tags, null, schema); +``` + ### Create a sparse serverless index The following is an example of creating a sparse serverless index in the `us-east-1` region of AWS. For more information on @@ -341,6 +440,38 @@ tags.put("env", "test"); pinecone.configureServerlessIndex(indexName, "enabled", tags); ``` +### Configure read capacity on an existing serverless index + +The following example shows how to configure or change the read capacity mode of an existing serverless index. You can switch between OnDemand and Dedicated modes, or scale dedicated read nodes. + +**Note:** Read capacity settings can only be updated once per hour per index. + +```java +import io.pinecone.clients.Pinecone; +import org.openapitools.db_control.client.model.IndexModel; +import java.util.HashMap; +... + +Pinecone pinecone = new Pinecone.Builder("PINECONE_API_KEY").build(); + +String indexName = "example-index"; +HashMap tags = new HashMap<>(); +tags.put("env", "test"); + +// Switch to Dedicated read capacity with manual scaling +// Parameters: indexName, deletionProtection, tags, embed, readCapacityMode, nodeType, shards, replicas +IndexModel indexModel = pinecone.configureServerlessIndex( + indexName, "enabled", tags, null, "Dedicated", "t1", 3, 2); + +// Switch to OnDemand read capacity +IndexModel onDemandIndex = pinecone.configureServerlessIndex( + indexName, "enabled", tags, null, "OnDemand", null, null, null); + +// Verify the configuration was applied +IndexModel desc = pinecone.describeIndex(indexName); +// Check desc.getSpec().getServerless().getReadCapacity()... +``` + ## Describe index statistics The following example returns statistics about an index. diff --git a/src/integration/java/io/pinecone/helpers/TestUtilities.java b/src/integration/java/io/pinecone/helpers/TestUtilities.java index b616e2c1..6a65dc87 100644 --- a/src/integration/java/io/pinecone/helpers/TestUtilities.java +++ b/src/integration/java/io/pinecone/helpers/TestUtilities.java @@ -38,6 +38,81 @@ public static IndexModel waitUntilIndexIsReady(Pinecone pineconeClient, String i return waitUntilIndexIsReady(pineconeClient, indexName, 200000); } + /** + * Waits until the read capacity status is Ready for a serverless index. + * This is needed before configuring read capacity, as the API requires read capacity to be Ready before updates. + * + * @param pineconeClient The Pinecone client instance + * @param indexName The name of the index + * @param totalMsToWait Maximum time to wait in milliseconds + * @return The IndexModel with read capacity status Ready + * @throws InterruptedException if the thread is interrupted + */ + public static IndexModel waitUntilReadCapacityIsReady(Pinecone pineconeClient, String indexName, Integer totalMsToWait) throws InterruptedException { + IndexModel index = pineconeClient.describeIndex(indexName); + int waitedTimeMs = 0; + int intervalMs = 2000; + + while (true) { + // Check if index has serverless spec with read capacity + if (index.getSpec() != null && index.getSpec().getIndexModelServerless() != null) { + ServerlessSpecResponse serverless = index.getSpec().getIndexModelServerless().getServerless(); + if (serverless != null && serverless.getReadCapacity() != null) { + ReadCapacityResponse readCapacityResponse = serverless.getReadCapacity(); + ReadCapacityStatus status = null; + + // Get status from the appropriate response type + try { + ReadCapacityDedicatedSpecResponse dedicatedResponse = readCapacityResponse.getReadCapacityDedicatedSpecResponse(); + status = dedicatedResponse.getStatus(); + } catch (ClassCastException e) { + try { + ReadCapacityOnDemandSpecResponse onDemandResponse = readCapacityResponse.getReadCapacityOnDemandSpecResponse(); + status = onDemandResponse.getStatus(); + } catch (ClassCastException e2) { + logger.warn("Unknown read capacity response type for index " + indexName); + } + } + + if (status != null && "Ready".equals(status.getState())) { + logger.info("Read capacity for index " + indexName + " is ready after " + waitedTimeMs + "ms"); + break; + } + } else { + // If no read capacity is configured (OnDemand by default), consider it ready + logger.info("Index " + indexName + " has no read capacity configured (defaults to OnDemand), considering ready"); + break; + } + } else { + // Not a serverless index or spec not available yet + logger.info("Index " + indexName + " spec not available yet, waiting..."); + } + + if (waitedTimeMs >= totalMsToWait) { + logger.info("WARNING: Read capacity for index " + indexName + " not ready after " + waitedTimeMs + "ms"); + break; + } + + Thread.sleep(intervalMs); + waitedTimeMs += intervalMs; + logger.info("Waited " + waitedTimeMs + "ms for read capacity of " + indexName + " to get ready"); + index = pineconeClient.describeIndex(indexName); + } + return index; + } + + /** + * Waits until the read capacity status is Ready for a serverless index (default timeout: 200 seconds). + * + * @param pineconeClient The Pinecone client instance + * @param indexName The name of the index + * @return The IndexModel with read capacity status Ready + * @throws InterruptedException if the thread is interrupted + */ + public static IndexModel waitUntilReadCapacityIsReady(Pinecone pineconeClient, String indexName) throws InterruptedException { + return waitUntilReadCapacityIsReady(pineconeClient, indexName, 200000); + } + public static CollectionModel createCollection(Pinecone pineconeClient, String collectionName, String indexName, boolean waitUntilReady) throws InterruptedException { CollectionModel collection = pineconeClient.createCollection(collectionName, indexName); diff --git a/src/main/java/io/pinecone/clients/Pinecone.java b/src/main/java/io/pinecone/clients/Pinecone.java index e3f8ff8d..a33e7e6f 100644 --- a/src/main/java/io/pinecone/clients/Pinecone.java +++ b/src/main/java/io/pinecone/clients/Pinecone.java @@ -82,6 +82,82 @@ public IndexModel createServerlessIndex(String indexName, String region, String deletionProtection, Map tags) throws PineconeException { + return createServerlessIndex(indexName, metric, dimension, cloud, region, deletionProtection, tags, null, null); + } + + /** + * Creates a new serverless index with the specified parameters, including optional read capacity and metadata schema configuration. + *

+ * This method allows you to configure dedicated read capacity nodes for better performance and cost predictability, + * and to limit metadata indexing to specific fields for improved performance. + *

+ * Example - Create index with OnDemand read capacity (default): + *

{@code
+     *     import org.openapitools.db_control.client.model.ReadCapacity;
+     *     import org.openapitools.db_control.client.model.ReadCapacityOnDemandSpec;
+     *     ...
+     *     
+     *     ReadCapacity readCapacity = new ReadCapacity(new ReadCapacityOnDemandSpec().mode("OnDemand"));
+     *     client.createServerlessIndex("YOUR-INDEX", "cosine", 1536, "aws", "us-west-2", 
+     *                                  DeletionProtection.ENABLED, null, readCapacity, null);
+     * }
+ *

+ * Example - Create index with Dedicated read capacity: + *

{@code
+     *     import org.openapitools.db_control.client.model.ReadCapacity;
+     *     import org.openapitools.db_control.client.model.ReadCapacityDedicatedSpec;
+     *     import org.openapitools.db_control.client.model.ReadCapacityDedicatedConfig;
+     *     import org.openapitools.db_control.client.model.ScalingConfigManual;
+     *     ...
+     *     
+     *     ScalingConfigManual manual = new ScalingConfigManual().shards(2).replicas(2);
+     *     ReadCapacityDedicatedConfig dedicated = new ReadCapacityDedicatedConfig()
+     *         .nodeType("t1")
+     *         .scaling("Manual")
+     *         .manual(manual);
+     *     ReadCapacity readCapacity = new ReadCapacity(
+     *         new ReadCapacityDedicatedSpec().mode("Dedicated").dedicated(dedicated));
+     *     client.createServerlessIndex("YOUR-INDEX", "cosine", 1536, "aws", "us-west-2", 
+     *                                  DeletionProtection.ENABLED, null, readCapacity, null);
+     * }
+ *

+ * Example - Create index with metadata schema: + *

{@code
+     *     import org.openapitools.db_control.client.model.BackupModelSchema;
+     *     import org.openapitools.db_control.client.model.BackupModelSchemaFieldsValue;
+     *     ...
+     *     
+     *     Map fields = new HashMap<>();
+     *     fields.put("genre", new BackupModelSchemaFieldsValue().filterable(true));
+     *     fields.put("year", new BackupModelSchemaFieldsValue().filterable(true));
+     *     BackupModelSchema schema = new BackupModelSchema().fields(fields);
+     *     client.createServerlessIndex("YOUR-INDEX", "cosine", 1536, "aws", "us-west-2", 
+     *                                  DeletionProtection.ENABLED, null, null, schema);
+     * }
+ * + * @param indexName The name of the index to be created. + * @param metric The metric type for the index. Must be one of "cosine", "euclidean", or "dotproduct". + * @param dimension The number of dimensions for the index. + * @param cloud The cloud provider for the index. + * @param region The cloud region for the index. + * @param deletionProtection Enable or disable deletion protection for the index. + * @param tags A map of tags to associate with the Index. + * @param readCapacity The read capacity configuration. If null, defaults to OnDemand mode. + * Use {@link ReadCapacityOnDemandSpec} for OnDemand or {@link ReadCapacityDedicatedSpec} for Dedicated mode. + * @param schema The metadata schema configuration. If null, all metadata fields are indexed. + * Use this to limit metadata indexing to specific fields for improved performance. + * @return {@link IndexModel} representing the created serverless index. + * @throws PineconeException if the API encounters an error during index creation or if any of the arguments are invalid. + */ + public IndexModel createServerlessIndex(String indexName, + String metric, + int dimension, + String cloud, + String region, + String deletionProtection, + Map tags, + ReadCapacity readCapacity, + BackupModelSchema schema) throws PineconeException { if (indexName == null || indexName.isEmpty()) { throw new PineconeValidationException("Index name cannot be null or empty"); } @@ -103,6 +179,15 @@ public IndexModel createServerlessIndex(String indexName, } ServerlessSpec serverlessSpec = new ServerlessSpec().cloud(cloud).region(region); + + if (readCapacity != null) { + serverlessSpec.readCapacity(readCapacity); + } + + if (schema != null) { + serverlessSpec.schema(schema); + } + IndexSpec createServerlessIndexRequestSpec = new IndexSpec(new IndexSpecServerless().serverless(serverlessSpec)); IndexModel indexModel = null; @@ -221,6 +306,91 @@ public IndexModel createIndexForModel(String name, CreateIndexForModelRequestEmbed embed, String deletionProtection, Map tags) throws PineconeException, ApiException { + return createIndexForModel(name, cloud, region, embed, deletionProtection, tags, null, null); + } + + /** + * Creates a new serverless index with an associated embedding model, including optional read capacity and metadata schema configuration. + *

+ * This method allows you to configure dedicated read capacity nodes for better performance and cost predictability, + * and to limit metadata indexing to specific fields for improved performance. + *

+ * Example - Create index for model with Dedicated read capacity: + *

{@code
+     *     import org.openapitools.db_control.client.model.ReadCapacity;
+     *     import org.openapitools.db_control.client.model.ReadCapacityDedicatedSpec;
+     *     import org.openapitools.db_control.client.model.ReadCapacityDedicatedConfig;
+     *     import org.openapitools.db_control.client.model.ScalingConfigManual;
+     *     ...
+     *     
+     *     ScalingConfigManual manual = new ScalingConfigManual().shards(1).replicas(1);
+     *     ReadCapacityDedicatedConfig dedicated = new ReadCapacityDedicatedConfig()
+     *         .nodeType("t1")
+     *         .scaling("Manual")
+     *         .manual(manual);
+     *     ReadCapacity readCapacity = new ReadCapacity(
+     *         new ReadCapacityDedicatedSpec().mode("Dedicated").dedicated(dedicated));
+     *     
+     *     CreateIndexForModelRequestEmbed embed = new CreateIndexForModelRequestEmbed();
+     *     embed.model("multilingual-e5-large");
+     *     Map fieldMap = new HashMap<>();
+     *     fieldMap.put("text", "my-sample-text");
+     *     embed.fieldMap(fieldMap);
+     *     
+     *     client.createIndexForModel("my-index", "aws", "us-east-1", embed, 
+     *                                DeletionProtection.DISABLED, null, readCapacity, null);
+     * }
+ *

+ * Example - Create index for model with metadata schema: + *

{@code
+     *     import org.openapitools.db_control.client.model.BackupModelSchema;
+     *     import org.openapitools.db_control.client.model.BackupModelSchemaFieldsValue;
+     *     ...
+     *     
+     *     Map fields = new HashMap<>();
+     *     fields.put("category", new BackupModelSchemaFieldsValue().filterable(true));
+     *     fields.put("tags", new BackupModelSchemaFieldsValue().filterable(true));
+     *     BackupModelSchema schema = new BackupModelSchema().fields(fields);
+     *     
+     *     CreateIndexForModelRequestEmbed embed = new CreateIndexForModelRequestEmbed();
+     *     embed.model("multilingual-e5-large");
+     *     Map fieldMap = new HashMap<>();
+     *     fieldMap.put("text", "my-sample-text");
+     *     embed.fieldMap(fieldMap);
+     *     
+     *     client.createIndexForModel("my-index", "aws", "us-east-1", embed, 
+     *                                DeletionProtection.DISABLED, null, null, schema);
+     * }
+ * + * @param name The name of the index to be created. The name must be between 1 and 45 characters, + * start and end with an alphanumeric character, and consist only of lowercase alphanumeric + * characters or hyphens ('-'). + * @param cloud The cloud provider where the index will be hosted. Must be one of the supported cloud providers. + * @param region The cloud region where the index will be created. + * @param embed The embedding model configuration. Once set, the model cannot be changed, but configurations + * such as field map and parameters can be updated. + * @param deletionProtection Whether deletion protection is enabled for the index. If enabled, the index + * cannot be deleted. Defaults to disabled if not provided. + * @param tags A map of custom user tags to associate with the index. Keys must be alphanumeric or contain + * underscores ('_') or hyphens ('-'). Values must be alphanumeric, or contain characters such + * as ';', '@', '_', '-', '.', '+', or spaces. + * @param readCapacity The read capacity configuration. If null, defaults to OnDemand mode. + * Use {@link ReadCapacityOnDemandSpec} for OnDemand or {@link ReadCapacityDedicatedSpec} for Dedicated mode. + * @param schema The metadata schema configuration. If null, all metadata fields are indexed. + * Use this to limit metadata indexing to specific fields for improved performance. + * @return {@link IndexModel} representing the created serverless index with the associated embedding model. + * @throws PineconeException if the API encounters an error during index creation, or if any of the arguments + * are invalid. + * @throws ApiException if an error occurs while communicating with the API. + */ + public IndexModel createIndexForModel(String name, + String cloud, + String region, + CreateIndexForModelRequestEmbed embed, + String deletionProtection, + Map tags, + ReadCapacity readCapacity, + BackupModelSchema schema) throws PineconeException, ApiException { CreateIndexForModelRequest createIndexForModelRequest = new CreateIndexForModelRequest() .name(name) @@ -230,6 +400,14 @@ public IndexModel createIndexForModel(String name, .deletionProtection(deletionProtection) .tags(tags); + if (readCapacity != null) { + createIndexForModelRequest.readCapacity(readCapacity); + } + + if (schema != null) { + createIndexForModelRequest.schema(schema); + } + return manageIndexesApi.createIndexForModel(Configuration.VERSION, createIndexForModelRequest); } @@ -721,6 +899,56 @@ public IndexModel configureServerlessIndex(String indexName, String deletionProtection, Map tags, ConfigureIndexRequestEmbed embed) throws PineconeException { + return configureServerlessIndex(indexName, deletionProtection, tags, embed, null, null, null, null); + } + + /** + * Configures an existing serverless index with deletion protection, tags, embed settings, and optional read capacity configuration. + *

+ * This method allows you to configure or change the read capacity mode of an existing serverless index. + * You can switch between OnDemand and Dedicated modes, or scale dedicated read nodes. + *

+ * Example - Switch to OnDemand read capacity: + *

{@code
+     *     client.configureServerlessIndex("my-index", "enabled", null, null, "OnDemand", null, null, null);
+     * }
+ *

+ * Example - Switch to Dedicated read capacity with manual scaling: + *

{@code
+     *     client.configureServerlessIndex("my-index", "enabled", null, null, "Dedicated", "t1", 2, 2);
+     * }
+ *

+ * Example - Scale up dedicated read capacity: + *

{@code
+     *     // Scale up by increasing shards and replicas
+     *     client.configureServerlessIndex("my-index", "enabled", null, null, "Dedicated", "t1", 4, 3);
+     *     
+     *     // Verify the configuration was applied
+     *     IndexModel desc = client.describeIndex("my-index");
+     *     // Check desc.getSpec().getServerless().getReadCapacity()...
+     * }
+ * + * @param indexName The name of the index to configure. + * @param deletionProtection Enable or disable deletion protection for the index. + * @param tags A map of tags to associate with the Index. + * @param embed Convert an existing index to an integrated index by specifying the embedding model and field_map. + * The index vector type and dimension must match the model vector type and dimension, and the index + * similarity metric must be supported by the model + * @param readCapacityMode The read capacity mode. Must be "OnDemand" or "Dedicated". If null, read capacity is not changed. + * @param nodeType The node type for Dedicated mode (e.g., "t1"). Required if readCapacityMode is "Dedicated", ignored otherwise. + * @param shards The number of shards for Dedicated mode. Required if readCapacityMode is "Dedicated", ignored otherwise. + * @param replicas The number of replicas for Dedicated mode. Required if readCapacityMode is "Dedicated", ignored otherwise. + * @return {@link IndexModel} representing the configured index. + * @throws PineconeException if an error occurs during the operation, the index does not exist, or if any of the arguments are invalid. + */ + public IndexModel configureServerlessIndex(String indexName, + String deletionProtection, + Map tags, + ConfigureIndexRequestEmbed embed, + String readCapacityMode, + String nodeType, + Integer shards, + Integer replicas) throws PineconeException { if (indexName == null || indexName.isEmpty()) { throw new PineconeValidationException("indexName cannot be null or empty"); } @@ -737,6 +965,44 @@ public IndexModel configureServerlessIndex(String indexName, configureIndexRequest.embed(embed); } + // Build ReadCapacity from primitive parameters if readCapacityMode is provided + ReadCapacity readCapacity = null; + if (readCapacityMode != null) { + if ("OnDemand".equals(readCapacityMode)) { + readCapacity = new ReadCapacity(new ReadCapacityOnDemandSpec().mode("OnDemand")); + } else if ("Dedicated".equals(readCapacityMode)) { + if (nodeType == null || nodeType.isEmpty()) { + throw new PineconeValidationException("nodeType is required when readCapacityMode is 'Dedicated'"); + } + if (shards == null || shards < 1) { + throw new PineconeValidationException("shards must be at least 1 when readCapacityMode is 'Dedicated'"); + } + if (replicas == null || replicas < 1) { + throw new PineconeValidationException("replicas must be at least 1 when readCapacityMode is 'Dedicated'"); + } + + ScalingConfigManual manual = new ScalingConfigManual().shards(shards).replicas(replicas); + ReadCapacityDedicatedConfig dedicated = new ReadCapacityDedicatedConfig() + .nodeType(nodeType) + .scaling("Manual") + .manual(manual); + readCapacity = new ReadCapacity( + new ReadCapacityDedicatedSpec().mode("Dedicated").dedicated(dedicated)); + } else { + throw new PineconeValidationException("readCapacityMode must be 'OnDemand' or 'Dedicated'"); + } + } + + // If readCapacity is provided, configure it via spec + if (readCapacity != null) { + ConfigureIndexRequestServerlessConfig serverlessConfig = new ConfigureIndexRequestServerlessConfig() + .readCapacity(readCapacity); + ConfigureIndexRequestServerless serverless = new ConfigureIndexRequestServerless() + .serverless(serverlessConfig); + ConfigureIndexRequestSpec spec = new ConfigureIndexRequestSpec(serverless); + configureIndexRequest.spec(spec); + } + IndexModel indexModel = null; try { indexModel = manageIndexesApi.configureIndex(Configuration.VERSION, indexName, configureIndexRequest); From 230163bb20027ce48d888107d41764594392ac1a Mon Sep 17 00:00:00 2001 From: rohanshah18 Date: Thu, 6 Nov 2025 11:18:33 -0500 Subject: [PATCH 2/3] add test --- .../serverless/ReadCapacityAndSchemaTest.java | 239 ++++++++++++++++++ 1 file changed, 239 insertions(+) create mode 100644 src/integration/java/io/pinecone/integration/controlPlane/serverless/ReadCapacityAndSchemaTest.java diff --git a/src/integration/java/io/pinecone/integration/controlPlane/serverless/ReadCapacityAndSchemaTest.java b/src/integration/java/io/pinecone/integration/controlPlane/serverless/ReadCapacityAndSchemaTest.java new file mode 100644 index 00000000..24e8c19a --- /dev/null +++ b/src/integration/java/io/pinecone/integration/controlPlane/serverless/ReadCapacityAndSchemaTest.java @@ -0,0 +1,239 @@ +package io.pinecone.integration.controlPlane.serverless; + +import io.pinecone.clients.Pinecone; +import io.pinecone.helpers.RandomStringBuilder; +import okhttp3.OkHttpClient; +import org.junit.jupiter.api.*; +import org.openapitools.db_control.client.ApiException; +import org.openapitools.db_control.client.model.*; + +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.TimeUnit; + +import static io.pinecone.helpers.TestUtilities.waitUntilIndexIsReady; +import static io.pinecone.helpers.TestUtilities.waitUntilReadCapacityIsReady; +import static org.junit.jupiter.api.Assertions.*; + +@TestMethodOrder(MethodOrderer.OrderAnnotation.class) +public class ReadCapacityAndSchemaTest { + private static final Pinecone controlPlaneClient = new Pinecone + .Builder(System.getenv("PINECONE_API_KEY")) + .withSourceTag("pinecone_test") + .withOkHttpClient(new OkHttpClient.Builder() + .connectTimeout(30, TimeUnit.SECONDS) + .readTimeout(120, TimeUnit.SECONDS) + .writeTimeout(30, TimeUnit.SECONDS) + .build()) + .build(); + + @Test + @Order(1) + public void createServerlessIndexWithOnDemandReadCapacity() throws InterruptedException { + String indexNameOnDemand = RandomStringBuilder.build("ondemand-index", 8); + Map tags = new HashMap<>(); + tags.put("env", "test"); + tags.put("read-capacity", "ondemand"); + + // Create index with OnDemand read capacity + ReadCapacity readCapacity = new ReadCapacity(new ReadCapacityOnDemandSpec().mode("OnDemand")); + IndexModel indexModel = controlPlaneClient.createServerlessIndex( + indexNameOnDemand, "cosine", 1536, "aws", "us-west-2", + "disabled", tags, readCapacity, null); + + assertNotNull(indexModel); + assertEquals(indexNameOnDemand, indexModel.getName()); + assertEquals("cosine", indexModel.getMetric()); + assertEquals(1536, indexModel.getDimension()); + assertEquals("disabled", indexModel.getDeletionProtection()); + assertEquals(tags, indexModel.getTags()); + + // Wait for index to be ready and verify read capacity + waitUntilIndexIsReady(controlPlaneClient, indexNameOnDemand); + IndexModel describedIndex = controlPlaneClient.describeIndex(indexNameOnDemand); + assertNotNull(describedIndex.getSpec().getIndexModelServerless()); + // Note: Read capacity response may not be immediately available in describe + } + + @Test + @Order(2) + public void createServerlessIndexWithDedicatedReadCapacity() throws InterruptedException { + String indexNameDedicated = RandomStringBuilder.build("dedicated-index", 8); + Map tags = new HashMap<>(); + tags.put("env", "test"); + tags.put("read-capacity", "dedicated"); + + // Create index with Dedicated read capacity + ScalingConfigManual manual = new ScalingConfigManual().shards(2).replicas(2); + ReadCapacityDedicatedConfig dedicated = new ReadCapacityDedicatedConfig() + .nodeType("t1") + .scaling("Manual") + .manual(manual); + ReadCapacity readCapacity = new ReadCapacity( + new ReadCapacityDedicatedSpec().mode("Dedicated").dedicated(dedicated)); + + IndexModel indexModel = controlPlaneClient.createServerlessIndex( + indexNameDedicated, "cosine", 1536, "aws", "us-west-2", + "disabled", tags, readCapacity, null); + + assertNotNull(indexModel); + assertEquals(indexNameDedicated, indexModel.getName()); + assertEquals("cosine", indexModel.getMetric()); + assertEquals(1536, indexModel.getDimension()); + assertEquals("disabled", indexModel.getDeletionProtection()); + assertEquals(tags, indexModel.getTags()); + + // Wait for index to be ready + waitUntilIndexIsReady(controlPlaneClient, indexNameDedicated); + } + + @Test + @Order(3) + public void createServerlessIndexWithMetadataSchema() throws InterruptedException { + String indexNameWithSchema = RandomStringBuilder.build("schema-index", 8); + Map tags = new HashMap<>(); + tags.put("env", "test"); + tags.put("schema", "configured"); + + // Create index with metadata schema + Map fields = new HashMap<>(); + fields.put("genre", new BackupModelSchemaFieldsValue().filterable(true)); + fields.put("year", new BackupModelSchemaFieldsValue().filterable(true)); + fields.put("description", new BackupModelSchemaFieldsValue().filterable(true)); + BackupModelSchema schema = new BackupModelSchema().fields(fields); + + IndexModel indexModel = controlPlaneClient.createServerlessIndex( + indexNameWithSchema, "cosine", 1536, "aws", "us-west-2", + "disabled", tags, null, schema); + + assertNotNull(indexModel); + assertEquals(indexNameWithSchema, indexModel.getName()); + assertEquals("cosine", indexModel.getMetric()); + assertEquals(1536, indexModel.getDimension()); + assertEquals("disabled", indexModel.getDeletionProtection()); + assertEquals(tags, indexModel.getTags()); + + // Wait for index to be ready + waitUntilIndexIsReady(controlPlaneClient, indexNameWithSchema); + } + + @Test + @Order(4) + public void createServerlessIndexWithBothReadCapacityAndSchema() throws InterruptedException { + String indexName = RandomStringBuilder.build("both-config-index", 8); + Map tags = new HashMap<>(); + tags.put("env", "test"); + + // Create index with both Dedicated read capacity and metadata schema + ScalingConfigManual manual = new ScalingConfigManual().shards(1).replicas(1); + ReadCapacityDedicatedConfig dedicated = new ReadCapacityDedicatedConfig() + .nodeType("t1") + .scaling("Manual") + .manual(manual); + ReadCapacity readCapacity = new ReadCapacity( + new ReadCapacityDedicatedSpec().mode("Dedicated").dedicated(dedicated)); + + Map fields = new HashMap<>(); + fields.put("category", new BackupModelSchemaFieldsValue().filterable(true)); + fields.put("tags", new BackupModelSchemaFieldsValue().filterable(true)); + BackupModelSchema schema = new BackupModelSchema().fields(fields); + + IndexModel indexModel = controlPlaneClient.createServerlessIndex( + indexName, "cosine", 1536, "aws", "us-west-2", + "disabled", tags, readCapacity, schema); + + assertNotNull(indexModel); + assertEquals(indexName, indexModel.getName()); + assertEquals("cosine", indexModel.getMetric()); + assertEquals(1536, indexModel.getDimension()); + + // Wait for index to be ready + waitUntilIndexIsReady(controlPlaneClient, indexName); + + // Clean up + controlPlaneClient.deleteIndex(indexName); + } + + @Test + @Order(5) + public void createIndexForModelWithReadCapacityAndSchema() throws InterruptedException, ApiException { + String indexNameForModel = RandomStringBuilder.build("model-index", 8); + Map tags = new HashMap<>(); + tags.put("env", "test"); + + // Create index for model with Dedicated read capacity and metadata schema + ScalingConfigManual manual = new ScalingConfigManual().shards(1).replicas(1); + ReadCapacityDedicatedConfig dedicated = new ReadCapacityDedicatedConfig() + .nodeType("t1") + .scaling("Manual") + .manual(manual); + ReadCapacity readCapacity = new ReadCapacity( + new ReadCapacityDedicatedSpec().mode("Dedicated").dedicated(dedicated)); + + Map fields = new HashMap<>(); + fields.put("category", new BackupModelSchemaFieldsValue().filterable(true)); + BackupModelSchema schema = new BackupModelSchema().fields(fields); + + CreateIndexForModelRequestEmbed embed = new CreateIndexForModelRequestEmbed(); + embed.model("multilingual-e5-large"); + Map fieldMap = new HashMap<>(); + fieldMap.put("text", "my-sample-text"); + embed.fieldMap(fieldMap); + + IndexModel indexModel = controlPlaneClient.createIndexForModel( + indexNameForModel, "aws", "us-east-1", embed, + "disabled", tags, readCapacity, schema); + + assertNotNull(indexModel); + assertEquals(indexNameForModel, indexModel.getName()); + assertEquals("disabled", indexModel.getDeletionProtection()); + assertEquals(tags, indexModel.getTags()); + + // Wait for index to be ready + waitUntilIndexIsReady(controlPlaneClient, indexNameForModel); + } + + @Test + @Order(6) + public void configureReadCapacityOnExistingIndex() throws InterruptedException { + String indexNameToConfigure = RandomStringBuilder.build("configure-index", 8); + Map tags = new HashMap<>(); + tags.put("env", "test"); + + // First, create an index without read capacity configuration (defaults to OnDemand) + IndexModel indexModel = controlPlaneClient.createServerlessIndex( + indexNameToConfigure, "cosine", 1536, "aws", "us-west-2", + "disabled", tags, null, null); + + assertNotNull(indexModel); + assertEquals(indexNameToConfigure, indexModel.getName()); + + // Wait for index to be ready + waitUntilIndexIsReady(controlPlaneClient, indexNameToConfigure); + // Wait for read capacity to be ready before configuring + waitUntilReadCapacityIsReady(controlPlaneClient, indexNameToConfigure); + + // Configure to Dedicated read capacity + IndexModel configuredIndex = controlPlaneClient.configureServerlessIndex( + indexNameToConfigure, "disabled", tags, null, "Dedicated", "t1", 2, 2); + + assertNotNull(configuredIndex); + assertEquals(indexNameToConfigure, configuredIndex.getName()); + + // Wait a bit for configuration to apply + Thread.sleep(10000); + + // Verify the configuration by describing the index + IndexModel describedIndex = controlPlaneClient.describeIndex(indexNameToConfigure); + assertNotNull(describedIndex); + assertEquals(indexNameToConfigure, describedIndex.getName()); + } + + // Note: Tests for switching read capacity modes and scaling are omitted due to API rate limits. + // Read capacity settings can only be updated once per hour per index. The following scenarios + // would require multiple configurations on the same index and would hit rate limits: + // - Switching from Dedicated to OnDemand + // - Scaling dedicated read capacity (changing shards/replicas) + // These operations are still supported by the API, but cannot be tested in CI/CD due to rate limits. +} + From d9f4be32f340f0029908aa458ed3bd04769c370c Mon Sep 17 00:00:00 2001 From: rohanshah18 Date: Thu, 6 Nov 2025 12:59:41 -0500 Subject: [PATCH 3/3] fix configure index test --- .../integration/controlPlane/pod/ConfigureIndexTest.java | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/integration/java/io/pinecone/integration/controlPlane/pod/ConfigureIndexTest.java b/src/integration/java/io/pinecone/integration/controlPlane/pod/ConfigureIndexTest.java index 158de0d6..dac812f6 100644 --- a/src/integration/java/io/pinecone/integration/controlPlane/pod/ConfigureIndexTest.java +++ b/src/integration/java/io/pinecone/integration/controlPlane/pod/ConfigureIndexTest.java @@ -5,6 +5,7 @@ import io.pinecone.exceptions.PineconeForbiddenException; import io.pinecone.exceptions.PineconeNotFoundException; import io.pinecone.helpers.TestResourcesManager; +import okhttp3.OkHttpClient; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; @@ -13,6 +14,8 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.util.concurrent.TimeUnit; + import static io.pinecone.helpers.AssertRetry.assertWithRetry; import static org.junit.jupiter.api.Assertions.*; @@ -22,6 +25,11 @@ public class ConfigureIndexTest { private static final Pinecone controlPlaneClient = new Pinecone .Builder(System.getenv("PINECONE_API_KEY")) .withSourceTag("pinecone_test") + .withOkHttpClient(new OkHttpClient.Builder() + .connectTimeout(10, TimeUnit.SECONDS) + .readTimeout(60, TimeUnit.SECONDS) + .writeTimeout(30, TimeUnit.SECONDS) + .build()) .build(); private static String indexName;