Passa al contenuto principale

Modello dati — Panoramica

Questo è il livello di osservabilità dei nostri dati stessi. Finora non c'era un unico posto in cui rispondere a "quali entità ha effettivamente Playroll, e come si collegano?" — dovevi leggere le migrazioni o frugare in Supabase. Questa sezione è quell'unico posto.

È scritta per essere letta in due modi:

  • Come PM / non-ingegnere — leggi questa pagina dall'inizio alla fine. Ogni dominio ha una scheda in linguaggio semplice e una riga di glossario. Puoi ignorare le tabelle delle colonne.
  • Come ingegnere (zoinkwiz, Vincenzo, Alessio, Marco) — le pagine per-dominio ti danno le colonne chiave e le relazioni; il riferimento completo ti dà ogni colonna, tipo e chiave esterna di tutte le 45 tabelle.
Fonte di verità

Questo atlante descrive lo schema public live del progetto Supabase di Playroll (il DB del recorder C++). È generato dal database stesso, non inventato a mano. La sede canonica dello schema è playroll-cpp/supabase/migrations — se atterra una migrazione che cambia la forma qui sotto, è questa sezione che viene aggiornata di pari passo.

Il contributor portal ha un proprio database separato e una propria pagina Modello dati; l'unico ponte tra i due è la tabella contributor_status_projection descritta in Identità e accesso.

Come leggere le mappe

Ogni pagina di dominio disegna un diagramma entità-relazione Mermaid. Due simboli portano tutto il significato:

  • Un riquadro è una tabella (un'entità). La sua riga superiore è il nome; le righe sotto di essa sono le colonne che vale la pena conoscere.
  • Una linea è una chiave esterna — un puntatore rigido da una tabella all'altra. Il piede di gallina (la piccola forcella <) sta sul lato "molti". Quindi ELIGIBLE_GAMES ||--o{ LOBBIES si legge "un eligible game può sostenere molte lobby."

Quando una relazione è per valore anziché una vera chiave esterna (es. una stringa di stato, o una regola JSON che nomina un tier), lo diciamo in prosa invece di disegnare una linea — esattamente come fa il modello quests per le regole di audience. Una manciata di archi sono disegnati verso playroll_users per leggibilità ma fisicamente fanno riferimento ad auth.users (i due sono 1:1 sullo stesso UUID); le pagine di dominio lo segnalano.

Un'ultima avvertenza che i diagrammi non possono mostrare: la row-level security è abilitata su ogni tabella. Il fatto che una relazione sia disegnata non significa che un client browser possa leggere attraverso di essa — molte tabelle sono raggiungibili solo tramite una edge function o la service key. Ogni pagina di dominio ha una breve nota "modello di accesso"; fidati di quelle più che del diagramma quando ragioni su ciò che l'app può effettivamente leggere.

L'intero ecosistema, a colpo d'occhio

Sei domini, più una manciata di tabelle di supporto. La spina dorsale della registrazione al centro è la cosa che tutto il resto decora — un utente registra una sessione, la sessione produce upload, e identità / catalogo / lobby / crescita pendono tutti da quell'atto.

I sei domini

🟦 Identità e accesso

Chi è un giocatore, e cosa gli è permesso fare. playroll_users è il fulcro dell'intero database — quasi tutto punta indietro a un utente. Attorno ad esso stanno l'identità Steam (allowlist, nonce OpenID, arricchimento), due sistemi RBAC separati (player roles per gli utenti dell'app, CRM roles per lo staff), il ponte verso il contributor-portal e il log dei consensi append-only. → Identità e accesso

🟩 Spina dorsale della registrazione

L'atto fondante del prodotto: registra il gameplay, dimostralo, caricalo. Una recording_session è l'unità di pagamento e di supporto. Produce s3_uploads (una riga per file caricato — l'MP4 + CSV + JSON validati, più i sidecar opzionali _mic.aac / _transcript.json), fa riferimento all'hardware su cui è girata (device_hw_snapshots), ed è l'input agli stadi di elaborazione GPU pipeline_*. Questa è la spina dorsale su cui si innesta il modello quests. → Spina dorsale della registrazione

Registrazione co-op, e i giochi che permettiamo. Una lobby è un party che registra insieme, vincolato a esattamente un gioco. Il catalogo (eligible_games e i suoi satelliti store / runtime / engine) definisce quali giochi sono registrabili e come catturarli; user_game_library è la libreria Playnite scansionata di ciascun giocatore. → Lobby e catalogo

🟪 Crescita e rete

Come i giocatori portano altri giocatori. Lo shadow_graph è la rete degli amici Steam dell'utente che alimenta la vista headhunter; gli invite_codes regolano l'accesso Fase-0; le colonne di referral su playroll_users chiudono il cerchio. → Crescita e rete

⬜ Release e notifiche

Far arrivare nuove build e messaggi ai giocatori giusti. Le tabelle app_releases_v2

  • rollout sono il cervello degli aggiornamenti scaglionati (track, percentuali, bucket A/B, override di force-update), auditate per ogni chiamata di resolve. Le tabelle notification_* sono il sistema di consegna in-app + email con preferenze per-utente e stato di lettura. → Release e notifiche

⬜ Diagnostica (condivisa)

Dirci perché una registrazione è andata storta. nvenc_known_contenders / nvenc_contender_user_links tracciano quali app di terze parti competono con Playroll per l'encoder GPU; bug_reports è il canale di segnalazione in-app. Questi sono documentati nelle pagine Spina dorsale della registrazione e Release e notifiche accanto a ciò che diagnosticano.

Vocabolario — una parola, un significato

Le stesse parole vengono riusate liberamente in chat. Ecco cosa significano nei dati:

TermineNel database significa…
Utente / giocatoreUna riga in playroll_users, identificata da user_id (un UUID di auth Supabase). L'entità singola più referenziata.
SessioneUna riga in recording_sessions — un tentativo di registrazione da start a stop, identificato da una stringa rec-<epoch-ms>. L'unità di pagamento e di supporto.
Set validatoGli artefatti che il gate di upload richiede per una sessione — MP4, CSV (_input.csv) e JSON (_meta.json). Se la validazione locale blocca, nessuno di essi viene registrato per l'upload (il gate è il punto tutto-o-niente, non l'upload stesso). Ogni file presente atterra poi come una propria riga in s3_uploads.
SidecarArtefatti opzionali che non condizionano mai la validazione: l'audio del microfono _mic.aac e la trascrizione _transcript.json. Una sessione può quindi avere fino a ~5 artefatti, non 3 — e un sidecar mancante è normale, non un fallimento.
Lobby / partyUna riga in lobbies — persone che registrano insieme per un tentativo, vincolate a un singolo gioco. Il "gruppo di N".
Eligible gameUna riga in eligible_games — un gioco per cui permettiamo la registrazione, identificato da igdb_id / slug. Il confine rigido contro cui un upload viene controllato.
Tierplayroll_users.contributor_tier — il livello di progressione attuale del giocatore (una stringa con default player, non ancora una tabella separata).
TrackUn canale di release su cui una build viene pubblicata — l'insieme chiuso `stable
TranscriptIl _transcript.json di una sessione — un artefatto di prima classe con la propria generazione GPU-gated, flusso di skip/edit da parte del giocatore e colonne di stato. Vedi Spina dorsale della registrazione → La trascrizione.
ContenderUn processo di terze parti visto detenere una sessione NVENC (encoder GPU) mentre Playroll registrava — cioè competizione per l'encoder.

Indice completo delle tabelle

Tutte le 45 tabelle, raggruppate per il dominio che le possiede. Clicca per arrivare alla pagina di dominio per il diagramma e le colonne chiave, oppure salta al riferimento per ogni colonna.

TabellaDominioIn una riga
playroll_usersIdentitàIl fulcro. Una riga per giocatore; 40 colonne che spaziano dall'identità Steam, all'arricchimento, al valore di rete e all'attribuzione.
steam_id_allowlistIdentitàID Steam pre-autorizzati. La edge fn di login controlla questa prima di emettere una sessione.
auth_user_steam_lookupIdentitàIndice steam_id → auth user UUID, mantenuto da trigger.
steam_openid_noncesIdentitàNonce di protezione da replay per il sign-in Steam OpenID.
player_role_catalogIdentitàCatalogo dei player role validi, ordinati per rango. Nuovi ruoli = INSERT, nessuna migrazione.
player_rolesIdentitàAssegnazioni di ruolo per gli utenti dell'app (un giocatore può detenerne diversi).
crm_user_rolesIdentitàRBAC dello staff per il CRM, identificato da email (verificata da Cloudflare Access).
permissionsIdentitàCatalogo delle coppie di permesso (resource, action).
role_permissionsIdentitàPacchetto Role → permission che la UI admin del CRM modifica.
device_auth_requestsIdentitàHandshake di login device-code legacy (0 righe; soppiantato dal redirect steam-login).
upload_credentialsIdentitàCredenziale per-MAC legacy/inutilizzata (0 righe, nessuno scrittore).
playroll_user_consentsIdentitàLog dei consensi append-only; consent_type a 5 valori (age/tos/privacy/eula/edc_gameplay).
contributor_status_projectionIdentitàPonte lato-app scritto da redeem-activation-code (stato del link, nessun PII).
recording_sessionsSpina dorsaleUn tentativo di registrazione; durata canonica + stato di uploadability.
s3_uploadsSpina dorsaleUna riga per file caricato (MP4/CSV/JSON validati + sidecar opzionali mic/transcript).
device_hw_snapshotsSpina dorsaleInventario hardware per-(utente, dispositivo); nuova riga a ogni cambio HW.
pipeline_sessionsSpina dorsaleVista della pipeline GPU sugli artefatti S3 di una sessione e sullo stadio di elaborazione.
pipeline_runsSpina dorsaleUna riga per esecuzione di pipeline (Prefect flow run) su una sessione.
nvenc_known_contendersDiagnosticaRegistro delle app di terze parti viste competere per NVENC.
nvenc_contender_user_linksDiagnosticaQuali utenti hanno visto ciascun contender (conteggio utenti distinti).
bug_reportsDiagnosticaSegnalazioni di bug in-app con contesto HW/versione.
lobbiesLobbyUn party che registra insieme, vincolato a un gioco.
lobby_participantsLobbyChi è in ciascuna lobby + il suo id di sessione.
lobby_join_eventsLobbyStorico di join/leave (INSERT al join, UPDATE di left_at al leave); guidato da trigger.
eligible_gamesCatalogoGiochi per cui permettiamo la registrazione (igdb_id / slug).
game_store_idsCatalogoID per-store (steam/epic/…) per un eligible game.
game_runtime_profilesCatalogoProfilo di cattura per-eseguibile (engine, applicazione delle impostazioni).
engine_defaultsCatalogoSpecifica delle impostazioni di cattura di default per-engine.
user_game_libraryCatalogoLa libreria Playnite scansionata di ciascun giocatore.
shadow_graphCrescitaAmici Steam degli utenti registrati; la rete di reclutamento.
invite_codesCrescitaPool fisso di invite code di Fase-0.
invite_code_consumptionsCrescitaUna riga per (code, user) — un utente riscatta un dato codice una volta.
redeem_attempt_logCrescita*Log del rate-limiter per il flusso activation-code (EC-227), non gli invite code. (*risiede nell'area activation/contributor nonostante il raggruppamento dell'elenco tabelle.)
app_releases_v2ReleaseUna build pubblicata (versioni installer + core + UI, note, hash).
app_rollouts_v2ReleaseUna regola di rollout: track, percentuale, bucket A/B, audience.
app_rollout_overrides_v2ReleaseOverride di pin o force-update per-utente / per-install.
app_update_resolve_audit_v1ReleaseUna riga per ogni chiamata di resolve "quale aggiornamento ricevo?".
app_update_resolve_rate_limitReleaseContatori di rate-limit a finestra fissa per la edge fn di resolve.
notification_eventNotificheUna notifica redatta (categoria, payload, filtro di audience).
notification_deliveriesNotificheTentativo di consegna per-utente-per-canale + stato.
notification_stateNotificheStato per-utente letto / scartato / agito per un evento.
notification_groupsNotificheCoorti di targeting nominate (founders, ops, beta…).
notification_group_membersNotificheAppartenenza a quelle coorti.
notification_user_prefsNotificheToggle di opt-in per-utente per canale + categoria.
Anche le viste sono documentate

Oltre alle 45 tabelle, 11 viste proiettano questi dati per VC, supporto e dashboard (investor_daily_recording_kpis_vc_view, support_recording_sessions, playroll_users_view, …). Sono elencate con le tabelle che le sostengono nella pagina di riferimento. Le dashboard che le consumano risiedono sotto Dashboard → Viste Supabase.