Sessioni

Questa pagina descrive il concetto avanzato di sessioni in Spanner, incluse le best practice per le sessioni durante la creazione di una libreria client, l'utilizzo delle API REST o RPC o l'utilizzo delle librerie client di Google.

Panoramica delle sessioni

Una sessione rappresenta un canale di comunicazione con il servizio di database Spanner. Una sessione viene utilizzata per eseguire transazioni che leggono, scrivono o modificano i dati in un database Spanner. Ogni sessione si applica a un singolo database.

Le sessioni possono eseguire una o più transazioni alla volta. Quando si eseguono più transazioni, questa sessione viene chiamata sessione multiplex.

Letture, scritture e query autonome utilizzano un'unica transazione internamente.

Vantaggi delle prestazioni di un pool di sessioni

Creare una sessione è costoso. Per evitare il costo delle prestazioni ogni volta che viene eseguita un'operazione di database, i client devono conservare un pool di sessioni, ovvero un pool di sessioni disponibili pronte all'uso. Il pool deve archiviare le sessioni esistenti e restituire il tipo appropriato di sessione quando richiesto, nonché gestire la pulizia delle sessioni inutilizzate. Per un esempio di come implementare un pool di sessioni, vedi il codice sorgente per una delle librerie client di Spanner, come la libreria client Go o la libreria client Java.

Le sessioni sono pensate per una lunga durata, quindi dopo che una sessione viene utilizzata per un'operazione di database, il client deve restituire la sessione al pool per riutilizzarla.

Panoramica dei canali gRPC

I canali gRPC vengono utilizzati dal client Spanner per le comunicazioni. Un canale gRPC è più o meno equivalente a una connessione TCP. Un canale gRPC può gestire fino a 100 richieste in parallelo. Ciò significa che un'applicazione avrà bisogno di un numero di canali gRPC pari almeno al numero di richieste in parallelo che eseguirà, diviso per 100.

Il client Spanner crea un pool di canali gRPC quando lo crei.

Best practice per l'utilizzo delle librerie client di Google

Di seguito vengono descritte le best practice per l'utilizzo delle librerie client di Google per Spanner.

Configura il numero di sessioni e canali gRPC nei pool

Le librerie client hanno un numero predefinito di sessioni nel pool di sessioni e un numero predefinito di canali gRPC nel pool di canali. Entrambi i valori predefiniti sono appropriati nella maggior parte dei casi. Di seguito sono riportate le sessioni minima e massima predefinite e il numero predefinito di canali gRPC per ogni linguaggio di programmazione.

C++

MinSessions: 100
MaxSessions: 400
NumChannels: 4

C#

MinSessions: 100
MaxSessions: 400
NumChannels: 4

Go

MinSessions: 100
MaxSessions: 400
NumChannels: 4

Java

MinSessions: 100
MaxSessions: 400
NumChannels: 4

Node.js

Il client Node.js non supporta più canali gRPC. Ti consigliamo quindi di creare più client anziché aumentare le dimensioni del pool di sessioni oltre le 100 sessioni per un singolo client.

MinSessions: 25
MaxSessions: 100

PHP

Il client PHP non supporta un numero configurabile di canali gRPC.

MinSessions: 1
MaxSessions: 500

Python

Python supporta quattro diversi tipi di pool di sessioni che puoi utilizzare per gestire le sessioni.

Ruby

Il client Ruby non supporta più canali gRPC. Ti consigliamo quindi di creare più client anziché aumentare le dimensioni del pool di sessioni oltre le 100 sessioni per un singolo client.

MinSessions: 10
MaxSessions: 100

Il numero di sessioni utilizzate dalla tua applicazione corrisponde al numero di transazioni simultanee eseguite dall'applicazione. Devi modificare le impostazioni predefinite del pool di sessioni solo se prevedi che una singola istanza di applicazione esegua più transazioni simultanee di quante sia in grado di gestire il pool di sessioni predefinito.

Per le applicazioni ad alta contemporaneità, si consiglia di:

  1. Imposta MinSessions sul numero previsto di transazioni simultanee che verrà eseguita da un singolo client.
  2. Imposta MaxSessions sul numero massimo di transazioni simultanee che un singolo client può eseguire.
  3. Imposta MinSessions=MaxSessions se la contemporaneità prevista non cambia molto durante il ciclo di vita dell'applicazione. In questo modo si impedisce lo scale up o lo scale down del pool di sessioni. Anche lo scale up o lo scale down del pool di sessioni richiede il consumo di alcune risorse.
  4. Imposta NumChannels su MaxSessions / 100. Un canale gRPC può gestire fino a 100 richieste contemporaneamente. Aumenta questo valore se noti una latenza di coda elevata (latenza p95/p99), perché potrebbe essere un'indicazione della congestione del canale gRPC.

L'aumento del numero di sessioni attive utilizza risorse aggiuntive nel servizio di database Spanner e nella libreria client. L'aumento del numero di sessioni che supera le effettive esigenze dell'applicazione potrebbe ridurre le prestazioni del sistema.

Aumentare il pool di sessioni rispetto all'aumento del numero di clienti

La dimensione del pool di sessioni per un'applicazione determina quante transazioni simultanee può eseguire una singola istanza di applicazione. Non è consigliabile aumentare le dimensioni del pool di sessioni oltre la contemporaneità massima gestibile da una singola istanza di applicazione. Se l'applicazione riceve un burst di richieste che supera il numero di sessioni nel pool, le richieste vengono messe in coda in attesa che una sessione diventi disponibile.

Le risorse utilizzate dalla libreria client sono le seguenti:

  1. Ogni canale gRPC utilizza una connessione TCP.
  2. Ogni chiamata gRPC richiede un thread. Il numero massimo di thread utilizzato dalla libreria client è uguale al numero massimo di query in parallelo eseguite dall'applicazione. Questi thread vengono posizionati sopra tutti i thread utilizzati dall'applicazione per la propria logica di business.

Non è consigliabile aumentare le dimensioni del pool di sessioni oltre il numero massimo di thread che una singola istanza di applicazione è in grado di gestire. Aumenta invece il numero di istanze dell'applicazione.

Gestisci la frazione delle sessioni di scrittura

Per alcune librerie client, Spanner riserva una parte delle sessioni per le transazioni di lettura-scrittura, chiamata frazione delle sessioni di scrittura. Se l'app utilizza tutte le sessioni di lettura, Spanner utilizza le sessioni di lettura e scrittura, anche per le transazioni di sola lettura. Le sessioni di lettura/scrittura richiedono spanner.databases.beginOrRollbackReadWriteTransaction. Se l'utente ha il ruolo IAM spanner.databaseReader, la chiamata non riesce e Spanner restituisce questo messaggio di errore:

generic::permission_denied: Resource %resource% is missing IAM permission:
spanner.databases.beginOrRollbackReadWriteTransaction

Per le librerie client che mantengono una frazione di sessioni di scrittura, puoi impostare la frazione di sessioni di scrittura.

C++

Tutte le sessioni C++ sono uguali. Non sono presenti sessioni di lettura o di sola lettura/scrittura.

C#

La frazione predefinita di sessioni di scrittura per C# è 0,2. Puoi modificare la frazione utilizzando il campo writeSessionsFraction di SessionPoolOptions.

Go

Tutte le sessioni Go sono uguali. Non sono presenti sessioni di lettura o di sola lettura/scrittura.

Java

Tutte le sessioni Java sono uguali. Non sono presenti sessioni di lettura o di sola lettura/scrittura.

Node.js

Tutte le sessioni Node.js sono uguali. Non sono presenti sessioni di lettura o di sola lettura/scrittura.

PHP

Tutte le sessioni PHP sono uguali. Non ci sono sessioni di lettura o di sola lettura/scrittura.

Python

Python supporta quattro diversi tipi di pool di sessioni che puoi utilizzare per gestire le sessioni di lettura e lettura/scrittura.

Ruby

La frazione predefinita di sessioni di scrittura per Ruby è 0,3. Puoi modificare la frazione utilizzando il metodo di inizializzazione client.

Best practice per la creazione di una libreria client o l'utilizzo di REST/RPC

Di seguito sono descritte le best practice per l'implementazione di sessioni in una libreria client per Spanner o per l'utilizzo di sessioni con le API REST o RPC.

Queste best practice si applicano solo se stai sviluppando una libreria client o se utilizzi API REST/RPC. Se utilizzi una delle librerie client di Google per Spanner, fai riferimento alle best practice quando si utilizzano le librerie client di Google.

crea e dimensiona il pool di sessioni

Per determinare una dimensione ottimale del pool di sessioni per un processo client, imposta il limite inferiore sul numero di transazioni simultanee previste e imposta il limite superiore su un numero di test iniziale, ad esempio 100. Se il limite superiore non è adeguato, aumentalo. L'aumento del numero di sessioni attive comporta l'utilizzo di risorse aggiuntive nel servizio di database Spanner, quindi la mancata pulizia delle sessioni inutilizzate potrebbe peggiorare le prestazioni. Per gli utenti che utilizzano l'API RPC, consigliamo di non avere più di 100 sessioni per canale gRPC.

Gestire le sessioni eliminate

Esistono tre modi per eliminare una sessione:

  • Un cliente può eliminare una sessione.
  • Il servizio di database Spanner può eliminare una sessione quando questa è inattiva per più di un'ora.
  • Il servizio di database Spanner può eliminare una sessione se risale a più di 28 giorni prima.

I tentativi di utilizzare una sessione eliminata restituiscono NOT_FOUND. Se si verifica questo errore, crea e utilizza una nuova sessione, aggiungi la nuova sessione al pool e rimuovi dal pool la sessione eliminata.

Mantieni attiva una sessione inattiva

Il servizio di database Spanner si riserva il diritto di eliminare una sessione inutilizzata. Se hai sicuramente bisogno di mantenere attiva una sessione inattiva, ad esempio se è previsto un aumento significativo a breve termine nell'utilizzo del database, puoi evitare che la sessione venga interrotta. Esegui un'operazione poco costosa come l'esecuzione della query SQL SELECT 1 per mantenere attiva la sessione. Se hai una sessione inattiva che non è necessaria per l'uso a breve termine, lascia che Spanner la elimini e ne crei una nuova la volta successiva che è necessaria una sessione.

Uno scenario per mantenere attive le sessioni è gestire picchi di domanda regolari nel database. Se l'utilizzo intensivo del database avviene ogni giorno dalle 9:00 alle 18:00, dovresti mantenere attive alcune sessioni inattive durante questo periodo di tempo, poiché è probabile che siano necessarie per l'utilizzo di picco. Dopo le 18:00, puoi consentire a Spanner di interrompere le sessioni inattive. Prima delle 09:00 di ogni giorno, crea alcune nuove sessioni in modo che siano pronti per la domanda prevista.

Un altro scenario è se un'applicazione utilizza Spanner, ma deve evitare l'overhead della connessione quando lo fa. Puoi mantenere attivo un insieme di sessioni per evitare il sovraccarico della connessione.

Nascondi i dettagli della sessione all'utente della libreria client

Se stai creando una libreria client, non esporre sessioni al consumer della libreria client. Consenti al client di effettuare chiamate al database senza la complessità della creazione e della gestione delle sessioni. Per un esempio di libreria client che nasconde i dettagli della sessione al consumer della libreria client, consulta la libreria client Spanner per Java.

Gestire gli errori per le transazioni di scrittura non idempotenti

Le transazioni di scrittura senza protezione dalla replica potrebbero applicare le mutazioni più di una volta. Se una mutazione non è idempotente, una mutazione applicata più di una volta potrebbe causare un errore. Ad esempio, un'inserimento potrebbe non riuscire con ALREADY_EXISTS anche se la riga non esisteva prima del tentativo di scrittura. Questo potrebbe verificarsi se il server di backend ha eseguito il commit della mutazione, ma non è stato in grado di comunicare l'esito positivo al client. In tal caso, sarà possibile riprovare la mutazione, generando l'errore ALREADY_EXISTS.

Ecco alcuni modi per risolvere questo scenario quando implementi la tua libreria client o utilizzi l'API REST:

  • Struttura le tue scritture in modo che siano idempotenti.
  • Utilizza operazioni di scrittura con protezione dalla riproduzione.
  • Implementare un metodo che esegue la logica "upsert": inserisci se è nuovo o aggiorna se esiste.
  • Gestire l'errore per conto del client.

Mantieni connessioni stabili

Per ottenere le migliori prestazioni, la connessione che utilizzi per ospitare una sessione deve rimane stabile. Quando la connessione che ospita una sessione cambia, Spanner potrebbe interrompere la transazione attiva sulla sessione e causare una piccola quantità di carico aggiuntivo sul database durante l'aggiornamento dei metadati della sessione. Alcune connessioni cambiano sporadicamente, ma è consigliabile evitare situazioni che cambierebbero un numero elevato di connessioni contemporaneamente. Se utilizzi un proxy tra il client e Spanner, devi mantenere la stabilità della connessione per ogni sessione.

Monitora le sessioni attive

Puoi utilizzare il comando ListSessions per monitorare le sessioni attive nel database dalla riga di comando, con l'API REST o con l'API RPC. ListSessions mostra le sessioni attive per un determinato database. Questa funzionalità è utile se devi individuare la causa di una fuga di sessione. Una fuga di sessioni è un incidente in cui vengono create sessioni ma non vengono restituite a un pool di sessioni per il riutilizzo.

ListSessions ti consente di visualizzare i metadati sulle sessioni attive, ad esempio quando è stata creata una sessione e quando è stata utilizzata l'ultima volta. L'analisi di questi dati ti indirizzerà nella giusta direzione per la risoluzione dei problemi delle sessioni. Se la maggior parte delle sessioni attive non ha un approximate_last_use_time recente, questo potrebbe indicare che le sessioni non vengono riutilizzate correttamente dalla tua applicazione. Consulta il riferimento dell'API RPC per ulteriori informazioni sul campo approximate_last_use_time.

Consulta il riferimento dell'API REST, il riferimento dell'API RPC o il riferimento dello strumento a riga di comando gcloud per ulteriori informazioni sull'utilizzo di ListSessions.

Pulizia automatica delle perdite di sessioni

Quando utilizzi tutte le sessioni del pool, ogni nuova transazione attende che venga restituita una sessione al pool. Quando le sessioni vengono create, ma non tornate nel pool di sessioni per essere riutilizzate, si parla di perdita di sessioni. In caso di perdita di una sessione, le transazioni in attesa di una sessione aperta rimangono bloccate in modo permanente e bloccano l'applicazione. Le perdite di sessioni sono spesso causate da transazioni problematiche che vengono eseguite per un tempo estremamente lungo e che non sono state eseguite.

Puoi configurare il pool di sessioni in modo da risolvere automaticamente queste transazioni non attive. Quando abiliti la libreria client per la risoluzione automatica delle transizioni inattive, questa identifica le transazioni problematiche che potrebbero causare una fuga di sessioni, le rimuove dal pool di sessioni e le sostituisce con una nuova sessione.

Il logging può anche aiutare a identificare queste transazioni problematiche. Se il logging è abilitato, i log degli avvisi vengono condivisi per impostazione predefinita quando più del 95% del pool di sessioni è in uso. Se l'utilizzo delle sessioni è superiore al 95%, è necessario aumentare il numero massimo di sessioni consentite nel pool di sessioni, oppure è possibile che si sia verificata una fuga di sessioni. I log di avviso contengono analisi dello stack di transazioni eseguite più a lungo del previsto e possono contribuire a identificare la causa dell'elevato utilizzo del pool di sessioni. Il push dei log degli avvisi dipende dalla configurazione dell'esportatore di log.

Attiva la libreria client per risolvere automaticamente le transazioni inattive

Puoi abilitare la libreria client per l'invio di log degli avvisi e risolvere automaticamente le transazioni inattive oppure abilitare la libreria client per ricevere solo i log degli avvisi.

Java

Per ricevere i log degli avvisi e rimuovere le transazioni non attive, utilizza setWarnAndCloseIfInactiveTransactions.

 final SessionPoolOptions sessionPoolOptions = SessionPoolOptions.newBuilder().setWarnAndCloseIfInactiveTransactions().build()

 final Spanner spanner =
         SpannerOptions.newBuilder()
             .setSessionPoolOption(sessionPoolOptions)
             .build()
             .getService();
 final DatabaseClient client = spanner.getDatabaseClient(databaseId);

Per ricevere solo i log degli avvisi, utilizza setWarnIfInactiveTransactions.

 final SessionPoolOptions sessionPoolOptions = SessionPoolOptions.newBuilder().setWarnIfInactiveTransactions().build()

 final Spanner spanner =
         SpannerOptions.newBuilder()
             .setSessionPoolOption(sessionPoolOptions)
             .build()
             .getService();
 final DatabaseClient client = spanner.getDatabaseClient(databaseId);

Go

Per ricevere i log degli avvisi e rimuovere le transazioni inattive, utilizza SessionPoolConfig con InactiveTransactionRemovalOptions.

 client, err := spanner.NewClientWithConfig(
     ctx, database, spanner.ClientConfig{SessionPoolConfig: spanner.SessionPoolConfig{
         InactiveTransactionRemovalOptions: spanner.InactiveTransactionRemovalOptions{
         ActionOnInactiveTransaction: spanner.WarnAndClose,
         }
     }},
 )
 if err != nil {
     return err
 }
 defer client.Close()

Per ricevere solo i log degli avvisi, utilizza customLogger.

 customLogger := log.New(os.Stdout, "spanner-client: ", log.Lshortfile)
 // Create a logger instance using the golang log package
 cfg := spanner.ClientConfig{
         Logger: customLogger,
     }
 client, err := spanner.NewClientWithConfig(ctx, db, cfg)

Sessioni multiplex

Le sessioni multiplex ti consentono di creare un numero illimitato di richieste simultanee contemporaneamente. Le sessioni multiplex presentano i seguenti vantaggi:

  • Requisiti delle risorse di backend ridotti. Ad esempio, evitano le attività di manutenzione della sessione associate alla manutenzione della proprietà della sessione e alla garbage collection.
  • Sessione di lunga durata che non richiede richieste keep-alive in caso di inattività.
  • Le librerie client tipiche possono utilizzare una sessione multiplex per ogni client. Il numero di sessioni regolari in uso è inferiore per i client che utilizzano sessioni multiplex per alcune operazioni rispetto ai client che utilizzano solo sessioni regolari.
  • Non è necessario avere affinità con un solo canale gRPC. I client possono inviare richieste su più canali per la stessa sessione multiplex.

Le sessioni multiplex supportano quanto segue:

  • Libreria client Java
  • gli strumenti dell'ecosistema Spanner che dipendono dalla libreria client Java, come PGAdapter, JDBC
  • API Spanner per le transazioni di sola lettura

Puoi utilizzare le metriche OpenTelemetry per vedere come il traffico viene suddiviso tra il pool di sessioni esistente e la sessione multiplex. OpenTelemetry ha un filtro per le metriche, is_multiplexed, che mostra le sessioni multiplex se impostato su true.

Le sessioni multiplex sono abilitate per impostazione predefinita per gli adattatori JDBC e PG. Per le librerie client Java, questa funzionalità è disattivata per impostazione predefinita. Per abilitare le sessioni multiplex puoi utilizzare la libreria client Java. Per attivare una sessione multiplex utilizzando Java, consulta Abilitare le sessioni multiplex.

Attiva sessioni multiplex

Per attivare le sessioni multiplex utilizzando il client Java, imposta GOOGLE_CLOUD_SPANNER_MULTIPLEXED_SESSIONS su true.

export GOOGLE_CLOUD_SPANNER_MULTIPLEXED_SESSIONS=TRUE

Visualizza il traffico per sessioni standard e multiplex

Opentelemetry ha il filtro is_multiplexed per mostrare il traffico per le sessioni multiplex. Imposti questo filtro su true to view multiplexed sessions andfalse per visualizzare le sessioni regolari.

  1. Configura Opentelemetry per Spanner utilizzando le procedure nella sezione Prima di iniziare di Spanner.
  2. Vai a Esplora metriche.

    Vai a Esplora metriche

  3. Nel menu a discesa Metrica, filtra in base a generic.

  4. Fai clic su Generic Task (Attività generica) e vai a Spanner > Spanner/num_acquired_sessions.

  5. Nel campo Filtro, seleziona una delle seguenti opzioni:

    a. is_multiplexed = false per visualizzare sessioni regolari. b. is_multiplexed = true per visualizzare le sessioni multiplex.

    L'immagine seguente mostra l'opzione Filtra con le sessioni multiplex selezionate.

Per saperne di più sull'uso di OpenTelemetry con Spanner, consulta Sfruttare OpenTelemetry per democratizzare Spanner Observability ed Esaminare la latenza in un componente Spanner con OpenTelemetry.

Apri la dashboard di telemetria che mostra il filtro è multiplex.