@dojocho/effect-ts 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (149) hide show
  1. package/DOJO.md +22 -0
  2. package/dojo.json +50 -0
  3. package/katas/001-hello-effect/SENSEI.md +72 -0
  4. package/katas/001-hello-effect/solution.test.ts +35 -0
  5. package/katas/001-hello-effect/solution.ts +16 -0
  6. package/katas/002-transform-with-map/SENSEI.md +72 -0
  7. package/katas/002-transform-with-map/solution.test.ts +33 -0
  8. package/katas/002-transform-with-map/solution.ts +16 -0
  9. package/katas/003-generator-pipelines/SENSEI.md +72 -0
  10. package/katas/003-generator-pipelines/solution.test.ts +40 -0
  11. package/katas/003-generator-pipelines/solution.ts +29 -0
  12. package/katas/004-flatmap-and-chaining/SENSEI.md +80 -0
  13. package/katas/004-flatmap-and-chaining/solution.test.ts +34 -0
  14. package/katas/004-flatmap-and-chaining/solution.ts +18 -0
  15. package/katas/005-pipe-composition/SENSEI.md +81 -0
  16. package/katas/005-pipe-composition/solution.test.ts +41 -0
  17. package/katas/005-pipe-composition/solution.ts +19 -0
  18. package/katas/006-handle-errors/SENSEI.md +86 -0
  19. package/katas/006-handle-errors/solution.test.ts +53 -0
  20. package/katas/006-handle-errors/solution.ts +30 -0
  21. package/katas/007-tagged-errors/SENSEI.md +79 -0
  22. package/katas/007-tagged-errors/solution.test.ts +82 -0
  23. package/katas/007-tagged-errors/solution.ts +37 -0
  24. package/katas/008-error-patterns/SENSEI.md +89 -0
  25. package/katas/008-error-patterns/solution.test.ts +41 -0
  26. package/katas/008-error-patterns/solution.ts +38 -0
  27. package/katas/009-option-type/SENSEI.md +96 -0
  28. package/katas/009-option-type/solution.test.ts +49 -0
  29. package/katas/009-option-type/solution.ts +26 -0
  30. package/katas/010-either-and-exit/SENSEI.md +86 -0
  31. package/katas/010-either-and-exit/solution.test.ts +33 -0
  32. package/katas/010-either-and-exit/solution.ts +17 -0
  33. package/katas/011-services-and-context/SENSEI.md +82 -0
  34. package/katas/011-services-and-context/solution.test.ts +23 -0
  35. package/katas/011-services-and-context/solution.ts +17 -0
  36. package/katas/012-layers/SENSEI.md +73 -0
  37. package/katas/012-layers/solution.test.ts +23 -0
  38. package/katas/012-layers/solution.ts +26 -0
  39. package/katas/013-testing-effects/SENSEI.md +88 -0
  40. package/katas/013-testing-effects/solution.test.ts +41 -0
  41. package/katas/013-testing-effects/solution.ts +20 -0
  42. package/katas/014-schema-basics/SENSEI.md +81 -0
  43. package/katas/014-schema-basics/solution.test.ts +35 -0
  44. package/katas/014-schema-basics/solution.ts +25 -0
  45. package/katas/015-domain-modeling/SENSEI.md +85 -0
  46. package/katas/015-domain-modeling/solution.test.ts +46 -0
  47. package/katas/015-domain-modeling/solution.ts +42 -0
  48. package/katas/016-retry-and-schedule/SENSEI.md +72 -0
  49. package/katas/016-retry-and-schedule/solution.test.ts +26 -0
  50. package/katas/016-retry-and-schedule/solution.ts +23 -0
  51. package/katas/017-parallel-effects/SENSEI.md +70 -0
  52. package/katas/017-parallel-effects/solution.test.ts +33 -0
  53. package/katas/017-parallel-effects/solution.ts +17 -0
  54. package/katas/018-race-and-timeout/SENSEI.md +75 -0
  55. package/katas/018-race-and-timeout/solution.test.ts +30 -0
  56. package/katas/018-race-and-timeout/solution.ts +27 -0
  57. package/katas/019-ref-and-state/SENSEI.md +72 -0
  58. package/katas/019-ref-and-state/solution.test.ts +29 -0
  59. package/katas/019-ref-and-state/solution.ts +16 -0
  60. package/katas/020-fibers/SENSEI.md +80 -0
  61. package/katas/020-fibers/solution.test.ts +23 -0
  62. package/katas/020-fibers/solution.ts +23 -0
  63. package/katas/021-acquire-release/SENSEI.md +57 -0
  64. package/katas/021-acquire-release/solution.test.ts +23 -0
  65. package/katas/021-acquire-release/solution.ts +22 -0
  66. package/katas/022-scoped-layers/SENSEI.md +52 -0
  67. package/katas/022-scoped-layers/solution.test.ts +35 -0
  68. package/katas/022-scoped-layers/solution.ts +19 -0
  69. package/katas/023-resource-patterns/SENSEI.md +52 -0
  70. package/katas/023-resource-patterns/solution.test.ts +20 -0
  71. package/katas/023-resource-patterns/solution.ts +13 -0
  72. package/katas/024-streams-basics/SENSEI.md +61 -0
  73. package/katas/024-streams-basics/solution.test.ts +30 -0
  74. package/katas/024-streams-basics/solution.ts +16 -0
  75. package/katas/025-stream-operations/SENSEI.md +59 -0
  76. package/katas/025-stream-operations/solution.test.ts +26 -0
  77. package/katas/025-stream-operations/solution.ts +17 -0
  78. package/katas/026-combining-streams/SENSEI.md +54 -0
  79. package/katas/026-combining-streams/solution.test.ts +20 -0
  80. package/katas/026-combining-streams/solution.ts +16 -0
  81. package/katas/027-data-pipelines/SENSEI.md +58 -0
  82. package/katas/027-data-pipelines/solution.test.ts +22 -0
  83. package/katas/027-data-pipelines/solution.ts +16 -0
  84. package/katas/028-logging-and-spans/SENSEI.md +58 -0
  85. package/katas/028-logging-and-spans/solution.test.ts +50 -0
  86. package/katas/028-logging-and-spans/solution.ts +20 -0
  87. package/katas/029-http-client/SENSEI.md +59 -0
  88. package/katas/029-http-client/solution.test.ts +49 -0
  89. package/katas/029-http-client/solution.ts +24 -0
  90. package/katas/030-capstone/SENSEI.md +63 -0
  91. package/katas/030-capstone/solution.test.ts +67 -0
  92. package/katas/030-capstone/solution.ts +55 -0
  93. package/katas/031-config-and-environment/SENSEI.md +77 -0
  94. package/katas/031-config-and-environment/solution.test.ts +38 -0
  95. package/katas/031-config-and-environment/solution.ts +11 -0
  96. package/katas/032-cause-and-defects/SENSEI.md +90 -0
  97. package/katas/032-cause-and-defects/solution.test.ts +50 -0
  98. package/katas/032-cause-and-defects/solution.ts +23 -0
  99. package/katas/033-pattern-matching/SENSEI.md +86 -0
  100. package/katas/033-pattern-matching/solution.test.ts +36 -0
  101. package/katas/033-pattern-matching/solution.ts +28 -0
  102. package/katas/034-deferred-and-coordination/SENSEI.md +85 -0
  103. package/katas/034-deferred-and-coordination/solution.test.ts +25 -0
  104. package/katas/034-deferred-and-coordination/solution.ts +24 -0
  105. package/katas/035-queue-and-backpressure/SENSEI.md +100 -0
  106. package/katas/035-queue-and-backpressure/solution.test.ts +25 -0
  107. package/katas/035-queue-and-backpressure/solution.ts +21 -0
  108. package/katas/036-schema-advanced/SENSEI.md +81 -0
  109. package/katas/036-schema-advanced/solution.test.ts +55 -0
  110. package/katas/036-schema-advanced/solution.ts +19 -0
  111. package/katas/037-cache-and-memoization/SENSEI.md +73 -0
  112. package/katas/037-cache-and-memoization/solution.test.ts +47 -0
  113. package/katas/037-cache-and-memoization/solution.ts +24 -0
  114. package/katas/038-metrics/SENSEI.md +91 -0
  115. package/katas/038-metrics/solution.test.ts +39 -0
  116. package/katas/038-metrics/solution.ts +23 -0
  117. package/katas/039-managed-runtime/SENSEI.md +75 -0
  118. package/katas/039-managed-runtime/solution.test.ts +29 -0
  119. package/katas/039-managed-runtime/solution.ts +19 -0
  120. package/katas/040-request-batching/SENSEI.md +87 -0
  121. package/katas/040-request-batching/solution.test.ts +56 -0
  122. package/katas/040-request-batching/solution.ts +32 -0
  123. package/package.json +22 -0
  124. package/skills/effect-patterns-building-apis/SKILL.md +2393 -0
  125. package/skills/effect-patterns-building-data-pipelines/SKILL.md +1876 -0
  126. package/skills/effect-patterns-concurrency/SKILL.md +2999 -0
  127. package/skills/effect-patterns-concurrency-getting-started/SKILL.md +351 -0
  128. package/skills/effect-patterns-core-concepts/SKILL.md +3199 -0
  129. package/skills/effect-patterns-domain-modeling/SKILL.md +1385 -0
  130. package/skills/effect-patterns-error-handling/SKILL.md +1212 -0
  131. package/skills/effect-patterns-error-handling-resilience/SKILL.md +179 -0
  132. package/skills/effect-patterns-error-management/SKILL.md +1668 -0
  133. package/skills/effect-patterns-getting-started/SKILL.md +237 -0
  134. package/skills/effect-patterns-making-http-requests/SKILL.md +1756 -0
  135. package/skills/effect-patterns-observability/SKILL.md +1586 -0
  136. package/skills/effect-patterns-platform/SKILL.md +1195 -0
  137. package/skills/effect-patterns-platform-getting-started/SKILL.md +179 -0
  138. package/skills/effect-patterns-project-setup--execution/SKILL.md +233 -0
  139. package/skills/effect-patterns-resource-management/SKILL.md +827 -0
  140. package/skills/effect-patterns-scheduling/SKILL.md +451 -0
  141. package/skills/effect-patterns-scheduling-periodic-tasks/SKILL.md +763 -0
  142. package/skills/effect-patterns-streams/SKILL.md +2052 -0
  143. package/skills/effect-patterns-streams-getting-started/SKILL.md +421 -0
  144. package/skills/effect-patterns-streams-sinks/SKILL.md +1181 -0
  145. package/skills/effect-patterns-testing/SKILL.md +1632 -0
  146. package/skills/effect-patterns-tooling-and-debugging/SKILL.md +1125 -0
  147. package/skills/effect-patterns-value-handling/SKILL.md +676 -0
  148. package/tsconfig.json +20 -0
  149. package/vitest.config.ts +3 -0
@@ -0,0 +1,451 @@
1
+ ---
2
+ name: effect-patterns-scheduling
3
+ description: Effect-TS patterns for Scheduling. Use when working with scheduling in Effect-TS applications.
4
+ ---
5
+ # Effect-TS Patterns: Scheduling
6
+ This skill provides 3 curated Effect-TS patterns for scheduling.
7
+ Use this skill when working on tasks related to:
8
+ - scheduling
9
+ - Best practices in Effect-TS applications
10
+ - Real-world patterns and solutions
11
+
12
+ ---
13
+
14
+ ## 🟢 Beginner Patterns
15
+
16
+ ### Retry Failed Operations
17
+
18
+ **Rule:** Use Effect.retry with a Schedule to handle transient failures gracefully.
19
+
20
+ **Good Example:**
21
+
22
+ ```typescript
23
+ import { Effect, Schedule, Data } from "effect"
24
+
25
+ // ============================================
26
+ // 1. Define error types
27
+ // ============================================
28
+
29
+ class NetworkError extends Data.TaggedError("NetworkError")<{
30
+ readonly message: string
31
+ }> {}
32
+
33
+ class RateLimitError extends Data.TaggedError("RateLimitError")<{
34
+ readonly retryAfter: number
35
+ }> {}
36
+
37
+ class NotFoundError extends Data.TaggedError("NotFoundError")<{
38
+ readonly resource: string
39
+ }> {}
40
+
41
+ // ============================================
42
+ // 2. Simulate a flaky API call
43
+ // ============================================
44
+
45
+ let callCount = 0
46
+ const fetchData = Effect.gen(function* () {
47
+ callCount++
48
+ yield* Effect.log(`API call attempt ${callCount}`)
49
+
50
+ // Simulate intermittent failures
51
+ if (callCount < 3) {
52
+ return yield* Effect.fail(new NetworkError({ message: "Connection timeout" }))
53
+ }
54
+
55
+ return { data: "Success!", attempts: callCount }
56
+ })
57
+
58
+ // ============================================
59
+ // 3. Basic retry - fixed attempts
60
+ // ============================================
61
+
62
+ const withBasicRetry = fetchData.pipe(
63
+ Effect.retry(Schedule.recurs(5)) // Retry up to 5 times
64
+ )
65
+
66
+ // ============================================
67
+ // 4. Retry with delay
68
+ // ============================================
69
+
70
+ const withDelayedRetry = fetchData.pipe(
71
+ Effect.retry(
72
+ Schedule.spaced("500 millis").pipe(
73
+ Schedule.intersect(Schedule.recurs(5))
74
+ )
75
+ )
76
+ )
77
+
78
+ // ============================================
79
+ // 5. Retry only specific errors
80
+ // ============================================
81
+
82
+ const fetchWithErrors = (shouldFail: boolean) =>
83
+ Effect.gen(function* () {
84
+ if (shouldFail) {
85
+ // Randomly fail with different errors
86
+ const random = Math.random()
87
+ if (random < 0.5) {
88
+ return yield* Effect.fail(new NetworkError({ message: "Timeout" }))
89
+ } else if (random < 0.8) {
90
+ return yield* Effect.fail(new RateLimitError({ retryAfter: 1000 }))
91
+ } else {
92
+ return yield* Effect.fail(new NotFoundError({ resource: "user:123" }))
93
+ }
94
+ }
95
+ return "Data fetched!"
96
+ })
97
+
98
+ // Only retry network and rate limit errors, not NotFoundError
99
+ const retryTransientOnly = fetchWithErrors(true).pipe(
100
+ Effect.retry({
101
+ schedule: Schedule.recurs(3),
102
+ while: (error) =>
103
+ error._tag === "NetworkError" || error._tag === "RateLimitError",
104
+ })
105
+ )
106
+
107
+ // ============================================
108
+ // 6. Retry with exponential backoff
109
+ // ============================================
110
+
111
+ const withExponentialBackoff = fetchData.pipe(
112
+ Effect.retry(
113
+ Schedule.exponential("100 millis", 2).pipe( // 100ms, 200ms, 400ms...
114
+ Schedule.intersect(Schedule.recurs(5)) // Max 5 retries
115
+ )
116
+ )
117
+ )
118
+
119
+ // ============================================
120
+ // 7. Run and observe
121
+ // ============================================
122
+
123
+ const program = Effect.gen(function* () {
124
+ yield* Effect.log("Starting retry demo...")
125
+
126
+ // Reset counter
127
+ callCount = 0
128
+
129
+ const result = yield* withBasicRetry
130
+ yield* Effect.log(`Final result: ${JSON.stringify(result)}`)
131
+ })
132
+
133
+ Effect.runPromise(program)
134
+ ```
135
+
136
+ **Rationale:**
137
+
138
+ Use `Effect.retry` to automatically retry operations that fail due to transient errors like network timeouts.
139
+
140
+ ---
141
+
142
+
143
+ Many failures are temporary:
144
+
145
+ 1. **Network issues** - Connection drops, timeouts
146
+ 2. **Rate limits** - Too many requests
147
+ 3. **Resource contention** - Database locks
148
+ 4. **Service restarts** - Brief unavailability
149
+
150
+ Automatic retries handle these without manual intervention.
151
+
152
+ ---
153
+
154
+ ---
155
+
156
+ ### Your First Schedule
157
+
158
+ **Rule:** Use Schedule to control when and how often effects run.
159
+
160
+ **Good Example:**
161
+
162
+ ```typescript
163
+ import { Effect, Schedule } from "effect"
164
+
165
+ // ============================================
166
+ // 1. Retry a failing operation
167
+ // ============================================
168
+
169
+ let attempts = 0
170
+ const flakyOperation = Effect.gen(function* () {
171
+ attempts++
172
+ if (attempts < 3) {
173
+ yield* Effect.log(`Attempt ${attempts} failed`)
174
+ return yield* Effect.fail(new Error("Temporary failure"))
175
+ }
176
+ return `Success on attempt ${attempts}`
177
+ })
178
+
179
+ // Retry up to 5 times
180
+ const withRetry = flakyOperation.pipe(
181
+ Effect.retry(Schedule.recurs(5))
182
+ )
183
+
184
+ // ============================================
185
+ // 2. Repeat a successful operation
186
+ // ============================================
187
+
188
+ const logTime = Effect.gen(function* () {
189
+ const now = new Date().toISOString()
190
+ yield* Effect.log(`Current time: ${now}`)
191
+ return now
192
+ })
193
+
194
+ // Repeat 3 times
195
+ const repeated = logTime.pipe(
196
+ Effect.repeat(Schedule.recurs(3))
197
+ )
198
+
199
+ // ============================================
200
+ // 3. Add delays between operations
201
+ // ============================================
202
+
203
+ // Repeat every second, 5 times
204
+ const polling = logTime.pipe(
205
+ Effect.repeat(
206
+ Schedule.spaced("1 second").pipe(
207
+ Schedule.intersect(Schedule.recurs(5))
208
+ )
209
+ )
210
+ )
211
+
212
+ // ============================================
213
+ // 4. Common schedule patterns
214
+ // ============================================
215
+
216
+ // Fixed delay between attempts
217
+ const fixedDelay = Schedule.spaced("500 millis")
218
+
219
+ // Increasing delay (1s, 2s, 4s, 8s...)
220
+ const exponentialBackoff = Schedule.exponential("1 second")
221
+
222
+ // Maximum number of attempts
223
+ const limitedAttempts = Schedule.recurs(3)
224
+
225
+ // Combine: exponential backoff, max 5 attempts
226
+ const retryPolicy = Schedule.exponential("100 millis").pipe(
227
+ Schedule.intersect(Schedule.recurs(5))
228
+ )
229
+
230
+ // ============================================
231
+ // 5. Run examples
232
+ // ============================================
233
+
234
+ const program = Effect.gen(function* () {
235
+ yield* Effect.log("--- Retry Example ---")
236
+ const result = yield* withRetry
237
+ yield* Effect.log(`Result: ${result}`)
238
+
239
+ yield* Effect.log("\n--- Repeat Example ---")
240
+ yield* repeated
241
+ })
242
+
243
+ Effect.runPromise(program)
244
+ ```
245
+
246
+ **Rationale:**
247
+
248
+ Use `Schedule` to control timing in Effect programs - retrying failed operations, repeating successful ones, or adding delays.
249
+
250
+ ---
251
+
252
+
253
+ Schedules solve common timing problems:
254
+
255
+ 1. **Retries** - Try again after failures
256
+ 2. **Polling** - Check for updates periodically
257
+ 3. **Rate limiting** - Control how fast things run
258
+ 4. **Backoff** - Increase delays between attempts
259
+
260
+ ---
261
+
262
+ ---
263
+
264
+
265
+ ## 🟡 Intermediate Patterns
266
+
267
+ ### Scheduling Pattern 1: Repeat an Effect on a Fixed Interval
268
+
269
+ **Rule:** Repeat effects at fixed intervals using Schedule.fixed for steady-state operations and background tasks.
270
+
271
+ **Good Example:**
272
+
273
+ This example demonstrates a health check service that polls multiple service endpoints every 30 seconds and reports their status.
274
+
275
+ ```typescript
276
+ import { Effect, Schedule, Duration } from "effect";
277
+
278
+ interface ServiceStatus {
279
+ readonly service: string;
280
+ readonly url: string;
281
+ readonly isHealthy: boolean;
282
+ readonly responseTime: number;
283
+ readonly lastChecked: number;
284
+ }
285
+
286
+ // Mock health check that calls an endpoint
287
+ const checkServiceHealth = (
288
+ url: string,
289
+ service: string
290
+ ): Effect.Effect<ServiceStatus> =>
291
+ Effect.gen(function* () {
292
+ const startTime = Date.now();
293
+
294
+ // Simulate HTTP call with occasional failures
295
+ const isHealthy = Math.random() > 0.1; // 90% success rate
296
+ const responseTime = Math.random() * 500; // 0-500ms
297
+
298
+ yield* Effect.sleep(Duration.millis(Math.round(responseTime)));
299
+
300
+ if (!isHealthy) {
301
+ yield* Effect.fail(new Error(`${service} is unhealthy`));
302
+ }
303
+
304
+ return {
305
+ service,
306
+ url,
307
+ isHealthy: true,
308
+ responseTime: Math.round(Date.now() - startTime),
309
+ lastChecked: Date.now(),
310
+ };
311
+ });
312
+
313
+ // Health check for multiple services
314
+ interface HealthCheckConfig {
315
+ readonly services: Array<{
316
+ readonly name: string;
317
+ readonly url: string;
318
+ }>;
319
+ readonly intervalSeconds: number;
320
+ }
321
+
322
+ // Keep track of service status
323
+ const serviceStatuses = new Map<string, ServiceStatus>();
324
+
325
+ // Check all services and report status
326
+ const checkAllServices = (
327
+ config: HealthCheckConfig
328
+ ): Effect.Effect<void> =>
329
+ Effect.gen(function* () {
330
+ for (const service of config.services) {
331
+ const status = yield* checkServiceHealth(service.url, service.name).pipe(
332
+ Effect.either
333
+ );
334
+
335
+ if (status._tag === "Right") {
336
+ serviceStatuses.set(service.name, status.right);
337
+ console.log(
338
+ `✓ ${service.name}: OK (${status.right.responseTime}ms)`
339
+ );
340
+ } else {
341
+ console.log(`✗ ${service.name}: FAILED`);
342
+ // Keep last known status if available
343
+ }
344
+ }
345
+ });
346
+
347
+ // Create the repeating health check
348
+ const createHealthCheckScheduler = (
349
+ config: HealthCheckConfig
350
+ ): Effect.Effect<void> =>
351
+ checkAllServices(config).pipe(
352
+ // Schedule with fixed interval (fixed = ignore execution time)
353
+ Effect.repeat(
354
+ Schedule.fixed(Duration.seconds(config.intervalSeconds))
355
+ )
356
+ );
357
+
358
+ // Report current status
359
+ const reportStatus = (): Effect.Effect<void> =>
360
+ Effect.sync(() => {
361
+ if (serviceStatuses.size === 0) {
362
+ console.log("\n[STATUS] No services checked yet");
363
+ return;
364
+ }
365
+
366
+ console.log("\n[STATUS REPORT]");
367
+ for (const [service, status] of serviceStatuses) {
368
+ const ago = Math.round((Date.now() - status.lastChecked) / 1000);
369
+ console.log(
370
+ ` ${service}: ${status.isHealthy ? "✓" : "✗"} (checked ${ago}s ago)`
371
+ );
372
+ }
373
+ });
374
+
375
+ // Run health checker in background and check status periodically
376
+ const program = Effect.gen(function* () {
377
+ const config: HealthCheckConfig = {
378
+ services: [
379
+ { name: "API", url: "https://api.example.com/health" },
380
+ { name: "Database", url: "https://db.example.com/health" },
381
+ { name: "Cache", url: "https://cache.example.com/health" },
382
+ ],
383
+ intervalSeconds: 5, // Check every 5 seconds
384
+ };
385
+
386
+ // Fork the health checker to run in background
387
+ const checker = yield* createHealthCheckScheduler(config).pipe(
388
+ Effect.fork
389
+ );
390
+
391
+ // Check and report status every 15 seconds for 60 seconds
392
+ yield* reportStatus().pipe(
393
+ Effect.repeat(
394
+ Schedule.addDelay(
395
+ Schedule.recurs(3), // 3 repetitions = 4 total (initial + 3)
396
+ () => Duration.seconds(15)
397
+ )
398
+ )
399
+ );
400
+
401
+ // Interrupt the background checker
402
+ yield* checker.interrupt();
403
+ });
404
+
405
+ Effect.runPromise(program);
406
+ ```
407
+
408
+ This pattern:
409
+
410
+ 1. **Defines service health checks** that may fail
411
+ 2. **Uses Schedule.fixed** to repeat every 5 seconds
412
+ 3. **Handles failures gracefully** (keeps last known status)
413
+ 4. **Runs in background** while main logic continues
414
+ 5. **Reports current status** at intervals
415
+
416
+ ---
417
+
418
+ **Rationale:**
419
+
420
+ When you need to run an effect repeatedly at regular intervals (e.g., every 5 seconds, every 30 minutes), use `Schedule.fixed` to specify the interval. This creates a schedule that repeats the effect indefinitely or until a condition stops it, with precise timing between executions.
421
+
422
+ ---
423
+
424
+
425
+ Many production systems need periodic operations:
426
+
427
+ - **Health checks**: Poll service availability every 30 seconds
428
+ - **Cache refresh**: Update cache every 5 minutes
429
+ - **Metrics collection**: Gather system metrics every 10 seconds
430
+ - **Data sync**: Sync data with remote service periodically
431
+ - **Cleanup tasks**: Remove stale data nightly
432
+
433
+ Without proper scheduling:
434
+
435
+ - Manual polling with `while` loops wastes CPU (busy-waiting)
436
+ - Thread.sleep blocks threads, preventing other work
437
+ - No automatic restart on failure
438
+ - Difficult to test deterministically
439
+
440
+ With `Schedule.fixed`:
441
+
442
+ - Efficient, non-blocking repetition
443
+ - Automatic failure handling and retry
444
+ - Testable with TestClock
445
+ - Clean, declarative syntax
446
+
447
+ ---
448
+
449
+ ---
450
+
451
+