Sesi

Halaman ini menjelaskan konsep lanjutan sesi di Spanner, termasuk praktik terbaik untuk sesi saat membuat library klien, menggunakan REST API atau RPC API, atau menggunakan library klien Google.

Ringkasan Sesi

Sesi mewakili saluran komunikasi dengan layanan database Spanner. Sesi digunakan untuk melakukan transaksi yang membaca, menulis, atau mengubah data dalam database Spanner. Setiap sesi berlaku untuk satu {i>database<i}.

Sesi hanya dapat menjalankan satu transaksi dalam satu waktu. Pembacaan, penulisan, dan kueri mandiri menggunakan transaksi secara internal, dan diperhitungkan dalam batas satu transaksi.

Manfaat performa dari kumpulan sesi

Membuat sesi itu mahal. Untuk menghindari biaya performa setiap kali operasi database dilakukan, klien harus menyimpan kumpulan sesi, yang merupakan kumpulan sesi yang tersedia dan siap digunakan. Kumpulan harus menyimpan sesi yang ada dan menampilkan jenis sesi yang sesuai saat diminta, serta menangani pembersihan sesi yang tidak digunakan. Untuk contoh cara mengimplementasikan kumpulan sesi, lihat kode sumber untuk salah satu library klien Spanner, seperti library klien Go atau library klien Java.

Sesi dimaksudkan untuk berumur panjang, jadi setelah sesi digunakan untuk operasi database, klien harus mengembalikan sesi tersebut ke kumpulan untuk digunakan kembali.

Ringkasan saluran gRPC

Saluran gRPC digunakan oleh klien Spanner untuk komunikasi. Satu saluran gRPC kurang lebih sama dengan koneksi TCP. Satu saluran gRPC dapat menangani hingga 100 permintaan serentak. Artinya, aplikasi akan memerlukan setidaknya saluran gRPC sebanyak jumlah permintaan serentak yang akan dijalankan aplikasi, dibagi 100.

Klien Spanner akan membuat kumpulan saluran gRPC saat dibuat.

Praktik terbaik saat menggunakan library klien Google

Berikut adalah penjelasan praktik terbaik saat menggunakan library klien Google untuk Spanner.

Mengonfigurasi jumlah sesi dan saluran gRPC dalam kumpulan

Library klien memiliki jumlah sesi default dalam kumpulan sesi dan jumlah default saluran gRPC di kumpulan saluran. Kedua default ini sudah memadai untuk sebagian besar kasus. Berikut adalah sesi minimum dan maksimum default serta jumlah default saluran gRPC untuk setiap bahasa pemrograman.

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

Klien Node.js tidak mendukung banyak saluran gRPC. Oleh karena itu, sebaiknya buat beberapa klien, bukan meningkatkan ukuran kumpulan sesi lebih dari 100 sesi untuk satu klien.

MinSessions: 25
MaxSessions: 100

PHP

Klien PHP tidak mendukung jumlah saluran gRPC yang dapat dikonfigurasi.

MinSessions: 1
MaxSessions: 500

Python

Python mendukung empat jenis kumpulan sesi berbeda yang dapat Anda gunakan untuk mengelola sesi.

Ruby

Klien Ruby tidak mendukung beberapa saluran gRPC. Oleh karena itu, sebaiknya buat beberapa klien, bukan meningkatkan ukuran kumpulan sesi lebih dari 100 sesi untuk satu klien.

MinSessions: 10
MaxSessions: 100

Jumlah sesi yang digunakan aplikasi Anda sama dengan jumlah transaksi serentak yang dijalankan aplikasi Anda. Sebaiknya ubah setelan kumpulan sesi default hanya jika Anda mengharapkan satu instance aplikasi menjalankan lebih banyak transaksi serentak daripada yang dapat ditangani oleh kumpulan sesi default.

Untuk aplikasi serentak tinggi, hal berikut direkomendasikan:

  1. Tetapkan MinSessions ke jumlah transaksi serentak yang diharapkan yang akan dijalankan oleh satu klien.
  2. Tetapkan MaxSessions ke jumlah maksimum transaksi serentak yang dapat dijalankan oleh satu klien.
  3. Tetapkan MinSessions=MaxSessions jika konkurensi yang diharapkan tidak banyak berubah selama masa aktif aplikasi. Tindakan ini mencegah kumpulan sesi meningkatkan atau menurunkan skala. Peningkatan atau penurunan skala kumpulan sesi juga menghabiskan beberapa resource.
  4. Tetapkan NumChannels ke MaxSessions / 100. Satu saluran gRPC dapat menangani hingga 100 permintaan secara serentak. Tingkatkan nilai ini jika Anda mengamati latensi tail yang tinggi (latensi p95/p99), karena hal ini mungkin merupakan indikasi kemacetan saluran gRPC.

Peningkatan jumlah sesi aktif akan menggunakan resource tambahan pada layanan database Spanner dan library klien. Meningkatkan jumlah sesi di luar kebutuhan aplikasi yang sebenarnya dapat menurunkan performa sistem Anda.

Meningkatkan kumpulan sesi versus meningkatkan jumlah klien

Ukuran kumpulan sesi untuk sebuah aplikasi menentukan jumlah transaksi serentak yang dapat dijalankan oleh satu instance aplikasi. Sebaiknya jangan meningkatkan ukuran kumpulan sesi melebihi konkurensi maksimum yang dapat ditangani satu instance aplikasi. Jika aplikasi menerima burst permintaan yang melebihi jumlah sesi dalam kumpulan, permintaan akan diantrekan saat menunggu sesi tersedia.

Resource yang digunakan oleh library klien adalah sebagai berikut:

  1. Setiap saluran gRPC menggunakan satu koneksi TCP.
  2. Setiap pemanggilan gRPC memerlukan thread. Jumlah maksimum thread yang digunakan oleh library klien sama dengan jumlah maksimum kueri serentak yang dijalankan aplikasi. Thread ini berada di atas thread apa pun yang digunakan aplikasi untuk logika bisnisnya sendiri.

Sebaiknya jangan meningkatkan ukuran kumpulan sesi melebihi jumlah maksimum thread yang dapat ditangani oleh satu instance aplikasi. Sebagai gantinya, tingkatkan jumlah instance aplikasi.

Mengelola fraksi sesi tulis

Untuk beberapa library klien, Spanner mencadangkan sebagian sesi untuk transaksi baca-tulis, yang disebut fraksi tulis-sesi. Jika aplikasi Anda menggunakan semua sesi baca, Spanner akan menggunakan sesi baca-tulis, bahkan untuk transaksi hanya baca. Sesi baca-tulis memerlukan spanner.databases.beginOrRollbackReadWriteTransaction. Jika pengguna dalam peran IAM spanner.databaseReader, panggilan akan gagal dan Spanner akan menampilkan pesan error ini:

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

Untuk library klien yang mempertahankan fraksi tulis-sesi, Anda dapat menetapkan fraksi tulis-sesi.

C++

Semua sesi C++ sama. Tidak ada sesi hanya baca atau baca-tulis.

C#

Fraksi {i>default write-sessions<i} untuk C# adalah 0,2. Anda dapat mengubah fraksi menggunakan kolom WriteSessionsFraction di SessionPoolOptions.

Go

Semua sesi Go sama. Tidak ada sesi hanya baca atau baca-tulis.

Java

Semua sesi Java sama. Tidak ada sesi hanya baca atau baca-tulis.

Node.js

Semua sesi Node.js sama. Tidak ada sesi hanya baca atau baca-tulis.

PHP

Semua sesi PHP sama. Tidak ada sesi hanya baca-tulis.

Python

Python mendukung empat jenis kumpulan sesi berbeda yang dapat Anda gunakan untuk mengelola sesi baca dan baca-tulis.

Ruby

Fraksi default sesi tulis untuk Ruby adalah 0,3. Anda dapat mengubah fraksi menggunakan metode inisialisasi client.

Praktik terbaik saat membuat library klien atau menggunakan REST/RPC

Berikut ini penjelasan praktik terbaik untuk menerapkan sesi dalam library klien untuk Spanner, atau untuk menggunakan sesi dengan REST atau RPC API.

Praktik terbaik ini hanya berlaku jika Anda mengembangkan library klien, atau jika Anda menggunakan REST/RPC API. Jika Anda menggunakan salah satu library klien Google untuk Spanner, lihat Praktik terbaik saat menggunakan library klien Google.

Membuat dan mengukur kumpulan sesi

Untuk menentukan ukuran kumpulan sesi yang optimal untuk proses klien, tetapkan batas bawah ke jumlah transaksi serentak yang diharapkan, dan tetapkan batas atas ke nomor pengujian awal, seperti 100. Jika batas atas tidak memadai, tingkatkan batasnya. Peningkatan jumlah sesi aktif akan menggunakan resource tambahan pada layanan database Spanner, sehingga kegagalan untuk membersihkan sesi yang tidak digunakan dapat menurunkan performa. Untuk pengguna yang menggunakan RPC API, sebaiknya miliki tidak lebih dari 100 sesi per saluran gRPC.

Menangani sesi yang dihapus

Ada tiga cara untuk menghapus sesi:

  • Klien dapat menghapus sesi.
  • Layanan database Spanner dapat menghapus sesi saat sesi tidak ada aktivitas selama lebih dari 1 jam.
  • Layanan database Spanner dapat menghapus sesi jika sesi tersebut sudah lebih dari 28 hari.

Percobaan untuk menggunakan sesi yang dihapus menyebabkan NOT_FOUND. Jika Anda mengalami error ini, buat dan gunakan sesi baru, tambahkan sesi baru ke kumpulan, dan hapus sesi yang dihapus dari kumpulan.

Mempertahankan sesi tidak ada aktivitas

Layanan database Spanner berhak menghapus sesi yang tidak digunakan. Jika Anda benar-benar perlu mempertahankan sesi tidak ada aktivitas, misalnya, jika diperkirakan adanya peningkatan penggunaan database yang signifikan dalam waktu dekat, Anda dapat mencegah penghapusan sesi tersebut. Lakukan operasi yang murah seperti mengeksekusi kueri SQL SELECT 1 agar sesi tetap aktif. Jika Anda memiliki sesi tidak ada aktivitas yang tidak diperlukan untuk penggunaan jangka pendek, izinkan Spanner menghentikan sesi, lalu buat sesi baru saat sesi diperlukan lagi.

Salah satu skenario untuk menjaga sesi tetap aktif adalah menangani permintaan puncak reguler pada database. Jika penggunaan database yang berat terjadi setiap hari dari pukul 09.00 hingga 18.00, Anda harus mempertahankan beberapa sesi tidak ada aktivitas selama waktu tersebut, karena sesi tersebut kemungkinan diperlukan untuk penggunaan puncak. Setelah pukul 18.00, Anda dapat mengizinkan Spanner membuat sesi tidak ada aktivitas. Sebelum pukul 09.00 setiap hari, buat beberapa sesi baru agar siap untuk permintaan yang diharapkan.

Skenario lainnya adalah jika Anda memiliki aplikasi yang menggunakan Spanner, tetapi harus menghindari overhead koneksi ketika menggunakannya. Anda dapat mempertahankan sekumpulan sesi tetap aktif untuk menghindari overhead koneksi.

Sembunyikan detail sesi dari pengguna library klien

Jika Anda membuat library klien, jangan tampilkan sesi ke konsumen library klien. Berikan kemampuan bagi klien untuk melakukan panggilan database tanpa kerumitan saat membuat dan mengelola sesi. Untuk contoh library klien yang menyembunyikan detail sesi dari konsumen library klien, lihat library klien Spanner untuk Java.

Menangani error untuk transaksi tulis yang tidak idempoten

Transaksi operasi tulis tanpa perlindungan replay dapat menerapkan mutasi lebih dari sekali. Jika mutasi tidak idempoten, mutasi yang diterapkan lebih dari satu kali dapat mengakibatkan kegagalan. Misalnya, penyisipan mungkin gagal dengan ALREADY_EXISTS meskipun baris tidak ada sebelum upaya tulis. Hal ini dapat terjadi jika server backend melakukan mutasi, tetapi tidak dapat menyampaikan keberhasilan kepada klien. Dalam peristiwa tersebut, mutasi dapat dicoba lagi, sehingga menghasilkan kegagalan ALREADY_EXISTS.

Berikut beberapa cara untuk mengatasi skenario ini saat Anda menerapkan library klien Anda sendiri atau menggunakan REST API:

  • Susun penulisan Anda agar bersifat idempoten.
  • Menggunakan operasi tulis dengan perlindungan replay.
  • Mengimplementasikan metode yang menjalankan logika "upsert": masukkan jika baru atau update jika ada.
  • Tangani error atas nama klien.

Mempertahankan koneksi yang stabil

Untuk performa terbaik, koneksi yang Anda gunakan untuk menghosting sesi harus tetap stabil. Saat koneksi yang menghosting sesi berubah, Spanner dapat membatalkan transaksi aktif di sesi tersebut dan menyebabkan sedikit beban tambahan pada database Anda saat memperbarui metadata sesi. Tidak masalah jika ada beberapa koneksi yang berubah secara sporadis, tetapi Anda harus menghindari situasi yang dapat mengubah sejumlah besar koneksi secara bersamaan. Jika menggunakan proxy antara klien dan Spanner, Anda harus menjaga stabilitas koneksi untuk setiap sesi.

Memantau sesi aktif

Anda dapat menggunakan perintah ListSessions untuk memantau sesi aktif dalam database dari command line, REST API, atau RPC API. ListSessions menampilkan sesi aktif untuk database tertentu. Ini berguna jika Anda perlu menemukan penyebab kebocoran sesi. (Kebocoran sesi adalah insiden saat sesi sedang dibuat, tetapi tidak dikembalikan ke kumpulan sesi untuk digunakan kembali.)

ListSessions memungkinkan Anda melihat metadata tentang sesi aktif, termasuk kapan sesi dibuat dan kapan sesi terakhir digunakan. Analisis data ini akan mengarahkan Anda ke cara yang tepat saat memecahkan masalah sesi. Jika sebagian besar sesi aktif tidak memiliki approximate_last_use_time terbaru, hal ini dapat menunjukkan bahwa sesi tidak digunakan kembali dengan benar oleh aplikasi Anda. Lihat referensi RPC API untuk mengetahui informasi selengkapnya tentang kolom approximate_last_use_time.

Lihat referensi REST API, referensi RPC API, atau referensi alat command line gcloud untuk mengetahui informasi selengkapnya tentang penggunaan ListSessions.

Pembersihan otomatis kebocoran sesi

Saat Anda menggunakan semua sesi dalam kumpulan sesi, setiap transaksi baru akan menunggu hingga sebuah sesi dikembalikan ke kumpulan sesi. Jika sesi dibuat tetapi tidak ditampilkan ke kumpulan sesi untuk digunakan kembali, hal ini disebut kebocoran sesi. Ketika ada kebocoran sesi, transaksi yang menunggu sesi terbuka terhenti tanpa batas dan memblokir aplikasi. Kebocoran sesi sering kali disebabkan oleh transaksi bermasalah yang berjalan untuk waktu yang sangat lama dan tidak di-commit.

Anda dapat menyiapkan kumpulan sesi untuk otomatis menyelesaikan transaksi yang tidak aktif ini. Jika Anda mengaktifkan library klien untuk otomatis menyelesaikan transisi tidak aktif, library ini akan mengidentifikasi transaksi bermasalah yang dapat menyebabkan kebocoran sesi, menghapusnya dari kumpulan sesi, dan menggantinya dengan sesi baru.

Pencatatan log juga dapat membantu mengidentifikasi transaksi bermasalah tersebut. Jika logging diaktifkan, log peringatan akan dibagikan secara default saat lebih dari 95% kumpulan sesi Anda digunakan. Jika penggunaan sesi lebih besar dari 95%, Anda perlu meningkatkan sesi maksimal yang diizinkan dalam kumpulan sesi, atau Anda mungkin mengalami kebocoran sesi. Log peringatan berisi pelacakan tumpukan transaksi yang berjalan lebih lama dari yang diharapkan dan dapat membantu mengidentifikasi penyebab penggunaan kumpulan sesi yang tinggi. Log peringatan dikirim bergantung pada konfigurasi pengekspor log Anda.

Aktifkan library klien untuk otomatis menyelesaikan transaksi yang tidak aktif

Anda dapat mengaktifkan library klien untuk mengirim log peringatan dan otomatis menyelesaikan transaksi tidak aktif, atau mengaktifkan library klien untuk hanya menerima log peringatan.

Java

Untuk menerima log peringatan dan menghapus transaksi yang tidak aktif, gunakan setWarnAndCloseIfInactiveTransactions.

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

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

Untuk hanya menerima log peringatan, gunakan setWarnIfInactiveTransactions.

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

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

Go

Untuk menerima log peringatan dan menghapus transaksi yang tidak aktif, gunakan SessionPoolConfig dengan 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()

Untuk hanya menerima log peringatan, gunakan 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)