Overview
Build resilient integrations with healthchecks, quotas (load distribution), and fallbacks (automatic failover).
What is Resilience?
Resilience in Ductape helps you build fault-tolerant integrations by:
- Healthchecks: Monitor provider availability and detect failures
- Quotas: Distribute load across multiple providers based on weights
- Fallbacks: Automatically failover to backup providers when primary fails
Quick Start
- TypeScript
- Java
- Go
- .NET
import Ductape from '@ductape/sdk';
const ductape = new Ductape({
accessKey: 'your-access-key',
});
import app.ductape.sdk.Ductape;
import app.ductape.sdk.core.EnvType;
import app.ductape.sdk.core.RequestContext;
RequestContext auth = new RequestContext(null, null, null, null, 'your-access-key');
Ductape ductape = new Ductape(EnvType.PRODUCTION, auth);
import (
"context"
"github.com/ductape/ductape/sdk/go/core"
ductapesdk "github.com/ductape/ductape/sdk/go/ductape"
)
auth := core.NewRequestContext("", "", "", "", 'your-access-key')
client, err := ductapesdk.New(core.EnvProduction, auth)
if err != nil {
return err
}
using Ductape.Sdk;
using Ductape.Sdk.Core;
var auth = new RequestContext(null, null, null, null, 'your-access-key', null);
var ductape = new Ductape(EnvType.Production, auth);
Code-First API
Define resilience configurations using a fluent, type-safe API:
- TypeScript
- Java
- Go
- .NET
const { resilience } = ductape;
// Define a healthcheck
const healthcheck = await resilience.healthcheck.define({
tag: 'stripe-health',
handler: async (ctx) => {
ctx.probe().app('stripe-app').action('health');
ctx.interval(30000); // Check every 30 seconds
ctx.retries(2);
ctx.env('prd');
},
});
// Define a quota for load distribution
const quota = await resilience.quota.define({
tag: 'sms-quota',
input: {
phone: { type: 'string' },
message: { type: 'string' },
},
handler: async (ctx) => {
ctx.provider('twilio')
.weight(70)
.healthcheck('twilio-health')
.app('twilio-app')
.action('send-sms')
.retries(2);
ctx.provider('nexmo')
.weight(30)
.healthcheck('nexmo-health')
.app('nexmo-app')
.action('send-sms')
.retries(2);
},
});
// Define a fallback for automatic failover
const fallback = await resilience.fallback.define({
tag: 'payment-fallback',
input: {
amount: { type: 'number' },
},
handler: async (ctx) => {
ctx.primary('stripe')
.healthcheck('stripe-health')
.app('stripe-app')
.action('charge')
.retries(2);
ctx.fallback('paypal')
.healthcheck('paypal-health')
.app('paypal-app')
.action('payment')
.retries(2);
},
});
Map<String, Object> Map.of( resilience ) = ductape;
// Define a healthcheck
Map<String, Object> healthcheck = resilience.healthcheck.define(Map.of(
"tag", "stripe-health",
handler: async (ctx) => Map.of(
ctx.probe().app('stripe-app').action('health');
ctx.interval(30000); // Check every 30 seconds
ctx.retries(2);
ctx.env('prd');
)
));
// Define a quota for load distribution
Map<String, Object> quota = resilience.quota.define(Map.of(
"tag", "sms-quota",
input: Map.of(
phone: Map.of( "type", "string" ),
message: Map.of( "type", "string" )
),
handler: async (ctx) => Map.of(
ctx.provider('twilio')
.weight(70)
.healthcheck('twilio-health')
.app('twilio-app')
.action('send-sms')
.retries(2);
ctx.provider('nexmo')
.weight(30)
.healthcheck('nexmo-health')
.app('nexmo-app')
.action('send-sms')
.retries(2);
)
));
// Define a fallback for automatic failover
Map<String, Object> fallback = resilience.fallback.define(Map.of(
"tag", "payment-fallback",
input: Map.of(
amount: Map.of( "type", "number" )
),
handler: async (ctx) => Map.of(
ctx.primary('stripe')
.healthcheck('stripe-health')
.app('stripe-app')
.action('charge')
.retries(2);
ctx.fallback('paypal')
.healthcheck('paypal-health')
.app('paypal-app')
.action('payment')
.retries(2);
)
));
const { resilience } = ductape;
// Define a healthcheck
healthcheck := resilience.healthcheck.define({
"tag": "stripe-health",
handler: async (ctx) => {
ctx.probe().app('stripe-app').action('health');
ctx.interval(30000); // Check every 30 seconds
ctx.retries(2);
ctx.env('prd');
},
});
// Define a quota for load distribution
quota := resilience.quota.define({
"tag": "sms-quota",
input: {
phone: { "type": "string" },
message: { "type": "string" },
},
handler: async (ctx) => {
ctx.provider('twilio')
.weight(70)
.healthcheck('twilio-health')
.app('twilio-app')
.action('send-sms')
.retries(2);
ctx.provider('nexmo')
.weight(30)
.healthcheck('nexmo-health')
.app('nexmo-app')
.action('send-sms')
.retries(2);
},
});
// Define a fallback for automatic failover
fallback := resilience.fallback.define({
"tag": "payment-fallback",
input: {
amount: { "type": "number" },
},
handler: async (ctx) => {
ctx.primary('stripe')
.healthcheck('stripe-health')
.app('stripe-app')
.action('charge')
.retries(2);
ctx.fallback('paypal')
.healthcheck('paypal-health')
.app('paypal-app')
.action('payment')
.retries(2);
},
});
var { resilience } = ductape;
// Define a healthcheck
var healthcheck = await resilience.healthcheck.define({
["tag"] = "stripe-health",
handler: async (ctx) => {
ctx.probe().app('stripe-app').action('health');
ctx.interval(30000); // Check every 30 seconds
ctx.retries(2);
ctx.env('prd');
},
});
// Define a quota for load distribution
var quota = await resilience.quota.define({
["tag"] = "sms-quota",
input: {
phone: { ["type"] = "string" },
message: { ["type"] = "string" },
},
handler: async (ctx) => {
ctx.provider('twilio')
.weight(70)
.healthcheck('twilio-health')
.app('twilio-app')
.action('send-sms')
.retries(2);
ctx.provider('nexmo')
.weight(30)
.healthcheck('nexmo-health')
.app('nexmo-app')
.action('send-sms')
.retries(2);
},
});
// Define a fallback for automatic failover
var fallback = await resilience.fallback.define({
["tag"] = "payment-fallback",
input: {
amount: { ["type"] = "number" },
},
handler: async (ctx) => {
ctx.primary('stripe')
.healthcheck('stripe-health')
.app('stripe-app')
.action('charge')
.retries(2);
ctx.fallback('paypal')
.healthcheck('paypal-health')
.app('paypal-app')
.action('payment')
.retries(2);
},
});
Running Resilience Operations
Run a Healthcheck
Manually trigger a healthcheck and cache the result:
- TypeScript
- Java
- Go
- .NET
const result = await ductape.health.run({
tag: 'stripe-health',
});
console.log(result.status); // 'available' or 'unavailable'
Map<String, Object> result = ductape.health.run(Map.of(
"tag", "stripe-health"
));
System.out.println(result.status); // 'available' or 'unavailable'
result := client.health.run({
"tag": "stripe-health",
});
fmt.Println(result.status); // 'available' or 'unavailable'
var result = await ductape.health.run({
["tag"] = "stripe-health",
});
Console.WriteLine(result.status); // 'available' or 'unavailable'
Check Health Status
Get the current cached status of a healthcheck:
- TypeScript
- Java
- Go
- .NET
const status = await ductape.health.status({
tag: 'stripe-health',
});
Map<String, Object> status = ductape.health.status(Map.of(
"tag", "stripe-health"
));
status := client.health.status({
"tag": "stripe-health",
});
var status = await ductape.health.status({
["tag"] = "stripe-health",
});
Run a Quota
- TypeScript
- Java
- Go
- .NET
const result = await ductape.quota.run({
tag: 'sms-quota',
input: { phone: '+1234567890', message: 'Hello!' },
});
Map<String, Object> result = ductape.quota.run(Map.of(
"tag", "sms-quota",
input: Map.of( "phone", "+1234567890", "message", "Hello!" )
));
result := client.quota.run({
"tag": "sms-quota",
input: { "phone": "+1234567890", "message": "Hello!" },
});
var result = await ductape.quota.run({
["tag"] = "sms-quota",
input: { ["phone"] = "+1234567890", ["message"] = "Hello!" },
});
Run a Fallback
- TypeScript
- Java
- Go
- .NET
const paymentResult = await ductape.fallback.run({
tag: 'payment-fallback',
input: { amount: 1000 },
});
Map<String, Object> paymentResult = ductape.fallback.run(Map.of(
"tag", "payment-fallback",
input: Map.of( "amount", 1000 )
));
paymentResult := client.fallback.run({
"tag": "payment-fallback",
input: { "amount": 1000 },
});
var paymentResult = await ductape.fallback.run({
["tag"] = "payment-fallback",
input: { ["amount"] = 1000 },
});
Key Concepts
Providers
Providers represent different services or APIs that can perform the same operation. For example, Twilio and Nexmo are both SMS providers.
Health-Aware Routing
Resilience automatically routes traffic away from unhealthy providers based on healthcheck results.
Weighted Distribution
Quotas distribute load across providers based on configured weights. A provider with weight 70 receives 70% of traffic.
Sequential Failover
Fallbacks try providers in order until one succeeds. If the primary fails, it automatically tries the fallback.
Next Steps
- Healthchecks - Monitor provider availability
- Quotas - Distribute load across providers
- Fallbacks - Automatic failover on failures