-
Notifications
You must be signed in to change notification settings - Fork 0
Best Practices
Follow these recommendations when deploying Vyra in production.
Always map your Java classes to versioned, domain-oriented string identifiers:
// Recommended: stable, versioned identifier
vyra.register("order.invoice.create.v1", CreateInvoiceRequest.class);
// Avoid: using raw Java class names as identifiers
vyra.register(CreateInvoiceRequest.class.getName(), CreateInvoiceRequest.class);Why: if you refactor your Java packages or move classes between modules, the wire contract remains unchanged and compatible across nodes.
Java generics are erased at runtime. Therefore, responseType must always be a concrete Class<T>:
// Won't work: List<Player>.class is not valid Java syntax
client.request("players", req, List.class, timeout); // Unsafe casting required!
// Recommended: wrap the list inside a typed record
public record PlayerListResponse(List<Player> players) {}
client.request("players", req, PlayerListResponse.class, timeout)
.thenAccept(res -> res.players().forEach(System.out::println));While auto-generated UUIDs work fine in testing, production services should have clear, predictable instance identifiers:
Vyra vyra = RedisVyra.redis(client)
.serializer(JacksonSerializer.jackson())
.nodeId("billing-service-" + System.getenv("HOSTNAME")) // e.g. Kubernetes Pod name
.build();All services connecting to the same broker must use the same serializer format and configuration:
- If Service A uses
JacksonSerializer.jackson(), Service B must also useJacksonSerializer.jackson(). - Do not mix binary formats (Smile/CBOR) with JSON on the same transport.
- Set up a shared common library in your organization that configures the serializer consistently.
Never set arbitrary or infinite timeouts. Consider the complete request path:
// Give your remote database and service room to respond under load
Duration timeout = Duration.ofSeconds(3);
client.request("orders", req, OrderResponse.class, timeout);Exception messages can change between releases. Inspect the structured RemoteError.Code instead:
if (cause instanceof VyraRemoteException remote) {
if (remote.getCode() == RemoteError.Code.SERVER_BUSY) {
// Safe to retry with exponential backoff
scheduleRetry(req);
} else {
// Log handler or domain failure
logger.error("Remote failure: {}", remote.getMessage());
}
}Because network timeouts can occur after a remote service has started or completed work, design state-mutating requests to be idempotent:
public record CreateChargeRequest(
String idempotencyKey, // e.g. UUID generated on the client
String customerId,
double amount
) {}The receiving handler can store processed idempotencyKey entries to avoid duplicate charges.
Events published via publish(...) provide at-most-once delivery:
- Use events for: cache invalidations, audit logs, UI live updates, metrics.
- Do not use events for: critical billing transactions or order processing. Use Request / response when delivery confirmation is required.
Integrate vyra.close() into your application shutdown hooks:
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
logger.info("Shutting down Vyra instance...");
vyra.close();
// redisClient.shutdown();
// executorService.shutdown();
}));Note
Following the lifecycle rule, calling vyra.close() closes only the
connections opened by that specific Vyra instance.
- Learn how to manage timeouts and retries in Timeouts & errors.
- Understand the transport layer in Transports.
- Explore all setup options in Configuration.
Vyra documentation