Notizie del settore

Disciplina di tempo e orologi in una piattaforma iGaming regolamentata

Ogni risposta che una piattaforma di gioco dà contiene un tempo. Una puntata è stata accettata in un istante, una sessione è iniziata in un altro, un round è stato regolato dopo, una finestra di bonus si è chiusa, un mercato è stato sospeso, un jackpot ha maturato, il giorno di un report è terminato, una riga di log è stata scritta. Nessuno di quei tempi è decorazione: ciascuno è un’affermazione che l’operatore potrebbe dover difendere un giorno — davanti a un giocatore con un reclamo, a un revisore con una domanda, a una revisione di incidente che ricostruisce ciò che è accaduto, o a una riconciliazione che non torna.

Il tempo è anche l’unica dipendenza che una piattaforma non smette mai di usare. Viene letto a ogni richiesta invece di essere invocato ogni tanto, non ha un endpoint di stato per impostazione predefinita, e quando è sbagliato la piattaforma non si ferma: continua a servire, in silenzio, con marche temporali che non significano più ciò che i registri circostanti presuppongono. Per questo il tempo merita lo stesso trattamento di un libro contabile: un’unica fonte di verità, una rappresentazione esplicita, confini dichiarati e test che riproducono il guasto.

Un'oracolo in veste con tratti pallidi e segni scuri attorno agli occhi afferra una lanterna d'ottone dall'anello e la stabilizza su una balaustra di pietra scura, mentre un serpente azzurro senza arti, con una sola testa crestata, si avvolge una volta sulla ringhiera tra le lanterne e una fila di lanterne d'ottone identiche si perde nell'ombra a destra; il terzo sinistro della scena è pietra scura vuota e silenziosa
La disciplina messa in scena come una fila di lanterne: lanterne identiche poste a intervalli regolari, con il serpente che tiene la spaziatura. L'illustrazione è arte concettuale generata per questo articolo e non dichiara alcuna misura, quantità o esito.

Le quattro parti di un’affermazione temporale

“14:03:07” non è ancora un’affermazione che qualcuno possa verificare. Un’affermazione temporale difendibile ha quattro parti, e una piattaforma che ne lasci una implicita finirà per contraddire i propri report.

Parte La domanda a cui risponde Dove fallisce quando non è scritta
L’orologio Quale macchina, sincronizzata a che cosa, entro quale tolleranza Due servizi ordinano gli stessi eventi in modo diverso
La rappresentazione Istante con scostamento, o ora locale senza fuso Un tempo conservato non può più essere collocato su una linea temporale
La regola Quale calendario, quale confine, inclusivo o esclusivo Una chiusura applicata in un modo nell’API e in un altro nel report
L’evidenza Che cosa è stato registrato, da chi, per quanto tempo Un reclamo si risolve con un log che non mostra lo scostamento

Il resto dell’articolo segue queste quattro parti in ordine, perché gli errori si sommano: una fonte di orologio non scritta rende ambigua la rappresentazione, una rappresentazione ambigua rende indimostrabile il confine, e un confine indimostrabile trasforma una riconciliazione di routine in una discussione.

Dare un nome all’orologio di cui la piattaforma si fida

Il Network Time Protocol è la risposta abituale, e l’RFC 5905 ne descrive lo scopo: NTP “è ampiamente usato per sincronizzare gli orologi dei computer su Internet”, e la versione 4 “include miglioramenti sostanziali negli algoritmi di mitigazione e disciplina che estendono la precisione potenziale a decine di microsecondi con workstation moderne e reti locali veloci”. Vale la pena leggere con attenzione quella frase prima di citarla internamente: è un’affermazione sulla precisione che il protocollo può raggiungere in condizioni favorevoli, non una promessa su una macchina virtuale su un hypervisor occupato, dietro un collegamento saturo, con un daemon che interroga lo stesso server da un mese.

Ne derivano quattro regole pratiche.

  • Più di una fonte e un sostituto con un nome. Un singolo server di riferimento è un punto singolo di guasto per qualcosa da cui dipende ogni richiesta. Una configurazione di piattaforma dovrebbe assomigliare a un piccolo insieme di fonti, e quella configurazione dovrebbe poter essere revisionata nel repository invece di essere stata digitata una volta su un host in esecuzione.
  • Autenticare la fonte quando l’esposizione lo giustifica. L’RFC 8915 specifica Network Time Security, che usa “Transport Layer Security (TLS) e cifratura autenticata con dati associati (AEAD) per fornire sicurezza crittografica al modo client-server del Network Time Protocol (NTP)”. Chrony lo espone come opzione nts di una direttiva server, e la sua documentazione avverte che, quando l’autenticazione è attiva, è importante disattivare le fonti non autenticate che altrimenti potrebbero essere usate per influenzare l’orologio.
  • Passo all’avvio, regolazione graduale dopo. La direttiva makestep di chrony “forza chronyd a eseguire un passo sull’orologio di sistema se l’aggiustamento supera un valore di soglia, ma solo se non ci sono stati più aggiornamenti dell’orologio da quando chronyd è stato avviato rispetto al limite indicato”. L’indicazione dello stesso progetto è che “sulla maggior parte dei sistemi è opportuno eseguire un passo sull’orologio di sistema solo all’avvio, prima di avviare programmi che si affidano a un tempo che avanza in modo monotono in avanti”. Una piattaforma che esegue passi sull’orologio in operatività normale ha deciso che un secondo può ripetersi o essere saltato mentre è in funzione.
  • Monitorare la fonte di tempo come una dipendenza. Scostamento e deriva per host, con allarme a una soglia più stretta del confine più fine che la piattaforma dichiara, e trattati come motivo per togliere un host dalle scritture sensibili al tempo, non come un grafico che qualcuno guarderà dopo. Un host il cui orologio è fuori tolleranza non è “a posto, solo un po’ avanti”: è un host che scriverà un registro che non concorda con il registro accanto.

Conservare l’istante, non l’impressione

Una marca temporale è un istante con una relazione dichiarata al Tempo Universale Coordinato. L’RFC 3339, il formato data e ora di Internet, è esplicito sul fatto che i suoi valori esprimono uno scostamento rispetto a UTC, e definisce una convenzione utile da conoscere: se l’ora UTC è nota ma lo scostamento locale non lo è, lo scostamento -00:00 significa esattamente questo e “differisce semanticamente da uno scostamento Z o +00:00, che implicano che UTC sia il punto di riferimento preferito per l’ora indicata”. Lo stesso documento osserva la proprietà che rende utile questa disciplina: se i valori condividono scostamento e precisione, si ordinano come testo in ordine temporale.

Il modo di fallire è l’abitudine opposta: conservare quello che mostrava lo schermo. Un’ora locale senza fuso è irrecuperabile una volta perso lo scostamento. La documentazione di PostgreSQL descrive la meccanica senza fronzoli: se nell’input non è indicato alcun fuso, si presume che sia “nel fuso indicato dal parametro TimeZone del sistema”, e in ogni caso “il valore è memorizzato internamente come UTC e il fuso indicato o presunto in origine non viene conservato”. Il database fa la cosa giusta con l’istante e scarta il contesto umano, il che significa che una regola che citava un fuso locale va ricostruita da qualcos’altro. Significa anche che la configurazione di un server può cambiare in silenzio il modo in cui viene letta una marca temporale senza fuso.

L’RFC 9557, pubblicato nel 2024, esiste proprio per questa lacuna: estende l’RFC 3339 “per rappresentare informazioni aggiuntive, incluso un fuso orario”, precisamente perché le applicazioni gestiscono “ciascun istante con un nome di fuso orario associato per tenere conto di eventi come i passaggi all’ora legale”. Che si adotti o no quel formato, la regola che codifica è quella da seguire: conservare l’istante e tenere accanto il nome del fuso quando una regola si riferiva a un’ora locale, invece di uno scostamento fisso calcolato al momento di scriverla.

Misurare con un orologio che non può essere riportato indietro

Nella maggior parte degli host vivono due orologi, e la documentazione di Go espone la distinzione meglio di molte specifiche: “I sistemi operativi forniscono sia un ‘wall clock’, soggetto a cambiamenti per la sincronizzazione dell’orologio, sia un ‘monotonic clock’, che non lo è. La regola generale è che il wall clock serve a dire l’ora e il monotonic clock a misurare il tempo”.

Questa divisione ha una conseguenza operativa. Tutto ciò che misura un intervallo — un timeout, un backoff di ritentativo, una finestra di limitazione di frequenza, un campione di latenza, un conto alla rovescia di sessione, un lease di lock — deve essere governato da una fonte monotona, altrimenti una correzione dell’orologio diventa un difetto: un passo in avanti fa scadere lavoro che aveva ancora tempo, un passo indietro rende negativa una durata trascorsa. Tutto ciò che registra un fatto — accettazione, regolazione, maturazione — deve usare il wall clock, perché un istante deve essere confrontabile con altre macchine.

I due divergono anche alla serializzazione, ed è lì che l’errore di solito arriva in produzione. La lettura monotona “non ha significato al di fuori del processo corrente”, quindi i codificatori di Go la omettono, e ogni costruttore di parsing crea un tempo senza di essa. Una durata conservata come differenza di marche temporali sopravvive quindi a un riavvio del processo come aritmetica di wall clock, con l’errore che l’orologio ha accumulato. Se l’intervallo conta attraverso un riavvio, conviene conservarlo come istante di scadenza invece di ricalcolarlo da un’ora di inizio.

I browser tracciano la stessa linea: la specifica High Resolution Time del W3C definisce una fonte di tempo con risoluzione sub-millisecondo “tale da non essere soggetta a deriva o aggiustamenti dell’orologio di sistema”. Una telemetria lato client che misura durate con un wall clock sta misurando l’orologio del dispositivo del giocatore, non quello della piattaforma.

C’è una trappola che nessuna disciplina lato piattaforma risolve, e merita di stare nelle note di accettazione invece di essere scoperta in produzione: su alcuni sistemi l’orologio monotono si ferma mentre la macchina è sospesa, quindi una durata misurata attraverso una sospensione può riportare meno tempo di quanto ne sia realmente passato.

Decidere che cosa fa un secondo intercalare al tuo libro contabile

Il Tempo Universale Coordinato non è un puro conteggio di secondi. NIST ne spiega il motivo: i secondi intercalari vengono inseriti per mantenere UTC “entro ±0,9 s dalla scala astronomica UT1”, l’attuale differenza tra UTC e Tempo Atomico Internazionale è di 37 secondi, e la sequenza di eventi in un secondo intercalare positivo va da 23h 59m 59s a 23h 59m 60s e poi a 00h 00m 00s. L’appendice dell’RFC 3339 ricorda che la decisione spetta allo IERS, che “un secondo intercalare avviene simultaneamente in tutti i fusi orari” e che l’inserimento è rappresentato come YYYY-MM-DDT23:59:60Z.

Molto poco di uno stack tipico modella tutto questo. La documentazione di Go dice che i suoi “calcoli calendariali assumono sempre un calendario gregoriano, senza secondi intercalari”; la maggior parte dei database e delle librerie di date prende la stessa posizione, il che è ragionevole: un sessantesimo secondo che esiste due volte per decennio non è qualcosa che un libro contabile delle puntate voglia rappresentare. La conseguenza è che la flotta deve scegliere una politica, e ce ne sono due in uso:

  • Passo. L’orologio viene riportato indietro o trattenuto durante il secondo extra. L’ordinamento attraverso quel secondo smette di essere monotono: due processi diversi possono marcare lo stesso secondo due volte, o saltarne uno. Alcuni sottosistemi lo gestiscono ripetendo l’ultimo secondo del giorno.
  • Smeared (spalmato). Il secondo extra viene distribuito su una finestra, in modo che non ci sia un salto unico. Il servizio NTP pubblico di Google descrive di farlo “dal 2008, invece di applicare secondi intercalari ai nostri server con passi dell’orologio”, raccomanda uno “smear lineare di 24 ore da mezzogiorno a mezzogiorno UTC” e dichiara il costo con onestà: “il tempo spalmato devia brevemente da UTC durante il periodo di smear, ma si riallinea dopo 24 ore”.

Quel costo conta per l’ordinamento. Durante una finestra di smear, un orologio spalmato e uno non spalmato differiscono fino a circa un secondo, quindi due servizi con politiche diverse possono ordinare in modo diverso la stessa coppia di eventi. Le regole pratiche sono brevi: mettere per iscritto quale politica segue la flotta, mantenere comunque tutte le marche temporali nello stesso formato, assicurarsi che tutto ciò che confronta orari di due fonti condivida una politica o tolleri un secondo di disaccordo, e tenere aggiornate le informazioni sui secondi intercalari del daemon del tempo: la direttiva leapsectz di chrony legge il database dei fusi orari di sistema per sapere quando avverrà il prossimo secondo intercalare, e la sua documentazione osserva che, quando ne viene annunciato uno, “il fuso orario deve essere aggiornato almeno 12 ore prima del secondo intercalare”, senza bisogno di riavviare il daemon.

Le chiusure sono una regola scritta, non un arrotondamento

Ogni prodotto regolamentato ha delle chiusure: l’istante in cui un mercato chiude, un periodo di bonus termina, una sessione scade, una finestra di prelievo si chiude, un giorno di report viene dichiarato concluso. Una chiusura che esiste solo come confronto da qualche parte nel codice non è una regola; è una coincidenza che regge finché qualcuno non scrive il servizio successivo.

Una chiusura dichiarata ha quattro proprietà, e vanno scritte dove il team di prodotto, quello di piattaforma e chi redige il report possono leggerle.

  • Quale orologio. Il tempo sincronizzato della piattaforma stessa, mai una marca temporale inviata dal client. Una regola che accetta l’orologio del dispositivo è una regola che il dispositivo può spostare.
  • Il confine, esattamente. Al secondo, al millisecondo o al tick del job pianificato, e inclusivo o esclusivo sul bordo. Un intervallo semiaperto come [apertura, chiusura) è una decisione che API, interfaccia e report possono implementare in modo identico; “chiude a mezzanotte” non lo è.
  • Che cosa accade al lavoro in volo. Una richiesta che arriva prima della chiusura e termina dopo ha bisogno di un esito definito. Nelle puntate, il punto di impegno è l’istante in cui la piattaforma l’ha accettata, e la guida all’architettura di accettazione delle puntate sportive spiega perché quel punto deve essere quello autoritativo e non il momento in cui il giocatore ha premuto un pulsante.
  • Che cosa si dice al giocatore. Una chiusura che l’interfaccia presenta in modo diverso da come la piattaforma la applica genera contatti al supporto, non dispute sui millisecondi.

La scadenza di sessione merita una riga a parte, perché è una chiusura che mescola i due orologi. Un conto alla rovescia che deve sopravvivere solo dentro un processo — un avviso di inattività, una finestra di limitazione di frequenza — dovrebbe correre su una scadenza monotona, così una correzione dell’orologio non può chiudere una sessione in anticipo né prolungarla. Una scadenza che deve sopravvivere a un riavvio, a un deploy o a un bilanciatore che sposta il giocatore su un altro nodo va conservata come istante con scostamento, e la sua regola deve dire che cosa accade quando l’orologio viene corretto con sessioni aperte.

Il calendario sono dati, e i dati cambiano

Una regola di business espressa in ora locale non è stabile, perché l’ora locale è definita da dati che la piattaforma non controlla. Due transizioni all’anno rendono un’ora locale dichiarata ambigua o impossibile: un’ora che cade due volte e un’ora che non cade affatto. Una regola che dice “la promozione termina alle 02:00 locali” ha bisogno di una risposta per la notte in cui le 02:00 non arrivano mai.

Il database dei fusi orari viene aggiornato regolarmente man mano che i governi cambiano le loro regole, il che ha una conseguenza per tutto ciò che è datato nel futuro: lo stesso testo di regola produce un istante diverso dopo un aggiornamento. Per questo una chiusura con data futura va conservata come regola — nome del fuso più ora locale — e valutata con la versione dei dati in vigore, e ogni esecuzione pianificata dovrebbe registrare la versione dei dati usata. Un istante calcolato e congelato con un anno di anticipo è una promessa sulla legislazione.

I database aggiungono un’ulteriore dipendenza silenziosa. PostgreSQL converte tra timestamp without time zone e timestamp with time zone presumendo che il valore “debba essere preso o dato come ora locale di timezone”, e avverte che la conversione normalmente presume il TimeZone della sessione. Una query che sembra corretta cambia significato con un’impostazione di connessione. Il rimedio è poco elegante ed efficace: nominare il fuso esplicitamente nella query e mantenere gli istanti conservati senza ambiguità, così che l’impostazione di sessione non abbia nulla da reinterpretare.

Dove finisce il giorno di un report è una decisione

I report giornalieri e mensili hanno un confine che è una decisione di prodotto travestita da questione tecnica. Un giorno UTC, un giorno locale del regolatore e un giorno di business dell’operatore sono tre intervalli diversi con tre durate diverse in un passaggio all’ora legale — 23 o 25 ore invece di 24 — e il confine scelto cambia quali puntate, depositi o bonus cadono in quale periodo.

Scegline uno, mettilo per iscritto e pubblicalo accanto ai numeri. Quella sola riga risolve la maggior parte dei disallineamenti prima che diventino ticket: la guida alla riconciliazione del portafoglio imposta già la riconciliazione sull’orologio del fornitore e sui confini del libro contabile, e un report il cui confine di giorno è documentato può essere confrontato con l’estratto conto di un fornitore invece che discusso con esso.

Testare i guasti del tempo che puoi riprodurre

I difetti del tempo sono invisibili in una suite di test che legge l’orologio di sistema, perché l’orologio di sistema è proprio ciò che è sotto sospetto. Il primo requisito è quindi strutturale: il tempo entra nel codice come dipendenza iniettata e i test la forniscono. Poi i guasti elencati sotto sono tutti riproducibili, e ciascuno ha un comportamento atteso definito invece di una speranza.

Guasto Che cosa mette sotto stress Che cosa dovrebbe fare la piattaforma
Orologio in avanti durante una sessione aperta Scadenze, finestre di frequenza, backoff dei ritentativi Il lavoro in volo non viene annullato in silenzio né contato due volte
Orologio all’indietro Ordine dei log, durate “trascorse”, finestre di deduplicazione Nessuna durata negativa, nessuna finestra ripetuta, ordine ancora spiegabile
Scostamento tra due servizi Ordine degli eventi ricostruito da un job L’ordine viene da un registro, non dal confronto degli orologi di due host
Fonte di tempo irraggiungibile Avvio e regime stazionario Il comportamento è definito, registrato e allertato, senza deriva silenziosa
Giorno del passaggio all’ora legale Job pianificati, regole in ora locale Una regola che nomina un fuso si risolve in modo coerente, né due volte né mai
Secondo intercalare, con qualsiasi politica Ordinamento dell’intera flotta La flotta concorda la propria politica e tollera il disaccordo che comporta
Sospensione o migrazione Intervalli monotoni Il lavoro scaduto non risorge; un lease non resta trattenuto due volte

Due di questi test richiedono qualcosa di più di un test unitario. Il comportamento della piattaforma quando la sua fonte di tempo è irraggiungibile appartiene all’ambiente non di produzione che può essere reso sbagliato di proposito, e qualsiasi test che misuri una durata su un’esecuzione lunga deve dichiarare con quale orologio ha misurato: la guida ai test di carico e tenuta copre la stessa disciplina applicata all’evidenza di capacità, dove una misurazione di wall clock che deriva invalida l’esecuzione che descrive. Quando una revisione di incidente ricostruisce una cronologia, il piano di risposta agli incidenti dipende da un ordine registrato correttamente prima dell’incidente, e la guida all’evidenza di audit è il posto in cui appartengono le aspettative su scostamento e fonte.

Trasformarlo in evidenza di accettazione

Un acquirente, un revisore o un nuovo team di piattaforma dovrebbero poter rispondere alle domande sul tempo partendo da documenti e non da interviste. Il pacchetto è abbastanza piccolo da essere messo insieme in un pomeriggio e abbastanza concreto da essere testato:

  1. L’elenco delle fonti di orologio, il metodo di autenticazione, la tolleranza e come scostamento e deriva vengono monitorati e allertati.
  2. Il formato dell’istante, la regola sullo scostamento e dove viene conservato un nome di fuso e perché.
  3. Il registro delle chiusure: ogni confine, la sua precisione, il suo bordo inclusivo o esclusivo, l’orologio che lo decide e il servizio che lo applica.
  4. La regola di calendario per ogni comportamento pianificato, con la versione dei dati sui fusi registrata a ogni esecuzione.
  5. La politica sui secondi intercalari e il ragionamento che ne ha accettato il costo.
  6. I test di guasto elencati sopra, con risultati, un responsabile e una data.
  7. Il confine del giorno di report, pubblicato insieme ai report che governa.

La ragione per dedicare quel pomeriggio è che il tempo è l’unica dipendenza che non può mai essere giù. Non si può disattivare con un flag, non ha una finestra di manutenzione, e una piattaforma che non ha deciso che cosa significano le sue marche temporali scoprirà la decisione durante il reclamo che ne aveva bisogno. Il lavoro di ingegneria di una piattaforma di gioco — la disciplina dello sviluppo di piattaforma di specificare il comportamento prima di costruirlo — si applica qui come ovunque: le regole costano poco da scrivere mentre il sistema è tranquillo.

Domande che pone un acquirente di piattaforma

Non se ne occupa il sistema operativo, dell’orologio?

Tiene l’orologio approssimativamente corretto, e questo è un lavoro più stretto di quanto sembri. Il sistema operativo non sceglie il formato delle marche temporali, non decide che cosa significa una chiusura sul suo bordo, non conserva il fuso a cui si riferiva una regola e non registra quale politica ha seguito la flotta durante un secondo intercalare. Quelle sono decisioni di prodotto e di piattaforma, e sono la parte su cui una revisione farà domande.

Basta conservare le marche temporali in UTC?

Conservare in UTC è il default corretto ed è solo metà della regola. Un istante con scostamento è inequivocabile; il contesto che lo circonda non è automatico. Se una regola citava un’ora locale, il nome del fuso va conservato da qualche parte perché la regola possa essere valutata dopo un aggiornamento dei dati sui fusi, e il confine — quale orologio, quale precisione, inclusivo o esclusivo — va comunque dichiarato.

Quanta precisione deve avere davvero l’orologio di una piattaforma?

Più stretta del confine più fine che la piattaforma dichiara, e non oltre. Se la chiusura di un mercato è dichiarata al secondo, l’incertezza della flotta deve stare comodamente entro un secondo; una tolleranza più larga del confine significa che quel confine viene applicato in modo diverso su host diversi, ed è così che due giocatori nello stesso secondo ricevono risposte diverse. Il modo corretto di fissare il numero è derivarlo dalla regola più stretta e poi monitorare su quello.

I secondi intercalari contano davvero per una piattaforma di casinò?

Non per la matematica del gioco, e sì per l’evidenza. La maggior parte delle librerie di date e dei database non modella un sessantesimo secondo, il che è una scelta ingegneristica difendibile. Ciò che conta è che la scelta sia scritta e coerente, così che un avanzamento di secondo intercalare — o una flotta che segue una politica in una regione e un’altra altrove — non lasci due registri dello stesso evento in disaccordo di un secondo senza alcun documento che spieghi perché.