おらくるのいる生活

OracleのDBAとしての、障害対応やらパフォーマンス・チューニングやらの日々を綴っています

JDBCで接続エラーが発生する事がある

数年前の事になりますが、OLTP処理で接続エラーが発生するとの連絡がありました。
リリースして数年たった本番環境で、構成は以下の通りです。

Oracle RAC(2ノード) EE 11.2.0.2
HP-UX

今まではずっと問題なく接続できており、計画停止でサーバ再起動を行った後から接続エラーが発生するようになったとか。それも必ずエラーになるのではなく、正常に接続できる時もあるとの話です。 

 

まず、初期化パラメータを確認しました。

orcl1.local_listener=LISTENER_LOCAL_ORCL1

orcl2.local_listener=LISTENER_LOCAL_ORCL2

orcl1.remote_listener=LISTENER_LOCAL_ORCL2

orcl2.remote_listener=LISTENER_LOCAL_ORCL1

tnsnames.oraも確認します。

LISTENER_LOCAL_ORCL1 =
  (ADDRESS_LIST =
    (ADDRESS = (PROTOCOL = TCP)(HOST = scan-vip)(PORT = 1521))
    (ADDRESS = (PROTOCOL = TCP)(HOST = node1-vip)(PORT = 1521))
  )

LISTENER_LOCAL_ORCL2 =
  (ADDRESS_LIST =
    (ADDRESS = (PROTOCOL = TCP)(HOST = scan-vip)(PORT = 1521))
    (ADDRESS = (PROTOCOL = TCP)(HOST = node2-vip)(PORT = 1521))
  )

何故かlocal_listenerにSCANリスナーが指定されています・・・

remote_listenerに相手ノードのローカル・リスナーとSCANリスナーを指定するのは正しいですが、local_listenerにSCANリスナーを指定するのは変です。

尚、この環境ではDNSによる名前解決は行っておらず、SCANリスナーは一つのみ起動しています。

local_listenerにSCANリスナーを指定してしまっているので、それが接続エラーの原因ではないかと考えたのですが、それだと今まで何年もずっと問題なく接続できていた理由が説明できません。

そこでAPからの接続文字列を確認しました。JdbcThinドライバ接続です。

jdbc:oracle:thin:@(DESCRIPTION =(ADDRESS = (PROTOCOL = TCP)(HOST = 192.168.0.101)(PORT = 1521))(ADDRESS = (PROTOCOL = TCP)(HOST = 192.168.0.102)(PORT = 1521))(CONNECT_DATA =(SERVICE_NAME = orcl)))

※192.168.0.101はnode1-vip、192.168.0.102はnode2-vipに該当

LOAD_BALANCE = onの指定がありません・・・

 APの担当者はデフォルトでLOAD_BALANCEが有効だと思っていたそうですが、デフォルトではoffです。FAILOVERはデフォルトでonですが、LOAD_BALANCEは明示的にonを指定しなければ、書いてある順番に接続します。

つまりAPからの接続は常にノード1につながり、そこからサーバーサイド・ロードバランス*1によってノード2に振り分けられている事になります。

 サーバーサイド・ロードバランスによって他ノードに振り分けられる時、クライアントは他ノードへの接続情報を受け取って、再度接続する事になりますが、この時、返されるのは他ノードのlocal_listenerに設定された情報です。

今回のケースではnode2-vipまたはscan-vipが返される事になるので、APサーバ側でこれを名前解決できないと接続エラーとなります。

APサーバのhostsファイルを確認すると、node1-vip、node2-vipは設定されているものの、scan-vipの設定はありません。AP側ではSCANリスナーに対する接続を行っていない為です。

SCANリスナーがどちらのノードで起動しているのか確認したところ、ノード2で起動していました。

 

APからの接続はまずノード1に・・・

SCANリスナーはノード2に・・・

 

頭の中で接続経路を辿っている内にエラーの原因がわかりました。

サーバーサイド・ロードバランスでscan-vipが返された時に名前解決ができずにエラーとなるのですが、サーバ再起動前はSCANリスナーがノード1で起動していた(と思われる)ので、今まではscan-vipが返される事が無かったのです。

ケース毎に説明すると以下のようになります。

 

・SCANリスナーがノード1で起動かつ、サーバーサイド・ロードバランスでノード2への再接続が発生するケース。

ノード2ではSCANリスナーが起動していないので、再接続先の情報としてnode2-vipが返され、名前解決によって接続が成功する。

 

・SCANリスナーがノード2で起動かつ、サーバーサイド・ロードバランスでノード2への再接続が発生するケース。

ノード2でSCANリスナーが起動しているので、再接続先の情報としてnode2-vipまたはscan-vipが返され、scan-vipが返された時に名前解決ができずに接続が失敗する。

 

サーバーサイド・ロードバランスで再接続が発生しない時は、いずれも問題ありません。接続が成功したり失敗したり、サーバ再起動前にエラーが発生していなかったのは、これが原因でした。

そもそもAPサーバからの接続でLOAD_BALANCE = onになっていれば、クライアント側の接続時ロードバランスによってノード1、ノード2にランダムに接続されていたのでもっと早くからエラーが発生していたはずですが、LOAD_BALANCE = onの設定が無かった為にずっとノード1に対して接続していたので、数年も経ってから問題が露呈したのです。

SCANリスナーが一つのみの構成では、先にCRSを起動したノードでSCANリスナーが起動します。どちらのノードから起動するという運用上の決まりは無いので、今までは何となくノード1を先に起動していたが、今回はたまたまノード2を先に起動したのでしょう。

 

あるべき論としてはDBの初期化パラメータと、APの接続文字列の両方を修正すべきですが、既に稼働中の本番環境で修正を入れるのは困難なので、APサーバのhostsファイルにscan-vipを追記する対応となりました。

何年も正常稼動しているからと言って、設定が正しいとは限らない・・・という例でした。

*1:負荷状況に応じて接続先を振り分ける機能