clockwork_schedule
A robust scheduling extension for the Clockwork library that provides a clean, type-safe way to define and manage recurring tasks with built-in OTP supervision support in Gleam applications.
Features
- π Cron Expression Support - Schedule tasks using familiar cron syntax via the Clockwork library
- π‘οΈ OTP Supervision - Built-in fault tolerance with automatic restart on failure
- π Time Zone Support - Configure tasks to run in specific time zones using UTC offsets
- π Logging Integration - Optional structured logging for monitoring job execution
- π― Multiple Schedulers - Run multiple independent scheduled tasks concurrently
- π§ Builder Pattern API - Clean, composable configuration interface
- β‘ Lightweight - Minimal dependencies, built on Gleamβs OTP abstractions
- π Graceful Lifecycle - Proper startup and shutdown with cleanup
Installation
gleam add clockwork_schedule@1
Quick Start
import clockwork
import clockwork_schedule
import gleam/io
pub fn main() {
// Create a cron expression (runs every 5 minutes)
let assert Ok(cron) = clockwork.from_string("*/5 * * * *")
// Define your job
let job = fn() { io.println("Task executed!") }
// Create and start the scheduler
let scheduler = clockwork_schedule.new("my_task", cron, job)
let assert Ok(schedule) = clockwork_schedule.start(scheduler)
// The task will run every 5 minutes until stopped
// Stop when done
clockwork_schedule.stop(schedule)
}
Usage Examples
Basic Scheduled Task
import clockwork
import clockwork_schedule
pub fn hourly_cleanup() {
let assert Ok(cron) = clockwork.from_string("0 * * * *") // Every hour
let scheduler =
clockwork_schedule.new("cleanup", cron, fn() {
// Your cleanup logic here
delete_old_temp_files()
compress_logs()
})
let assert Ok(schedule) = clockwork_schedule.start(scheduler)
// Returns a Schedule handle for control
}
With Logging Enabled
let scheduler =
clockwork_schedule.new("data_sync", cron, sync_function)
|> clockwork_schedule.with_logging() // Enable execution logging
// Logs will show:
// [CLOCKWORK] Running job: data_sync at 1706025600.0
// [CLOCKWORK] Stopping job: data_sync
Time Zone Configuration
import gleam/time/duration
// Configure for UTC+9 (Tokyo)
let tokyo_offset = duration.hours(9)
let tokyo_scheduler =
clockwork_schedule.new("tokyo_report", cron, generate_report)
|> clockwork_schedule.with_time_offset(tokyo_offset)
// Configure for UTC-5 (New York)
let ny_offset = duration.hours(-5)
let ny_scheduler =
clockwork_schedule.new("ny_report", cron, generate_report)
|> clockwork_schedule.with_time_offset(ny_offset)
Supervised Scheduling (Recommended for Production)
import clockwork
import clockwork_schedule
import gleam/erlang/process
import gleam/otp/static_supervisor as supervisor
pub fn main() {
let assert Ok(cron) = clockwork.from_string("0 0 * * *") // Daily at midnight
let scheduler =
clockwork_schedule.new("daily_backup", cron, backup_database)
|> clockwork_schedule.with_logging()
// Create a receiver for the schedule handle
let schedule_receiver = process.new_subject()
// Create the child specification
let child_spec =
clockwork_schedule.supervised(scheduler, schedule_receiver)
// Add to supervision tree
let assert Ok(sup) =
supervisor.new(supervisor.OneForOne)
|> supervisor.add(child_spec)
|> supervisor.start()
// Receive the schedule handle
let assert Ok(schedule) = process.receive(schedule_receiver, 1000)
// The scheduler is now running under supervision
// It will automatically restart if it crashes
}
Multiple Concurrent Schedulers
pub fn start_all_schedulers() {
// Metrics collection every 5 minutes
let assert Ok(metrics_cron) = clockwork.from_string("*/5 * * * *")
let metrics_scheduler =
clockwork_schedule.new("metrics", metrics_cron, collect_metrics)
// Database backup every day at 2 AM
let assert Ok(backup_cron) = clockwork.from_string("0 2 * * *")
let backup_scheduler =
clockwork_schedule.new("backup", backup_cron, backup_database)
// Cache cleanup every hour
let assert Ok(cache_cron) = clockwork.from_string("0 * * * *")
let cache_scheduler =
clockwork_schedule.new("cache", cache_cron, clear_cache)
// Start all schedulers
let assert Ok(metrics_schedule) = clockwork_schedule.start(metrics_scheduler)
let assert Ok(backup_schedule) = clockwork_schedule.start(backup_scheduler)
let assert Ok(cache_schedule) = clockwork_schedule.start(cache_scheduler)
// All three schedulers now run independently
#(metrics_schedule, backup_schedule, cache_schedule)
}
Cron Expression Format
This library uses the Clockwork library for cron expression parsing:
ββββββββββββββ minute (0 - 59)
β ββββββββββββββ hour (0 - 23)
β β ββββββββββββββ day of the month (1 - 31)
β β β ββββββββββββββ month (1 - 12)
β β β β ββββββββββββββ day of the week (0 - 6) (Sunday to Saturday)
β β β β β
β β β β β
* * * * *
Common Patterns
Pattern | Description | Example |
---|---|---|
* * * * * | Every minute | Runs at :00, :01, :02, etc. |
*/5 * * * * | Every 5 minutes | Runs at :00, :05, :10, etc. |
0 * * * * | Every hour | Runs at the top of each hour |
0 0 * * * | Daily at midnight | Runs at 00:00 |
0 0 * * 0 | Weekly on Sunday | Runs Sunday at midnight |
0 0 1 * * | Monthly on the 1st | Runs on the 1st at midnight |
30 2 * * 1-5 | Weekdays at 2:30 AM | Mon-Fri at 02:30 |
0 */4 * * * | Every 4 hours | Runs at 00:00, 04:00, 08:00, etc. |
0 9-17 * * 1-5 | Business hours | Mon-Fri, every hour 9 AM - 5 PM |
Architecture
Actor Model
Each scheduler runs as an independent actor, ensuring:
- Isolation between schedulers
- Non-blocking job execution
- Clean error boundaries
Job Execution
- Jobs run in separate processes
- Long-running jobs donβt block the scheduler
- Next occurrence calculated after each execution
Supervision Tree Integration
When supervised:
- Schedulers restart automatically on crash
- State is rebuilt from configuration
- No manual intervention required
Testing
Run the test suite:
gleam test
The test suite covers:
- Scheduler creation and configuration
- Start/stop lifecycle
- OTP supervision integration
- Multiple concurrent schedulers
- Time zone offset handling
- Builder pattern API
Performance Considerations
- Memory: Each scheduler uses minimal memory (< 1KB idle)
- CPU: Near-zero CPU usage when idle
- Concurrency: Supports hundreds of concurrent schedulers
- Job Execution: Jobs run in separate processes for isolation
Best Practices
-
Use Supervision in Production
- Always use
supervised/2
for production deployments - Ensures reliability and automatic recovery
- Always use
-
Enable Logging for Critical Jobs
- Use
with_logging/1
for important scheduled tasks - Helps with debugging and monitoring
- Use
-
Handle Errors in Jobs
- Wrap job logic in proper error handling
- Log failures for investigation
-
Time Zone Awareness
- Use
with_time_offset/2
for time-zone-specific scheduling - Consider daylight saving time changes
- Use
-
Resource Cleanup
- Always call
stop/1
when shutting down - Ensures clean termination
- Always call
Troubleshooting
Job Not Running
- Verify cron expression with Clockwork directly
- Check system time and time zone settings
- Enable logging to see execution attempts
Memory Issues
- Ensure jobs arenβt accumulating state
- Check for memory leaks in job functions
- Monitor process counts
Supervision Restarts
- Check job function for crashes
- Review error logs
- Consider adding error handling in jobs
Contributing
Contributions are welcome! Please:
- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-feature
) - Write tests for new functionality
- Ensure all tests pass (
gleam test
) - Commit changes (
git commit -m 'Add amazing feature'
) - Push to branch (
git push origin feature/amazing-feature
) - Open a Pull Request
Development Setup
# Clone the repository
git clone https://github.com/renatillas/clockwork_schedule.git
cd clockwork_schedule
# Install dependencies
gleam deps download
# Run tests
gleam test
# Format code
gleam format
# Build documentation
gleam docs build
Related Projects
- Clockwork - The underlying cron expression library
- gleam_otp - OTP abstractions for Gleam
- logging - Structured logging for Gleam
Support
- GitHub Issues - Bug reports and feature requests
- Hex Documentation - API documentation
- Gleam Discord - Community support