or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

compute-resources.mdcontainer-images.mdcore-application-client.mdfunction-decorators-helpers.mdindex.mdinfrastructure-services.mdruntime-utilities.mdscheduling-reliability.mdstorage-data.mdutility-classes.mdweb-api-integration.md

scheduling-reliability.mddocs/

0

# Scheduling & Reliability

1

2

Modal provides comprehensive scheduling and reliability features for automated task execution and resilient function behavior. These capabilities enable building robust serverless applications with automatic retry policies and flexible scheduling patterns.

3

4

## Capabilities

5

6

### Cron Scheduling

7

8

Schedule functions to run at specific times using Unix cron syntax, supporting timezone-aware scheduling for automated tasks.

9

10

```python { .api }

11

class Cron:

12

def __init__(self, cron_string: str, timezone: str = "UTC") -> None:

13

"""Create a cron schedule from cron expression string"""

14

```

15

16

#### Usage Examples

17

18

```python

19

import modal

20

21

app = modal.App("scheduled-tasks")

22

23

# Run every minute

24

@app.function(schedule=modal.Cron("* * * * *"))

25

def check_system_health():

26

"""Monitor system health every minute"""

27

health_status = check_all_services()

28

if not health_status["healthy"]:

29

send_alert(health_status["issues"])

30

return health_status

31

32

# Run daily at 4:05 AM UTC

33

@app.function(schedule=modal.Cron("5 4 * * *"))

34

def daily_backup():

35

"""Perform daily database backup"""

36

print("Starting daily backup...")

37

backup_result = create_database_backup()

38

upload_to_cloud_storage(backup_result)

39

cleanup_old_backups()

40

print("Daily backup completed")

41

42

# Run every Thursday at 9 AM UTC

43

@app.function(schedule=modal.Cron("0 9 * * 4"))

44

def weekly_report():

45

"""Generate weekly analytics report"""

46

report_data = generate_weekly_analytics()

47

send_report_email(report_data)

48

store_report(report_data)

49

50

# Run daily at 6 AM New York time (timezone-aware)

51

@app.function(schedule=modal.Cron("0 6 * * *", timezone="America/New_York"))

52

def business_hours_task():

53

"""Task that runs at business hours in New York"""

54

# Automatically adjusts for daylight saving time

55

process_business_data()

56

57

# Complex cron expressions

58

@app.function(schedule=modal.Cron("15 2 * * 1-5")) # Weekdays at 2:15 AM

59

def weekday_maintenance():

60

"""Maintenance tasks during weekdays"""

61

perform_system_maintenance()

62

63

@app.function(schedule=modal.Cron("0 12 1 * *")) # First day of month at noon

64

def monthly_billing():

65

"""Process monthly billing on first day of month"""

66

process_monthly_invoices()

67

send_billing_notifications()

68

```

69

70

### Period Scheduling

71

72

Schedule functions to run at regular intervals using natural time periods, supporting precise timing for recurring tasks.

73

74

```python { .api }

75

class Period:

76

def __init__(

77

self,

78

*,

79

years: int = 0,

80

months: int = 0,

81

weeks: int = 0,

82

days: int = 0,

83

hours: int = 0,

84

minutes: int = 0,

85

seconds: float = 0

86

) -> None:

87

"""Create a periodic schedule with specified intervals"""

88

```

89

90

#### Usage Examples

91

92

```python

93

import modal

94

95

app = modal.App("periodic-tasks")

96

97

# Run every day

98

@app.function(schedule=modal.Period(days=1))

99

def daily_data_sync():

100

"""Synchronize data daily"""

101

sync_external_apis()

102

update_local_cache()

103

print("Daily sync completed")

104

105

# Run every 4 hours

106

@app.function(schedule=modal.Period(hours=4))

107

def frequent_health_check():

108

"""Check system health every 4 hours"""

109

status = perform_detailed_health_check()

110

log_health_metrics(status)

111

112

# Run every 15 minutes

113

@app.function(schedule=modal.Period(minutes=15))

114

def cache_refresh():

115

"""Refresh application cache every 15 minutes"""

116

refresh_application_cache()

117

update_cache_metrics()

118

119

# Run every 30 seconds

120

@app.function(schedule=modal.Period(seconds=30))

121

def high_frequency_monitor():

122

"""High-frequency monitoring task"""

123

collect_realtime_metrics()

124

check_alert_conditions()

125

126

# Complex periods

127

@app.function(schedule=modal.Period(weeks=2, days=3)) # Every 17 days

128

def bi_weekly_plus_task():

129

"""Task that runs every 2 weeks + 3 days"""

130

perform_special_analysis()

131

132

@app.function(schedule=modal.Period(months=1)) # Monthly (same day each month)

133

def monthly_report():

134

"""Generate monthly report on same day each month"""

135

# Handles varying month lengths correctly

136

generate_monthly_analytics()

137

archive_old_data()

138

139

# High precision timing

140

@app.function(schedule=modal.Period(seconds=3.14159)) # Every π seconds

141

def precise_timing_task():

142

"""Task with precise timing requirements"""

143

collect_high_precision_measurements()

144

```

145

146

### Retry Policies

147

148

Configure automatic retry behavior for functions to handle transient failures and improve reliability.

149

150

```python { .api }

151

class Retries:

152

def __init__(

153

self,

154

*,

155

max_retries: int,

156

backoff_coefficient: float = 2.0,

157

initial_delay: float = 1.0,

158

max_delay: float = 60.0

159

) -> None:

160

"""Configure retry policy with exponential or fixed backoff"""

161

```

162

163

#### Usage Examples

164

165

```python

166

import modal

167

168

app = modal.App("reliable-functions")

169

170

# Simple retry configuration

171

@app.function(retries=4) # Retry up to 4 times with default settings

172

def simple_retry_task():

173

"""Task with basic retry policy"""

174

result = call_external_api()

175

return result

176

177

# Fixed-interval retries

178

@app.function(

179

retries=modal.Retries(

180

max_retries=3,

181

backoff_coefficient=1.0, # Fixed delay

182

initial_delay=5.0, # 5 seconds between retries

183

)

184

)

185

def fixed_retry_task():

186

"""Task with fixed 5-second delay between retries"""

187

try:

188

return process_critical_data()

189

except Exception as e:

190

print(f"Attempt failed: {e}")

191

raise # Will trigger retry

192

193

# Exponential backoff

194

@app.function(

195

retries=modal.Retries(

196

max_retries=5,

197

backoff_coefficient=2.0, # Double delay each retry

198

initial_delay=1.0, # Start with 1 second

199

max_delay=30.0 # Cap at 30 seconds

200

)

201

)

202

def exponential_backoff_task():

203

"""Task with exponential backoff: 1s, 2s, 4s, 8s, 16s delays"""

204

return call_rate_limited_service()

205

206

# Custom backoff configuration

207

@app.function(

208

retries=modal.Retries(

209

max_retries=6,

210

backoff_coefficient=1.5, # 50% increase each retry

211

initial_delay=2.0, # Start with 2 seconds

212

max_delay=60.0 # Cap at 1 minute

213

)

214

)

215

def custom_backoff_task():

216

"""Custom retry pattern: 2s, 3s, 4.5s, 6.75s, 10.125s, 15.1875s"""

217

return perform_database_operation()

218

219

# Retry with error handling

220

@app.function(

221

retries=modal.Retries(

222

max_retries=3,

223

initial_delay=1.0,

224

backoff_coefficient=2.0

225

)

226

)

227

def resilient_task():

228

"""Task with retry and comprehensive error handling"""

229

attempt = 0

230

try:

231

# Simulate getting current attempt (in real usage, this would be tracked differently)

232

result = unreliable_external_service()

233

print(f"Success on attempt {attempt + 1}")

234

return result

235

except TemporaryError as e:

236

print(f"Temporary error on attempt {attempt + 1}: {e}")

237

raise # Retry

238

except PermanentError as e:

239

print(f"Permanent error, not retrying: {e}")

240

return {"error": "permanent_failure", "message": str(e)}

241

```

242

243

## Advanced Scheduling Patterns

244

245

### Multi-Timezone Scheduling

246

247

```python

248

import modal

249

250

app = modal.App("global-scheduling")

251

252

# Different tasks in different timezones

253

@app.function(schedule=modal.Cron("0 9 * * *", timezone="America/New_York"))

254

def us_business_hours():

255

"""Run at 9 AM Eastern Time"""

256

process_us_customer_data()

257

258

@app.function(schedule=modal.Cron("0 9 * * *", timezone="Europe/London"))

259

def uk_business_hours():

260

"""Run at 9 AM London Time"""

261

process_uk_customer_data()

262

263

@app.function(schedule=modal.Cron("0 9 * * *", timezone="Asia/Tokyo"))

264

def japan_business_hours():

265

"""Run at 9 AM Japan Time"""

266

process_japan_customer_data()

267

268

@app.function(schedule=modal.Cron("0 0 * * *", timezone="UTC"))

269

def global_midnight_task():

270

"""Run at midnight UTC (global coordination)"""

271

consolidate_global_reports()

272

```

273

274

### Cascading Scheduled Tasks

275

276

```python

277

import modal

278

279

app = modal.App("cascading-tasks")

280

281

# Shared queue for task coordination

282

task_queue = modal.Queue.persist("cascade-queue")

283

284

@app.function(schedule=modal.Cron("0 2 * * *")) # 2 AM daily

285

def stage_1_data_extraction():

286

"""First stage: Extract data"""

287

print("Stage 1: Starting data extraction...")

288

raw_data = extract_raw_data()

289

290

# Queue next stage

291

task_queue.put({

292

"stage": "transform",

293

"data_id": raw_data["id"],

294

"timestamp": time.time()

295

})

296

297

print("Stage 1: Data extraction completed")

298

299

@app.function(schedule=modal.Period(minutes=5)) # Check every 5 minutes

300

def stage_2_data_transformation():

301

"""Second stage: Transform data when available"""

302

try:

303

task = task_queue.get(timeout=1) # Non-blocking check

304

if task and task["stage"] == "transform":

305

print(f"Stage 2: Transforming data {task['data_id']}")

306

transformed_data = transform_data(task["data_id"])

307

308

# Queue final stage

309

task_queue.put({

310

"stage": "load",

311

"data_id": transformed_data["id"],

312

"timestamp": time.time()

313

})

314

315

print("Stage 2: Data transformation completed")

316

except:

317

# No tasks available, continue

318

pass

319

320

@app.function(schedule=modal.Period(minutes=10)) # Check every 10 minutes

321

def stage_3_data_loading():

322

"""Final stage: Load data when ready"""

323

try:

324

task = task_queue.get(timeout=1)

325

if task and task["stage"] == "load":

326

print(f"Stage 3: Loading data {task['data_id']}")

327

load_data_to_warehouse(task["data_id"])

328

send_completion_notification(task["data_id"])

329

print("Stage 3: Data loading completed")

330

except:

331

pass

332

```

333

334

### Conditional Scheduling with Circuit Breaker

335

336

```python

337

import modal

338

339

app = modal.App("reliable-scheduling")

340

341

# Shared state for circuit breaker

342

circuit_state = modal.Dict.persist("circuit-breaker")

343

344

@app.function(

345

schedule=modal.Period(minutes=10),

346

retries=modal.Retries(max_retries=2, initial_delay=30.0)

347

)

348

def monitored_task():

349

"""Task with circuit breaker pattern"""

350

# Check circuit breaker state

351

is_open = circuit_state.get("circuit_open", False)

352

failure_count = circuit_state.get("failure_count", 0)

353

last_failure = circuit_state.get("last_failure_time", 0)

354

355

# Circuit breaker logic

356

if is_open:

357

# Check if enough time has passed to try again

358

if time.time() - last_failure > 300: # 5 minutes

359

print("Circuit breaker: Attempting to close circuit")

360

circuit_state["circuit_open"] = False

361

else:

362

print("Circuit breaker: Circuit is open, skipping execution")

363

return {"status": "circuit_open"}

364

365

try:

366

# Attempt the operation

367

result = potentially_failing_operation()

368

369

# Success - reset failure count

370

circuit_state["failure_count"] = 0

371

circuit_state["circuit_open"] = False

372

373

print("Task completed successfully")

374

return {"status": "success", "result": result}

375

376

except Exception as e:

377

# Handle failure

378

new_failure_count = failure_count + 1

379

circuit_state["failure_count"] = new_failure_count

380

circuit_state["last_failure_time"] = time.time()

381

382

# Open circuit if too many failures

383

if new_failure_count >= 3:

384

circuit_state["circuit_open"] = True

385

print("Circuit breaker: Opening circuit due to repeated failures")

386

387

print(f"Task failed (attempt {new_failure_count}): {e}")

388

raise # Will trigger retry policy

389

```

390

391

### Dynamic Scheduling Based on Load

392

393

```python

394

import modal

395

396

app = modal.App("adaptive-scheduling")

397

398

# Shared metrics storage

399

metrics = modal.Dict.persist("system-metrics")

400

401

@app.function(schedule=modal.Period(seconds=30))

402

def collect_system_metrics():

403

"""Collect system metrics every 30 seconds"""

404

current_load = get_system_load()

405

queue_depth = get_queue_depth()

406

error_rate = get_error_rate()

407

408

metrics.update({

409

"load": current_load,

410

"queue_depth": queue_depth,

411

"error_rate": error_rate,

412

"timestamp": time.time()

413

})

414

415

@app.function(schedule=modal.Period(minutes=1))

416

def adaptive_processor():

417

"""Process tasks with adaptive frequency based on load"""

418

current_metrics = metrics.get("load", 0)

419

queue_depth = metrics.get("queue_depth", 0)

420

421

# Adapt processing based on system state

422

if current_metrics < 0.5 and queue_depth > 100:

423

# Low load, high queue - process more aggressively

424

batch_size = 50

425

process_batch_tasks(batch_size)

426

print(f"High throughput mode: processed {batch_size} tasks")

427

428

elif current_metrics > 0.8:

429

# High load - reduce processing

430

batch_size = 10

431

process_batch_tasks(batch_size)

432

print(f"Conservative mode: processed {batch_size} tasks")

433

434

else:

435

# Normal processing

436

batch_size = 25

437

process_batch_tasks(batch_size)

438

print(f"Normal mode: processed {batch_size} tasks")

439

440

@app.function(

441

schedule=modal.Period(minutes=5),

442

retries=modal.Retries(max_retries=3, initial_delay=60.0)

443

)

444

def health_based_processing():

445

"""Processing task that adapts based on system health"""

446

error_rate = metrics.get("error_rate", 0)

447

448

if error_rate > 0.1: # More than 10% error rate

449

print("High error rate detected, running diagnostic tasks")

450

run_system_diagnostics()

451

return {"status": "diagnostics"}

452

else:

453

print("System healthy, running normal processing")

454

return process_normal_workload()

455

```