# Building a Cron Job Wrapper for Django
So⠀lately,⠀I⠀have⠀been⠀fiddling⠀with⠀cron⠀jobs⠀and⠀I⠀decided⠀to⠀take⠀it⠀a⠀step⠀further⠀and⠀write⠀a⠀wrapper⠀around⠀them...
What⠀is⠀CRON?
A⠀CRON⠀is⠀a⠀daemon⠀used⠀to⠀execute⠀scheduled⠀commands.⠀You⠀can⠀set⠀the⠀schedule⠀for⠀when⠀a⠀script⠀or⠀command⠀should⠀run⠀and⠀let⠀the⠀process⠀run⠀at⠀the⠀desired⠀intervals.
- Schedule⠀a⠀command
- Define⠀the⠀command
- Define⠀when⠀it⠀should⠀run
- Wait⠀for⠀the⠀specified⠀interval
- Check⠀the⠀current⠀time
- Wait⠀until⠀the⠀scheduled⠀time
- Execute⠀the⠀command
- Capture⠀the⠀output
- Handle⠀errors
- Repeat
---
The⠀Original⠀Idea
The⠀rationale⠀behind⠀this⠀decision⠀was⠀that⠀I⠀wanted⠀to⠀automate⠀the⠀process⠀of⠀sending⠀emails.
Specifically,⠀the⠀email⠀handling⠀for⠀this⠀blog⠀among⠀other⠀things.
At⠀the⠀start,⠀I⠀considered⠀creating⠀an⠀SMTP⠀server,⠀but⠀after⠀some⠀digging,⠀I⠀found⠀out⠀that⠀the⠀web⠀hosting⠀service⠀I'm⠀using⠀does⠀not⠀recommend⠀it.
Although,⠀since⠀then⠀I⠀changed⠀providers⠀so⠀I⠀looking⠀forward⠀to⠀it!
The⠀idea⠀would⠀eventually⠀look⠀something⠀like⠀this:
```
┌───────────────┐
│ Django │
│ Application │
└───────┬───────┘
│
▼
┌───────────────┐
│ CRON Storage │
└───────┬───────┘
│
▼
┌───────────────┐
│ CRON Wrapper │
│ (Python) │
└───────┬───────┘
│
▼
┌───────────────┐
│ System CRON │
└───────┬───────┘
│
▼
┌───────────────┐
│ Script │
└───────┬───────┘
│
▼
Email
```
Also,⠀in⠀the⠀past,⠀I⠀used⠀a⠀similar⠀
---
Why⠀Create⠀Another⠀CRON⠀Service?
So,⠀the⠀CRON⠀service⠀I⠀was⠀planning⠀to⠀create...
(because⠀I⠀was⠀too⠀bored⠀to⠀simply⠀create⠀a⠀simple⠀CRON⠀after⠀SSHing⠀into⠀the⠀server)
...was⠀supposed⠀to⠀be⠀able⠀to⠀parse⠀CRON⠀jobs⠀from⠀the⠀DB⠀into⠀JSON⠀or⠀Python,⠀register⠀the⠀CRON⠀to⠀the⠀system,⠀and⠀hopefully⠀run⠀it.
Why⠀keep⠀the⠀3⠀places,⠀you⠀ask?
Good⠀question.
Because⠀who⠀doesn't⠀need⠀another⠀point⠀of⠀failure?
Obviously.
---
The⠀Three⠀Layers
The⠀general⠀idea⠀was⠀to⠀keep⠀the⠀CRON⠀information⠀in⠀three⠀different⠀places:
- Database
- Stores⠀the⠀CRON⠀configuration.
- Stores⠀information⠀about⠀the⠀job.
- Potentially⠀stores⠀logs.
2.⠀JSON⠀/⠀Python⠀representation
- Used⠀to⠀translate⠀the⠀DB⠀representation⠀into⠀something⠀executable.
- Acts⠀as⠀an⠀intermediate/main⠀representation.
3.⠀System⠀CRON
Something⠀roughly⠀like:
```python
CronService(
Schedule(),
Script(),
Commands(),
Logs()
)
```
The⠀wrapper⠀would⠀then⠀be⠀responsible⠀for⠀converting⠀that⠀request⠀into⠀an⠀actual⠀system⠀CRON.
Why⠀Keep⠀Everything⠀Separate?
The⠀main⠀reason⠀is⠀that,⠀in⠀this⠀format,⠀I⠀could⠀post⠀CRON⠀jobs⠀to⠀the⠀server⠀I⠀wanted⠀and⠀let⠀them⠀run.
This⠀means⠀the⠀Django⠀application⠀doesn't⠀necessarily⠀have⠀to⠀be⠀responsible⠀for⠀actually⠀executing⠀the⠀task.
Instead,⠀it⠀can⠀simply⠀manage⠀the⠀configuration.
Django⠀manages⠀the⠀jobs. Control⠀over⠀posting⠀jobs CRON⠀executes⠀the⠀jobs.
---
Rate⠀Limiting
The⠀service⠀was⠀also⠀supposed⠀to⠀be⠀used⠀as⠀a⠀rate-limiting⠀measure⠀in⠀combination⠀with⠀actual⠀rate⠀limits⠀on⠀the⠀endpoint.
The⠀idea⠀is⠀to⠀create⠀a⠀single⠀point⠀where⠀I⠀can⠀carefully⠀manage⠀how⠀many⠀emails⠀are⠀being⠀sent.
For⠀example:
```text
┌──────────────┐
│ Rate Limit │
└──────┬───────┘
│
▼
┌──────────────┐
│ Django API │
└──────┬───────┘
│
▼
┌──────────────┐
│ Rate Limit │
└──────┬───────┘
│
▼
┌──────────────┐
│ CRON Queue │
└──────┬───────┘
│
▼
┌──────────────┐
│ Email Worker │
└──────────────┘
```
This⠀would⠀make⠀it⠀possible⠀to⠀control⠀the⠀number⠀of⠀emails⠀being⠀sent⠀instead⠀of⠀allowing⠀every⠀request⠀to⠀immediately⠀trigger⠀an⠀email.
---
Current⠀Progress
After⠀some⠀time⠀fiddling⠀with⠀the⠀code,⠀I⠀managed⠀to⠀scrap⠀together⠀a⠀small⠀framework⠀for⠀managing⠀CRONs.
Currently,⠀I'm⠀at⠀the⠀point⠀where⠀I⠀can⠀sufficiently:
- [x]⠀Create⠀CRONs⠀from⠀various⠀formats
- [x]⠀Delete⠀CRONs
- [x]⠀Register⠀CRONs
- [x]⠀Execute⠀CRONs
- [x]⠀Manually⠀verify⠀that⠀jobs⠀can⠀run
- [⠀]⠀Gather⠀logs
- [⠀]⠀Verify⠀job⠀results⠀automatically
- [⠀]⠀Integrate⠀everything⠀with⠀Django
The⠀implementation⠀is⠀still⠀evolving,⠀but⠀the⠀basic⠀functionality⠀is⠀there.
---
The⠀Hard⠀Part
I⠀have⠀manually⠀verified⠀that⠀the⠀jobs⠀can⠀run.
The⠀hard⠀part,⠀however,⠀is⠀verifying⠀that⠀the⠀CRON⠀is⠀actually⠀doing⠀what⠀it⠀is⠀supposed⠀to⠀do⠀without⠀using⠀any⠀automations.
Running⠀the⠀script⠀is⠀easy.
Knowing⠀whether⠀the⠀script⠀actually⠀accomplished⠀its⠀intended⠀purpose⠀is⠀a⠀different⠀art:
```text
CRON
│
▼
Script
│
├── Did it run? ✓
│
├── Did it crash? ?
│
├── Did the task work? ?
│
└── Did the result make
sense? ?
```
I⠀suppose⠀a⠀solution⠀could⠀be⠀that⠀each⠀time⠀the⠀CRON⠀is⠀called,⠀it⠀can⠀check⠀on⠀itself.
But⠀that's⠀a⠀thought⠀for⠀later.
---
The⠀next⠀step⠀is⠀making⠀the⠀system⠀more⠀reliable.
In⠀particular,⠀I⠀need⠀to⠀figure⠀out:
- How⠀to⠀properly⠀gather⠀logs
- How⠀to⠀detect⠀failed⠀jobs
- How⠀to⠀verify⠀that⠀a⠀job⠀actually⠀completed⠀its⠀intended⠀task
- How⠀to⠀integrate⠀the⠀wrapper⠀cleanly⠀with⠀Django
- How⠀to⠀handle⠀jobs⠀that⠀appear⠀successful⠀but⠀actually⠀fail⠀somewhere⠀internally
---
No comments yet. Be the first!