Quickstart¶
Build a failover-enabled service in 5 minutes — one dependency, one annotation, and your service is protected.
1. Add the Dependency¶
2. Configure the Store¶
CREATE TABLE DEMO_FAILOVER_STORE (
FAILOVER_NAME VARCHAR(50) NOT NULL,
FAILOVER_KEY VARCHAR(256) NOT NULL,
AS_OF TIMESTAMP(9) WITH TIME ZONE NOT NULL,
EXPIRE_ON TIMESTAMP(9) WITH TIME ZONE NOT NULL,
PAYLOAD VARCHAR(4000),
PAYLOAD_CLASS VARCHAR(256),
PRIMARY KEY (FAILOVER_NAME, FAILOVER_KEY)
);
3. Define Your Domain Type¶
Your return type must extend Referential or implement ReferentialAware to carry failover metadata (upToDate, asOf) back to callers.
@Data
@EqualsAndHashCode(callSuper = false)
public class Country extends Referential {
private String code;
private String name;
private String currency;
}
Referential adds three fields:
| Field | Type | Description |
|---|---|---|
upToDate | boolean | true when fetched live; false when recovered from store |
asOf | Instant | When the payload was originally stored |
metadata | Metadata | Optional carrier for additional context |
@Data
public class Country implements ReferentialAware {
private String code;
private String name;
private boolean upToDate;
private Instant asOf;
private Metadata metadata;
@Override
public void setUpToDate(boolean upToDate) {
this.upToDate = upToDate;
}
@Override
public void setAsOf(Instant asOf) {
this.asOf = asOf;
}
@Override
public void setMetadata(Metadata metadata) {
this.metadata = metadata;
}
}
Use ReferentialAware when you cannot extend Referential (e.g. the class already has a superclass).
4. Annotate Your Method¶
Place @Failover on a method of a Spring-managed bean. The annotation works on any bean type: @Service, @Component, @FeignClient, etc.
@FeignClient(name = "country-service", url = "${country.service.url}")
public interface CountryClient {
@Failover(
name = "country-by-code",
expiryDuration = 24,
expiryUnit = ChronoUnit.HOURS
)
Country findByCode(@RequestParam String code);
@Failover(
name = "all-countries",
expiryDuration = 1,
expiryUnit = ChronoUnit.DAYS
)
List<Country> findAll();
}
Annotate the implementation, not the interface
Spring AOP uses CGLIB proxies on concrete classes. If your @Failover is on an interface method (like a Feign client), the framework still intercepts it — but if you use CGLIB proxies directly, the annotation must be on the concrete class method.
5. What You Get¶
On every successful upstream call, Failover stores the result with the configured TTL:
INFO FailoverHandler: Storing information on 'country-by-code' for failover.
ReferentialPayload: {name=country-by-code, key=<UUID>, upToDate=true, asOf=..., expireOn=...}
On upstream failure, the last stored result is served automatically:
INFO FailoverHandler: Recovering information on 'country-by-code' from failover store
due to exception: Connection refused
INFO FailoverHandler: Successfully recovered the information on 'country-by-code'.
ReferentialPayload: {upToDate=false, asOf=2024-01-15T10:30:00Z}
The returned object has upToDate=false and asOf set to the original store timestamp:
Country country = countryClient.findByCode("FR");
System.out.println(country.isUpToDate()); // false (recovered from store)
System.out.println(country.getAsOf()); // 2024-01-15T10:30:00Z
6. Scatter / Gather (Optional)¶
For collection-returning methods, use a PayloadSplitter to store each entity individually — enabling partial recovery when only some entries are available.
@Failover(
name = "countries-by-codes",
domain = "country", // shares store with country-by-code
payloadSplitter = "countrySplitter",
expiryDuration = 24,
expiryUnit = ChronoUnit.HOURS
)
List<Country> findByCodes(@RequestParam String codes); // codes = "FR,DE,US"
See Scatter / Gather for the full implementation guide.
Next Steps¶
- How It Works — full lifecycle explanation
- Properties Reference — all configuration options
- Store Types — choose the right backing store