Health Dashboard, 8. deo: prvo pitati, pa govoriti
Osmi deo mog build log-a za Health Dashboard: najsvežiji deo sistema — jutarnji one-tap check-in preko Telegrama. Još nije dokazao svoju korist, pod posmatranjem je i ima eksplicitne non-uses, da ne ponovi failure modes na koje je upozoravao 6. deo.
U 6. delu formulisano je pravilo: subjective input ne može da potvrdi formulu za stres. To pravilo ostaje na snazi. Formula koja se automatski podešava na user self-report postaje zatvorena petlja bez spoljne reference. Korisnik racionalizuje post-hoc, formula se ažurira ka toj racionalizaciji, a sledeći krug samoizveštavanja biva iskrivljen prethodnim rezultatom formule. Lek je držati rubric spoljašnjom: HRV sledećeg jutra, pomeraj RHR, arhitektura sna. Brojevi koje korisnik ne zapisuje svesno.
Zašto sam onda, nekoliko nedelja kasnije, pustio jutarnji one-tap check-in koji pita korisnika kako se oseća?
Zato što se pravilo odnosilo na kalibraciju. Nije zabranjivalo subjective input u svim drugim ulogama, bar u teoriji. Ovaj deo je o tome koje te druge uloge mogu biti, uz iskrenu napomenu unapred: ovo je najsvežija funkcija u celoj seriji i ona koja još nije dokazala svoju korist. Nekoliko dana upotrebe u production-u nije presuda. Check-in je pod posmatranjem, ne proglašava se uspešnim. Sve što sledi je današnji dizajn i hipoteze iza njega, a ne retrospektiva o već rešenom pitanju.
Kako je check-in mehanički uređen
Svakog jutra, na istom scheduler tick-u koji bi inače poslao jutarnji izveštaj, server šalje poruku u Telegram:
How do you feel today?
[ Great ] [ OK ]
[ Meh ] [ Sick ]
Četiri dugmeta inline keyboard-a1. Tapneš jedno — poruka se ažurira na „Sačuvano“, jutarnji izveštaj se šalje nekoliko sekundi kasnije. Ako do morning cap-a (MorningCapHour, obično 11:00 po lokalnom vremenu) nema odgovora, red se označava kao expired, izveštaj se ipak šalje uz kratku meku napomenu („Danas jutarnji check-in nije popunjen“), a tap kasnije tog dana beleži se kao late_answered samo za analitiku.
Format prenosa je Telegram callback. callback_data2 je checkin:<answer>:<YYYY-MM-DD>. U najgorem slučaju 26 bajtova, ispod Telegram limita od 64 bajta. Kada korisnik tapne, Telegram šalje POST sa callback-om na /api/telegram/webhook/<secret>3 na serveru. Server proverava secret u putanji, validira X-Telegram-Bot-Api-Secret-Token header, traži tenant po chat_id — tako da Telegram-potpisan callback usmeren na pogrešan tenant bude odbijen — parsira payload i upisuje red.
Cela funkcija je nekoliko stotina linija Go koda, jedna tabela, dva scheduler hook-a i jedan webhook handler.
Zašto baš ova četiri dugmeta
Prvobitna ideja bila je slider 1–5, slično tome kako Whoop i Oura prikupljaju subjektivne podatke. Odustao sam od toga. Razlozi su isti kao u 6. delu, gde se objašnjava validacija stresa, i jednako važe za bilo koji subjektivni interfejs, bez obzira na to čemu podaci kasnije služe:
Kontinuirana skala izaziva confirmation bias. „Danas sam imao deadline, znači sigurno sam bio pod stresom, upisaću 4“. Diskretne kategorije bez jasnog reda između „nisko“ i „srednje nisko“ smanjuju iskušenje da se traži odgovor koji potvrđuje ocenu.
Slider-i 1–5 se zauzetim jutrima po default-u pretvaraju u „3“, a većina jutara je zauzeta. Tri od četiri diskretna dugmeta staju u jedan red mobilnog Telegrama, i nijedno nije očigledna opcija „ne razmišljam“.
Downstream podaci ionako žele kategorije. „Sick“ je poseban flag, ne 4 na slider-u. Action se razlikuje: illness signature iz 6. dela, AI recommendation ide ka rest. Ne vredi izgubiti to zbog pomeranja slider-a za jedan piksel.
Četiri kategorije koje sam izabrao otprilike znače: „iznad moje normale“ (Great), „u mojoj normali“ (OK), „ispod moje normale“ (Meh), „bolestan sam“ (Sick). Asimetrija sa tri dugmeta za manje-više običan dan i jednim za bolest je namerna. Bolest je slučaj u kom sistemu posebno treba jasan signal koji ne može da izvede samo iz physiology: wrist temperature postoji tek od oktobra 2025, RHR kasni, HRV je sparse. I baš tu se downstream behaviour najviše menja. Posebno dugme je jeftinije nego terati korisnika da bira između „Meh“ i „Sick“ na petostepenoj skali.
Labels su namerno kratke engleske reči. Telegram client renderuje inline-keyboard buttons u uskom horizontalnom rasporedu, i dugi prevodi bi se ružno lomili. Lokalizacija se dešava drugde: prompt text iznad dugmadi je potpuno lokalizovan, ali sama dugmad ostaju kratka.
Za šta se odgovor ne koristi
Tri eksplicitna non-uses, svaki od njih iskušenje kojem je trebalo odoleti:
Odgovor ne podešava β u formuli stresa. Rubric iz 6. dela zahteva četiri objective channels: HRV sledećeg jutra, RHR sledećeg jutra, sleep architecture, test-retest stability. Check-in ne postaje peti. Dodavanje self-reported channel-a otvorilo bi upravo onaj feedback loop koji rubric treba da zatvori.
Odgovor ne retrain-uje readiness sub-scores. Četvrti deo je zatvorio Fazu 1 na naive baselines, jer linear i tree models nisu prošli unapred objavljeni floor. Dodati feature „user said meh today“ u modele tačno je potez koji bi podigao AUC, ali bi taj lift bio artefact. Tap korisnika jako korelira sa inputs koje model već vidi: „Meh“ korelira sa snom prethodne noći, a model već čita san. To bi bilo predviđanje samog sebe.
Odgovor ne menja izračunate brojeve na dashboard-u. Readiness ostaje onakav kakvim ga je server izračunao. Check-in se pojavljuje kao odvojena potvrdna linija na dashboard-u: „Vaš jutarnji odgovor: Meh“. Pored brojeva, ali nikada unutar njih.
Svaki od ovih non-uses je konkretan komad koda koji ne postoji. Red u subjective_checkins čita se na tačno dva mesta u codebase-u: morning-report scheduler, da gate-uje slanje, i dashboard renderer, da prikaže jednolinijsku potvrdu. Nigde drugde.
Čemu odgovor uopšte služi
Tri nameravane uloge, nijedna ne liči na „kalibraciju“. Svaka je hipoteza koju će naredne nedelje production run-a ili potvrditi ili tiho opovrgnuti. Pišem ih ovde kao design intent, ne kao outcomes:
Gating jutarnjeg izveštaja. Ovo je glavna hipotetička svrha. Jutarnji izveštaj se ne šalje dok korisnik ne pritisne dugme ili dok morning cap ne prođe. Ulog je da jedno to pravilo obavi većinu posla u formiranju navike: korisnik ujutru otvara Telegram, prvo vidi pitanje, tapne, zatim vidi svoje brojeve. Čin odgovora postaje ulazna tačka. Brojevi stižu posle malog subjektivnog commitment-a, ne pre njega. To izvrće uobičajenu vezu „uređaj ti govori kako se osećaš“. Da li će takva inverzija učvrstiti naviku ili ću samo naučiti da svako jutro na autopilotu tapnem „OK“ — otvoreno je pitanje zbog kog production run i postoji.
Coverage signal na dashboard-u. Hero block na / prikazuje liniju „Vaš jutarnji odgovor: …“ kada odgovor postoji. Ako dan istekne bez odgovora, dashboard ne prikazuje ništa. Ne CTA „tapni da zabeležiš!“, samo odsustvo. Odsustvo je signal: niz dana sa statusom expired pored dana answered postaje coverage map same navike, odvojeno od coverage maps za sleep i HRV.
Budući narrative review. Četvrti deo je označio spike strict-event-rate u 2022 — 5.7% naspram 1–2% drugih godina — kao „možda illness cluster koji treba ručno pregledati pre treniranja modela“. Ta fraza je bila placeholder za „nemam s čime da ga cross-reference-ujem“. Kada se check-in log nakupi, za nekoliko godina moći ću da se vratim bilo kom anomaly cluster-u i pitam: da li su ti dani bili tagged sick? Da li je pre njih postojao stabilan meh run? To nije training data. Nijedan model se na tome ne trenira, nijedna formula se po tome ne calibrate-uje. To je evidence koji pomaže da se odluči da li je anomaly cluster bio fiziologija ili instrumentation, i to kroz nezavisan kanal koji nije video output formule.
Treća tačka je razlog zašto late_answered postoji kao poseban state. Tap koji stigne sat vremena posle cap-a nije deo primary validation pool-a. Prevelik deo dana se već odigrao; odgovor više nije „pre jutarnjeg izveštaja“, nego „naknadno, nakon što sam video brojeve“. Pomešati ga sa answered znači pustiti answer-latency drift da tiho zaprlja budući cohort analysis. Zato postoje oba buckets, dashboard može da prikaže obe varijante — „Vaš jutarnji odgovor: …“ / „Zabeleženo kasno: …“ — a analitički upiti sami biraju odgovarajući pool.
State machine
Četiri stanja, sa jasnim prelazima u pure Go helper-u, koji postoji pre svega da bi policy bila auditable na jednom mestu:
nextCheckinStatus(current, action, expiresAt, now)
// actions:
// "tap" : user pressed a button
// "expire" : scheduler decided the cap has passed
prompted→answered, kada korisnik tapne preexpires_atprompted→expired, kada scheduler pokreneExpireCheckinposleexpires_atprompted→late_answered, kada tap stigne posleexpires_atexpired→late_answered, kada tap ipak stigneanswered→answeredpri ponovnom tap-u — idempotent4, closed set štiti od double-counting-a
Prikaži nextCheckinStatus u celosti (Go, 22 linije)
// nextCheckinStatus computes the target status for a row given its
// current state, the action applied, the row's expires_at, and the
// current wall clock. Pure function, exercised by tests without a
// DB so the policy stays explicit and trivially auditable.
func nextCheckinStatus(current, action string, expiresAt, now time.Time) (string, error) {
switch action {
case "tap":
if current == CheckinStatusAnswered {
return CheckinStatusAnswered, nil // idempotent re-tap
}
if now.Before(expiresAt) && current == CheckinStatusPrompted {
return CheckinStatusAnswered, nil
}
// expired / prompted-past-cap / late_answered all collapse here
return CheckinStatusLateAnswered, nil
case "expire":
if current != CheckinStatusPrompted {
return "", errors.New("can only expire a prompted row")
}
if now.Before(expiresAt) {
return "", errors.New("expires_at not reached yet")
}
return CheckinStatusExpired, nil
}
return "", fmt.Errorf("unknown action %q", action)
}
Dvadeset dve linije. Cela state machine check-in funkcije. Svaka grana je jedan test case u table-driven test-u.
nextCheckinStatus je kanonski policy document. To je pure function: bez DB, bez clock, bez Telegrama. Table-driven test koji pokriva sve transitions izvršava se za milisekunde. DB strane ExpireCheckin i SaveCheckinAnswer ponavljaju ista pravila u SQL-u kroz FOR UPDATE5 row locks i conditional UPDATE, jer policy mora da se primeni atomically kada se scheduler-expire i user-tap race-uju u istoj milisekundi.
Takav race se ne hvata u prvom PR-u. PR #121 prošao je kroz otprilike šest rounds review-and-fix pre stabilizacije. Redom: feature-flag gap — check-in gate se aktivirao u morning scheduler-u čak i kada webhook secret nije bio podešen, pa su korisnici bez check-in setup-a mogli čekati tap koji nikada neće stići; race u prvoj verziji ExpireCheckin između read-then-update iz dva statement-a i dolaznog tap-a, sveden na jedan conditional UPDATE u 7a50aa1; zaboravljeni render expired-day note u morning report-u — i18n key je dodat, ali nije provučen u cap-path, 983125f; stale-callback case, gde tap na jučerašnji prompt može da promeni jučerašnju answer row, odbijen pre save-a u 4e45212; lazy-init secrets mismatch, kada je scheduler gate čitao drugi izvor od registration path-a (60e4a34); i na kraju partial-POST case u admin settings endpoint-u koji je mogao slučajno da deleteWebhook za env-backed installs.
Šest rounds na funkciji koja u schema izgleda mala. Svaki round bio je bug drugog tipa. Race — concurrency, feature flag — boot-time integration, expired-note — product-copy contract u cap-path koji je lako promašiti jer nije happy path; stale callback — idempotency semantics, secrets mismatch — lazy-init order, partial POST — API surface. Nijedan nisu uhvatili unit tests izolovanih komponenti. Isplivali su na code review integracije između komponenti.
Bezbednost webhook-a
Check-in je prva funkcija projekta koja prima inbound HTTP od third-party service-a (Telegram). Sve ostalo je outbound: server dobija podatke iz HealthKit-a preko iOS app, šalje Telegram messages, pita Gemini. Inbound webhooks imaju drugu threat model.
Četiri sloja validation callback-a:
Path secret. Webhook URL je
/api/telegram/webhook/<secret>, gde je<secret>per-tenant vrednost koja se rotira nezavisno od bot token-a. Curenje Telegram bot token-a ne daje attacker-u webhook path.Header secret.
X-Telegram-Bot-Api-Secret-Tokense postavlja pri registraciji webhook-a i validira na svakom incoming request-u. To je Telegram-ov anti-spoofing mehanizam.chat_idlookup. Čak i validan Telegram-signed callback za pogrešan chat se odbija. Handler prolazi kroz multi-tenant pool map i traži tenant čiji configuredTELEGRAM_CHAT_IDodgovara chat-u iz callback-a. Nema match → reject.HTTPS-only na startu. Server odbija da se pokrene ako webhook URL nije HTTPS. Telegram ionako zahteva HTTPS za webhook delivery, ali misconfigured deployment koji bi pao na HTTP tiho bi izgubio svaki callback. Startup guard pokazuje problem pre prvog morning prompt-a.
Kombinacija (1) i (3) je load-bearing. Path secret zaustavlja opšteg attacker-a koji pogodi URL pattern webhook-a. chat_id lookup zaustavlja attacker-a koji nekako zna path, ali ne zna koji tenant chat_id odgovara kom path-u, i ne dozvoljava attacker-u sa bilo kojim Telegram account-om da se predstavi kao proizvoljan tenant na validnom path-u.
Auto-registration pri promeni token-a — PR #122 — rešava operativni slučaj kada bot token rotates. Server vidi promenu, poziva Telegram setWebhook sa novim secret i ažurira registry. Neuspešna registration pojavljuje se u admin UI kao obojeni badge: „Webhook · failed · …“, da tiha rotation ne slomi check-in neprimetno.
Telegram-first, ali sa mestom za dashboard
Pre bilo kakvog koda, brainstorm doc je uporedio tri entry points:
A. Telegram-first. Jedan tap tamo gde već stiže morning report. Navika se formira u postojećoj notification surface.
B. Dashboard-first. Nativni widget u dashboard hero. Traži daily dashboard opens, a ja to ne radim pouzdano.
C. Hybrid MVP. Obe varijante, zajednički backend i jedna row na
(tenant, date). Najbolji dugoročni shape, najveći prvi PR.
Verdict iz brainstorm-a: „A za prvi slice, ali dizajnirati tako da C bude lak sledeći korak“. Schema to odražava. Primary key u subjective_checkins je (date, source), a ne samo date. Enum source trenutno ima jednu vrednost — telegram; budući dashboard source moći će da živi na isti datum bez prepisivanja Telegram answer-a. Pure transition helper je source-agnostic. Webhook handler ne pretpostavlja da je Telegram jedini producer.
Razlog zašto hybrid nije ušao u prvi PR je princip iz 7. dela: stvar ima pravo da se pojavi čim može biti korisna sama po sebi. Telegram path je sam po sebi koristan: habit, gate, log, narrative. Dashboard path bi bio additive, ne required. Raditi oba u PR #1 značilo bi pisati dashboard surface pre nego što storage layer dobije production use i bude u stanju da se posvađa sa svojim assumptions.
Expired-note putanja
Kada cap prođe bez odgovora, morning report se ipak šalje. Korisnik i dalje ima pravo na svoje numbers. Ali report nosi jednolinijsku note, lokalizovanu po tenant report_lang:
Danas nema jutarnjeg check-in-a. Tapnite „Kako se osećate?“ u jučerašnjem prompt-u da retroaktivno zabeležite odgovor.
Note postoji iz dva razloga. Prvo, silence ne sme da bude invisible. Dashboard coverage signal treba da pokaže da danas subjective channel nije kalibrisan. Drugo, silence ne sme da bude punishment: report ipak stiže, numbers su čitljivi, korisnikov dan nije talac jednog tap-a.
Ovo rešenje ima isti oblik kao data-quality gates u 5. i 6. delu. Kada sensor data missing, bank se ne renderuje; kada stress coverage nije dovoljan, drain se vraća na v2.0; kada check-in expired, report se šalje sa note. Svaki slučaj ima svoj degraded-mode UI koji pošteno priznaje gap umesto da fabricira placeholder.
render checkin_expired_note in morning report cap-path je commit koji je to dodao (983125f). On me je naterao i da proverim da note text nije editorial. Ne „propustili ste!“, ne „ne zaboravite check in sutra“, nego gola činjenica: danas subjective channel nije kalibrisan, i postoji link nazad ka prompt-u. Poenta je coverage signal, ne nudge.
Šta sme da se pita, a šta ne sme
Jednostavan okvir koji se pojavio tokom sklapanja: sistem sme da pita korisnika kako se oseća. Sistem ne sme da prikaže da je to pitanje promenilo bilo koje druge brojeve. To su različite radnje sa različitim metodološkim posledicama.
Pitanje je UX surface. Učvršćuje morning habit, stvara coverage signal, generiše audit trail za budući narrative review. Pitanje takođe priznaje da korisnik ima perceptions koje sensors ne mogu potpuno da uhvate: u danima kada formulas kažu „readiness 73, u redu si“, korisnik i dalje legitimno može da tapne „Sick“.
Prikazati da je pitanje nešto promenilo — to je calibration. Calibration po self-report-u je zatvorena petlja koju je 6. deo dve hiljade reči objašnjavao kako izbeći. Sistem može da pita bez promene. Check-in row stoji pored dnevnih numbers u bazi, ali nikada unutar njihovog computation-a.
Iskušenje da se te stvari spoje — „korisnik je rekao Sick, hajde da spustimo današnji readiness sa 73 na 50“ — najveći je skriveni lever u personal health system. Neću ga povući. Brojevi koje prikazujem moraju biti brojevi koje su proizvele formulas. Tap korisnika je odvojena nezavisna evidencija. Ako se ne slažu, to je interesantan signal, i želim da ostane vidljiv, a ne zamazan.
Šta još nije dokazalo korist
Sve u ovom delu je design i code. Ništa nije verdict.
Check-in je pokrenut dovoljno skoro da nemam pošten production read o tome da li zaslužuje mesto u sistemu. Pet pitanja ostaje otvoreno, i svako je razlog zašto ovaj deo build log-a nije zatvoren:
Da li gating zaista učvršćuje naviku, ili prolazim kroz njega na autopilotu? Jutro kada tapnem „OK“ bez čitanja pitanja ne razlikuje se od jutra bez check-in-a — osim što sada postoji row u
subjective_checkinskoji tvrdi da je check-in postojao. Signal tiho degradira. Nemam instrumentiran način da razlikujem reflexive taps od considered ones, i verovatno ga neću imati. Korisnik je jedini sensor za tu razliku.Da li iskreno tapnem u danima kada je važno? Dugme Sick ima najveće downstream consequences: illness signature, AI recommendation pivots. I baš to je dugme koje ću ređe pritisnuti u graničnom danu kada nisam siguran. Ako ga nedovoljno koristim, coverage signal je gori nego ništa. Podrazumeva „not sick“ kada je istina bila „uncertain“.
Da li four-category design drži vodu? Moguće je da ću u praksi koristiti samo dva od četiri dugmeta, i discrete scale će uglavnom biti dekorativan. Još gore, OK može da pojede Meh, jer tapnuti Meh na običan low day psihološki može delovati kao samosažaljenje. Asimetrija koju sam dizajnirao može da se uruši u pogrešnom smeru.
Da li je late-answered pool koristan sam po sebi? Dodao sam
late_answeredjer je mešanje post-hoc taps u validation pool očigledno pogrešno. Da li late pool ima sopstvenu analitičku vrednost ili je samo malo pristojniji od „izgubljeno“, pokazaće meseci podataka.Da li je narrative-review argument stvaran? „Za dve godine mogu da se vratim anomaly clusters i pitam da li koreliraju sa Sick taps“ pretpostavlja da je tap record čist. Ako su odgovori na pitanja 1–3 loši, record će biti polluted, i argument o budućoj arheologiji se raspada.
Pošten put je posmatrati kvartal, pa postaviti ista pitanja po podacima, a ne po pretpostavkama. Schema i četiri states namerno su napravljeni tako da se odgovori dobiju bez promena u kodu. status, answer, prompted_at i answered_at dovoljni su da se izračunaju response latency, button distribution i coverage over time.
Ako podaci pokažu da je check-in uglavnom šum, ispravno rešenje je ukloniti ga, a ne vrteti methodology dok ne počne da izgleda korisno. Odbijanje iz 6. dela da se tune-uje na slab signal važi i ovde: feature mora da prođe sopstveni threshold pre nego što zasluži pravo da ostane u sistemu. Ostavljam sebi lični podsetnik da se vratim na ovo na jesen.
Prava lekcija
Ovo je prvi trenutak u build log-u kada arhitektonsko pitanje više ne zvuči kao „šta formula treba da računa“, nego kao „šta korisnik treba da ima pravo da kaže“. Oba pitanja su stvarna, oba traže pažljive odgovore. To su različita pitanja. I, za razliku od ostalih delova serije, odgovor na ovo je još pod posmatranjem. Design postoji, code radi, production data tek počinje da se skuplja.
Prvi tekst bio je o poverenju u podatke. Drugi — o poverenju u kolone. Treći — o poverenju u izvor svake kolone. Četvrti — o poverenju u target. Peti — o poverenju u ćutanje formule. Šesti — o poverenju u verdict. Sedmi — o poverenju u granicu između servera i klijenta. Ovaj je o poverenju u pitanje: jasno razumeti šta sistem sme da pita korisnika, čemu odgovor služi, čemu ne služi, i pošteno priznati da odgovor na pitanje „da li uopšte vredi pitati“ još nije dobijen.
Ovde build log sustiže sadašnjost. Pet readiness sub-scores, bank, flags, validation rubrics, tanak iOS klijent i morning check-in nisu završeni. To je sistem koji će generisati još podataka, nalaziti još anomalies i terati nove redesigns. Posebno check-in: najsvežiji je element, najmanje proveren i najverovatniji kandidat za reviziju za nekoliko meseci. Cilj ove serije nije bio da objavi endpoint. Cilj je bio da pokaže redosled kojim je sistem prestao da bude lep grafikon i postao nešto što sam spreman da čitam prvo ujutru, kao i da jasno kaže kojim delovima verujem jer su to zaslužili, a koji su još na probi.
Prilog poruci na Telegramu: mreža tastera koje možete dodirnuti, prikazana direktno ispod sadržaja poruke. Kada korisnik dodirne jedan, Telegram šalje POST zahtev na webhook bota sa podacima koji identifikuju koji taster je pritisnut i na kojoj poruci. Za razliku od “odgovarajuće tastature” (koja zamenjuje tastaturu korisnika); inline tastature ostaju povezane sa određenom porukom. ↩︎
Neprozirni niz koji Telegram bot pridružuje svakom dugmetu inline tastature. Kada korisnik dodirne dugme, ovaj niz je ono što Telegram šalje nazad na webhook bota. Ograničeno na 64 bajta po dugmetu. Obično kodira “koju radnju i koji kontekst”; ovde je
checkin:<answer>:<date>dovoljno za identifikaciju koji je dugme pritisnuto i kojem danu pripada prompt. ↩︎Šablon u kojem server registruje HTTPS URL kod spoljnje usluge i ta usluga šalje POST zahtev na taj URL kad god se dogodi događaj. Obrnuto od periodičnog proveravanja: server je obavešten kada se nešto dogodi, umesto da se stalno pita. Za Telegram botove, alternativa je “dugo proveravanje” (klijent bota pita Telegram za ažuriranja u petlji). Webhook-ovi su jeftiniji i imaju nižu latenciju, ali zahtevaju da server ima javni HTTPS krajnik. ↩︎
Svojstvo operacije: njena primena jednom daje isti rezultat kao i njena primena bilo koji broj puta.
UPDATE foo SET status='X' WHERE status='X'je idempotentno (ne menja stanje pri drugoj primeni).UPDATE foo SET counter=counter+1nije. Ključno za sigurnu logiku ponovnog pokušaja u distribuiranim sistemima, jer mreža može dostaviti isti zahtev dva puta. ↩︎Klauza Postgres na
SELECTkoja uzima zapisni blok na nivou reda za svaki vraćeni red tokom trajanja transakcije. Druge transakcije koje pokušaju da ažuriraju iste redove blokiraju se dok prva transakcija ne potvrdi ili ne vrati. Standardni alat za „pročitaj ovaj red i zatim ga ažuriraj bez da se bilo ko drugi meša“. Bez toga, dve istovremene transakcije mogle bi svaka da pročita isti statuspromptedi obe ga promene, što dovodi do neusaglašenog stanja. ↩︎