[Oracle] DockerにSQL*Plus, SQL*Loaderがつながらない

2020年5月11日月曜日

Oracle

つながらないのだ

Oracle 19cをDockerで動作させて、ネットワーク越しに(というかコンテナ外から)SQL*Plus, SQL*Loaderでつなげようとしたら、つながらない現象が起きた。

SQL*Plus
$ sqlplus user/password@localhost/pdb1

SQL*Plus: Release 19.0.0.0.0 - Production on Sat May 2 20:11:32 2020
Version 19.5.0.0.0

Copyright (c) 1982, 2019, Oracle.  All rights reserved.

ERROR:
ORA-12637: Packet receive failed 

SQL*Loader
$ sqlldr control=tbl1.ctl,userid=user/password@localhost/pdb1

SQL*Loader: Release 19.0.0.0.0 - Production on Sat May 2 19:36:11 2020
Version 19.5.0.0.0

Copyright (c) 1982, 2019, Oracle and/or its affiliates.  All rights reserved.

SQL*Loader-704: Internal error: ulconnect: OCIServerAttach [0]
ORA-12637: Packet receive failed

ちなみにOacleとの接続は普通にポートフォワードで行っている。
$ docker ps
CONTAINER ID        IMAGE                       COMMAND                  CREATED             STATUS                             PORTS                                            NAMES
84ff35f3125e        oracle/database:19.3.0-ee   "/bin/sh -c 'exec $O…"   8 months ago        Up 42 seconds (health: starting)   0.0.0.0:1521->1521/tcp, 0.0.0.0:5500->5500/tcp   ora1930

OOB?

どうも、エラーメッセージからしてTCP/IPレベルでエラーが起きているようだけど、いろいろ調べた結果、クライアント側のsqlnet.oraの設定でつながるようになった。

instantclient_19_5/network/admin/sqlnet.ora
DISABLE_OOB=on
OOBを無効にすればいいらしい。DISABLEをonにするから無効。ややこしい。
で、OOBって何だ?という話なんだけど、Out Of Bandの略で帯域外の通信のことらしい。 特にTCPの場合はURGフラグによる緊急パケットのこと。
URGパケット - 通信用語の基礎知識
実際にはこれがどういうときに使われるかというと、例えば以下のようにSQL*Plusで時間のかかる処理を実行中にCtrl-cで処理を中断したときに、サーバに処理の中断を伝える場合だ。
SQL< exec dbms_session.sleep(10);     
^CBEGIN dbms_session.sleep(10); END;

*
ERROR at line 1:
ORA-01013: user requested cancel of current operation
ORA-06512: at "SYS.DBMS_SESSION", line 435
ORA-06512: at line 1
このときに、OOBが有効だと、URGパケットを使って、優先的に中断要求が伝えられる。
OOBが無効だと、通常のパケットの優先順位で伝えられるため、中断に時間がかかる場合があるみたい。
結局、Docker(のバージョンによっては?)のポートフォワードでは、TCPのOut Of Bandをうまくハンドリングしてくれていないことが原因のようです。