Skip to content

Conversation

aulorbe
Copy link
Contributor

@aulorbe aulorbe commented Apr 5, 2024

Problem

It's currently cumbersome to force users to create CreateIndexRequest object, a ServerlessSpec object, and the required enums (IndexMetric and CloudEnum.

Solution

This PR is a start on the road towards removing the createIndex method.

In this PR, you will find a new method called CreateServerlessIndex. This is what users will use to create a serverless index. All they have to do is pass strings/ints for indexName, metric, dimension, cloud, and region. All validation of the inputs, transforms into enum objects, and construction of the Request objects takes place within the function itself.

Note:

  • From reading and asking ChatGPT, it seems that it's more idiomatic in Java to force users to construct the enums (necessary for metric and cloud), rather than pass in strings, since constructing enums directly is more type-safe and self-documenting. However, I think ease-of-use trumps those concerns, since we are building these SDKs primarily to increase developer experience.

LMK if you agree in the comments!

Asana ticket: https://app.asana.com/0/1203260648987893/1206510790411726/f

Follow-ups

  • Next up will be doing the same thing, but for a new CreatePodsIndex method (ticket)
  • After that, I will remove CreateIndex from the client altogether, updating unit tests and integration tests as necessary.
    • Within this PR ^, I will also make the necessary documentation updates to Rohan's README, located here.

Type of Change

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • This change requires a documentation update
  • Infrastructure change (CI configs, etc)
  • Non-code change (docs, etc)
  • None of the above: (explain here)

Test Plan

Unit test & integration tests included in PR.

@aulorbe aulorbe force-pushed the audrey/createIndex branch from 14c2bc2 to 53da9c9 Compare April 5, 2024 20:46
@aulorbe aulorbe changed the title [DRAFT] Add new CreateServerlessIndex method + unit tests Add new CreateServerlessIndex method Apr 5, 2024
@aulorbe aulorbe marked this pull request as ready for review April 5, 2024 21:10
// Serverless currently has limited availability in specific regions, hard-code us-west-2 for now
private static final String serverlessRegion = "us-west-2";
private static final Pinecone controlPlaneClient = new Pinecone.Builder(System.getenv("PINECONE_API_KEY")).build();
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Just a lint; ignore

Copy link
Contributor

@ssmith-pc ssmith-pc left a comment

Choose a reason for hiding this comment

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

Looks good! I like the direct/action oriented/semantics.

Couple comments throughout, LMK if you have questions!

Comment on lines 43 to 46
public <E extends Enum<E>> boolean validateEnums(String stringToValidate, List<E> validEnums) {
return validEnums.stream()
.anyMatch(validEnum -> Objects.equals(stringToValidate, validEnum.toString()));
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Looks like there is a method in these enums called fromValue to convert a string into its equivalent enum, and I see you using that below post-validation. Is there a reason you can't use it here?

IndexMetric metric = IndexMetric.fromValue("cosine");

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ahh yes, thank you so much! Removing validation method now :)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

}
List<IndexMetric> indexMetricEnums = Arrays.asList(IndexMetric.values());
if (!validateEnums(metric, indexMetricEnums)) {
throw new PineconeValidationException(String.format("Metric must be one of %s", IndexMetric.values()));
Copy link
Contributor

Choose a reason for hiding this comment

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

I'm not sure this will stringify the contents of the array properly. You might need to use Arrays.toString:

......... String.format("Metric must be one of %s", Arrays.toString(IndexMetric.values())) ...........

Copy link
Contributor Author

Choose a reason for hiding this comment

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

oh wow, incredible catch, thank you!

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@@ -35,6 +40,64 @@ public IndexModel createIndex(CreateIndexRequest createIndexRequest) throws Pine
return indexModel;
}

public <E extends Enum<E>> boolean validateEnums(String stringToValidate, List<E> validEnums) {
Copy link
Contributor

Choose a reason for hiding this comment

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

This method doesn't need to be public, I don't think? Also, I'd suggest moving it down towards the bottom of the class.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I actually had it w/o any access modifier previously, but my tests wouldn't run because my IDE said it had to be public.

Is there something special I need to do? I'm using IntelliJ and I've marked the dir as a 'test source root'...

Comment on lines 62 to 64
if (dimension < 1) {
throw new PineconeValidationException("Dimension must be greater than 0");
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Do we have an upper bound on dimension? I am not sure what we have in our other SDKs.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Technically the limit is 4.2B haha (for dense vectors). I don't see in the Python or Node SDKs, at least any upper limit enacted through code.

I'll add a link to our limits section, though. That'll be helpful for users.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Comment on lines 66 to 68
if (cloud == null || cloud.isEmpty()) {
throw new PineconeValidationException("Cloud cannot be null or empty.");
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Would be helpful to include the possible values, like in the error message below

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done!

Comment on lines +48 to +49
public IndexModel createServerlessIndex(String indexName, String metric, int dimension, String cloud,
String region) {
Copy link
Contributor

Choose a reason for hiding this comment

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

I like these as simple string types for simplicity. Can we add javadoc to this method? I bet copilot will write most of it for you.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I might leave this to @rohanshah18 since I think he's been working on that for this SDK in general, but I'll confirm in Slack :)

Copy link
Contributor

Choose a reason for hiding this comment

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

Good callout though, yeah we need to add javadoc comments all over.

Copy link
Contributor

@ssmith-pc ssmith-pc left a comment

Choose a reason for hiding this comment

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

Looks great!

Copy link
Contributor

@austin-denoble austin-denoble left a comment

Choose a reason for hiding this comment

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

Nice work, looks great. 🚢

Comment on lines +48 to +49
public IndexModel createServerlessIndex(String indexName, String metric, int dimension, String cloud,
String region) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Good callout though, yeah we need to add javadoc comments all over.

}

if (region == null || region.isEmpty()) {
throw new PineconeValidationException("Region cannot be null or empty.");
Copy link
Contributor

Choose a reason for hiding this comment

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

Really small nit I'm sorry - let's drop the period since we don't use one for any of the other thrown errors.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ha! Don't be sorry at all. I love grammatical consistency :)

OkHttpClient mockClient = mock(OkHttpClient.class);
when(mockClient.newCall(any(Request.class))).thenReturn(mockCall);

Pinecone client = new Pinecone.Builder("testAPiKey").withOkHttpClient(mockClient).build();
Copy link
Contributor

Choose a reason for hiding this comment

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

Thanks for covering all these test cases!

@aulorbe
Copy link
Contributor Author

aulorbe commented Apr 8, 2024

Merging this w/the integration test failure b/c the failure is a known flake, as documented here https://www.notion.so/Integration-Tests-Error-Log-a422cf031ca44271a76d899324ed385d?pvs=4

@aulorbe aulorbe merged commit 9326c72 into main Apr 8, 2024
@aulorbe aulorbe deleted the audrey/createIndex branch April 8, 2024 19:23
@aulorbe aulorbe mentioned this pull request Apr 9, 2024
7 tasks
aulorbe added a commit that referenced this pull request Apr 10, 2024
## Problem

It's currently cumbersome to make the user create a bunch of objects
from our generated code, e.g. `CreateIndexRequest`, etc. just to create
an index.

## Solution

Piggybacking off of [my last
PR](#94), this
PR creates a new method called `CreatePodsIndex`. Together with
`CreateServerlessIndex`, we can now deprecate `createIndex` altogether
(in a forthcoming PR).

The overloaded method signatures for the `createPodsIndex` methods are
as follows. (Please lmk if you having feelings about these options. It
was a bit of a crapshoot figuring out which I thought would be most
useful to users.)

- The minimal required parameters (note: `podType` is required by the
backend, not our OpenAPI spec, FWIW):
    - String indexName
    - Integer dimension
    - String environment
    - String podType
    
- Minimal + metric: 
    - String indexName
    - Integer dimension
    - String environment
    - String podType
    - String metric

- Minimal + metric + metadata:
    -  String indexName
    - Integer dimension
    - String environment
    - String podType
    - String metric
    - PodSpecMetadataConfig metadataConfig

- Minimal + metric + source collection
    - String indexName
    - Integer dimension
    - String environment
    - String podType
    - String metric
    - String sourceCollection

- Minimal + pods
    - String indexName
    - Integer dimension
    - String environment
    - String podType
    - Integer pods

- Minimal + pods + metadata:
    - String indexName
    - Integer dimension
    - String environment
    - String podType
    - Integer pods
    - PodSpecMetadataConfig metadataConfig

- Minimal + replicas and shards
    - String indexName
    - Integer dimension
    - String environment
    - String podType
    - Integer replicas
    - Integer shards

- Minimal + replicas, shards + metadata:
    - String indexName
    - Integer dimension
    - String environment
    - String podType
    - Integer replicas
    - Integer shards
    - PodSpecMetadataConfig metadataConfig

- Max: 
    - String indexName
    - Integer dimension
    - String environment
    - String podType
    - String metric
    - Integer replicas
    - Integer shards
    - Integer pods
    - PodSpecMetadataConfig metadataConfig
    - String sourceCollection

## Callouts: 
- When a user provides `pods`, they should not provide `shards` or
`replicas`, and vice versa (unless in the "max" method)
- If `replicas * shards` do not equal `pods`, an error is thrown
- Like with `createServerlessIndex` and `cloud`, the user passes
`metric` as a string, and it's internally validated against the
`IndexMetric.Enum` types
- Besides checking for nulls and empties, there are no checks for the
actual values in `environment`, `podType`, or `sourceCollection`.

## Type of Change

- [ ] Bug fix (non-breaking change which fixes an issue)
- [x] New feature (non-breaking change which adds functionality)
- [x] Breaking change (fix or feature that would cause existing
functionality to not work as expected)
- [x] This change requires a documentation update
- [ ] Infrastructure change (CI configs, etc)
- [ ] Non-code change (docs, etc)
- [ ] None of the above: (explain here)

## Test Plan

Unit and integration tests included.

Note: in the next PR I will deprecate `createIndex` altogether. Within
this PR, I will update the `TestIndexResourcesManager` singleton to work
with the new methods. I will also update the README, as per this
[ticket](https://app.asana.com/0/1203260648987893/1206994598304493/f).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants