Unix vremenski žigovi u keširanju i TTL — Kako zapravo funkcionira istjecanje

Ako ste ikad gledali zastarjelu cache stavku i razmišljali zašto se ne briše kada bi trebala, odgovor je obično negdje u vremenskom žigu. Sustavi keširanja — od Redisa do CDN-a do HTTP zaglavlja — intenzivno koriste Unix vremenske žigove, a greška u izračunu čak i za nekoliko sekundi može uzrokovati greške koje su teško reproducirati.

Koristite Unix Timestamp Converter da pretvorite bilo koji vremenski žig istjecanja cache memorije u čitljiv oblik, ili da dobijete trenutni vremenski žig za otklanjanje grešaka u TTL izračunima.

Što TTL znači i kako se izražava

TTL je kratica za time-to-live (vrijeme-do-života). To je koliko dugo keširana vrijednost treba biti smatrana valjalom prije nego što je trebam osvježiti ili odbaciti.

TTL se može izraziti na dva načina ovisno o sustavu:

Relativni TTL: Broj sekundi koliko stavka cache memorije treba "živjeti" od vremena kada je pohranjena. Redis i Memcached se podrazumevano tako ponašaju. Ako postavite ključ s TTL-om od 3600, istječe nakon 3600 sekundi (sat vremena) od postavljanja, bez obzira kada je to bilo.

Apsolutni vremenski žig istjecanja: Unix vremenski žig u kojem keširana vrijednost istječe. HTTP Cache-Control zaglavlja s max-age funkcioniraju kao relativni TTL, ali Expires zaglavlja koriste apsolutnu vrijednost datuma i vremena. CDN-ovi i neki cache sustavi na razini aplikacije pohranjuju apsolutni vremenski žig istjecanja — trenutno vrijeme plus TTL — i provjeravaju je li sada > istjecanje kako bi utvrdili zastarjelost.

Oba pristupa rezultiraju istom temeljnom logikom: usporedbe trenutnog Unix vremenskog žiga s pohranjenim vremenskim žigom istjecanja. Razlika je gdje se ta matematika javlja — pri pisanju ili čitanju.

Kako Redis upravlja istjecanjem ključeva

Redis je najčešće korištena cache memorija u memoriji, a njegovo TTL ponašanje vrijedi razumjeti detaljno.

Kada postavite ključ s EXPIRE key 3600, Redis bilježi apsolutni Unix vremenski žig u kojem ključ trebao istjeći: trenutno_vrijeme + 3600. To možete vidjeti s EXPIRETIME key, koja vraća Unix vremenski žig istjecanja (Redis 7.0+). TTL key vraća preostale sekunde.

Redis koristi lenije istjecanje kombinirano s periodičkim čišćenjima. Ključ se ne briše čim istekne — briše se kada se sljedeći put pristupi (i pronađe se kao istekao) ili kada ga pozadinski proces Redis-a podigne. To znači da ključ s TTL-om od 0 možda i dalje bude vidljiv u DEBUG SLEEP scenarijima ili kada još nije došlo do čišćenja.

Praktična posljedica: ako vaša aplikacija pročita ključ, provjeri da nije nil, pa ga onda koristi — i čak i ako postoji mali vremenski prozor između GET i TTL provjere — možda ćete pročitati vrijednost koja je upravo istekla. U sustavima s visokim protokom, ovo može uzrokovati cache lavine kada mnogi zahtjevi istovremeno pronađe istekao ključ i svi pokušaju ponovno ga generiati.

HTTP keširanja i Unix vremenski žigovi

HTTP keširanja koristi mješavinu relativnih i apsolutnih vremenskih vrijednosti, a međudjelovanje između njih uzrokuje puno zbunje.

Cache-Control: max-age=3600 govori pregledniku ili CDN-u da je odgovor validan 3600 sekundi od kada je primljen. Ovo je relativno — svaki klijent prati svoje vrijeme od kada je dobio odgovor.

Expires: Thu, 17 Apr 2026 10:00:00 GMT je apsolutni vremenski žig. Govori klijentu da ne koristi keširani odgovor nakon te točke. Problem je što zahtijeva da je satni sat klijenta točan. Ako je satni sat klijenta pogrešan za sat vremena, ponašanje cache memorije je pogrešno za sat vremena.

Age zaglavlje: Kada CDN već ima odgovor keširan 600 sekundi, šalje Age: 600 klijentu. U kombinaciji s max-age, klijent zna da je preostalo svježe vrijeme max-age - Age. Ako Age premašuje max-age, odgovor je već zastarjelan.

Last-Modified i ETag: Ova su zaglavlja za provjeru valjanosti — omogućavaju klijentu da pita "je li ovo još svježe?" umjesto da samo koristi TTL. Poslužitelj vraća 304 Not Modified (bez tijela, samo zaglavlja) ako se sadržaj nije promijenio, što je mnogo brže od slanja kompletnog odgovora.

CDN istjecanje i logika čišćenja

CDN-ovi poput Cloudflareja, Fastlyja i CloudFronta keširaju odgovore na rubu i koriste TTL kako bi utvrdili koliko dugo poslužiti keširanu kopiju bez povratka na izvorni poslužitelj.

CDN-ovi obično pohranjuju apsolutni vremenski žig istjecanja izračunat u vremenu keširanja odgovora: keširano_u + max_age. Za svaki zahtjev provjeravaju trenutno_vrijeme > istjecanje. Ako je istina, dohvaćaju svježu kopiju s izvora.

Granični slučaj koji ljude zbunjuje: CDN je mogao keširati odgovor na više lokacija na rubu u neznatno različitim vremenima. CDN u Frankfurtu je keširao odgovor 12 sekundi prije nego onaj u Singapuru. Njihovi vremenski žigovi istjecanja razlikuju se za 12 sekundi. Ako napravite "cache čišćenje", to invalidira sve njih istovremeno bez obzira na TTL. Ali ako se oslanjate samo na TTL istjecanje, možete dobiti zastarjelu stavku iz jedne lokacije na rubu nekoliko sekundi nakon što je druga lokacija već osvježena.

Otklanjanje grešaka zastarjele cache memorije vremenskim žigovima

Kada sumnjate da cache memorija vraća zastarjele podatke, prvo što trebate napraviti je dobiti raw vremenske žigove:

1. Dobijte trenutni Unix vremenski žig — koristite Unix Timestamp Converter ili pokrenite date +%s u terminalu.

2. Pronađite vremenski žig istjecanja cache stavke — u Redisu, EXPIRETIME key; u HTTP odgovorima, raščlanite Expires zaglavlje ili izračunajte iz Date + max-age - Age; u cache memoriji vaše aplikacije, zabilježite pohranjenu vrijednost istjecanja.

3. Usporedite ih — ako trenutno_vrijeme > istjecanje, stavka trebala biti istekla. Ako se ne briše, provjerite vašu politiku evakcije, postavke ljenoga ili stranog istjecanja, i jesu li stavke osvježavane prije istjecanja.

4. Provjerite vremenske greške — ako vaš aplikacijski poslužitelj i cache poslužitelj imaju različite satne sate, TTL izračuni će biti pogrešni. Česta greška u produkciji je cache poslužitelj čiji satni sat podrhtava 5 minuta unazad. Stavke koje bi trebale istjeći prije 4 minute još se vraćaju kao svježe.

NTP sinkronizacija sprječava vremenski drift, ali ga ne eliminira potpuno. Za kritičnu logiku istjecanja, koristite vremenske žigove generirane serverom iz jednog izvora umjesto da se oslanjate da se svi hostovi slažu oko trenutnog vremena.

Pohrana vremenskih žigova istjecanja u vašoj aplikaciji

Kada gradite cache sustav na razini aplikacije — pohrana izračunatih rezultata u bazu podataka ili key-value spremniku s istjecanjem — trebate odlučiti kako reprezentirati istjecanje.

Opcija 1: Pohranite TTL (relativni). Stavka zna da bi trebala živjeti 3600 sekundi, ali ne zna kada je stvorena. Trebate izdvojeno polje created_at kako bi izračunali je li istekla.

Opcija 2: Pohranite apsolutni vremenski žig istjecanja. Pri pisanju izračunajte expires_at = trenutni_unix_vremenski_žig + ttl. Pri čitanju provjerite trenutni_unix_vremenski_žig > expires_at. Lakše za razumijevanje — trebate samo jedno polje.

Pristup apsolutnog vremenskog žiga je gotovo uvijek čišći. Pohrana expires_at: 1775000000 je jasnija nego pohrana ttl: 3600 odvojeno od created_at: 1774996400. Također preživljava restarte čisto — relativni TTL znači ništa ako ne znate kada je stavka stvorena.

Cache lavina i kako vremenski žigovi pomažu da je se izbjegne

Cache lavina se javlja kada mnogi zahtjevi istovremeno pronađe da je keširana vrijednost istekla i svi pokušaju ponovno je generiati odjednom. Za skupu bazu podataka ili vanjski API poziv, 50 istovremenih zahtjeva za generiranje može opteretiti pozadinu.

Rješenje osviješteno vremenskim žigovima je probabilističko rano istjecanje (zvano i cache zagrijavanje): umjesto istjecanja stavke točno u vrijeme_istjecanja, počnite probabilistički osvježavati je ranije. Jednostavna verzija:

if trenutno_vrijeme > (vrijeme_istjecanja - slučajni_faktor * preostali_ttl):
    osvježi cache

Gdje je slučajni_faktor obično 0–1. Ovo širi cache osvježavanja umjesto da se klastriraju u točnu točku istjecanja.

Jednostavniji pristup: dodajte nasumičnost na TTL-e. Umjesto postavljanja svake stavke da istekne točno za 3600 sekundi, postavite je da istekne za 3600 + nasumičan(-300, 300) sekundi. Ovo desinkronizira vremenske žigove istjecanja između različitih stavki i sprječava da isteknu istovremeno.

Označeni i neoznačeni vremenski žigovi i problem 2038. godine

Većina sustava keširanja pohranjuje TTL vrijednosti kao 32-bitne cijele brojeve. Za TTL vrijednosti (relativne duljine trajanja), ovo je u redu — nitko ne postavlja cache TTL od 2 milijarde sekundi. Ali za apsolutne vremenske žigove istjecanja, označeni 32-bitni cijeli broj se prekoračuje 19. siječnja 2038.

Ako vaša infrastruktura keširanja pohranjuje apsolutne Unix vremenske žigove u 32-bitne cijele brojeve, stavke postavljene da isteknu nakon siječnja 2038 će imati netočne vrijednosti istjecanja. Ovo je granični slučaj danas — većina proizvodnih TTL-a su minuti ili sati, ne godine — ali sustavi koji keširaju dugovječne tokene, certifikate ili podatke o konfiguraciji s godinama istjecanja mogli bi ovo naići.

Moderni sustavi i jezici ovo ispravno rukovode s 64-bitnim cijelim brojevima. Vrijedi provjeriti radite li sa starijim ugradbenim sustavima, naslijeđenim C kodom, ili bilo kojim sustavom koji rukuje vremenskim žigovima kao int umjesto int64 ili long long.

Povezani članci