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 Google.

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 vengono eseguite più transazioni, la sessione viene chiamata una sessione multiplexata. Le letture, le scritture e le query autonome utilizzano internamente una transazione.

Vantaggi in termini di prestazioni di un pool di sessioni

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

Le sessioni sono progettate per essere di lunga durata, quindi dopo che una sessione viene utilizzata per un'operazione di database, il client deve restituirla al pool per il riutilizzo.

Panoramica dei canali gRPC

I canali gRPC vengono utilizzati dal client Spanner per la comunicazione. Un canale gRPC è approssimativamente equivalente a una connessione TCP. Un canale gRPC può gestire fino a 100 richieste simultanee. Ciò significa che un'applicazione avrà bisogno di almeno tanti canali gRPC quanto il numero di richieste simultanee che l'applicazione eseguirà, diviso per 100.

Quando crei il client Spanner, questo crea un pool di canali gRPC.

Best practice per l'utilizzo delle librerie client Google

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

Configurare 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 adeguati nella maggior parte dei casi. Di seguito sono riportate le sessioni minime e massime 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

Vai

MinSessions: 100
MaxSessions: 400
NumChannels: 4

Java

MinSessions: 100
MaxSessions: 400
NumChannels: 4

Node.js

Il client Node.js non supporta più canali gRPC. Pertanto, è consigliabile creare più client anziché aumentare le dimensioni del pool di sessioni oltre 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. Pertanto, è consigliabile creare più client anziché aumentare le dimensioni del pool di sessioni oltre 100 sessioni per un singolo client.

MinSessions: 10
MaxSessions: 100

Il numero di sessioni utilizzate dall'applicazione è uguale al numero di transazioni simultanee eseguite dall'applicazione. Devi modificare le impostazioni predefinite del pool di sessioni solo se prevedi che una singola istanza dell'applicazione esegua più transazioni simultanee di quelle che il pool di sessioni predefinito può gestire.

Per le applicazioni con elevata simultaneità, è consigliabile:

  1. Imposta MinSessions sul numero previsto di transazioni simultanee che un singolo client eseguirà.
  2. Imposta MaxSessions sul numero massimo di transazioni simultanee che un singolo client può eseguire.
  3. Imposta MinSessions=MaxSessions se la simultaneità prevista non cambia molto durante il ciclo di vita dell'applicazione. In questo modo, il pool di sessioni non viene scalato verso l'alto o verso il basso. La scalabilità del pool di sessioni verso l'alto o verso il basso consuma anche alcune risorse.
  4. Imposta NumChannels su MaxSessions / 100. Un canale gRPC può gestire fino a 100 richieste simultaneamente. Aumenta questo valore se osservi una latenza di coda elevata (latenza p95/p99), perché potrebbe indicare una 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 oltre le esigenze effettive dell'applicazione potrebbe ridurre le prestazioni del sistema.

Aumentare il pool di sessioni anziché il numero di client

Le dimensioni del pool di sessioni per un'applicazione determinano il numero di transazioni simultanee che una singola istanza dell'applicazione può eseguire. Non è consigliabile aumentare le dimensioni del pool di sessioni oltre la simultaneità massima che una singola istanza dell'applicazione può gestire. Se l'applicazione riceve un picco 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 utilizzati dalla libreria client è uguale al numero massimo di query simultanee eseguite dall'applicazione. Questi thread si aggiungono a 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 dell'applicazione può gestire. Aumenta invece il numero di istanze dell'applicazione.

Gestire la frazione di sessioni di scrittura

Per alcune librerie client, Spanner riserva una parte delle sessioni per le transazioni di lettura-scrittura, chiamata frazione di sessioni di scrittura. Se la tua app utilizza tutte le sessioni di lettura, Spanner utilizza le sessioni di lettura-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 il seguente 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 esistono sessioni di sola lettura o di sola lettura-scrittura.

C#

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

Vai

Tutte le sessioni Go sono uguali. Non esistono sessioni di sola lettura o di sola lettura-scrittura.

Java

Tutte le sessioni Java sono uguali. Non esistono sessioni di sola lettura o di sola lettura-scrittura.

Node.js

Tutte le sessioni Node.js sono uguali. Non esistono sessioni di sola lettura o di sola lettura-scrittura.

PHP

Tutte le sessioni PHP sono uguali. Non esistono sessioni di sola 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 di sessioni di scrittura predefinita per Ruby è 0,3. Puoi modificare la frazione utilizzando il client metodo di inizializzazione.

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 delle sessioni in una libreria client per Spanner o per l'utilizzo delle sessioni con le API REST o RPC.

Queste best practice si applicano solo se stai sviluppando una libreria client o se utilizzi le API REST/RPC. Se utilizzi una delle librerie client Google per Spanner, consulta Best practice per l'utilizzo delle librerie client Google.

Creare e dimensionare 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 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 utilizza risorse aggiuntive nel servizio di database Spanner, quindi la mancata pulizia delle sessioni inutilizzate può ridurre le prestazioni. Per gli utenti che lavorano con 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 client può eliminare una sessione.
  • Il servizio di database Spanner può eliminare una sessione quando la sessione è inattiva per più di 1 ora.
  • Il servizio di database Spanner può eliminare una sessione se ha più di 28 giorni.

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

Mantenere attiva una sessione inattiva

Il servizio di database Spanner si riserva il diritto di eliminare una sessione inutilizzata. Se devi assolutamente mantenere attiva una sessione inattiva, ad esempio se prevedi un aumento significativo dell'utilizzo del database a breve termine, puoi impedire che la sessione venga eliminata. Esegui un'operazione poco costosa, ad esempio la query SQL SELECT 1, per mantenere attiva la sessione. Se hai una sessione inattiva che non è necessaria per l'utilizzo a breve termine, lascia che Spanner la elimini e poi crea una nuova sessione la prossima volta che ne hai bisogno.

Uno scenario per mantenere attive le sessioni è la gestione della domanda di picco regolare sul database. Se l'utilizzo intensivo del database si verifica ogni giorno dalle 9:00 alle 18:00, devi mantenere attive alcune sessioni inattive durante questo periodo, poiché è probabile che siano necessarie per l'utilizzo di picco. Dopo le 18:00, puoi lasciare che Spanner elimini le sessioni inattive. Prima delle 9:00 di ogni giorno, crea alcune nuove sessioni in modo che siano pronte per la domanda prevista.

Un altro scenario è se hai un'applicazione che utilizza Spanner, ma devi evitare l'overhead di connessione quando lo fa. Puoi mantenere attivo un insieme di sessioni per evitare l'overhead di connessione.

Nascondere i dettagli della sessione all'utente della libreria client

Se stai creando una libreria client, non esporre le sessioni al consumatore della libreria client. Consenti al client di effettuare chiamate al database senza la complessità di creare e gestire le sessioni. Per un esempio di libreria client che nasconde i dettagli della sessione al consumatore 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 riproduzione possono 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 è riuscito a comunicare l'esito positivo al client. In questo caso, la mutazione potrebbe essere ritentata, con conseguente errore ALREADY_EXISTS.

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

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

Mantenere connessioni stabili

Per ottenere prestazioni ottimali, la connessione utilizzata per ospitare una sessione deve rimanere stabile. Quando la connessione che ospita una sessione cambia, Spanner potrebbe interrompere la transazione attiva sulla sessione e causare un piccolo carico aggiuntivo sul database durante l'aggiornamento dei metadati della sessione. È normale che alcune connessioni cambino sporadicamente, ma devi evitare situazioni che modifichino un numero elevato di connessioni contemporaneamente. Se utilizzi un proxy tra il client e Spanner, devi mantenere la stabilità della connessione per ogni sessione.

Monitorare le sessioni attive

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

ListSessions ti consente di visualizzare i metadati delle sessioni attive, inclusi la data di creazione e l'ultimo utilizzo di una sessione. L'analisi di questi dati ti indirizzerà nella direzione giusta durante la risoluzione dei problemi relativi alle sessioni. Se la maggior parte delle sessioni attive non ha un valore recente per approximate_last_use_time, ciò potrebbe indicare che le sessioni non vengono riutilizzate correttamente dall'applicazione. Per ulteriori informazioni sul campo approximate_last_use_time, consulta il riferimento dell'API RPC .

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

Pulizia automatica delle perdite di sessioni

Quando utilizzi tutte le sessioni nel pool di sessioni, ogni nuova transazione attende che una sessione venga restituita al pool. Quando le sessioni vengono create ma non restituite al pool di sessioni per il riutilizzo, si parla di perdita di sessioni. Quando si verifica una perdita di sessioni, le transazioni in attesa di una sessione aperta rimangono bloccate a tempo indeterminato e bloccano l'applicazione. Le perdite di sessioni sono spesso causate da transazioni problematiche che vengono eseguite per un periodo di tempo estremamente lungo e non vengono eseguite.

Puoi configurare il pool di sessioni in modo che risolva automaticamente queste transazioni inattive. Quando abiliti la libreria client per risolvere automaticamente la transizione inattiva, questa identifica le transazioni problematiche che potrebbero causare una perdita di sessioni, le rimuove dal pool di sessioni e le sostituisce con una nuova sessione.

La registrazione può anche aiutare a identificare queste transazioni problematiche. Se la registrazione è abilitata, i log di avviso vengono condivisi per impostazione predefinita quando viene utilizzato più del 95% del pool di sessioni. Se l'utilizzo delle sessioni è superiore al 95%, devi aumentare il numero massimo di sessioni consentite nel pool di sessioni oppure potrebbe esserci una perdita di sessioni. I log di avviso contengono stack trace delle transazioni che vengono eseguite più a lungo del previsto e possono aiutare a identificare la causa dell'elevato utilizzo del pool di sessioni. I log di avviso vengono inviati in base alla configurazione dell'esportatore di log.

Abilitare la libreria client per risolvere automaticamente le transazioni inattive

Puoi abilitare la libreria client per inviare log di avviso e risolvere automaticamente le transazioni inattive oppure abilitare la libreria client per ricevere solo log di avviso.

Java

Per ricevere log di avviso e rimuovere le transazioni inattive, 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 log di avviso, utilizza setWarnIfInactiveTransactions.

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

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

Vai

Per ricevere log di avviso 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 log di avviso, 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 multiplexate

Le sessioni multiplexate consentono di creare un numero elevato di richieste simultanee su una singola sessione. Una sessione multiplexata è un identificatore che utilizzi su più canali gRPC. Non introduce colli di bottiglia aggiuntivi. Le sessioni multiplexate presentano i seguenti vantaggi:

  • Riduzione del consumo di risorse di backend grazie a un protocollo di gestione delle sessioni più semplice. Ad esempio, evitano le attività di manutenzione delle sessioni associate alla manutenzione della proprietà delle sessioni e alla garbage collection.
  • Sessione di lunga durata che non richiede richieste keep-alive quando è inattiva.

Le sessioni multiplexate sono supportate in:

  • Le librerie client C++, Go, Java, Node.js, PHP, Python e Ruby client.
  • Strumenti dell'ecosistema Spanner che dipendono dalle librerie client menzionate, come PGAdapter, JDBC, Hibernate, driver database/sql, driver dbAPI e GORM.

  • Strumenti dell'ecosistema Spanner che dipendono dalle librerie client Java e Go, come PGAdapter, JDBC, Hibernate, driver database o sql e GORM. Puoi utilizzare le metriche OpenTelemetry per vedere come il traffico viene suddiviso tra il pool di sessioni esistente e la sessione multiplexata. OpenTelemetry ha un filtro di metriche, is_multiplexed, che mostra le sessioni multiplexate quando è impostato su true.

Le sessioni multiplexate sono supportate per tutti i tipi di transazioni.

Le librerie client ruotano le sessioni multiplexate ogni 7 giorni per evitare di inviare transazioni su sessioni obsolete.

Le sessioni multiplexate sono abilitate per impostazione predefinita in alcune librerie client. Per altre, devi utilizzare le variabili di ambiente per abilitarle. Per maggiori dettagli, vedi Abilitare le sessioni multiplexate.

Considerazioni

Se stai tentando di eseguire il commit di un corpo di transazione di lettura o scrittura vuoto o di una transazione in cui ogni query o istruzione DML non è riuscita, devi considerare alcuni scenari con le sessioni multiplexate. Le sessioni multiplexate richiedono l'inclusione di un token di pre-commit generato dal server in ogni richiesta di commit. Per le transazioni che contengono query o DML, deve essere presente almeno una query o una transazione DML precedente riuscita affinché il server restituisca un token valido alla libreria client. Se non sono state eseguite query o transazioni DML riuscite, la libreria client aggiunge implicitamente SELECT 1 prima di un commit.

Per una transazione di lettura o scrittura su una sessione multiplexata che contiene solo mutazioni, se una delle mutazioni riguarda una tabella o una colonna che NON esiste nello schema, il client potrebbe restituire un errore INVALID_ARGUMENT anziché un errore NOT_FOUND.

Abilitare le sessioni multiplexate

Le sessioni multiplexate sono abilitate per impostazione predefinita nelle seguenti librerie client:

  • C++ nella versione 2.41.0 e successive.
  • Vai nella versione 1.85.0 e successive.
  • Java nella versione 6.98.0 e successive.
  • Node.js nella versione 8.3.0 e successive.
  • PHP nella versione 2.0.0 e successive.
  • Python nella versione 3.57.0 e successive.
  • Ruby nella versione 2.30.0 e successive.

Per utilizzare le sessioni multiplexate nelle versioni precedenti delle librerie client Node.js, Java e Go, devi prima impostare una variabile di ambiente per abilitarle.

Per abilitare le sessioni multiplexate, imposta la variabile di ambiente GOOGLE_CLOUD_SPANNER_MULTIPLEXED_SESSIONS su TRUE. Questo flag abilita anche il supporto delle sessioni multiplexate per le transazioni ReadOnly.

export GOOGLE_CLOUD_SPANNER_MULTIPLEXED_SESSIONS=TRUE

Per abilitare il supporto delle operazioni partizionate per le sessioni multiplexate, imposta la variabile di ambiente GOOGLE_CLOUD_SPANNER_MULTIPLEXED_SESSIONS_PARTITIONED_OPS su TRUE.

export GOOGLE_CLOUD_SPANNER_MULTIPLEXED_SESSIONS_PARTITIONED_OPS=TRUE

Per abilitare il supporto delle transazioni di lettura-scrittura per le sessioni multiplexate, imposta la variabile di ambiente GOOGLE_CLOUD_SPANNER_MULTIPLEXED_SESSIONS_FOR_RW su TRUE.

export GOOGLE_CLOUD_SPANNER_MULTIPLEXED_SESSIONS_FOR_RW=True

Devi impostare GOOGLE_CLOUD_SPANNER_MULTIPLEXED_SESSIONS su TRUE come prerequisito per supportare una transazione su una sessione multiplexata.

Visualizzare il traffico per le sessioni regolari e multiplexate

OpenTelemetry ha il filtro is_multiplexed per mostrare il traffico per le sessioni multiplexate. Imposta questo filtro su true to view multiplexed sessions andfalse` per visualizzare le sessioni regolari.

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

    Vai a Esplora metriche

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

  4. Fai clic su 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 le sessioni regolari. b. is_multiplexed = true per visualizzare le sessioni multiplexate.

    L'immagine seguente mostra l'opzione Filtro con le sessioni multiplexate selezionate.

Per ulteriori informazioni sull'utilizzo di OpenTelemetry con Spanner, consulta Utilizzare OpenTelemetry per democratizzare l'osservabilità di Spanner ed Esaminare la latenza in un componente Spanner con OpenTelemetry.

Dashboard di OpenTelemetry che mostra il filtro is-multiplexed.

Risoluzione dei problemi

Gli errori comuni relativi alle sessioni che la tua applicazione potrebbe riscontrare includono:

  • Session not found
  • RESOURCE_EXHAUSTED

Per saperne di più, consulta Errori di sessione.