Common vector store functionality for Spring AI providing a portable abstraction layer for integrating vector databases with comprehensive filtering, similarity search, and observability support.
Learn how to monitor and trace your vector store operations with Micrometer.
Spring AI Vector Store includes built-in observability support through Micrometer, providing:
All vector store operations (add, delete, query) are automatically observed when observability is configured.
<!-- Micrometer for metrics -->
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-core</artifactId>
</dependency>
<!-- Prometheus export (optional) -->
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-registry-prometheus</artifactId>
</dependency>
<!-- Tracing (optional) -->
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-tracing-bridge-brave</artifactId>
</dependency>import io.micrometer.observation.ObservationRegistry;
import org.springframework.ai.vectorstore.SimpleVectorStore;
import org.springframework.ai.vectorstore.observation.DefaultVectorStoreObservationConvention;
@Configuration
public class VectorStoreConfig {
@Bean
public VectorStore vectorStore(
EmbeddingModel embeddingModel,
ObservationRegistry observationRegistry) {
return SimpleVectorStore.builder(embeddingModel)
.observationRegistry(observationRegistry)
.customObservationConvention(new DefaultVectorStoreObservationConvention())
.build();
}
}// These operations are automatically observed
vectorStore.add(documents); // Tracked: add operation
vectorStore.similaritySearch(request); // Tracked: query operation
vectorStore.delete(ids); // Tracked: delete operationAdd to application.yml:
management:
metrics:
export:
prometheus:
enabled: true
tracing:
sampling:
probability: 1.0 # Sample 100% in dev (reduce in prod)
observations:
key-values:
application: my-vector-app
environment: ${SPRING_PROFILES_ACTIVE:dev}Or application.properties:
management.metrics.export.prometheus.enabled=true
management.tracing.sampling.probability=1.0
management.observations.key-values.application=my-vector-app
management.observations.key-values.environment=${spring.profiles.active:dev}Add Spring Boot Actuator for metrics endpoints:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>Access metrics at: http://localhost:8080/actuator/prometheus
When observability is configured, the following metrics are collected:
| Metric Name | Type | Description |
|---|---|---|
db.vector.client.operation | Timer | Duration of vector store operations |
db.vector.client.operation.active | Gauge | Currently active operations |
db.vector.client.operation.max | Gauge | Maximum operation duration |
| Tag | Example Values | Description |
|---|---|---|
db.system | simple, pinecone, chroma | Vector store type |
db.operation.name | add, delete, query | Operation type |
db.vector.similarity.metric | cosine, euclidean | Similarity metric |
| Tag | Description |
|---|---|
db.collection.name | Collection/index name |
db.vector.dimension_count | Embedding dimensions |
db.vector.query.top_k | Number of results requested |
db.vector.query.similarity_threshold | Similarity threshold |
db.vector.query.response.documents.count | Number of results returned |
# HELP db_vector_client_operation_seconds
# TYPE db_vector_client_operation_seconds summary
db_vector_client_operation_seconds_count{db_operation_name="query",db_system="simple"} 50.0
db_vector_client_operation_seconds_sum{db_operation_name="query",db_system="simple"} 1.234
db_vector_client_operation_seconds_count{db_operation_name="add",db_system="simple"} 10.0
db_vector_client_operation_seconds_sum{db_operation_name="add",db_system="simple"} 0.523Add dependency:
<dependency>
<groupId>io.zipkin.reporter2</groupId>
<artifactId>zipkin-reporter-brave</artifactId>
</dependency>Configure in application.yml:
management:
tracing:
sampling:
probability: 1.0
zipkin:
tracing:
endpoint: http://localhost:9411/api/v2/spansdocker run -d -p 9411:9411 openzipkin/zipkinAccess UI at: http://localhost:9411
Trace: user-request-12345
├─ HTTP GET /search
│ └─ db.vector.client.operation (query documents)
│ ├─ Span ID: abc123
│ ├─ Duration: 45ms
│ ├─ Tags:
│ │ ├─ db.system: simple
│ │ ├─ db.operation.name: query
│ │ ├─ db.vector.query.top_k: 10
│ │ └─ db.vector.query.response.documents.count: 8
│ └─ Events:
│ ├─ query.start
│ └─ query.completeCreate a custom convention to customize metric names and tags:
import org.springframework.ai.vectorstore.observation.VectorStoreObservationConvention;
import org.springframework.ai.vectorstore.observation.VectorStoreObservationContext;
import io.micrometer.common.KeyValue;
import io.micrometer.common.KeyValues;
public class CustomObservationConvention implements VectorStoreObservationConvention {
@Override
public String getName() {
return "custom.vector.operation"; // Custom metric name
}
@Override
public String getContextualName(VectorStoreObservationContext context) {
return context.getOperationName() + " on " + context.getCollectionName();
}
@Override
public KeyValues getLowCardinalityKeyValues(VectorStoreObservationContext context) {
return KeyValues.of(
KeyValue.of("db.type", context.getDatabaseSystem()),
KeyValue.of("operation", context.getOperationName()),
KeyValue.of("environment", System.getenv("ENV")) // Custom tag
);
}
@Override
public KeyValues getHighCardinalityKeyValues(VectorStoreObservationContext context) {
return KeyValues.of(
KeyValue.of("collection", context.getCollectionName() != null ?
context.getCollectionName() : "unknown")
);
}
}
// Use in configuration
@Bean
public VectorStore vectorStore(
EmbeddingModel embeddingModel,
ObservationRegistry observationRegistry) {
return SimpleVectorStore.builder(embeddingModel)
.observationRegistry(observationRegistry)
.customObservationConvention(new CustomObservationConvention())
.build();
}docker run -d -p 3000:3000 grafana/grafanahttp://localhost:3000 (admin/admin)http://localhost:9090Query Duration:
rate(db_vector_client_operation_seconds_sum[5m]) /
rate(db_vector_client_operation_seconds_count[5m])Operations Per Second:
rate(db_vector_client_operation_seconds_count[5m])Operation Count by Type:
sum by (db_operation_name) (db_vector_client_operation_seconds_count)Don't trace every request in production:
# Development: 100%
management.tracing.sampling.probability=1.0
# Production: 10%
management.tracing.sampling.probability=0.1Example Prometheus alert:
groups:
- name: vector_store
rules:
- alert: VectorStoreHighLatency
expr: |
rate(db_vector_client_operation_seconds_sum[5m]) /
rate(db_vector_client_operation_seconds_count[5m]) > 1
for: 5m
labels:
severity: warning
annotations:
summary: "Vector store operations taking > 1s"Track these metrics:
Low cardinality (limited values):
High cardinality (many values):
import org.springframework.ai.vectorstore.SimpleVectorStore;
import org.springframework.ai.vectorstore.observation.DefaultVectorStoreObservationConvention;
import io.micrometer.observation.ObservationRegistry;
import io.micrometer.core.instrument.MeterRegistry;
import io.micrometer.core.instrument.simple.SimpleMeterRegistry;
public class ObservabilityExample {
public static void main(String[] args) {
// 1. Create meter registry
MeterRegistry meterRegistry = new SimpleMeterRegistry();
// 2. Create observation registry
ObservationRegistry observationRegistry = ObservationRegistry.create();
observationRegistry.observationConfig()
.observationHandler(new DefaultMeterObservationHandler(meterRegistry));
// 3. Create vector store with observability
SimpleVectorStore vectorStore = SimpleVectorStore.builder(embeddingModel)
.observationRegistry(observationRegistry)
.customObservationConvention(new DefaultVectorStoreObservationConvention())
.build();
// 4. Perform operations (automatically tracked)
vectorStore.add(documents);
List<Document> results = vectorStore.similaritySearch(request);
// 5. View metrics
meterRegistry.getMeters().forEach(meter -> {
System.out.println("Metric: " + meter.getId());
System.out.println("Measurements: " + meter.measure());
});
}
}spring:
ai:
vectorstore:
initialize-schema: false
# Metrics export
management:
metrics:
export:
prometheus:
enabled: true
step: 1m
datadog:
enabled: true
api-key: ${DATADOG_API_KEY}
step: 1m
# Distributed tracing
tracing:
sampling:
probability: 0.1 # 10% sampling
# Custom tags
observations:
key-values:
application: vector-store-app
environment: production
datacenter: ${DATACENTER:us-east-1}
version: ${APP_VERSION:unknown}
# Endpoints
endpoints:
web:
exposure:
include: health,prometheus,metrics
# Health checks
health:
defaults:
enabled: true
# Logging
logging:
level:
org.springframework.ai.vectorstore: INFO
io.micrometer.observation: INFOCheck:
// Verify registry is working
@Autowired
private ObservationRegistry observationRegistry;
@PostConstruct
public void verify() {
logger.info("ObservationRegistry: {}", observationRegistry.getClass());
}Solution: Reduce high-cardinality tags
// BAD: Document IDs create too many unique metrics
KeyValue.of("document.id", doc.getId())
// GOOD: Use counts instead
KeyValue.of("document.count", String.valueOf(docs.size()))Solution: Ensure tracing is configured
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-tracing-bridge-brave</artifactId>
</dependency>Install with Tessl CLI
npx tessl i tessl/maven-org-springframework-ai--spring-ai-vector-store@1.1.0