cx_OracleからAutonomous Databaseに接続する際の高速化

## 初めに
 本記事は、[Autonomous Database で DRCP（データベース常駐接続プーリング）が使えるようになったので、お試ししてみる。](https://qiita.com/ora_gonsuke777/items/f78c2844f5dc03bf3432)の結果が芳しくなかったことを受け、少しクライアント側の環境を変えて試してみた結果となります。先に結論を書いてしまうと、

- 遅い原因はアプリケーションのロジック(処理の傾向)とDRCPの設定(チューニング)が合っていないため。残念ながらAutonomous環境ではDRCPのチューニングは不可
- DRCPを使用するよりも、cx_Oracleのコネクションプールを使用すると爆速

となります。

## 検証環境
- Oracle Cloud 大阪リージョン
- Computeインスタンス(テストアプリケーション実行環境)：VM.Standard2.1
    - OSイメージ：マーケットプレイスにあるOracle Cloud Developer Imange
- Databaseインスタンス：Autonomous Transaction Database 18c (1OCPU)
- ComputeインスタンスにてPython3を使用してテストアプリケーションを作成、300回接続を実施した時間を計測



## DRCPで思ったほど早くならなかった原因の調査
　まず、DRCPを利用しない検証アプリケーションのソースを提示します。
　接続先の指定にはtnsnames.oraを使用しています。ですので、DRCPの利用はtnsnames.ora内にて指定しています。

```python:test1.py
# -*- coding: utf-8 -*-
import cx_Oracle
import time

USERID = 'admin'
PASSWORD = '要置換'
DESTINATION = 'atp_low'
REPEAT_TIMES = 300
elapsed_times = []

for i in range(REPEAT_TIMES):
        t1 = time.time()
        connection = cx_Oracle.connect(USERID, PASSWORD, DESTINATION)
        t2 = time.time()
        connection.close()
        elapsed_times.append(t2 - t1)
print(f'接続時間合計 : {sum(elapsed_times)}秒')
print(f'接続時間平均 : {sum(elapsed_times) / REPEAT_TIMES}秒')
```

　次いで、DRCPを利用する検証アプリケーションのソースです。

```python:test2.py
# -*- coding: utf-8 -*-
import cx_Oracle
import time

USERID = 'admin'
PASSWORD = '要置換'
DESTINATION = 'atp_low_pooled'
REPEAT_TIMES = 300
elapsed_times = []

for i in range(REPEAT_TIMES):
        t1 = time.time()
        connection = cx_Oracle.connect(USERID, PASSWORD, DESTINATION, cclass='MYCLASS', purity=cx_Oracle.ATTR_PURITY_SELF)
        t2 = time.time()
        connection.close()
        elapsed_times.append(t2 - t1)
print(f'接続時間合計 : {sum(elapsed_times)}秒')
print(f'接続時間平均 : {sum(elapsed_times) / REPEAT_TIMES}秒')
```

　これらのアプリケーションを数回施行した結果、DRCP利用時でおおよそ倍近い性能になりました。元記事はSQL*Plusの起動時間も含んでいるので、元記事よりはよい結果となっていますが、それでも更なる性能が欲しいところです。
　そこで、まずは通信の暗号化がボトルネックになっている可能性を考え、DB Server側をいじれるOracle Database Cloud Service(DBCS)に接続先を切り替え、暗号化の設定を外して試したところ、DRCPなしよりありの方が遅いという結果に。DRCPがない場合だけ高速化しました。つまり、通信の暗号化は、DRCP利用時は影響が軽微ということです。
　ここで元記事作者と相談の上、DRCPのチューニング(MINSIZEの増加)をDBCS上で実施してみました。結果、DBCS環境では、通信を暗号化してもDRCPの接続速度が10倍程度になりました。直接的な原因は、DRCPのチューニング不足、アプリケーションのロジックに合った設定になっていなかったためと判断しました。
　ここで問題なのは、ATPの場合、DRCPの設定を変更することができません。なので、結果的に、ATP(ADWでも同様だと思われます)では、今回の検証アプリケーションだと接続速度をそれなりレベルでしか向上させられない、ということです。
　今回は単体アプリケーションから接続/切断を繰り返す形で実施していますが、1回の接続を実施するアプリケーションをテストツールで複数起動させ、繰り返し実行すると、また違った結果が出ると思われます。

## cx_Oracleのコネクションプール機能を試す
　Python用のOracle Database接続ドライバであるcx_Oracleには、コネクションプールの機能が備わっています。この機能を利用すると、DRCPが存在しない状態であっても接続速度の高速化が期待できます。ということで以下のアプリケーションを試してみたところ、ATPへの接続でも非常に高速(300回接続しても1秒かからず)に稼働しました。Python(cx_Oracle)のような接続ドライバのコネクションプールが利用できる環境であればDRCPは不要そう、という、無慈悲な結論となりました。念のため、該当機能が存在しない環境のために、DRCP自体の存在意義はあると思います。

```python:test3.py
# -*- coding: utf-8 -*-
import cx_Oracle
import time

USERID = 'admin'
PASSWORD = '要置換'
DESTINATION = 'atp_low'
REPEAT_TIMES = 300
elapsed_times = []

pool = cx_Oracle.SessionPool(USERID, PASSWORD, DESTINATION, min=4, max=40, increment=1)

for i in range(REPEAT_TIMES):
        t1 = time.time()
        connection = pool.acquire()
        t2 = time.time()
        pool.release(connection)
        elapsed_times.append(t2 - t1)
pool.close()
print(f'接続時間合計 : {sum(elapsed_times)}秒')
print(f'接続時間平均 : {sum(elapsed_times) / REPEAT_TIMES}秒')

```

## 各パターンの性能比
最後に、本記事で実施したテストの性能比のグラフを提示します。cx_Oracleのコネクションプール利用時のパフォーマンスが良すぎて、嘘っぽい感じになってしまいました。。。
<img width="640" alt="screenshot.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/125837/7f9c57a2-44d4-343e-ac40-0f28d4821b2f.png">