[OCI]Autonomous Database：毎日決まった時間に行うスケールアップ/スケールダウンをAutonomous Database単体で実現する（改良版）

# はじめに

Autonomous Databaseを使用するにあたって、アクセスの多い日中はCPUをスケールアップし、アクセスの少ない夜間はCPUをスケールダウンしたい、そしてそれを自動で行いたい、でも外部のサーバー等は使用したくない、という声をいただいたので、いただいたリクエストをAutonomous Database単体で実現する方法を考えて検証してみました。

なお、こちらの検証ではAutonomous Databaseのリソース・プリンシパルを利用しています。
リソース・プリンシパルの有効化手順は、こちらのマニュアルを参照してください。

https://docs.oracle.com/cd//E83857_01/paas/autonomous-database/adbsa/resource-principal-enable.html

※こちらの内容はあくまで基礎検証ですので、実際の環境で使用される際はエラーハンドリング等を実装してください。


# 1. Autonomous DatabaseのOCPU数を変更するPL/SQLプロシージャの作成

OCPU数の設定値をパラメータとして渡すと、そのOCPU数にスケールアップ/ダウンするプロシージャchange_ocpu_countを作成します。

```sql:change_ocpu_countプロシージャ
CREATE OR REPLACE PROCEDURE change_ocpu_count( ocpu_count IN NUMBER )
  IS
  	autonomous_database_details	dbms_cloud_oci_database_update_autonomous_database_details_t;
  	response					dbms_cloud_oci_db_database_update_autonomous_database_response_t;
	  
  	adb_ocid    VARCHAR2(200);
	adb_region  VARCHAR2(100);

  BEGIN
-- Autonomous Databaseの情報をv$pdbsから取得
	SELECT json_value(cloud_identity, '$.REGION'), LOWER(json_value(cloud_identity, '$.DATABASE_OCID'))
      INTO adb_region, adb_ocid FROM v$pdbs;
-- 確認用に各変数の値を表示
  	DBMS_OUTPUT.PUT_LINE ('ADB OCID:'||adb_ocid);
  	DBMS_OUTPUT.PUT_LINE ('ADB Region:'||adb_region);
  	DBMS_OUTPUT.PUT_LINE ('New OCPU count:'||ocpu_count);
--　変数を初期化し、新しく設定するOCPUの値をcpu_core_countにセット
  	autonomous_database_details := dbms_cloud_oci_database_update_autonomous_database_details_t();
  	autonomous_database_details.cpu_core_count := ocpu_count;
-- DBMS_CLOUD_OCI_DB_DATABASE.UPDATE_AUTONOMOUS_DATABASEを実行してOCPUの値を変更
  	response := DBMS_CLOUD_OCI_DB_DATABASE.UPDATE_AUTONOMOUS_DATABASE (
  		autonomous_database_id => adb_ocid,
  		update_autonomous_database_details => autonomous_database_details,
  		region => adb_region,
  		credential_name => 'OCI$RESOURCE_PRINCIPAL');

  END;
/
```

```
SQL> CREATE OR REPLACE PROCEDURE change_ocpu_count( ocpu_count IN NUMBER )
  2    IS
  3    	autonomous_database_details	dbms_cloud_oci_database_update_autonomous_database_details_t;
  4    	response					dbms_cloud_oci_db_database_update_autonomous_database_response_t;
  5  
  6    	adb_ocid    VARCHAR2(200);
  7  	adb_region  VARCHAR2(100);
  8  
  9    BEGIN
 10  
 11      SELECT json_value(cloud_identity, '$.REGION'), LOWER(json_value(cloud_identity, '$.DATABASE_OCID'))
 12        INTO adb_region, adb_ocid FROM v$pdbs;
 13  
 14      DBMS_OUTPUT.PUT_LINE ('ADB OCID:'||adb_ocid);
 15    	 DBMS_OUTPUT.PUT_LINE ('ADB Region:'||adb_region);
 16      DBMS_OUTPUT.PUT_LINE ('New OCPU count:'||ocpu_count);
 17  
 18      autonomous_database_details := dbms_cloud_oci_database_update_autonomous_database_details_t();
 19      autonomous_database_details.cpu_core_count := ocpu_count;
 20  
 21      response := DBMS_CLOUD_OCI_DB_DATABASE.UPDATE_AUTONOMOUS_DATABASE (
 22          autonomous_database_id => adb_ocid,
 23          update_autonomous_database_details => autonomous_database_details,
 24          region => adb_region,
 25          credential_name => 'OCI$RESOURCE_PRINCIPAL');
 26  
 27    END;
 28  /

プロシージャが作成されました。

SQL>
```
# 2. 作成したPL/SQLプロシージャの動作確認

Autonomous Databaseの詳細画面で、現在のOCPU数を確認します。
このAutonomous Databaseは現在1 OCPUで稼働しています。
![スクリーンショット 2022-12-08 18.26.40.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/784824/c5745d9e-b0ee-612a-0c04-54ae06630e8d.png)
作成したchange_ocpu_countプロシージャを使用して、こちらのAutonomous Databaseを2 OCPUにスケールアップしてみます。
2 OCPUにスケールアップするので、パラメータとして"2"を渡してchange_ocpu_countプロシージャを実行します。

```
SQL> EXEC change_ocpu_count(2);
ADB OCID:ocid1.autonomousdatabase.oc1.ap-tokyo-1.anxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxiuq
ADB Region:ap-tokyo-1
New OCPU count:2

PL/SQLプロシージャが正常に完了しました。

SQL>
```
Autonomous Databaseの詳細画面を確認します。
状態が「スケーリング進行中」になりました。
![スクリーンショット 2022-12-08 18.27.58.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/784824/77f7cf0b-b1fb-1be8-a49e-6cb5d80eb947.png)
少し待つと、状態が「使用可能」になり、OCPU数が2に変わりました。
![スクリーンショット 2022-12-08 18.31.04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/784824/710dd517-f9ed-52b7-677d-3e2476c65e61.png)
無事にchange_ocpu_countプロシージャでOCPU数をスケールアップできました。

同様に、作成したchange_ocpu_countプロシージャを使用して、こちらのAutonomous Databaseを1 OCPUにスケールダウンしてみます。
1 OCPUにスケールダウンするので、パラメータとして"1"を渡してchange_ocpu_countプロシージャを実行します。

```
SQL> EXEC change_ocpu_count(1);
ADB OCID:ocid1.autonomousdatabase.oc1.ap-tokyo-1.anxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxiuq
ADB Region:ap-tokyo-1
New OCPU count:1

PL/SQLプロシージャが正常に完了しました。

SQL>
```
Autonomous Databaseの詳細画面を確認します。
先ほどと同様に、状態が「スケーリング進行中」になりました。
![スクリーンショット 2022-12-08 18.33.32.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/784824/275c3a66-e029-963d-4f8f-4a7625990f6a.png)
少し待つと、状態が「使用可能」になり、OCPU数が1に変わりました。
![スクリーンショット 2022-12-08 18.36.16.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/784824/dc1d87da-f8ec-66a8-cbc9-1033c6a48e41.png)

無事にchange_ocpu_countプロシージャでOCPU数をスケールダウンできました。

# 3. PL/SQLプロシージャの自動実行の設定

Autonomous DatabaseのOCPU数を変更するプロシージャが完成したので、こちらを決まった時間に自動実行すれば、Autonomous Databaseに自分自身を定期的にスケールアップ/スケールダウンさせることができます。

作成したプロシージャを決まった時間に自動実行するには、DBMS_SCHEDULERのジョブ作成します。
まず最初に、DBMS_SCHEDULER.CREATE_JOBプロシージャを使用して、Autonomous Databaseをスケールアップするためのジョブを作成します。

こちらの例では、change_ocpu_countプロシージャを初回は日本時間2022/12/8の20時に実行し、その後毎日同時刻に実行されるジョブscaleup_adb_jobを作成しています。

```sql:
BEGIN
  DBMS_SCHEDULER.CREATE_JOB (
    job_name            => 'SCALEUP_ADB_JOB', -- ジョブの名前
    job_type            => 'STORED_PROCEDURE', -- ジョブのタイプ
    job_action          => 'ADMIN.CHANGE_OCPU_COUNT', --実行するプロシージャ名
    number_of_arguments => 1, -- 引数（パラメータ）の数
    start_date          => TO_TIMESTAMP_TZ('2022-12-08 20:00:00 +9:00','YYYY-MM-DD HH24:MI:SS TZH:TZM'),  --初回実行のタイムスタンプ
    repeat_interval     => 'FREQ=DAILY;INTERVAL=1', -- 実行頻度
    auto_drop           => FALSE); -- 自動削除するかどうか
END;
/
```

```
SQL> BEGIN
  2    DBMS_SCHEDULER.CREATE_JOB (
  3      job_name            => 'SCALEUP_ADB_JOB',
  4      job_type            => 'STORED_PROCEDURE',
  5      job_action          => 'ADMIN.CHANGE_OCPU_COUNT',
  6      number_of_arguments => 1,
  7      start_date          => TO_TIMESTAMP_TZ('2022-12-08 20:00:00 +9:00','YYYY-MM-DD HH24:MI:SS TZH:TZM'), 
  8      repeat_interval     => 'FREQ=DAILY;INTERVAL=1',
  9      auto_drop           => FALSE);
 10  END;
 11  /

PL/SQLプロシージャが正常に完了しました。

SQL>
```

次に、DBMS_SCHEDULER.SET_JOB_ARGUMENT_VALUEプロシージャを使用して、ジョブで実行するプロシージャに渡すパラメータ（引数）をセットします。
ここではOCPU数として2を渡すので、argument_valueに2をセットします。
```sql:
BEGIN
  DBMS_SCHEDULER.SET_JOB_ARGUMENT_VALUE (
    job_name           => 'SCALEUP_ADB_JOB', -- パラメータをセットするジョブの名前
    argument_position  => 1, -- パラメータの位置
    argument_value     => 2); -- パラメータの値
END;
/
```

```
SQL> BEGIN
  2    DBMS_SCHEDULER.SET_JOB_ARGUMENT_VALUE (
  3      job_name           => 'SCALEUP_ADB_JOB',
  4      argument_position  => 1,
  5      argument_value     => 2);
  6  END;
  7  /

PL/SQLプロシージャが正常に完了しました。

SQL>
```
最後に、DBMS_SCHEDULER.ENABLEプロシージャを使用して、ジョブを有効化します。
```sql:
EXEC DBMS_SCHEDULER.ENABLE('SCALEUP_ADB_JOB');
```

```
SQL> EXEC DBMS_SCHEDULER.ENABLE('SCALEUP_ADB_JOB');

PL/SQLプロシージャが正常に完了しました。

SQL>
```
ジョブの情報は、USER_SCHEDULER_JOBビューで確認できます。
作成したジョブscaleup_adb_jobの情報をUSER_SCHEDULER_JOBで確認します。

```
SQL> SELECT job_name, enabled, next_run_date
  2  FROM user_scheduler_jobs
  3  WHERE job_name = 'SCALEUP_ADB_JOB';

JOB_NAME		          ENABL NEXT_RUN_DATE
------------------------- ----- -----------------------------------
SCALEUP_ADB_JOB 	      TRUE	22-12-08 20:00:00.000000 +09:00

SQL>
```

同様に、DBMS_SCHEDULER.CREATE_JOBプロシージャを使用して、Autonomous Databaseをスケールアップするためのジョブscaledown_adb_jobを作成します

こちらの例では、change_ocpu_countプロシージャを初回は日本時間2022/12/8の21時に実行し、その後毎日同時刻に実行されるジョブscaledown_adb_jobを作成しています。

```sql:
BEGIN
  DBMS_SCHEDULER.CREATE_JOB (
    job_name            => 'SCALEDOWN_ADB_JOB',
    job_type            => 'STORED_PROCEDURE',
    job_action          => 'ADMIN.CHANGE_OCPU_COUNT',
    number_of_arguments => 1,
    start_date          => TO_TIMESTAMP_TZ('2022-12-08 21:00:00 +9:00','YYYY-MM-DD HH24:MI:SS TZH:TZM'), 
    repeat_interval     => 'FREQ=DAILY;INTERVAL=1',
    auto_drop           => FALSE);
END;
/
```

```
SQL> BEGIN
  2    DBMS_SCHEDULER.CREATE_JOB (
  3      job_name            => 'SCALEDOWN_ADB_JOB',
  4      job_type            => 'STORED_PROCEDURE',
  5      job_action          => 'ADMIN.CHANGE_OCPU_COUNT',
  6      number_of_arguments => 1,
  7      start_date          => TO_TIMESTAMP_TZ('2022-12-08 21:00:00 +9:00','YYYY-MM-DD HH24:MI:SS TZH:TZM'), 
  8      repeat_interval     => 'FREQ=DAILY;INTERVAL=1',
  9      auto_drop           => FALSE);
 10  END;
 11  /

PL/SQLプロシージャが正常に完了しました。

SQL>
```

次に、DBMS_SCHEDULER.SET_JOB_ARGUMENT_VALUEプロシージャを使用して、ジョブで実行するプロシージャに渡すパラメータ（引数）をセットします。
ここではOCPU数として1を渡すので、argument_valueに1をセットします。

```sql:
BEGIN
  DBMS_SCHEDULER.SET_JOB_ARGUMENT_VALUE (
    job_name           => 'SCALEDOWN_ADB_JOB',
    argument_position  => 1,
    argument_value     => 1);
END;
/
```

```
SQL> BEGIN
  2    DBMS_SCHEDULER.SET_JOB_ARGUMENT_VALUE (
  3      job_name           => 'SCALEDOWN_ADB_JOB',
  4      argument_position  => 1,
  5      argument_value     => 1);
  6  END;
  7  /

PL/SQLプロシージャが正常に完了しました。

SQL>
```
最後に、DBMS_SCHEDULER.ENABLEプロシージャを使用して、ジョブを有効化します。
```sql:
EXEC DBMS_SCHEDULER.ENABLE('SCALEDOWN_ADB_JOB');
```

```
SQL> EXEC DBMS_SCHEDULER.ENABLE('SCALEDOWN_ADB_JOB');

PL/SQLプロシージャが正常に完了しました。

SQL>
```
作成したジョブscaledown_adb_jobの情報をUSER_SCHEDULER_JOBで確認します。
```
SQL> SELECT job_name, enabled, next_run_date
  2  FROM user_scheduler_jobs
  3  WHERE job_name = 'SCALEDOWN_ADB_JOB';

JOB_NAME		          ENABL NEXT_RUN_DATE
------------------------- ----- -----------------------------------
SCALEDOWN_ADB_JOB	      TRUE	22-12-08 21:00:00.000000 +09:00

SQL>
```
以上で、毎日20時にスケールアップを行うジョブscaleup_adb_job、毎日21時にスケールダウンを行うジョブscaledown_adb_jobの登録が完了しました。

# 4. ジョブの動作確認

設定したジョブの実行時間のあとに、ジョブの実行状況を確認します。
ジョブの実行状況はUSER_SCHEDULER_JOB_RUN_DETAILSビューで確認できます。
```
SQL> col job_name for a25
SQL> col log_timestamp for a35
SQL> col status for a15
SQL> SELECT
  2  job_name,
  3  TO_CHAR(FROM_TZ(sys_extract_utc(log_date), 'UTC') at time zone 'Asia/Tokyo','YYYY/MM/DD HH24:Mi:SS') log_timestamp, 
  4  status
  5  FROM user_scheduler_job_run_details
  6  WHERE job_name = 'SCALEUP_ADB_JOB' OR job_name = 'SCALEDOWN_ADB_JOB'
  7  ORDER BY log_date;

JOB_NAME		          LOG_TIMESTAMP 		              STATUS
------------------------- ----------------------------------- ---------------
SCALEUP_ADB_JOB 	      2022/12/08 20:00:02		          SUCCEEDED
SCALEDOWN_ADB_JOB 	      2022/12/08 21:00:02		          SUCCEEDED

SQL>
```
スケールアップのジョブscaleup_adb_job、スケールダウンのジョブscaledown_adb_jobのいずれも問題なく実行されたことがわかりました。

Autonomous Databaseの詳細画面で作業リクエストを確認してみます。
![スクリーンショット 2022-12-08 21.07.31.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/784824/06cfb3cd-c7cb-13d1-f274-a2cf06b2178e.png)

表示がUTCになっているのでわかりにくいですが、日本時間の20:00と21:00に「Scale Autonomous Database」の操作が行われていたことがわかります。

このことから、DBMS_SCHEDULERで作成したジョブが設定した時間通りに実行されたことがわかります。

# まとめ

PL/SQLプロシージャとDBMS_SCHEDULERを使用して、Autonomous Database単体で毎日決まった時間に行うスケールアップ/スケールダウンを実現することができました。

めでたし、めでたし。

# 参考資料
・[DBMS_CLOUD_OCI_DB_DATABASE.UPDATE_AUTONOMOUS_DATABASE](https://docs.oracle.com/en-us/iaas/pl-sql-sdk/doc/dbms_cloud_oci_db_database.html#ADSDK-GUID-ED5170F2-84C3-4326-9333-2244CED7D837)
・[DBMS_SCHEDULER.CREATE_JOB](https://docs.oracle.com/cd/F19136_01/arpls/DBMS_SCHEDULER.html#GUID-7E744D62-13F6-40E9-91F0-1569E6C38BBC)
・[DBMS_SCHEDULER.SET_JOB_ARGUMENT_VALUE](https://docs.oracle.com/cd/F19136_01/arpls/DBMS_SCHEDULER.html#GUID-8B7CB779-6DE3-414C-999B-B0C82E71A19B)
・[DBMS_SCHEDULER.ENABLE](https://docs.oracle.com/cd/F19136_01/arpls/DBMS_SCHEDULER.html#GUID-33CD9F19-8448-4BA8-AAB3-3B82A670085D)
・[USER_SCHEDULER_JOBS](https://docs.oracle.com/cd/F19136_01/refrn/USER_SCHEDULER_JOBS.html)
・[USER_SCHEDULER_JOB_RUN_DETAILS](https://docs.oracle.com/cd/F19136_01/refrn/USER_SCHEDULER_JOB_RUN_DETAILS.html)