scheduling
SKILL.md
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
Weekly Installs
8
Repository
andrueandersonc…ffect-tsGitHub Stars
5
First Seen
Jan 24, 2026
Security Audits
Installed on
claude-code7
opencode6
gemini-cli6
github-copilot6
codex6
antigravity5