Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

google-cloud-go/spanner のセッション周りの設定についての検討事項の一例 #128

Open
apstndb opened this issue Apr 14, 2020 · 0 comments

Comments

@apstndb
Copy link
Contributor

apstndb commented Apr 14, 2020

#127 を踏まえて spanner/v1.5.0 時点におけるセッション周りの設定の検討事項について書く。なお特に検証はしていないため正しさは保証しない。

SessionPoolConfig

https://pkg.go.dev/cloud.google.com/go/spanner?tab=doc#SessionPoolConfig

MaxOpened

// MaxOpened is the maximum number of opened sessions allowed by the session
// pool. If the client tries to open a session and there are already
// MaxOpened sessions, it will block until one becomes available or the
// context passed to the client method is canceled or times out.
// Defaults to NumChannels * 100.

そのクライアントがセッションプールに入れることができるセッションの最大数。これ以上のセッション数を要求された場合ブロックする。

基本的にはあまり気にする必要がないが、 gRPC の1チャンネルあたり100セッションが限界であることに注意。

なお公式ドキュメントのセッションの説明には下記のように書かれている。

https://cloud.google.com/spanner/docs/sessions?hl=en#create_and_size_the_session_cache

set the upper bound to an initial test number, such as 100. (For users working with the RPC API, we recommend having the cache store no more than 100 sessions, because 100 is the maximum number of concurrent sessions per gRPC channel.)

デフォルトでは gRPC チャンネル数は4になるため MaxOpened は 400 となるが、1インスタンスで並行トランザクション数 400 というのは App Engine や Cloud Run の max concurrency 80 と比べると十分すぎるほど大きい。

低すぎるとインスタンスのリソースを十分に活用できないが、高すぎるとセッションのオーバーヘッドが上がるケースがあると考えられる。
よって、セッション数に対してスケールする限界値に定めておくと良さそう。
ベンチマーク等によって1インスタンスあたりのセッション数をそれ以上上げてもパフォーマンスが増えず、セッションキャッシュのためのリソースが無駄になることが判明する事があれば、 MaxOpened を増やしてスケールアップするよりもインスタンス数をスケールアウトした方が良いだろう。

MinOpened

// MinOpened is the minimum number of opened sessions that the session pool
// tries to maintain. Session pool won't continue to expire sessions if
// number of opened connections drops below MinOpened. However, if a session
// is found to be broken, it will still be evicted from the session pool,
// therefore it is possible that the number of opened sessions drops below
// MinOpened.
// Defaults to 100.

セッションプールの中のセッションが使われていなくてもこれ以上は減らさない最低数。
spanner/v1.2.0 以降はクライアント作成直後に initPool として BatchCreateSessions RPC を使ってこの数のセッションを必ず確保する。

少なすぎるとコールドスタート直後や一定時間以上リクエストが低下した後のスパイク時に不足しているセッションを CreateSession RPC を発行するための待ち時間が発生する。
多すぎると無駄なセッションを確保したまま解放できなくなる。

特に、CLI ツールやバッチのような並行してトランザクションを実行せずにクライアントの寿命も短いものでは大量のセッションを作成して即解放するような挙動になる場合がある。この場合はデフォルトの100は適していないため、最低限必要な数まで下げると良いだろう。

また、クライアントが Close されないなど何らかの理由で DeleteSession RPC が呼ばれずにセッションリークした場合は1時間はセッションが生き続けることになり、場合によっては Cloud Spanner のサーバサイドのリソースが枯渇するので注意すること。

MaxIdle

// MaxIdle is the maximum number of idle sessions, pool is allowed to keep.
// Defaults to 0.

直近使われたセッション数よりも余裕を持ってセッションプールの idleList に入れておく数。
spanner/v1.1.0 で session pool maintenance に修正が入って以降は
直近10分間で最大の in_use_session + MaxIdle の数のセッション数を維持するため、比較的セッション数が安定しやすくなり、 CreateSession RPC と DeleteSession RPC が繰り返されるケースが減るとのこと。
少なすぎるとスパイク時に CreateSession RPC の待ちが発生する。
多すぎると無駄なセッションによるオーバーヘッドが発生する。

MaxBurst

spanner/v1.6.0 の BatchCreateSessions 導入以降全く意味を持たない。ライブラリの互換性を保つために削除はされていないが、ノイズになるので指定しない。
googleapis/google-cloud-go#4115

v1.6.0 より前の挙動についての記述
// MaxBurst is the maximum number of concurrent session creation requests.
// Defaults to 10.

セッションが足りない時に一度に新しく作成できる最大数。これを超える数が一度に要求された場合はブロックする。
少なすぎるとトランザクション数の変化に十分に反応できない。
多すぎるとセッション数の変化が急になりすぎ、セッション数が安定しなくなったり、 Cloud Spanner の API 側で CreateSession RPC が弾かれるかもしれない。

WriteSessions

// WriteSessions is the fraction of sessions we try to keep prepared for
// write.
// Defaults to 0.2.

デフォルトの 0.2 は SessionPoolConfig を使わない時のみ適用されるため、 SessionPoolConfig を指定した場合のゼロ値は 0.0 となる。

idleList から BeginTransaction RPC を発行済の ReadWriteTransactionidleWriteList に分けてプールしておく割合。
少なすぎると ReadWriteTransaction の中の処理の前に BeginTransaction RPC によるレイテンシが発生する頻度が多くなる。
多すぎると ReadWriteTransaction には使われず、そのまま abort もしくはセッション自体が解放されたり ReadOnlyTransaction に使われたりして単に BeginTransaction RPC しただけ無駄になるセッションが多くなるとは思われるが、10秒間オペレーションがない idle transaction が abort されるのは一度でもオペレーションを行ってからなので、単に BeginTransaction しただけのトランザクションが abort されることは稀である。
仮に pool から取得された ReadWriteTransaction が既に abort されていた場合のペナルティは

  • 初回の RPC がエラーになり 1 round-trip 無駄になる
  • retry backoff (デフォルトでは 20ms) のウェイトが入る
  • BeginTransaction からやり直す
    というものになる。

通常のケースで BeginTransaction 1回分のラウンドトリップが減らせる方がメリットが大きいため、 MinOpened が 5 なら 0.2 以上など、1以上の write session pool があるには設定すると良い。
(1ラウンドトリップに掛かる時間は通常は短いため許容できることも多いが、何らかの理由で RPC のレイテンシが安定しない場合に差が広がることがある。)

なお、データベースを更新する必要が全くない場合に spanner.databaseReader ロールを設定するなどして ReadWriteTransaction として BeginTransaction RPC を発行するパーミッションがない場合にはエラーは無視される ためそのまま動作さえることができるが、 BeginTransaction RPC を発行するだけ無駄なので明示的に WriteSessions を0に設定することが好ましい。

HealthCheckWorkers, HealthCheckInterval

コメントにはデフォルト値でも理論上 300K セッションを捌けると書かれているが、実際には spanner/v1.3.0, spanner/v1.4.0, spanner/v1.5.0 でヘルスチェック周りは大きく変更されており、1時間でセッションタイムアウトする前に約50分間隔で SELECT 1 を発行して keepalive するという公式に推奨される適切な挙動になっているので特に設定を変更するモチベーションはないと考えられる。
なお spanner/v1.1.0 以降のセッション数計算に使われる healthCheckSampleIntervalmaintenanceWindowSizeは export された変数ではないので設定は不可能。

gRPC チャンネル設定

spanner/v1.3.0ClientConfig.NumChannels から option.WithGRPCConnectionPool(numConns) に設定項目が変わり google-cloud-go 独自から gRPC-Go の Balancer API V2 に実装が変わったが、単純なラウンドロビンなのでアルゴリズムはあまり気にする必要がない。
MaxOpened に書いたように、gRPC チャンネル1つあたり100セッションまでしか扱えないため、 ceil(MaxOpened / 100) 以上に設定する。

観測方法

  • interal/trace.TracePrintf を使って OpenCensus Trace attribute に色々な情報を出力しているので、 exporter を設定すると良い。
  • spanner/v1.5.0 で OpenCensus metrics を使ってセッションの利用状況を詳細にモニタリング可能になった。
  • TrackSessionHandles でセッション周りのエラー時にセッション作成時のスタックトレースが出力される。
  • ListSessions でセッションがサイドに使われた時間の確認が可能。 ClientConfig.SessionLabels を設定することでより情報量が増える。

追記

  • spanner/v1.6.0 以降はセッションプールの初期化だけでなく拡張も CreateSession ではなく BatchCreateSessions を使うようになった。これと同時に MaxBurst は使われなくなった。(BatchCreateSessions で一括で作られるセッション数 incStep は export されていないので設定不可能)
  • 利用可能なセッションがない時にはセッションが利用可能になるまでブロックされるが、 spanner/v1.6.0 以降は BatchCreateSessions と他の goroutine によるセッションの解放のどちらか速い方でブロックが終わるようになった。

Refs

@apstndb apstndb changed the title google-cloud-go/spanner のセッション周りの設定について検討事項を記録するスレ google-cloud-go/spanner のセッション周りの設定についての検討事項の一例 Apr 14, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant