Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
39 changes: 39 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,45 @@ public class DisableTLSExample {
}
```

#### Capturing response metadata for observability

The SDK supports capturing response metadata for all data plane operations (upsert, query, fetch, update, delete). This enables you to track latency metrics and integrate with observability tools like OpenTelemetry, Prometheus, or Datadog.

```java
import io.pinecone.clients.Pinecone;
import io.pinecone.clients.Index;

Pinecone pinecone = new Pinecone.Builder("PINECONE_API_KEY")
.withResponseMetadataListener(metadata -> {
System.out.printf("Operation: %s | Client: %dms | Server: %dms | Network: %dms%n",
metadata.getOperationName(),
metadata.getClientDurationMs(),
metadata.getServerDurationMs(),
metadata.getNetworkOverheadMs());
})
.build();

Index index = pinecone.getIndexConnection("example-index");
index.query(5, Arrays.asList(1.0f, 2.0f, 3.0f));
// Output: Operation: query | Client: 45ms | Server: 32ms | Network: 13ms
```

The `ResponseMetadata` object provides:

| Method | Description |
|--------|-------------|
| `getOperationName()` | Operation type: upsert, query, fetch, update, delete |
| `getClientDurationMs()` | Total round-trip time measured by the client |
| `getServerDurationMs()` | Server processing time from `x-pinecone-response-duration-ms` header |
| `getNetworkOverheadMs()` | Computed: client duration - server duration |
| `getIndexName()` | Name of the index |
| `getNamespace()` | Namespace used |
| `isSuccess()` | Whether the operation succeeded |
| `getGrpcStatusCode()` | gRPC status code |
| `getErrorType()` | Error category when failed |

For a complete OpenTelemetry integration example with Prometheus and Grafana, see the [java-otel-metrics example](examples/java-otel-metrics/).

# Indexes

Operations related to the building and managing of Pinecone indexes are called [control plane](https://docs.pinecone.io/reference/api/introduction#control-plane) operations.
Expand Down
12 changes: 10 additions & 2 deletions examples/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -10,20 +10,28 @@ repositories {
jcenter()
}

def opentelemetryVersion = '1.35.0'

dependencies {
implementation project(':pinecone-client')

implementation "org.slf4j:slf4j-simple:1.7.30"
implementation 'org.codehaus.groovy:groovy-all:2.4.15'

// OpenTelemetry dependencies for java-otel-metrics example
implementation "io.opentelemetry:opentelemetry-sdk:${opentelemetryVersion}"
implementation "io.opentelemetry:opentelemetry-sdk-metrics:${opentelemetryVersion}"
implementation "io.opentelemetry:opentelemetry-exporter-otlp:${opentelemetryVersion}"
implementation "io.opentelemetry:opentelemetry-exporter-logging:${opentelemetryVersion}"
}

sourceSets {
main {
java {
srcDirs = ['java-basic-mvn/src']
srcDirs = ['java-basic-mvn/src', 'java-otel-metrics/src/main/java']
}
groovy {
srcDirs = ['groovy-basic']
}
}
}
}
151 changes: 151 additions & 0 deletions examples/java-otel-metrics/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
# Pinecone Java SDK - OpenTelemetry Metrics Example

This example demonstrates how to integrate OpenTelemetry metrics with the Pinecone Java SDK using the `ResponseMetadataListener` feature. It captures latency metrics for all data plane operations and exports them to Prometheus/Grafana for visualization.

## What This Example Does

- Captures **client-side latency** (total round-trip time) for Pinecone operations
- Captures **server-side latency** from the `x-pinecone-response-duration-ms` header
- Calculates **network overhead** (client - server duration)
- Exports metrics to OpenTelemetry-compatible backends (Prometheus, Grafana, Datadog, etc.)

## Metrics Recorded

| Metric | Type | Description |
|--------|------|-------------|
| `db.client.operation.duration` | Histogram | Client-measured round-trip time (ms) |
| `pinecone.server.processing.duration` | Histogram | Server processing time from header (ms) |
| `db.client.operation.count` | Counter | Total number of operations |

### Attributes

| Attribute | Description |
|-----------|-------------|
| `db.system` | Always "pinecone" |
| `db.operation.name` | Operation type (upsert, query, fetch, update, delete) |
| `db.namespace` | Pinecone namespace |
| `pinecone.index_name` | Index name |
| `server.address` | Pinecone host |
| `status` | "success" or "error" |

## Prerequisites

- Java 8+
- Maven 3.6+
- Docker and Docker Compose
- A Pinecone account with an API key and index

## Project Structure

```
java-otel-metrics/
├── pom.xml # Maven dependencies
├── README.md # This file
├── observability/ # Local observability stack
│ ├── docker-compose.yml # Prometheus + Grafana + OTel Collector
│ ├── otel-collector-config.yaml # OTel Collector configuration
│ └── prometheus.yml # Prometheus scrape config
└── src/main/java/pineconeexamples/
├── PineconeOtelMetricsExample.java # Main example
└── PineconeMetricsRecorder.java # Reusable metrics recorder
```

## Quick Start

### 1. Start the Observability Stack

```bash
cd examples/java-otel-metrics/observability
docker-compose up -d
```

This starts:
- **OpenTelemetry Collector** (port 4317) - receives metrics via OTLP
- **Prometheus** (port 9090) - stores metrics
- **Grafana** (port 3000) - visualizes metrics

### 2. Run the Example

```bash
cd examples/java-otel-metrics

export PINECONE_API_KEY=your-api-key
export PINECONE_INDEX=your-index-name
export OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4317

mvn package exec:java -Dexec.mainClass="pineconeexamples.PineconeOtelMetricsExample"
```

### 3. View Metrics in Grafana

1. Open http://localhost:3000
2. Login with `admin` / `admin`
3. Go to **Connections** → **Data sources** → **Add data source**
4. Select **Prometheus**, set URL to `http://prometheus:9090`, click **Save & test**
5. Go to **Dashboards** → **New** → **New Dashboard** → **Add visualization**

### 4. Sample Grafana Queries

**P50 Client vs Server Latency:**
```promql
histogram_quantile(0.5, sum(rate(db_client_operation_duration_milliseconds_bucket[5m])) by (le))
histogram_quantile(0.5, sum(rate(pinecone_server_processing_duration_milliseconds_bucket[5m])) by (le))
```

**P95 Latency by Operation:**
```promql
histogram_quantile(0.95, sum(rate(db_client_operation_duration_milliseconds_bucket[5m])) by (le, db_operation_name))
```

**Operation Count by Type:**
```promql
sum by (db_operation_name) (db_client_operation_count_total)
```

## Understanding the Metrics

### Percentiles Explained

| Percentile | Meaning |
|------------|---------|
| P50 | Median - typical latency |
| P90 | 90% of requests are faster |
| P95 | Tail latency - good for SLAs |
| P99 | Worst-case for most users |

### Network Overhead

The difference between client and server duration shows network overhead:

```
Network Overhead = Client Duration - Server Duration
```

This helps identify whether latency issues are:
- **Server-side** (high server duration)
- **Network-side** (high network overhead)

## Cleanup

```bash
cd examples/java-otel-metrics/observability
docker-compose down
```

## Using in Your Project

Copy `PineconeMetricsRecorder.java` into your project:

```java
Meter meter = meterProvider.get("pinecone.client");
PineconeMetricsRecorder recorder = new PineconeMetricsRecorder(meter);

Pinecone client = new Pinecone.Builder(apiKey)
.withResponseMetadataListener(recorder)
.build();

// All operations now emit metrics automatically
Index index = client.getIndexConnection(indexName);
index.upsert(...); // Metrics recorded!
index.query(...); // Metrics recorded!
```
30 changes: 30 additions & 0 deletions examples/java-otel-metrics/observability/docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
version: '3.8'

services:
otel-collector:
image: otel/opentelemetry-collector-contrib:0.96.0
command: ["--config=/etc/otel-collector-config.yaml"]
volumes:
- ./otel-collector-config.yaml:/etc/otel-collector-config.yaml
ports:
- "4317:4317" # OTLP gRPC receiver

prometheus:
image: prom/prometheus:v2.49.1
volumes:
- ./prometheus.yml:/etc/prometheus/prometheus.yml
ports:
- "9090:9090" # Prometheus UI
depends_on:
- otel-collector

grafana:
image: grafana/grafana:10.3.1
ports:
- "3000:3000" # Grafana UI
environment:
- GF_SECURITY_ADMIN_PASSWORD=admin
- GF_AUTH_ANONYMOUS_ENABLED=true
depends_on:
- prometheus

Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
receivers:
otlp:
protocols:
grpc:
endpoint: 0.0.0.0:4317

exporters:
prometheus:
endpoint: "0.0.0.0:8889"
debug:
verbosity: detailed

service:
pipelines:
metrics:
receivers: [otlp]
exporters: [prometheus, debug]

9 changes: 9 additions & 0 deletions examples/java-otel-metrics/observability/prometheus.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
global:
scrape_interval: 5s
evaluation_interval: 5s

scrape_configs:
- job_name: 'otel-collector'
static_configs:
- targets: ['otel-collector:8889']

98 changes: 98 additions & 0 deletions examples/java-otel-metrics/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
<?xml version="1.0" encoding="UTF-8"?>

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<groupId>pineconeexamples</groupId>
<artifactId>java-otel-metrics</artifactId>
<version>1.0-SNAPSHOT</version>

<name>java-otel-metrics</name>
<description>Example showing how to integrate OpenTelemetry metrics with Pinecone Java SDK</description>
<url>https://github.com/pinecone-io/pinecone-java-client</url>

<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<opentelemetry.version>1.35.0</opentelemetry.version>
</properties>

<dependencies>
<!-- Pinecone Java SDK -->
<dependency>
<groupId>io.pinecone</groupId>
<artifactId>pinecone-client</artifactId>
<version>6.1.0</version>
</dependency>

<!-- OpenTelemetry SDK -->
<dependency>
<groupId>io.opentelemetry</groupId>
<artifactId>opentelemetry-sdk</artifactId>
<version>${opentelemetry.version}</version>
</dependency>

<!-- OpenTelemetry Metrics SDK -->
<dependency>
<groupId>io.opentelemetry</groupId>
<artifactId>opentelemetry-sdk-metrics</artifactId>
<version>${opentelemetry.version}</version>
</dependency>

<!-- OpenTelemetry OTLP Exporter (for production use with Grafana, Jaeger, etc.) -->
<dependency>
<groupId>io.opentelemetry</groupId>
<artifactId>opentelemetry-exporter-otlp</artifactId>
<version>${opentelemetry.version}</version>
</dependency>

<!-- OpenTelemetry Logging Exporter (for console output / debugging) -->
<dependency>
<groupId>io.opentelemetry</groupId>
<artifactId>opentelemetry-exporter-logging</artifactId>
<version>${opentelemetry.version}</version>
</dependency>

<!-- SLF4J Simple binding for logging -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
<version>1.7.36</version>
</dependency>
</dependencies>

<build>
<pluginManagement>
<plugins>
<plugin>
<artifactId>maven-clean-plugin</artifactId>
<version>3.1.0</version>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<version>3.1.0</version>
</plugin>
<plugin>
<artifactId>maven-resources-plugin</artifactId>
<version>3.0.2</version>
</plugin>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.0</version>
</plugin>
<plugin>
<artifactId>maven-jar-plugin</artifactId>
<version>3.0.2</version>
</plugin>
<plugin>
<artifactId>maven-install-plugin</artifactId>
<version>2.5.2</version>
</plugin>
</plugins>
</pluginManagement>
</build>
</project>

Loading
Loading