Release e Notifiche
Riepilogo in linguaggio semplice. Due sistemi di consegna condividono questa pagina perché svolgono lo stesso tipo di compito: decidere chi riceve cosa, e quando. Il sistema di release decide a quale build dell'app un dato install deve aggiornarsi (per track, per percentuale, per bucket A/B, con override d'emergenza). Il sistema di notifiche decide quali messaggi in-app/email un dato giocatore vede, e ricorda se li ha letti.
Parte 1 — Release e rollout dell'app
Mappa
Come viene risolto un aggiornamento
Quando un client chiede "c'è un aggiornamento per me?", la edge function
resolve-update percorre queste tabelle e scrive una riga di audit. I pezzi:
app_releases_v2— una build pubblicata. Raggruppa le tre versioni che vengono distribuite insieme (installer_version,core_version,ui_version) sotto unarelease_key, su unrelease_track— un insieme chiuso di tre valori:stable | dev | test(non esistebeta;dev/testsono privati, gestiti tramite allowlist). Più undownload_url, l'integritàinstaller_sha256, e le note di rilascio sia rivolte all'utente sia tecniche.app_rollouts_v2— una regola che espone una release a una fetta della flotta: untrack(defaultstable), unapercentage(0–100, default 100),experiment_key/ab_bucketopzionali (A/B/null) per i test A/B, array esplicitiinclude_user_ids/exclude_user_ids/include_install_ids, unapriority(default 100; valore più basso = precedenza più alta), un flagforce_update(default false), un gateenabled(default true — i rollout disabilitati vengono saltati), e una finestra temporale (starts_at/ends_at).app_rollout_overrides_v2— una via di fuga per singolo target: fissa uno specificotarget_type(user|install) +target_ida unarelease_id, con un flagenabled,force_update(qui default true — nota la differenza rispetto ai rollout), unreason, eexpires_at. Univoco su(target_type, target_id). È così che si esegue un hotfix per un singolo utente o si forza un aggiornamento critico al di fuori della rampa normale.app_update_resolve_audit_v1— una riga per ogni esito di risoluzione significativo (25 colonne), deduplicata dashould_write_resolve_auditcosì che risoluzioni identiche/no-op non riempiano la tabella di righe. Registra lo stato attuale dell'install (installed_*_version,installed_track), cosa è stato deciso (assignment_source—rollout/override/none—assignment_track,rollout_id,override_id,bucket), e l'esito (target_release_id, le versioni di destinazione,update_available,update_required,force_update,reason). È questa la tabella da interrogare per rispondere a "perché questa macchina ha ricevuto / non ha ricevuto l'aggiornamento?"
Ordine di risoluzione: gli override vincono per primi (un override user
prima di un override install); altrimenti il resolver prova i rollout
abilitati in ordine (priority ASC, created_at ASC) e vince il primo che
qualifica (la priority è la chiave di selezione primaria, non solo un
tie-break). Quando non viene selezionato nulla, assignment_source = 'none'.
app_update_resolve_rate_limit— contatori a finestra fissa chiavati su(ip_sha256, finestra 60s)così che l'endpoint pubblico di risoluzione non possa essere martellato. Le righe vecchie vengono potate a ogni controllo.
La view user_current_track deriva il release track corrente di ciascun
utente dall'ultimo installed_track nella tabella di audit; resolve_audience()
la usa per il filtro di notifica app_track. Vedi
Fasi e Policy del Programma e
il runbook Workflow di Release e CI.
Parte 2 — Notifiche
Mappa
auth.users, non a playroll_usersLe colonne user_id / added_by / created_by sulle tabelle di notifica fanno
tutte FK a auth.users(id) (stesso UUID di playroll_users, tabella fisica
diversa), ON DELETE CASCADE (eccetto created_by/added_by, che fanno SET
NULL). Il diagramma le disegna verso PLAYROLL_USERS per leggibilità. Chiavi di
idempotenza: notification_deliveries è UNIQUE su (event_id, user_id, channel), la PK di notification_state è (event_id, user_id).
La forma di una notifica
notification_event— il messaggio redatto. Una riga per notifica. Il suourgency_tierè un enum strettoT0 | T1 | T2 | T3che porta con sé un comportamento: T0 richiede una CTA, forzain_app+email, e non può essere disattivato; i tier inferiori vengono instradati per tier + category.source ∈ (backend, portal, release, ops_admin, discord_bridge);locale ∈ (it, en)(defaultit).categoryè testo libero — nessun CHECK (es.payment_readiness,app_update); non è un enum chiuso. Porta anchetype,payload/cta(jsonb),allowed_channels(default{in_app}), il jsonbaudience_filter(risolto daresolve_audience()), unrecipient_countdenormalizzato, e unexpires_atopzionale.notification_deliveries— il fan-out solo per i canali esterni: una riga per ogni tentativo (event, user, channel) dovechannel ∈ (email, push, discord).in_appnon è qui di proposito — viene servito danotification_state.status ∈ (queued [default], sent, failed, skipped), conerror_code,queued_at,sent_at. UNIQUE su(event_id, user_id, channel)per idempotenza.notification_state— il record di engagement per singolo utente (e la superficie di consegnain_app): quando un utente ha eseguitoread_at/dismissed_at/acted_atsu un dato evento. PK(event_id, user_id). Alimenta il badge dei non letti e le metriche di azione-su-CTA.notification_groups+notification_group_members— coorti nominate (founders, ops, beta…) che puoi usare come target, invece di elencare gli user id ogni volta. Un gruppo ha unoslug/name; l'appartenenza è(group_id, user_id).notification_user_prefs— gli interruttori di ciascun utente (enable_payment_and_validation,enable_app_update_and_terms,enable_catalog_and_announcements,enable_sound,enable_email; categorie + email default true, suono default false). Importante: la riga viene creata in modo lazy — l'assenza significa "opt-in (default)". E la consegna lato server (emit_notification) attualmente consulta soloenable_email(per gli invii email non-T0); i tre toggle di categoria eenable_soundsono memorizzati ma consumati lato client / non ancora applicati alla consegna. Il mapping category→toggle stesso vive nel codice applicativo (le stringhecategorynon corrispondono letteralmente ai nomi delle colonne), non nel DB.
Vedi Flusso di Consegna delle Notifiche e il runbook Ops del Composer delle Notifiche per il ciclo di vita redazione → consegna → stato.
Vedi anche: riferimento completo delle colonne per release e notifiche.