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

[Bug] Proxyconfiguration does not work #117

Open
2 tasks done
BernhardEbn opened this issue Apr 26, 2024 · 2 comments
Open
2 tasks done

[Bug] Proxyconfiguration does not work #117

BernhardEbn opened this issue Apr 26, 2024 · 2 comments
Assignees
Labels
bug Something isn't working

Comments

@BernhardEbn
Copy link

Is this a new bug?

  • I believe this is a new bug
  • I have searched the existing issues, and I could not find an existing issue for this bug

Current Behavior

I am using the possibility to configure a okhttpclient with custom Timeout-Settings and expecially with a ProxySetting which I need:
class WdrPineconeClient() {
lateinit var pinecone: Pinecone

constructor(properties: PineconeModelProperties) : this() {
    val proxyhost: String? = System.getenv("PROXY_HOST")
    val proxyport: String? = System.getenv("PROXY_PORT")
    var httpClientBuilder = Builder().connectTimeout(21, java.util.concurrent.TimeUnit.SECONDS)
        .readTimeout(22, java.util.concurrent.TimeUnit.SECONDS)
        .writeTimeout(23, java.util.concurrent.TimeUnit.SECONDS)
    if (proxyhost != null && proxyport != null) {
        val proxy = Proxy(Proxy.Type.HTTP, InetSocketAddress(proxyhost, proxyport.toInt()))
        httpClientBuilder = httpClientBuilder.proxy(proxy)
    }
    val httpClient = httpClientBuilder.build()
    this.pinecone = Pinecone.Builder(properties.apiKey).withOkHttpClient(httpClient).build()
}

}

I want to use this client in order to make some Queries on Pinecone:

private fun invokePineconeEndpoint(
referenceItemId: String, nItems: Int, contentOwners: List
): QueryResponseWithUnsignedIndices? {
return try {
val index = pineconeClient.getIndexConnection(pineconeIndex)
val requestBuilder = QueryRequest.newBuilder()
.setId(referenceItemId)
.setNamespace(pineconeNamespace)
.setTopK(nItems)

        if (contentOwners.isNotEmpty()) {
            val filterBuilder = Struct.newBuilder()
            JsonFormat.parser().ignoringUnknownFields()
                .merge("{'content_owner': {'\$in':${contentOwners}}}", filterBuilder)
            requestBuilder.setFilter(filterBuilder.build())
            index.queryByVectorId(nItems, referenceItemId, pineconeNamespace, filterBuilder.build())
        } else {
            index.queryByVectorId(nItems, referenceItemId, pineconeNamespace)
        }
    } catch (e: Exception) {
        log.warn("Exception while invoking pinecone", e)
        null
    }
}

If I use in the Debugger the function pineconeClient.listIndexes() I receive a Result from the Pinecone Server through the Proxy which is great.

But when I call the Function .getIndexConnection(pineconeIndex) the Index-Object doesn't and expecially the function .queryByVectorId(nItems, referenceItemId, pineconeNamespace) use some hidden http-client with the standard Timeout Settings and without Proxy-Configuration.

Expected Behavior

When I set a okhttpclient in the Pinecone builder. I expect the client Object to use this specially configured httpclient. Especially:

this.pinecone = Pinecone.Builder(properties.apiKey).withOkHttpClient(httpClient).build()

Should work in all of the function:
val index = pineconeClient.getIndexConnection(pineconeIndex)
val requestBuilder = QueryRequest.newBuilder()
.setId(referenceItemId)
.setNamespace(pineconeNamespace)
.setTopK(nItems)

        if (contentOwners.isNotEmpty()) {
            val filterBuilder = Struct.newBuilder()
            JsonFormat.parser().ignoringUnknownFields()
                .merge("{'content_owner': {'\$in':${contentOwners}}}", filterBuilder)
            requestBuilder.setFilter(filterBuilder.build())
            index.queryByVectorId(nItems, referenceItemId, pineconeNamespace, filterBuilder.build())
        } else {
            **index.queryByVectorId(nItems, referenceItemId, pineconeNamespace)**
        }

Especially when I execute the queryByVectorId(...)

Steps To Reproduce

My code examples are mentioned above. Two possible testenvironements should work.

If a PC is behind a Proxy, it's easy. Only with a correct Proxy-Setting will the client work.

Without a Proxy tests can be made be setting different Timeout, for example 10 seconds. Then using a wrong server-configuration should expose the correct working:
Is the Timeout 10 seconds the error is fixed.
Is the Timeout still 30 seconds the error is not fixed.

Relevant log output

io.grpc.StatusRuntimeException: UNAVAILABLE: io exception
	at io.grpc.stub.ClientCalls.toStatusRuntimeException(ClientCalls.java:268)
	at io.grpc.stub.ClientCalls.getUnchecked(ClientCalls.java:249)
	at io.grpc.stub.ClientCalls.blockingUnaryCall(ClientCalls.java:167)
	at io.pinecone.proto.VectorServiceGrpc$VectorServiceBlockingStub.query(VectorServiceGrpc.java:587)
	at io.pinecone.clients.Index.query(Index.java:211)
	at io.pinecone.clients.Index.queryByVectorId(Index.java:314)
	at de.zdf.backend.configuration.WdrPineconeModel.invokePineconeEndpoint(WdrPineconeModel.kt:62)
	at 
.................
Caused by: io.netty.channel.ConnectTimeoutException: connection timed out after 30000 ms: xxxxxxxxxx

Environment

- **OS**: Ubuntu 20.04
- **Language version**: Kotlin 1.9.22
- **Pinecone client version**: 1.0.0

Additional Context

No response

@BernhardEbn BernhardEbn added the bug Something isn't working label Apr 26, 2024
@rohanshah18 rohanshah18 self-assigned this May 1, 2024
@rohanshah18
Copy link
Contributor

Thanks for reporting this issue, proxy configuration is definitely a part of our roadmap.

@rohanshah18
Copy link
Contributor

@BernhardEbn To answer your question, the data plane operations use NettyChannelBuilder (gRPC) and control plane operations use OkHttpClient (REST), so that's why you're able to call describe_index_stats but not query.

The Pinecone api reference page details both the control and data plane operations: https://docs.pinecone.io/reference/api/data-plane/query

I'm currently working on adding proxy configuration support for both data (gRPC) and control plane (REST) endpoints so you can simply pass in the parameters: proxy host, proxy port, pathToCaCert, and optionally proxy username + proxy password.

Although below is an example on how to manually configure NettyChannelBuilder for a proxy until I add the support to configure proxy by passing the required parameters:

import io.grpc.HttpConnectProxiedSocketAddress;
import io.grpc.ManagedChannel;
import io.grpc.ProxiedSocketAddress;
import io.grpc.ProxyDetector;
import io.pinecone.clients.Index;
import io.pinecone.configs.PineconeConfig;
import io.pinecone.configs.PineconeConnection;
import io.grpc.netty.GrpcSslContexts;
import io.grpc.netty.NegotiationType;
import io.grpc.netty.NettyChannelBuilder;
import io.pinecone.exceptions.PineconeException;

import javax.net.ssl.SSLException;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.util.concurrent.TimeUnit;

import java.util.Arrays;

public class DPProxyExample {
    public static void main(String[] args) {
        String apiKey = System.getenv("PINECONE_API_KEY");
        String proxyHost = "127.0.0.1"; // Spun up MITM proxy locally so using local host
        int proxyPort = 8080;

        PineconeConfig config = new PineconeConfig(apiKey);
        String endpoint = System.getenv("PINECONE_HOST"); // can be obtained from console or describe_index_stats()
        NettyChannelBuilder builder = NettyChannelBuilder.forTarget(endpoint);

        ProxyDetector proxyDetector = new ProxyDetector() {
            @Override
            public ProxiedSocketAddress proxyFor(SocketAddress targetServerAddress) {
                SocketAddress proxyAddress = new InetSocketAddress(proxyHost, proxyPort);
                
                return HttpConnectProxiedSocketAddress.newBuilder()
                        .setTargetAddress((InetSocketAddress) targetServerAddress)
                        .setProxyAddress(proxyAddress)
                        .build();
            }
        };

        // Create custom channel
        try {
            builder = builder.overrideAuthority(endpoint)
                    .negotiationType(NegotiationType.TLS)
                    .keepAliveTimeout(5, TimeUnit.SECONDS)
                    .sslContext(GrpcSslContexts.forClient().build())
                    .proxyDetector(proxyDetector);
        } catch (SSLException e) {
            throw new PineconeException("SSL error opening gRPC channel", e);
        }

        // Build the managed channel with the configured options
        ManagedChannel channel = builder.build();
        config.setCustomManagedChannel(channel);
        PineconeConnection connection = new PineconeConnection(config);
        Index index = new Index(connection, "PINECONE_INDEX_NAME");
        // Data plane operations
        // 1. Upsert data
        System.out.println(index.upsert("v1", Arrays.asList(1F, 2F, 3F, 4F)));
        // 2. Describe index stats
        System.out.println(index.describeIndexStats());
    }
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

2 participants