Overview
Vespa provides multiple methods for feeding data into your application. The primary methods are:
Feed Client - High-performance Java client for asynchronous feeding
HTTP API - Direct HTTP/JSON API for document operations
vespa-feed-client CLI - Command-line tool for feeding
Feed Client
The Vespa Feed Client is a high-throughput, asynchronous Java client for feeding documents to Vespa.
Creating a Feed Client
Create a feed client by specifying the endpoint URI:
import ai.vespa.feed.client.FeedClient;
import ai.vespa.feed.client.FeedClientBuilder;
// Single endpoint with load balancer
FeedClient client = FeedClientBuilder . create ( URI . create ( "https://vespa.example.com" ))
. build ();
// Multiple endpoints (distributes load)
List < URI > endpoints = List . of (
URI . create ( "https://node1.example.com" ),
URI . create ( "https://node2.example.com" )
);
FeedClient client = FeedClientBuilder . create (endpoints)
. build ();
Configuring the Feed Client
The feed client provides extensive configuration options:
FeedClient client = FeedClientBuilder . create (endpoint)
. setConnectionsPerEndpoint ( 8 ) // Number of connections per endpoint
. setMaxStreamPerConnection ( 128 ) // Max concurrent requests per connection
. setCertificate (certFile, keyFile) // mTLS authentication
. setRetryStrategy (retryStrategy) // Custom retry logic
. setCircuitBreaker (breaker) // Circuit breaker for fault tolerance
. build ();
Reference: vespa-feed-client-api/src/main/java/ai/vespa/feed/client/FeedClientBuilder.java:21
Connection Settings
The total inflight capacity is connectionsPerEndpoint × maxStreamsPerConnection. The feed client automatically throttles to achieve optimal throughput.
FeedClient client = FeedClientBuilder . create (endpoint)
// For high-throughput feeding
. setConnectionsPerEndpoint ( 16 )
. setMaxStreamPerConnection ( 256 )
// Recycle connections periodically
. setConnectionTimeToLive ( Duration . ofMinutes ( 10 ))
. build ();
Feeding Operations
The feed client supports three main operations: put, update, and remove.
Put Document
Update Document
Remove Document
import ai.vespa.feed.client.DocumentId;
import ai.vespa.feed.client.OperationParameters;
import ai.vespa.feed.client.Result;
DocumentId id = DocumentId . of ( "mynamespace" , "music" , "doc1" );
String documentJson = """{
"fields": {
"title": "Bohemian Rhapsody",
"artist": "Queen",
"year": 1975
}
}""" ;
CompletableFuture < Result > promise = client . put (
id,
documentJson,
OperationParameters . empty ()
);
// Handle result
promise . whenComplete ((result, error) -> {
if (error != null ) {
System . err . println ( "Failed: " + error . getMessage ());
} else {
System . out . println ( "Success: " + result . documentId ());
}
});
Reference: vespa-feed-client-api/src/main/java/ai/vespa/feed/client/FeedClient.java:24-40
Operation Parameters
Customize operations with parameters:
import ai.vespa.feed.client.OperationParameters;
OperationParameters params = OperationParameters . empty ()
. createIfNonExistent ( true ) // Create document if missing
. timeout ( Duration . ofSeconds ( 30 )) // Operation timeout
. route ( "default" ) // Custom route
. tracelevel ( 5 ) // Enable tracing
. testAndSetCondition ( "music.year > 1970" ); // Conditional operation
client . put (id, documentJson, params);
Batch Feeding
For high-throughput batch feeding, send multiple operations concurrently:
import java.util.List;
import java.util.concurrent.CompletableFuture;
List < CompletableFuture < Result >> promises = new ArrayList <>();
// Send multiple operations
for ( Document doc : documents) {
DocumentId id = DocumentId . of ( "mynamespace" , "music" , doc . getId ());
CompletableFuture < Result > promise = client . put (id, doc . toJson (), OperationParameters . empty ());
promises . add (promise);
}
// Wait for all operations to complete
try {
List < Result > results = FeedClient . await (promises);
System . out . println ( "Successfully fed " + results . size () + " documents" );
} catch ( MultiFeedException e ) {
System . err . println ( "Some operations failed: " + e . getMessage ());
// Handle failures
}
Reference: vespa-feed-client-api/src/main/java/ai/vespa/feed/client/FeedClient.java:47-58
Monitoring Feed Progress
The feed client provides statistics for monitoring:
import ai.vespa.feed.client.OperationStats;
// Get current statistics
OperationStats stats = client . stats ();
System . out . println ( "Requests sent: " + stats . requests ());
System . out . println ( "Responses received: " + stats . responses ());
System . out . println ( "Average latency: " + stats . averageLatencyMillis () + "ms" );
System . out . println ( "Min inflight: " + stats . minInflight ());
System . out . println ( "Max inflight: " + stats . maxInflight ());
// Reset stats (useful for filtering out warmup)
client . resetStats ();
Circuit Breaker
The feed client includes a circuit breaker to handle failures gracefully:
import ai.vespa.feed.client.FeedClient.CircuitBreaker;
import ai.vespa.feed.client.GracePeriodCircuitBreaker;
// Configure circuit breaker
CircuitBreaker breaker = new GracePeriodCircuitBreaker (
Duration . ofSeconds ( 10 ), // Start probing after 10s of failures
Duration . ofSeconds ( 20 ) // Go OPEN if failures persist for 20s
);
FeedClient client = FeedClientBuilder . create (endpoint)
. setCircuitBreaker (breaker)
. build ();
// Check circuit breaker state
CircuitBreaker . State state = client . circuitBreakerState ();
switch (state) {
case CLOSED -> System . out . println ( "Normal operation" );
case HALF_OPEN -> System . out . println ( "Probing recovery" );
case OPEN -> System . out . println ( "Circuit breaker opened, failing fast" );
}
The circuit breaker transitions to HALF_OPEN after the grace period and OPEN after the doom period. This prevents overwhelming a struggling backend.
Reference: vespa-feed-client-api/src/main/java/ai/vespa/feed/client/FeedClient.java:86-169
Retry Strategy
Customize retry behavior for different operation types:
import ai.vespa.feed.client.FeedClient.RetryStrategy;
import ai.vespa.feed.client.FeedClient.OperationType;
RetryStrategy strategy = new RetryStrategy () {
@ Override
public boolean retry ( OperationType type ) {
// Don't retry remove operations
return type != OperationType . REMOVE ;
}
@ Override
public int retries () {
// Maximum 5 retries per operation
return 5 ;
}
};
FeedClient client = FeedClientBuilder . create (endpoint)
. setRetryStrategy (strategy)
. build ();
HTTP Document API
You can also feed documents directly using the HTTP API:
# Put document
curl -X POST \
http://localhost:8080/document/v1/mynamespace/music/docid/doc1 \
-H 'Content-Type: application/json' \
-d '{
"fields": {
"title": "Bohemian Rhapsody",
"artist": "Queen"
}
}'
# Update document
curl -X PUT \
http://localhost:8080/document/v1/mynamespace/music/docid/doc1 \
-H 'Content-Type: application/json' \
-d '{
"fields": {
"year": { "assign": 1975 }
}
}'
# Remove document
curl -X DELETE \
http://localhost:8080/document/v1/mynamespace/music/docid/doc1
Reference: vespa-feed-client/src/main/java/ai/vespa/feed/client/impl/HttpFeedClient.java:295-315
Closing the Client
Always close the feed client when done:
// Graceful close - wait for in-flight operations
client . close ();
// Force close - abort in-flight operations
client . close ( false );
Best Practices
Size Your Connection Pool
Set connectionsPerEndpoint to allow all feed clients to collectively have a small multiple of container nodes. Keep this as low as possible while maintaining throughput.
Enable compression for large documents:
FeedClient client = FeedClientBuilder . create (endpoint)
. setCompression ( FeedClientBuilder . Compression . gzip )
. build ();
Regularly check statistics to identify bottlenecks:
ScheduledExecutorService scheduler = Executors . newScheduledThreadPool ( 1 );
scheduler . scheduleAtFixedRate (() -> {
OperationStats stats = client . stats ();
log . info ( "Feed stats - requests: {}, latency: {}ms" ,
stats . requests (), stats . averageLatencyMillis ());
}, 10 , 10 , TimeUnit . SECONDS );
Implement proper error handling for feed failures:
promise . whenComplete ((result, error) -> {
if (error != null ) {
if (error instanceof FeedException) {
FeedException fe = (FeedException) error;
// Log and potentially retry
log . error ( "Feed failed for {}: {}" , fe . documentId (), fe . getMessage ());
}
}
});
See Also