scheduling
Scheduling in Effect
Overview
Effect's Schedule type describes patterns for:
- Retrying failed operations
- Repeating successful operations
- Polling at intervals
- Backoff strategies for resilience
Schedule<Out, In, Requirements>;
// ^^^ ^^ Output and input types
Built-In Schedules
Fixed Intervals
import { Schedule } from "effect";
const everySecond = Schedule.spaced("1 second");
const fixed = Schedule.fixed("500 millis");
Recurrence Limits
const fiveTimes = Schedule.recurs(5);
const once = Schedule.once;
const forever = Schedule.forever;
Exponential Backoff
const exponential = Schedule.exponential("100 millis");
const capped = Schedule.exponential("100 millis").pipe(Schedule.upTo("30 seconds"));
const jittered = Schedule.exponential("100 millis").pipe(Schedule.jittered);
Time-Based Limits
const forOneMinute = Schedule.spaced("1 second").pipe(Schedule.upTo("1 minute"));
const untilSuccess = Schedule.recurWhile((result) => result.status === "pending");
Using Schedules
Effect.retry - Retry on Failure
const resilientFetch = fetchData().pipe(
Effect.retry(Schedule.exponential("1 second").pipe(Schedule.compose(Schedule.recurs(5)))),
);
Effect.repeat - Repeat on Success
const polling = checkStatus().pipe(Effect.repeat(Schedule.spaced("5 seconds")));
Effect.schedule - Full Control
const scheduled = effect.pipe(Effect.schedule(mySchedule));
Schedule Combinators
Composing Schedules
const exponentialWithLimit = Schedule.exponential("1 second").pipe(Schedule.compose(Schedule.recurs(10)));
const eitherSchedule = Schedule.union(Schedule.spaced("1 second"), Schedule.recurs(5));
Adding Jitter
const jittered = Schedule.exponential("1 second").pipe(Schedule.jittered);
const customJitter = Schedule.exponential("1 second").pipe(Schedule.jittered({ min: 0.8, max: 1.2 }));
Delaying First Execution
const delayed = Schedule.spaced("1 second").pipe(Schedule.delayed(() => "5 seconds"));
Resetting Schedule
const resetting = Schedule.exponential("1 second").pipe(Schedule.resetAfter("1 minute"));
Conditional Retrying
Retry While Condition
// Use Match.tag for error type checking in predicates
const retryTransient = effect.pipe(
Effect.retry({
schedule: Schedule.exponential("1 second"),
while: (error) =>
Match.value(error).pipe(
Match.tag("TransientError", () => true),
Match.orElse(() => false),
),
}),
);
Retry Until Condition
const retryUntilFatal = effect.pipe(
Effect.retry({
schedule: Schedule.recurs(10),
until: (error) =>
Match.value(error).pipe(
Match.tag("FatalError", () => true),
Match.orElse(() => false),
),
}),
);
Cron Scheduling
import { Cron } from "effect";
const daily = Cron.parse("0 0 * * *");
const hourly = Cron.parse("0 * * * *");
const cronSchedule = Schedule.cron(daily);
Schedule Outputs
Schedules can produce values:
const withElapsed = Schedule.elapsed;
const withCount = Schedule.count;
const collecting = Schedule.collectAll<number>();
Using Schedule Output
const [result, elapsed] =
yield * effect.pipe(Effect.retry(Schedule.exponential("1 second").pipe(Schedule.compose(Schedule.elapsed))));
console.log(`Took ${elapsed}ms after retries`);
Common Patterns
API Retry with Backoff
const apiCall = fetchFromApi().pipe(
Effect.retry(
Schedule.exponential("500 millis").pipe(
Schedule.jittered,
Schedule.compose(Schedule.recurs(5)),
Schedule.upTo("30 seconds"),
),
),
);
Polling with Timeout
const poll = checkJobStatus(jobId).pipe(
Effect.repeat(Schedule.spaced("2 seconds").pipe(Schedule.upTo("5 minutes"))),
Effect.timeout("5 minutes"),
);
Circuit Breaker Pattern
const circuitBreaker = (effect: Effect.Effect<A, E>) => {
let failures = 0;
const maxFailures = 5;
const resetTimeout = "30 seconds";
return effect.pipe(
Effect.retry(
Schedule.exponential("1 second").pipe(
Schedule.compose(Schedule.recurs(3)),
Schedule.tapOutput(() =>
Effect.sync(() => {
failures++;
}),
),
),
),
);
};
Retry with Logging
const retryWithLogs = effect.pipe(
Effect.retry(
Schedule.exponential("1 second").pipe(
Schedule.compose(Schedule.recurs(5)),
Schedule.tapInput((error) => Effect.log(`Retrying after error: ${error}`)),
),
),
);
Schedule Reference
| Schedule | Pattern |
|---|---|
Schedule.forever |
Never stops |
Schedule.once |
Single execution |
Schedule.recurs(n) |
Exactly n times |
Schedule.spaced(d) |
Fixed delay d |
Schedule.fixed(d) |
Fixed interval from start |
Schedule.exponential(d) |
d, 2d, 4d, 8d... |
Schedule.fibonacci(d) |
d, d, 2d, 3d, 5d... |
Schedule.linear(d) |
d, 2d, 3d, 4d... |
Best Practices
- Always add recurs limit - Avoid infinite retries
- Use jitter for distributed systems - Prevents thundering herd
- Cap exponential backoff - Use upTo() for max delay
- Log retry attempts - Use tapInput for visibility
- Different schedules for different errors - Transient vs permanent
Additional Resources
For comprehensive scheduling documentation, consult ${CLAUDE_PLUGIN_ROOT}/references/llms-full.txt.
Search for these sections:
- "Built-In Schedules" for schedule types
- "Schedule Combinators" for composition
- "Repetition" for repeat patterns
- "Retrying" for retry patterns
- "Cron" for cron expressions
More from andrueandersoncs/claude-skill-effect-ts
schema
This skill should be used when the user asks about "Effect Schema", "Schema.Struct", "Schema.decodeUnknown", "data validation", "parsing", "Schema.transform", "Schema filters", "Schema annotations", "JSON Schema", "Schema.Class", "Schema branded types", "encoding", "decoding", "Schema.parseJson", or needs to understand how Effect handles data validation and transformation.
13testing
This skill should be used when the user asks about "Effect testing", "@effect/vitest", "it.effect", "it.live", "it.scoped", "it.layer", "it.prop", "Schema Arbitrary", "property-based testing", "fast-check", "TestClock", "testing effects", "mocking services", "test layers", "TestContext", "Effect.provide test", "time testing", "Effect test utilities", "unit testing Effect", "generating test data", "flakyTest", "test coverage", "100% coverage", "service testing", "test doubles", "mock services", or needs to understand how to test Effect-based code.
13traits
This skill should be used when the user asks about "Effect Equal", "Effect Hash", "Equivalence", "Order", "structural equality", "custom equality", "comparing objects", "sorting", "Equal.equals", "Hash.hash", "Equivalence.make", "Order.lessThan", "comparable types", or needs to understand how Effect handles equality, hashing, and ordering of values.
12configuration
This skill should be used when the user asks about "Effect Config", "environment variables", "configuration management", "Config.string", "Config.number", "ConfigProvider", "Config.nested", "Config.withDefault", "Config.redacted", "sensitive values", "config validation", "loading config from JSON", "config schema", or needs to understand how Effect handles application configuration.
12concurrency
This skill should be used when the user asks about "Effect concurrency", "fibers", "Fiber", "forking", "Effect.fork", "Effect.forkDaemon", "parallel execution", "Effect.all concurrency", "Deferred", "Queue", "PubSub", "Semaphore", "Latch", "fiber interruption", "Effect.race", "Effect.raceAll", "concurrent effects", or needs to understand how Effect handles parallel and concurrent execution.
11observability
This skill should be used when the user asks about "Effect logging", "Effect.log", "Effect metrics", "Effect tracing", "spans", "telemetry", "Metric.counter", "Metric.gauge", "Metric.histogram", "OpenTelemetry", "structured logging", "log levels", "Effect.logDebug", "Effect.logInfo", "Effect.logWarning", "Effect.logError", or needs to understand how Effect handles logging, metrics, and distributed tracing.
10