Fast Client#
The Fast Client is Venice's high-performance read client that routes requests directly to servers (bypassing the Router), and provides lower latency than the Thin Client.
Characteristics#
- Partition-aware: Maintains metadata to route directly to the correct server
- Network hops: 1 (client → server)
- Typical latency: < 2ms
- Best for: Applications requiring low latency with moderate resource overhead
When to Use#
Choose the Fast Client when:
- You need lower latency than the Thin Client (< 2ms vs < 10ms)
- You can accept some additional memory overhead for metadata caching
- Your application makes frequent reads and benefits from direct server routing
- You want built-in long-tail retry and load control features
For the lowest latency, consider the Da Vinci Client (0 hops, < 1ms). For simplicity, consider the Thin Client.
Usage#
Dependency#
Add the Venice client dependency to your project:
Creating a Client#
Use ClientFactory from the fastclient package:
// Create R2 client for HTTP communication
HttpClientFactory httpClientFactory = new HttpClientFactory.Builder()
.setUsePipelineV2(true) // Enable HTTP/2
.build();
Client r2Client = new TransportClientAdapter(
httpClientFactory.getClient(Collections.emptyMap())
);
// Create D2 client for service discovery
D2Client d2Client = new D2ClientBuilder()
.setZkHosts("zookeeper.example.com:2181") // Your ZooKeeper hosts
.build();
D2ClientUtils.startClient(d2Client); // Start the D2 client
// Create Fast Client configuration
ClientConfig clientConfig = new ClientConfig.ClientConfigBuilder<>()
.setStoreName("my-store")
.setR2Client(r2Client)
.setD2Client(d2Client)
.setClusterDiscoveryD2Service("VeniceController")
.build();
// Create and start the Fast Client
AvroGenericStoreClient<String, MyValue> client =
ClientFactory.getAndStartGenericStoreClient(clientConfig);
With SSL, add SSL properties to the R2 and D2 clients:
// R2 client: Add SSL properties to httpClientFactory.getClient()
Map<String, Object> sslProperties = new HashMap<>();
sslProperties.put(HttpClientFactory.HTTP_SSL_CONTEXT, sslFactory.getSSLContext());
sslProperties.put(HttpClientFactory.HTTP_SSL_PARAMS, sslFactory.getSSLParameters());
Client r2Client = new TransportClientAdapter(
httpClientFactory.getClient(sslProperties) // Use sslProperties instead of Collections.emptyMap()
);
// D2 client: Add SSL configuration to builder
D2Client d2Client = new D2ClientBuilder()
.setZkHosts("zookeeper.example.com:2181")
.setIsSSLEnabled(true)
.setSSLContext(sslFactory.getSSLContext())
.setSSLParameters(sslFactory.getSSLParameters())
.build();
Reading Data#
The Fast Client implements the same AvroGenericStoreClient interface as the Thin Client:
Single Get#
// Asynchronous single-key lookup
CompletableFuture<MyValue> future = client.get("my-key");
MyValue value = future.get();
Batch Get#
Set<String> keys = Set.of("key1", "key2", "key3");
Map<String, MyValue> results = client.batchGet(keys).get();
Using Specific Records#
For type-safe access with Avro-generated classes:
ClientConfig clientConfig = new ClientConfig.ClientConfigBuilder<String, MyValueRecord, MyValueRecord>()
// ... basic config ...
.setSpecificValueClass(MyValueRecord.class)
.build();
AvroSpecificStoreClient<String, MyValueRecord> client =
ClientFactory.getAndStartSpecificStoreClient(clientConfig);
Key Features#
Long-Tail Retry#
Automatically retry slow requests to improve P99 latency:
ClientConfig clientConfig = new ClientConfig.ClientConfigBuilder<>()
// ... basic config ...
.setLongTailRetryEnabledForSingleGet(true)
.setLongTailRetryThresholdForSingleGetInMicroSeconds(5000) // 5ms
.setLongTailRetryEnabledForBatchGet(true)
.build();
Advanced Configuration#
Configure retry budget, load control, and dual-read mode:
ClientConfig clientConfig = new ClientConfig.ClientConfigBuilder<>()
// ... basic config ...
// Retry budget (enabled by default) - prevents retry storms
.setRetryBudgetEnabled(true)
.setRetryBudgetPercentage(0.1) // Allow 10% of requests to retry
// Load control - automatic request rejection when overloaded
.setStoreLoadControllerEnabled(true)
.setStoreLoadControllerMaxRejectionRatio(0.5) // Max 50% rejection
// Dual read - run alongside Thin Client for gradual migration
.setDualReadEnabled(true)
.setGenericThinClient(thinClient) // Your existing Thin Client instance
.build();
Configuration Options#
Key configuration options for ClientConfig:
| Option | Description | Default |
|---|---|---|
setStoreName(String) | Store name (required) | - |
setR2Client(Client) | R2 client for HTTP (required unless using gRPC) | - |
setD2Client(D2Client) | D2 client for service discovery | - |
setClusterDiscoveryD2Service(String) | D2 service name for controller | - |
setMetadataRefreshIntervalInSeconds(long) | Metadata refresh interval | 60 |
setLongTailRetryEnabledForSingleGet(boolean) | Enable retry for single get | false |
setLongTailRetryEnabledForBatchGet(boolean) | Enable retry for batch get | false |
setRetryBudgetEnabled(boolean) | Enable retry budget | true |
setRetryBudgetPercentage(double) | Max retry rate percentage (0.0-1.0) | 0.1 |
setStoreLoadControllerEnabled(boolean) | Enable load control | false |
Routing Strategies#
Configure different routing strategies:
ClientConfig clientConfig = new ClientConfig.ClientConfigBuilder<>()
// ... basic config ...
.setClientRoutingStrategyType(ClientRoutingStrategyType.LEAST_LOADED)
.build();
Available strategies:
LEAST_LOADED: Route to the server with the least pending requestsHELIX_ASSISTED: Use Helix routing information
gRPC Support#
Enable gRPC transport for potentially lower latency:
GrpcClientConfig grpcConfig = new GrpcClientConfig.Builder()
.setR2Client(r2Client) // Required for non-storage requests
.setNettyServerToGrpcAddress(serverToGrpcMap)
.build();
ClientConfig clientConfig = new ClientConfig.ClientConfigBuilder<>()
// ... basic config ...
.setUseGrpc(true)
.setGrpcClientConfig(grpcConfig)
.build();
Health Monitoring#
Configure instance health monitoring to avoid unhealthy servers:
InstanceHealthMonitorConfig healthConfig = new InstanceHealthMonitorConfig.Builder()
.setClient(r2Client)
.setRoutingRequestDefaultTimeoutMS(1000)
.setRoutingPendingRequestCounterInstanceBlockThreshold(50)
.setHeartBeatIntervalSeconds(30)
.setHeartBeatRequestTimeoutMS(10000)
.build();
ClientConfig clientConfig = new ClientConfig.ClientConfigBuilder<>()
// ... basic config ...
.setInstanceHealthMonitor(new InstanceHealthMonitor(healthConfig))
.build();
Client Lifecycle#
Closing the Client#
Always close clients properly to release resources:
// Close the Venice client
client.close();
// Shutdown D2 client
D2ClientUtils.shutdownClient(d2Client);
// Shutdown R2 client
r2Client.shutdown(null);
Best Practices#
- Configure D2 properly: The Fast Client relies on D2 for metadata discovery
- Enable retry for critical paths: Long-tail retry improves P99 latency
- Use retry budget: Prevents retry storms under load
- Monitor health metrics: The Fast Client exposes metrics for monitoring
- Start with conservative settings: Tune retry thresholds based on observed latency
- Always close clients: Properly shutdown all clients to prevent resource leaks