Unix vremenske oznake u zakazanim zadacima i cron poslovima

Cron poslovi izgleda jednostavno na površini: izvršiti naredbu u to vrijeme. Ali produkcijsko zakazivanje brzo postaje komplizirano. Trebate pratiti kada se posao zadnji put izvršio, detektirati je li zakašnjen, rukovati preklapajućim izvršavanjima i sve logirati s dovoljnom preciznosti da biste kasnije otklonili greške. Unix vremenske oznake su praktičan alat za sve to.

Koristite Unix Timestamp Converter da pretvorite bilo koju vremensku oznaku u čitljiv oblik datuma ili da dohvatite trenutnu Unix vremensku oznaku. Ovaj članak pokriva kako se vremenske oznake koriste u sustavima zakazivanja — od osnovnog crona do složenijih redova čekanja za poslove.

Zašto zakazani zadaci koriste Unix vremenske oznake

Cron izraz kao 0 2 određuje kada* izvršiti posao. Ali on vam ne govori ništa o onome što se dogodilo tijekom izvršavanja. Za to trebate vremenske oznake zabilježene tijekom izvršavanja: kada je posao počeo, kada je završio, je li uspio.

Unix vremenske oznake su prirodan oblik za to jer su:

  • Jednoznačne — nema zabune oko vremenskih zona, nema formatiranja ovisnog o lokalizaciji
  • Usporedive — možete oduzeti dvije vremenske oznake da dobijete protekle sekunde
  • Kompaktne — jedan cijeli broj se pohranjuje u bilo koji stupac baze podataka ili liniju dnevnika
  • Sortabilne — uređivanje cijelih brojeva odgovara kronološkom redoslijedu

Zakazani zadatak koji zapiše started_at: 1712534400 u bazu podataka bilježi nešto precizno i dostupno za upite. Zadatak koji zapiše started_at: "8. travnja 2026. u 02:00" bilježi nešto što zahtijeva parsiranje prije nego što je korisno.

Pohrana vremenskih oznaka zadnje izvršitve

Najčešća upotreba vremenskih oznaka u zakazanim zadacima je praćenje zadnje uspješne izvršitve. Jednostavan obrazac:

1. Posao počinje, čita last_run vremensku oznaku iz pohrane 2. Posao obavlja svoj rad 3. Kod uspjeha, zapiše trenutnu Unix vremensku oznaku u last_run 4. Kod neuspjeha, ostavlja last_run nepromijenjeno (ili zapiše u odvojeno polje last_failed_at)

# Primjer u shell-u
LAST_RUN=$(cat /var/run/myjob.timestamp 2>/dev/null || echo 0)
NOW=$(date +%s)

# Obavite rad ovdje...
if [ $? -eq 0 ]; then
    echo $NOW > /var/run/myjob.timestamp
fi

Ovaj obrazac čini trivialnim odgovaranje na „kada se ovaj posao zadnji put uspješno izvršio?" — samo pročitajte datoteku i pretvorite vremensku oznaku. Također olakšava detektiranje zastarjelosti: ako je now - last_run > expected_interval, posao je zakašnjen.

Detektiranje zakašnjenih ili propuštenih poslova

Cron ne detektira vlastite greške. Ako je server okvaran tijekom zakazane izvršitve, cron se ne pokušava ponovno i ne upozori. Ako se posao izvršava ali završi s greškom, cron ga ne označava kao neuspješan. Detektiranje ovih situacija zahtijeva vanjski nadzor koji provjerava vremenske oznake.

Jednostavna provjera zakašnjenja: ako se trenutno vrijeme minus last_run premaši prag, nešto je pošlo po zlu.

import time

EXPECTED_INTERVAL_SECONDS = 3600  # posao bi trebao raditi svaki sat
TOLERANCE_SECONDS = 300           # dopusti 5 minuta skretanja

last_run = get_last_run_timestamp()  # iz baze ili datoteke
now = int(time.time())

if now - last_run > EXPECTED_INTERVAL_SECONDS + TOLERANCE_SECONDS:
    alert("Posao je zakašnjen — zadnji put je pokrenut {}".format(last_run))

Tolerancija je važna. Cron poslovi se ne pokreu uvijek točno prema rasporedu — opterećenje sustava, skretanje sata i vrijeme pokretanja znače da se posao zakazan za 02:00:00 može zapravo pokrenuti u 02:00:04. Bez tolerancije, provjera nadzora koja se izvršava u 02:59:56 može vidjeti posao kao zakašnjen iako se normalno izvršio u 02:00:04 i trebao bi ponovno pokrenuti za 4 sekunde.

Za satne poslove, 5 minuta je razumna tolerancija. Za dnevne poslove, tipično je 30–60 minuta.

Sprječavanje preklapajućih izvršavanja s vremenskim oznakama

Dugotrajna poslova se mogu preklapati ako sljedeća zakazana izvršitev počne prije nego što se trenutna završi. Dnevni posao sigurnosne kopije koji traje 2 sata je prihvatljiv. Onaj koji traja 26 sati počinje s preklapanjem i na kraju uzrokuje kaskadne greške.

Vremenske oznake rješavaju to s jednostavnim obrascem zaključavanja:

1. Posao počinje, čita started_at iz zapisa zaključavanja 2. Ako started_at postoji i now - started_at < timeout, druga izvršitev je u tijeku — izađi 3. Ako nema zaključavanja ili je zaključavanje isteklo, zapiši trenutnu vremensku oznaku kao started_at 4. Obavi rad 5. Očisti zaključavanje nakon završetka

LOCK_TIMEOUT = 7200  # 2 sata — ako posao traje duže, pretpostavi da je zapeo

lock_time = get_lock_timestamp()
now = int(time.time())

if lock_time and (now - lock_time) < LOCK_TIMEOUT:
    print("Posao je već u tijeku, počeo je {}".format(lock_time))
    exit(0)

set_lock_timestamp(now)
# ... obavite rad ...
clear_lock()

Vrijeme isteka rukuje slučajem gdje posao padne bez čišćenja zaključavanja. Bez vremena isteka, srušeni posao bi blokirao sve buduće izvršite zauvijek. S vremenskom oznakom zaključavanja, zaključavanje srušene izvršitve istječe nakon vremenske zadane vrijednosti, a sljedeća zakazana izvršitev može nastaviti.

Zakazivanje budućih poslova s vremenskim oznakama

Redovi čekanja za poslove (Sidekiq, Celery, BullMQ, RQ) često zakazuju buduće zadatke sprema Unix vremensku oznaku za kada bi se posao trebao izvršiti. Radnik reda čekanja sprema se za poslove gdje je run_at <= current_timestamp.

-- Pronađi poslove spremne za izvršavanja
SELECT * FROM scheduled_jobs
WHERE run_at <= EXTRACT(EPOCH FROM NOW())::int
  AND status = 'pending'
ORDER BY run_at ASC;

To je fleksibilnije od crona za dinamičko zakazivanje. Umjesto „izvršiti svakih sat vremena," možete reći „izvršiti 24 sata nakon što se korisnik registrira" ili „izvršiti 15 minuta nakon što je ova uplata propala." Posao se umeće s run_at = NOW_UNIX + delay_seconds, a radnik ga preuzima kada dođe vrijeme.

Logika pokušaja ponovno pokušavanja također funkcionira na ovaj način. Nakon greške, preplanira se s eksponencijalnim povratom:

attempt = job.attempt_count
delay = min(2 ** attempt * 60, 3600)  # 1min, 2min, 4min... do 1 sata
job.run_at = int(time.time()) + delay
job.save()

Nakon 1 pokušaja: pokušaj ponovno za 60 sekundi. Nakon 2: 120 sekundi. Nakon 3: 240 sekundi. Gornja granica od 3600 sprječava beskonačna odgađanja.

Bilježenje izvršavanja poslova s vremenskim oznakama

Dnevnici izvršavanja poslova su najkorisnije kada uključuju precizno vremenski prikaz. Linija dnevnika kao:

[1712534400] backup-job počeo
[1712534447] backup-job završio za 47s, 3.2GB zapisano

odmah je korisna za otklanjanje greške. Možete pretvoriti 1712534400 u čitljiv oblik datuma koristeći Unix Timestamp Converter, povezati ga s drugim dnevnicima sustava i izračunati točno trajanje bez parsiranja stringova datuma.

Alternativa — bilježenje oblikovanih stringova kao "2026-04-08 02:00:00 UTC" — čitljiva je ljudima ali fragilan je za strojeve. Različitim brodovima dnevnika date se formatiraju različito, konverzije vremenskih zona uvode greške, a usporedba stringova je sporija od usporedbe cijelih brojeva za upite raspona vremena.

Čest obrazac je bilježiti oboje: sirovu vremensku oznaku za čitljivost stroja i oblikovani datum za čitljivost čovjeka.

started_at=1712534400 started_at_human="2026-04-08T02:00:00Z" duration_s=47

Mjerenje trajanja poslova i performansi tijekom vremena

Vremenske oznake omogućuju praćenje performansi kroz izvršavanja. Zabilježite started_at i completed_at za svaku izvršaciju, a možete se upitati:

  • Prosječno trajanje tijekom zadnjih 30 izvršavanja
  • Najduže izvršavanje u proteklom tjednu
  • Je li trajanje u uzlaznom trendu (moguća regresija performansi)
SELECT
    AVG(completed_at - started_at) AS avg_duration_seconds,
    MAX(completed_at - started_at) AS max_duration_seconds,
    COUNT(*) AS run_count
FROM job_runs
WHERE job_name = 'nightly-report'
  AND started_at > EXTRACT(EPOCH FROM NOW())::int - (30 * 86400);

Ova vrsta upita moguća je samo jer se trajanja pohranjuju kao jednostavna oduzimanja cijelih brojeva. Ako ste pohranili oblikovane datume, trebali biste parsiranje stringova prije aritmetike.

Preciznost vremenske oznake: Sekunde naspram milisekundi u zakazivanju

Za većinu zakazanih zadataka, preciznost na razini sekunde je dovoljna. Posao zakazan da se izvršava na 1712534400 ne trebas sub-sekundnu preciznost.

Ali za redove čekanja za poslove visoke frekvencije — poslove koji se mogu izvršiti stotine puta po sekundi — milisekundne vremenske oznake su važne. Ako se dva posla ubace u istu sekundu i sortirate po vremenskoj oznaci da odredite redoslijed obrade, vremenske oznake na razini sekunde proizvode veze. Milisekundne vremenske oznake čuvaju redoslijed umetanja unutar svake sekunde.

JavaScript-ov Date.now() vraća milisekunde. Python-ov time.time() vraća float s mikrosekundnom preciznošću. Većina baza podataka podržava vremenske oznake s mikrosekundnom preciznošću. Odabir preciznosti trebao bi odgovarati vašoj frekvenciji zakazivanja — milisekunde za redove s visokim volumenom, sekunde za standardne cron-ove poslove.

Rukovanje prijelazom na ljetno vrijeme u zakazanim zadacima

Ovo je gdje se kriju mnoge greške zakazivanja. Cron posao konfiguriran kao 0 2 * izvršava se u 02:00 lokalnog vremena. Kada se satovi pomaknu naprijed, 02:00 ne postoji — sat ide od 01:59 do 03:00. Posao ili se preskoči ili se izvršava u 03:00 ovisno o implementaciji crona.

Kada se satovi pomaknu unazad, 02:00 se javlja dvaput. Posao se može izvršiti dvaput.

Rješenje: pokrenite cron u UTC-u. 0 2 * u UTC-u je uvijek 02:00 UTC — nema dvoznačnosti, nema iznenađenja zbog ljetnog vremena. Vrijeme posla se mijenja u odnosu na lokalno vrijeme dva puta godišnje, ali se uvijek izvršava točno jednom.

Unix vremenske oznake su po definiciji UTC, zbog čega su imune na ovaj problem. Posao zakazan da se izvršava na vremenskoj oznaci 1712534400 izvršava se u tom točnom trenutku bez obzira na vremensku zonu u kojoj se server nalazi ili koji DST pomak primjenjuje se u to vrijeme. Ovo je temeljni argument za korištenje vremenski-obilježenih rasporeditelja umjesto crona za bilo što gdje je točno vremenski prikaz bitan.

Za poslove gdje su semantika lokalnog vremena važna — „izvršiti u 9 ujutro poslovnog vremena" — trebate eksplicitno rukovanje vremenskom zonom, ne samo UTC. Pohranite ciljnu vremensku zonu zajedno s rasporedom, pretvorite u UTC tijekom stvaranja rasporeda i ponovno izračunajte ako se DST pravila vremenske zone promijene.

Povezani članci