CronSchedulerAdapter trait defines the interface for distributed locking in III’s cron system. Cron adapters ensure that scheduled jobs run exactly once across multiple engine instances.
Trait Definition
/workspace/source/src/modules/cron/structs.rs:26
Methods
try_acquire_lock
job_id- Unique identifier for the cron job
trueif the lock was successfully acquiredfalseif another instance already holds the lock
- Must be atomic to prevent race conditions
- Lock should have a TTL to prevent deadlocks if an instance crashes
- Only one instance across the cluster should acquire the lock
- Typical TTL: 30 seconds
release_lock
job_id- Unique identifier for the cron job
- Should only release locks owned by the current instance
- Must be atomic to prevent releasing another instance’s lock
- Safe to call even if lock is not held
- Called automatically after job execution completes
Available Adapters
RedisCronAdapter
Redis-based distributed locking using atomic SET operations with TTL.- True distributed locking across multiple instances
- Atomic lock acquisition using Redis SET NX PX
- Automatic lock expiration after 30 seconds
- Lua script for atomic lock release
- Prevents duplicate job executions in clustered environments
/workspace/source/src/modules/cron/adapters/redis_adapter.rs
KvCronAdapter
Process-local key-value based locking for single-instance deployments.- In-memory locking using built-in KV store
- Zero external dependencies
- Shared lock store across adapter instances in the same process
- Suitable for development and single-instance deployments
RedisCronAdapter for distributed setups.
Configuration:
lock_ttl_ms- Lock time-to-live in milliseconds (default: 30000)lock_index- KV store index for locks (default: “cron_locks”)
/workspace/source/src/modules/cron/adapters/kv_adapter.rs
How Cron Locking Works
- Scheduled Time Arrives: Each engine instance calculates the next execution time independently
- Lock Attempt: When the time arrives, all instances try to acquire the lock
- Single Execution: Only one instance succeeds in acquiring the lock
- Job Execution: The lock holder executes the cron job function
- Lock Release: After completion (success or failure), the lock is released
- Lock Expiration: If the instance crashes, the lock auto-expires after TTL
Example Implementation
Redis Implementation Details
TheRedisCronAdapter uses Redis atomic operations to ensure distributed locking:
Lock Acquisition
Lock Release
Best Practices
Lock TTL
- Set TTL longer than the maximum expected job execution time
- Default 30 seconds works for most jobs
- Consider job-specific TTLs for long-running tasks
- TTL prevents deadlocks if an instance crashes
Error Handling
Instance Identification
- Use UUIDs for instance IDs to ensure uniqueness
- Store instance ID when acquiring locks
- Verify ownership before releasing locks
- Prevents accidentally releasing another instance’s lock