Skip to content
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 collections operations to PineconeControlPlaneClient with integration tests #65

Merged
merged 22 commits into from
Feb 21, 2024
Merged
Show file tree
Hide file tree
Changes from 14 commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
3ea964b
add collections calls to PineconeControlPlaneClient, add new integrat…
austin-denoble Feb 14, 2024
535bf5d
tweak testCreateCollectionFromNotReadyIndex test
austin-denoble Feb 14, 2024
a056f5c
tweak dimension for indexes in collection tests
austin-denoble Feb 14, 2024
7ca2739
add new vector builder for collections
austin-denoble Feb 14, 2024
7c522d9
remove the call to close from setup in collection tests
austin-denoble Feb 14, 2024
0544083
adjust ConfigureIndexTest to only create the initial index BeforeAll
austin-denoble Feb 14, 2024
18b7164
refine cleanUp steps in collection tests to make sure we're deleting …
austin-denoble Feb 14, 2024
8dafe1f
fix logic issue in findIndexWithDimensionAndType
austin-denoble Feb 14, 2024
a90eb2a
Set max-parallel to 1 for integration tests, remove extra sleep from …
austin-denoble Feb 14, 2024
1bfd814
back out gradle wrapper changes, tweak findIndexByDimensionAndType ag…
austin-denoble Feb 14, 2024
45b8af2
make sure we're closing PineconeConnection objects properly in tests …
austin-denoble Feb 14, 2024
4a2305d
a bit more integration test whack-a-mole with sleeps
austin-denoble Feb 14, 2024
817da41
sleep tweaks
austin-denoble Feb 14, 2024
20d3e67
adding loggin to the error tests so we can see what the actual error …
austin-denoble Feb 14, 2024
71785ff
fix environments in collection error test, sleep adjusts again
austin-denoble Feb 15, 2024
c3dc789
review feedback, use logger instead of println, update error handling…
austin-denoble Feb 20, 2024
1bcbf95
remove testLogging configs from build.gradle
austin-denoble Feb 20, 2024
bf60c79
adjust assertWithRetry error throwing
austin-denoble Feb 20, 2024
80606f3
tweak assertWithRetry one more time
austin-denoble Feb 20, 2024
e0a6247
remove unused imports
austin-denoble Feb 20, 2024
41e8f5d
up the amount of time isIndexReady waits
austin-denoble Feb 20, 2024
4b956c3
bump backoff up a bit more for configure index tests
austin-denoble Feb 21, 2024
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
3 changes: 2 additions & 1 deletion .github/workflows/pr.yml
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ jobs:
{ java: 8, gradle: 6.8 },
{ java: 17, gradle: 7.3.1 }
]
max-parallel: 1
rohanshah18 marked this conversation as resolved.
Show resolved Hide resolved
steps:
- uses: actions/checkout@v4

Expand All @@ -83,5 +84,5 @@ jobs:
env:
PINECONE_API_KEY: ${{ secrets.PINECONE_API_KEY }}
PINECONE_ENVIRONMENT: ${{ secrets.PINECONE_ENVIRONMENT }}


63 changes: 63 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
import org.gradle.api.tasks.testing.logging.TestExceptionFormat;
import org.gradle.api.tasks.testing.logging.TestLogEvent;

plugins {
id 'com.github.johnrengelman.shadow' version '6.1.0'
id 'java-library'
Expand Down Expand Up @@ -93,13 +96,73 @@ tasks.named('build') {

test {
useJUnitPlatform()

testLogging {
rohanshah18 marked this conversation as resolved.
Show resolved Hide resolved
events = [TestLogEvent.FAILED, TestLogEvent.PASSED, TestLogEvent.SKIPPED, TestLogEvent.STANDARD_OUT]
exceptionFormat = TestExceptionFormat.FULL
showExceptions = true
showCauses = true
showStackTraces = true

debugOptions {
events = [
TestLogEvent.STARTED,
TestLogEvent.FAILED,
TestLogEvent.PASSED,
TestLogEvent.SKIPPED,
TestLogEvent.STANDARD_ERROR,
TestLogEvent.STANDARD_OUT
]
exceptionFormat = TestExceptionFormat.FULL
}

afterSuite { desc, result ->
if (!desc.parent) {
def output = "Results: ${result.resultType} (${result.testCount}) tests, ${result.successfulTestCount} successful, ${result.failedTestCount} failed, ${result.skippedTestCount} skipped."
def startItem = "| ", endItem = " |"
def repeatLength = startItem.length() + output.length() + endItem.length()
println('\n' + ('-' * repeatLength) + '\n' + startItem + output + endItem + '\n' + ('-' * repeatLength))
}
}
}
}

task integrationTest(type: Test) {
useJUnitPlatform()
testClassesDirs = sourceSets.integrationTest.output.classesDirs
classpath = sourceSets.integrationTest.runtimeClasspath
outputs.upToDateWhen { false }


testLogging {
events = [TestLogEvent.FAILED, TestLogEvent.PASSED, TestLogEvent.SKIPPED, TestLogEvent.STANDARD_OUT]
exceptionFormat = TestExceptionFormat.FULL
showExceptions = true
showCauses = true
showStackTraces = true

debugOptions {
events = [
TestLogEvent.STARTED,
TestLogEvent.FAILED,
TestLogEvent.PASSED,
TestLogEvent.SKIPPED,
TestLogEvent.STANDARD_ERROR,
TestLogEvent.STANDARD_OUT
]
exceptionFormat = TestExceptionFormat.FULL

}

afterSuite { desc, result ->
if (!desc.parent) {
def output = "Results: ${result.resultType} (${result.testCount}) tests, ${result.successfulTestCount} successful, ${result.failedTestCount} failed, ${result.skippedTestCount} skipped."
def startItem = "| ", endItem = " |"
def repeatLength = startItem.length() + output.length() + endItem.length()
println('\n' + ('-' * repeatLength) + '\n' + startItem + output + endItem + '\n' + ('-' * repeatLength))
}
}
}
}

// Configure Auto Relocation
Expand Down
15 changes: 9 additions & 6 deletions src/integration/java/io/pinecone/helpers/AssertRetry.java
Original file line number Diff line number Diff line change
@@ -1,18 +1,21 @@
package io.pinecone.helpers;

import io.pinecone.exceptions.PineconeException;

import java.io.IOException;
import java.util.concurrent.ExecutionException;

public class AssertRetry {
private static final int maxRetry = 4;
private static int delay = 1500;
private static final int delay = 1500;

public static void assertWithRetry(AssertionRunnable assertionRunnable) throws InterruptedException {
public static void assertWithRetry(AssertionRunnable assertionRunnable) throws InterruptedException, PineconeException {
assertWithRetry(assertionRunnable, 2);
}

public static void assertWithRetry(AssertionRunnable assertionRunnable, int backOff) throws InterruptedException {
public static void assertWithRetry(AssertionRunnable assertionRunnable, int backOff) throws InterruptedException, PineconeException {
int retryCount = 0;
int delayCount = delay;
rohanshah18 marked this conversation as resolved.
Show resolved Hide resolved
boolean success = false;

while (retryCount < maxRetry && !success) {
Expand All @@ -21,14 +24,14 @@ public static void assertWithRetry(AssertionRunnable assertionRunnable, int back
success = true;
austin-denoble marked this conversation as resolved.
Show resolved Hide resolved
} catch (AssertionError | ExecutionException | IOException e) {
retryCount++;
delay*=backOff;
rohanshah18 marked this conversation as resolved.
Show resolved Hide resolved
Thread.sleep(delay);
Thread.sleep(delayCount);
delayCount*=backOff;
}
}
}

@FunctionalInterface
public interface AssertionRunnable {
void run() throws AssertionError, ExecutionException, InterruptedException, IOException;
void run() throws AssertionError, ExecutionException, InterruptedException, IOException, PineconeException;
}
}
33 changes: 29 additions & 4 deletions src/integration/java/io/pinecone/helpers/BuildUpsertRequest.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,7 @@
import io.pinecone.proto.UpsertRequest;
import io.pinecone.proto.Vector;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.*;

public class BuildUpsertRequest {
private static final float[][] upsertData = {{1.0F, 2.0F, 3.0F}, {4.0F, 5.0F, 6.0F}, {7.0F, 8.0F, 9.0F}};
Expand Down Expand Up @@ -44,6 +41,23 @@ public static UpsertRequest buildRequiredUpsertRequest(List<String> upsertIds, S
.build();
}

public static UpsertRequest buildRequiredUpsertRequestByDimension(List<String> upsertIds, int dimension, String namespace) {
rohanshah18 marked this conversation as resolved.
Show resolved Hide resolved
if (upsertIds.isEmpty()) upsertIds = Arrays.asList("v1", "v2", "v3");

List<Vector> upsertVectors = new ArrayList<>();
for (String upsertId : upsertIds) {
upsertVectors.add(Vector.newBuilder()
.addAllValues(generateVectorValuesByDimension(dimension))
.setId(upsertId)
.build());
}

return UpsertRequest.newBuilder()
.addAllVectors(upsertVectors)
.setNamespace(namespace)
.build();
}

public static UpsertRequest buildOptionalUpsertRequest() {
return buildOptionalUpsertRequest(new ArrayList<>(), "");
}
Expand Down Expand Up @@ -108,4 +122,15 @@ public static HashMap<String, List<String>> createAndGetMetadataMap() {

return metadataMap;
}

public static ArrayList<Float> generateVectorValuesByDimension(int dimension) {
ArrayList<Float> values = new ArrayList<>();
Random random = new Random();

for (int i = 0; i < dimension; i++) {
values.add(random.nextFloat());
}

return values;
}
}
79 changes: 77 additions & 2 deletions src/integration/java/io/pinecone/helpers/IndexManager.java
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
package io.pinecone.helpers;

import io.pinecone.*;
import io.pinecone.exceptions.PineconeException;
import org.openapitools.client.model.*;

import java.io.IOException;
import java.util.List;

import static io.pinecone.helpers.AssertRetry.assertWithRetry;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.fail;

public class IndexManager {
private static PineconeClientConfig config;
Expand Down Expand Up @@ -45,9 +48,11 @@ private static String findIndexWithDimensionAndType(IndexList indexList, int dim
List<IndexModel> indexModels = indexList.getIndexes();
while (i < indexModels.size()) {
IndexModel indexModel = isIndexReady(indexModels.get(i).getName(), controlPlaneClient);
// ToDo: add pod type support
if (indexModel.getDimension() == dimension
&& ((indexType.equalsIgnoreCase(IndexModelSpec.SERIALIZED_NAME_POD) && indexModel.getSpec().getPod() != null && indexModel.getSpec().getPod().getReplicas() == 1 && indexModel.getSpec().getPod().getPodType().equalsIgnoreCase("p1.x1"))
&& ((indexType.equalsIgnoreCase(IndexModelSpec.SERIALIZED_NAME_POD)
rohanshah18 marked this conversation as resolved.
Show resolved Hide resolved
&& indexModel.getSpec().getPod() != null
&& indexModel.getSpec().getPod().getReplicas() == 1
&& indexModel.getSpec().getPod().getPodType().equalsIgnoreCase("p1.x1"))
|| (indexType.equalsIgnoreCase(IndexModelSpec.SERIALIZED_NAME_SERVERLESS)))) {
return indexModel.getName();
}
Expand Down Expand Up @@ -79,6 +84,76 @@ private static String createNewIndex(PineconeControlPlaneClient controlPlaneClie
return indexName;
}

public static IndexModel waitUntilIndexIsReady(PineconeControlPlaneClient controlPlaneClient, String indexName, Integer totalMsToWait) throws InterruptedException {
austin-denoble marked this conversation as resolved.
Show resolved Hide resolved
IndexModel index = controlPlaneClient.describeIndex(indexName);
int waitedTimeMs = 0;
int intervalMs = 1500;

while (!index.getStatus().getReady()) {
index = controlPlaneClient.describeIndex(indexName);
if (waitedTimeMs >= totalMsToWait) {
System.out.println("Index " + indexName + " not ready after " + waitedTimeMs + "ms");
austin-denoble marked this conversation as resolved.
Show resolved Hide resolved
break;
}
if (index.getStatus().getReady()) {
System.out.println("Index " + indexName + " is ready after " + waitedTimeMs + "ms");
break;
}
Thread.sleep(intervalMs);
waitedTimeMs += intervalMs;
}
return index;
}

public static IndexModel waitUntilIndexIsReady(PineconeControlPlaneClient controlPlaneClient, String indexName) throws InterruptedException {
return waitUntilIndexIsReady(controlPlaneClient, indexName, 120000);
}

public static PineconeConnection createNewIndexAndConnect(PineconeControlPlaneClient controlPlaneClient, String indexName, int dimension, IndexMetric metric, CreateIndexRequestSpec spec) throws InterruptedException, PineconeException {
CreateIndexRequest createIndexRequest = new CreateIndexRequest().name(indexName).dimension(dimension).metric(metric).spec(spec);
controlPlaneClient.createIndex(createIndexRequest);

// Wait until index is ready
waitUntilIndexIsReady(controlPlaneClient, indexName, 200000);
// wait a bit more before we connect...
Thread.sleep(15000);

String host = controlPlaneClient.describeIndex(indexName).getHost();

PineconeClientConfig specificConfig = new PineconeClientConfig().withApiKey(System.getenv("PINECONE_API_KEY"));
PineconeClient dataPlaneClient = new PineconeClient(specificConfig);

return dataPlaneClient.connect(
new PineconeConnectionConfig()
.withConnectionUrl("https://" + host));
}

public static CollectionModel createCollection(PineconeControlPlaneClient controlPlaneClient, String collectionName, String indexName, boolean waitUntilReady) throws InterruptedException {
CreateCollectionRequest createCollectionRequest = new CreateCollectionRequest().name(collectionName).source(indexName);
CollectionModel collection = controlPlaneClient.createCollection(createCollectionRequest);

assertEquals(collection.getStatus(), CollectionModel.StatusEnum.INITIALIZING);

// Wait until collection is ready
if (waitUntilReady) {
int timeWaited = 0;
CollectionModel.StatusEnum collectionReady = collection.getStatus();
while (collectionReady != CollectionModel.StatusEnum.READY && timeWaited < 120000) {
System.out.println("Waiting for collection" + collectionName + " to be ready. Waited " + timeWaited + " milliseconds...");
Thread.sleep(5000);
timeWaited += 5000;
collection = controlPlaneClient.describeCollection(collectionName);
collectionReady = collection.getStatus();
}

if (timeWaited > 120000) {
fail("Collection: " + collectionName + " is not ready after 120 seconds");
}
}

return collection;
}

public static IndexModel isIndexReady(String indexName, PineconeControlPlaneClient controlPlaneClient)
throws InterruptedException {
final IndexModel[] indexModels = new IndexModel[1];
Expand Down
Loading
Loading