データベース
PR

Aurora の Blue/Green 切替後に –read-only option エラーが続く

saratogax
記事内に商品プロモーションを含む場合があります

Aurora の Blue/Green デプロイで切り替えたあと、次のエラーが止まらなくなったことはないでしょうか。

SQLTransientConnectionException: (conn=10383)
The MySQL server is running with the --read-only option so it cannot execute this statement

切り替え中に RDS が出したイベントでは、書き込みのダウンタイムは約 4 秒と報告されていました。

ところが私の検証では、アプリは待っていても復旧せず、Pod を再起動するまでこのエラーを返し続けました。

本記事では、実測したログからその原因と、AWS Advanced JDBC Driver で解消した結果を紹介します。

切り替え後も更新系だけが失敗し続けた

検証環境で Aurora MySQL の Blue/Green 切り替えを実行し、0.5 秒間隔で API を叩き続けて様子を見ました。

アプリは Java(Spring)で、コネクションプールは HikariCP、ドライバは MariaDB Connector/J です。

項目結果
RDS イベントの書き込みダウンタイム約 4 秒
更新系の API失敗し続けた
参照系の APIほぼ影響なし
自動復旧しない(3 分以上兆候なし)
復旧方法Pod 再起動
※表は横スクロールできます

参照系がほぼ影響を受けていない点が手がかりになります。

DB に繋がらないのであれば、参照系も同じように失敗するはずです。

切り替え前の writer へのコネクションが残り続けていた

記録されたエラー 306 件の内訳を見ると、原因がはっきりします。

  • 306 件中 304 件が --read-only option による INSERT 拒否
  • そのうち大半が同一のコネクション conn=10383
  • Connection is not available(プール枯渇)はわずか 2 件

つまり「プールが枯渇して接続できない」のではありません。

接続はできていて、その接続先が読み取り専用になっていたのです。

Blue/Green の切り替えでは、旧クラスタが -old1 のような名前にリネームされて残り、読み取り専用の状態に固定されます。

これは AWS の公式ドキュメントにも明記されている仕様です。

切り替え前に writer だったインスタンスが reader に変わったにもかかわらず、アプリはそのコネクションを掴んだまま INSERT を投げ続けていました。

HikariCP が古いコネクションを破棄しない理由

ではなぜ、コネクションプールは役に立たなくなったコネクションを捨てないのでしょうか。

理由は単純で、プールから見るとそのコネクションは「正常」だからです。

HikariCP がコネクションを破棄するのは、それが切断されたと判断したときです。

しかし今回の実測では、切り替えをまたいで conn=10383 という同じコネクション ID が使われ続けていました。

さらに厄介なのが生存確認の仕組みです。

多くの設定で使われる connection-init-sql: SELECT 1 は、読み取り専用のインスタンスに対しても成功します。

SELECT 1 は参照系なので、read-only だろうと問題なく通ってしまうのです。

つまり生存確認をすり抜け、実際に INSERT を投げた瞬間にだけエラーになります。

これが自動復旧しない理由であり、プール設定の調整では回避できない理由でもあります。

maxLifetime を短くすれば、いずれコネクションは作り直されます。

ただしそれは「寿命が来るまで待つ」だけで、切り替えを検知しているわけではありません。

公式ドキュメントの記述と食い違う点

ここで、私の観察と AWS のドキュメントが食い違っている点を正直に書いておきます。

先ほどの公式ドキュメントの Switchover actions には、切り替え時の動作としてこう書かれています。

「両環境の DB インスタンスへのコネクションを切断し、新規の接続を許可しない」(Drops connections to the DB instances in both environments and doesn’t allow new connections.)

ドキュメントどおりなら、コネクションは切断されるはずです。

切断されれば HikariCP はそれを検知して破棄し、張り直すので、今回のような固着は起きません。

しかし実測では、切り替えをまたいで同じ conn=10383 が read-only エラーを出し続けていました。

切断と再接続が起きていれば、コネクション ID は新しいものに変わるはずです。

以前は同じ構成でも自力で復旧していた

実はこのシステムでは、以前にも同じ構成で Blue/Green の切り替えを行ったことがあります。

そのときのダウンタイムは 1 分未満で、アプリは Pod を再起動しなくても自力で復旧していました。

ところが今年に入ってから同じ手順で切り替えたところ、既存の Pod が古い接続先に書き込み続けるようになりました。

アプリの構成は変えていないので、変わったのは AWS 側だと考えられます。

そこで気になるのが、2026 年 1 月 20 日に AWS が発表した Blue/Green の切り替えの高速化です。

この発表では、単一リージョン構成のダウンタイムが「通常 5 秒以下」になったとされています。

時期が重なることから、高速化によって切り替え時のコネクションの扱いが変わり、既存のコネクションが切断されなくなったのではないかと私は考えています。

ただしこれは挙動の変化と時期から立てた仮説で、AWS がコネクションの扱いを変えたと明言している資料は見つけられていません。

ここでお伝えしたいのは原因の断定ではなく、ドキュメントどおりなら起きないはずの事象が実際に起きたという点です。

後述の手順で再現できるので、気になる方はご自身の環境で確かめてみてください。

既存の Pod は自力では復旧しない

興味深いのは、エラーが続いている間も一部のリクエストは成功していたことです。

成否を分けていたのは Pod がいつ起動したかでした。

Pod の起動タイミング経過時間結果
切り替え前から稼働4h19m500 を返し続けた
切り替え後に起動2m47s201 を返した
※表は横スクロールできます

切り替え後に起動した Pod は、当然ながら新しいクラスタに接続するので正常に動きます。

一方で切り替え前から動いていた Pod は、古いコネクションを抱えたままエラーを返し続けました。

kubectl rollout restart で全 Pod を入れ替えたところ、ようやく解消しました。

裏を返せば、切り替え直後にアクセスが少なくスケールアウトが起きない時間帯ほど、この問題は長引きます。

AWS Advanced JDBC Driver では自動復旧した

同じ条件で、接続に AWS Advanced JDBC Driver(JDBC Wrapper)を使って再検証しました。

結果は明確でした。

指標MariaDB Connector/JAWS Advanced JDBC Driver
自力での復旧しないする
アプリが復旧するまで―(復旧しない)31 秒
復旧手段Pod 再起動が必要不要
記録されたエラー–read-only optionConnection is closed
※表は横スクロールできます

RDS のイベントが報告した約 4 秒と比べるべきなのは、この 31 秒のほうです。

接続先がきちんと切り替わる構成になって、初めて「アプリから見た断の時間」が測れるようになります。

DB 側の断が約 4 秒でも、アプリが新しい writer で書き込めるようになるまでには 31 秒かかりました。

メンテナンスの影響を見積もるときは、RDS のイベントの値ではなくアプリ側で計測した値を使ったほうが安全です。

注目すべきは、Wrapper では --read-only エラーが 1 件も出なかったことです。

代わりに記録されていたのは Connection is closed でした。

つまりドライバが切り替えを検知し、古いコネクションを自分で破棄しているということです。

破棄されれば HikariCP は新しいコネクションを張り直すので、結果として自動復旧します。

AWS 自身も公式ブログでこの構成を案内しており、ドライバ側が Blue/Green の状態を監視して接続先を切り替える仕組みが解説されています。

2 つのドライバの違いは復旧までの時間の長短ではなく、自力で復旧するかどうかです。

人が気付いて Pod を再起動しないと戻らない状態は、深夜のメンテナンスでは特に重くのしかかります。

この現象を再現する手順

同じことを自分の環境で確かめたい場合、ちょっとしたコツがあります。

エンジンバージョンを変えずに Blue/Green を作るのがポイントです。

たとえば 3.10.5 から 3.10.5 へ、という形で作成します。

こうするとアップグレード処理が挟まらないため、切り替えの動作だけを切り出して観察できます。

そのうえで、更新系の API を 0.5 秒間隔で叩き続けた状態で切り替えを実行します。

このとき curl には必ずタイムアウトを付けてください。

curl --connect-timeout 5 --max-time 30 ...

付けないと断の瞬間にハングしてしまい、肝心の記録が飛びます。

切り替えの正確な時刻と書き込みダウンタイムは、RDS のイベントで確認できます。

aws rds describe-events --region ap-northeast-1 \
  --source-identifier your-cluster-name --source-type db-cluster \
  --duration 60 --query 'Events[].{Time:Date,Message:Message}' --output table

なお macOS の date はミリ秒に対応していないため、記録には gdate +%H:%M:%S.%3N を使うと細かく追えます。

まとめ

Aurora の Blue/Green 切り替えで --read-only option エラーが続く現象について整理します。

  • 根本の問題は、切り替え前の writer へのコネクションが切り替わらないまま残ること
  • 旧クラスタは削除されず読み取り専用で残るため、そこを掴んだままだと更新系だけが落ちる
  • SELECT 1 は read-only でも成功するので生存確認をすり抜ける
  • 既存 Pod は自力で復旧しない。Pod 再起動が必要
  • AWS Advanced JDBC Driver では古いコネクションが破棄され、31 秒で自動復旧した
  • RDS のイベントが報告する書き込みダウンタイム(約 4 秒)と比べるべきはこの 31 秒。影響の見積もりにはアプリ側の計測値を使う

公式ドキュメントには「コネクションを切断する」と書かれているにもかかわらず、実測では同じコネクションが生き残っていました。

以前は同じ構成で問題なかったことから、2026 年 1 月の高速化で挙動が変わったのではないかと考えていますが、裏付けは取れていません。

いずれにしても、以前うまくいった構成だから、ドキュメントに「切断する」とあるから大丈夫、と考えるのは危険だと感じています。

Blue/Green の切り替えを本番で行う前に、一度は検証環境で実際に計測してみることをおすすめします。

ABOUT ME
saratoga
saratoga
フリーランスエンジニア
仕事にも趣味にも IT を駆使するフリーランスエンジニア。技術的な TIPS や日々の生活の中で深堀りしてみたくなったことを備忘録として残していきます。
記事URLをコピーしました