はじめに
Automoous Databaseは、ソースとなるAutomomous Databaseがあるリージョンと異なるリージョンにリフレッシュ可能クローン(クロス・リージョン・リフレッシュ可能クローン)を作成することができます。
Autonomous Databaseのリフレッシュ可能クローンには、以下の特徴があります。
・クローン元(ソース)のAutonomoous Databaseとは異なる数のOCPUを設定することが可能
・リフレッシュ中もソースのAutonomous Databaseは利用可能
・作成後または前回のリフレッシュから1週間以内にリフレッシュ操作が必要
・作成後または前回のリフレッシュから1週間以内の任意のタイミングでリフレッシュ操作が可能
・リフレッシュ時にソースのどの時点までの変更を反映するか(リフレッシュ・ポイント)を指定可能
これらの特徴から、リフレッシュ可能クローンは、ソースのAutonomous Databaseへのアクセスを停止することなく取得できる、特定時点のバックアップとして利用が可能です。
今回は、ソースと異なるリージョンにリフレッシュ可能クローンを作成し、DBMS_SCHEDULERで以下の処理を行うPL/SQLプロシージャを1時間に1回実行することで、リフレッシュ可能クローンをDRを目的としたリモート・バックアップとして使用する環境を構築してみました。
- リフレッシュ可能クローンの状態を確認
- リフレッシュ可能クローンが停止していたら起動
- 起動が完了し、使用可能になるのを待つ
- 直前の正時(0分)のタイムスタンプを生成
- 直前の正時(0分)のタイムスタンプをリフレッシュ・ポイントに指定してリフレッシュを実行
- リフレッシュが完了し、使用可能になる完了を待つ
- リフレッシュが完了したら、リフレッシュ可能クローンを停止
なお、こちらで作成するプロシージャでは、以下の2つの記事で作成しているget_adb_stateファンクションとwait_adb_stateプロシージャを使用しています。
1. 異なるリージョンへのリフレッシュ可能クローンの作成
コンソールでクローンのソースとなるAutonomous Databaseの詳細画面にアクセスします。

「他のアクション」から「クローンの作成」を選択します。

「クローン・タイプの選択」で「リフレッシュ可能クローン」を選択します。
「優先リージョンの選択」でクローンを作成するリージョンを選択します。
ここでは東京リージョンにあるAutonomous Databaseのクローンを大阪リージョンに作成するので「日本中央部(大阪)」を選択します。
作成するコンパートメント、表示名、データベース名を入力します。

作成するクローンのOCPUの設定、ネットワーク・アクセスの情報を入力します。

ライセンス・タイプを選択し、通知先のメールアドレスを入力します。
入力が完了したら、「Autonomous Databaseのクローンの作成」をクリックします。

リフレッシュ可能クローンのプロビジョニングが始まります。

しばらく待つとリフレッシュ可能クローンが利用可能になります。
画面上部にリフレッシュの期限が、「一般情報」の「クローン情報」のセクションに
・このAutonomous Databaseがリフレッシュ可能クローンであること
・クローン元(ソース)のAutonomous Database
・リフレッシュ・ポイント(ソースのどの時点のデータが反映されているか)
が表示されます。



2. リフレッシュ可能クローンを起動→リフレッシュ→停止するPL/SQLプロシージャの作成
プロシージャを実行するAutonomous Databaseのリフレッシュ可能クローンのリフレッシュを実行するPL/SQLプロシージャrefresh_remote_cloneを作成します。
以下のような処理の流れになっています。
1.APIをコールして、自身がソースとなっているリフレッシュ可能クローンの情報を取得
2.取得したリフレッシュ可能クローンの情報からリージョンとOCIDを抽出
3.リフレッシュ可能クローンが停止していたら起動し、起動が完了するのを待つ
4.直前の正時(0分)のタイムスタンプを生成
5.直前の正時(0分)のタイムスタンプをリフレッシュ・ポイントに指定してリフレッシュを実行
6.リフレッシュの完了を待つ
7.リフレッシュが完了したら、リフレッシュ完了クローンを停止
CREATE OR REPLACE PROCEDURE refresh_remote_clone
IS
source_adb_region VARCHAR2(100);
source_adb_ocid VARCHAR2(100);
clone_adb_region VARCHAR2(100);
clone_adb_ocid VARCHAR2(100);
request_uri VARCHAR2(300);
resp DBMS_CLOUD_TYPES.resp;
response_start dbms_cloud_oci_db_database_start_autonomous_database_response_t;
response_stop dbms_cloud_oci_db_database_stop_autonomous_database_response_t;
response_refresh dbms_cloud_oci_db_database_autonomous_database_manual_refresh_response_t;
refresh_details dbms_cloud_oci_database_autonomous_database_manual_refresh_details_t := dbms_cloud_oci_database_autonomous_database_manual_refresh_details_t();
BEGIN
-- ソースとなるAutonomous Database(プロシージャを実行しているADB)のOCIDとリージョンをv$pdbsから取得
SELECT json_value(cloud_identity, '$.REGION'), LOWER(json_value(cloud_identity, '$.DATABASE_OCID'))
INTO source_adb_region, source_adb_ocid FROM v$pdbs;
-- リフレッシュ可能クローンの情報を取得するAPIのURIを生成
request_uri := 'https://database.'||source_adb_region||'.oraclecloud.com/20160918/autonomousDatabases/'||source_adb_ocid||'/refreshableClones';
-- APIをコールしてリフレッシュ可能クローンの情報を取得
resp := DBMS_CLOUD.send_request(
credential_name => 'OCI$RESOURCE_PRINCIPAL',
uri => request_uri,
method => DBMS_CLOUD.METHOD_GET);
-- 取得したリフレッシュ可能クローンの情報からリージョンとOCIDを取得
clone_adb_region := json_value(DBMS_CLOUD.get_response_text(resp), '$.items.region');
clone_adb_ocid := json_value(DBMS_CLOUD.get_response_text(resp), '$.items.id');
-- クローンの状態を確認し、停止していたら起動
IF get_adb_state(clone_adb_region, clone_adb_ocid) = 'STOPPED'
THEN
DBMS_OUTPUT.PUT_LINE('Starting ADB.'||CHR(10));
response_start := DBMS_CLOUD_OCI_DB_DATABASE.START_AUTONOMOUS_DATABASE(
credential_name => 'OCI$RESOURCE_PRINCIPAL',
autonomous_database_id => clone_adb_ocid,
region => clone_adb_region);
wait_adb_state(clone_adb_region, clone_adb_ocid, 'AVAILABLE');
DBMS_OUTPUT.PUT_LINE('ADB is started.'||CHR(10));
ELSE
DBMS_OUTPUT.PUT_LINE('ADB is already running.'||CHR(10));
END IF;
-- 直前の正時(0分)のタイムスタンプを生成し、リフレッシュのタイムスタンプに設定
SELECT TO_TIMESTAMP_TZ(TO_CHAR(sysdate,'YYYY-MM-DD HH24')||':00:00 +9:00','YYYY-MM-DD HH24:MI:SS TZH:TZM')
INTO refresh_details.time_refresh_cutoff FROM dual;
-- 直前の正時(0分)のタイムスタンプを指定してリフレッシュ可能クローンのリフレッシュを実行
DBMS_OUTPUT.PUT_LINE('Refreshing ADB.'||CHR(10));
response_refresh := DBMS_CLOUD_OCI_DB_DATABASE.AUTONOMOUS_DATABASE_MANUAL_REFRESH(
credential_name => 'OCI$RESOURCE_PRINCIPAL',
autonomous_database_id => clone_adb_ocid,
region => clone_adb_region,
autonomous_database_manual_refresh_details => refresh_details);
-- リフレッシュが終了し、クローンが利用可能になるのを待つ
wait_adb_state(clone_adb_region, clone_adb_ocid, 'AVAILABLE');
DBMS_OUTPUT.PUT_LINE('ADB is refreshed.'||CHR(10));
--クローンを停止する
DBMS_OUTPUT.PUT_LINE('Stopping ADB.'||CHR(10));
response_stop := DBMS_CLOUD_OCI_DB_DATABASE.STOP_AUTONOMOUS_DATABASE(
credential_name => 'OCI$RESOURCE_PRINCIPAL',
autonomous_database_id => clone_adb_ocid,
region => clone_adb_region);
wait_adb_state(clone_adb_region, clone_adb_ocid, 'STOPPED');
DBMS_OUTPUT.PUT_LINE('ADB is stopped.'||CHR(10));
END;
/
ソースのAutonomous DatabaseにSQL*Plusから接続し、実際に作成してみます。
SQL> CREATE OR REPLACE PROCEDURE refresh_remote_clone
2 IS
3
4 source_adb_region VARCHAR2(100);
5 source_adb_ocid VARCHAR2(100);
6 clone_adb_region VARCHAR2(100);
7 clone_adb_ocid VARCHAR2(100);
8
9 request_uri VARCHAR2(300);
10 resp DBMS_CLOUD_TYPES.resp;
11
12 response_start dbms_cloud_oci_db_database_start_autonomous_database_response_t;
13 response_stop dbms_cloud_oci_db_database_stop_autonomous_database_response_t;
14 response_refresh dbms_cloud_oci_db_database_autonomous_database_manual_refresh_response_t;
15 refresh_details dbms_cloud_oci_database_autonomous_database_manual_refresh_details_t := dbms_cloud_oci_database_autonomous_database_manual_refresh_details_t();
16
17 BEGIN
18
19 SELECT json_value(cloud_identity, '$.REGION'), LOWER(json_value(cloud_identity, '$.DATABASE_OCID'))
20 INTO source_adb_region, source_adb_ocid FROM v$pdbs;
21
22 request_uri := 'https://database.'||source_adb_region||'.oraclecloud.com/20160918/autonomousDatabases/'||source_adb_ocid||'/refreshableClones';
23
24 resp := DBMS_CLOUD.send_request(
25 credential_name => 'OCI$RESOURCE_PRINCIPAL',
26 uri => request_uri,
27 method => DBMS_CLOUD.METHOD_GET);
28
29 clone_adb_region := json_value(DBMS_CLOUD.get_response_text(resp), '$.items.region');
30 clone_adb_ocid := json_value(DBMS_CLOUD.get_response_text(resp), '$.items.id');
31
32 IF get_adb_state(clone_adb_region, clone_adb_ocid) = 'STOPPED'
33 THEN
34 DBMS_OUTPUT.PUT_LINE('Starting ADB.'||CHR(10));
35 response_start := DBMS_CLOUD_OCI_DB_DATABASE.START_AUTONOMOUS_DATABASE(
36 credential_name => 'OCI$RESOURCE_PRINCIPAL',
37 autonomous_database_id => clone_adb_ocid,
38 region => clone_adb_region);
39
40 wait_adb_state(clone_adb_region, clone_adb_ocid, 'AVAILABLE');
41 DBMS_OUTPUT.PUT_LINE('ADB is started.'||CHR(10));
42 ELSE
43 DBMS_OUTPUT.PUT_LINE('ADB is already running.'||CHR(10));
44 END IF;
45
46 SELECT TO_TIMESTAMP_TZ(TO_CHAR(sysdate,'YYYY-MM-DD HH24')||':00:00 +9:00','YYYY-MM-DD HH24:MI:SS TZH:TZM')
47 INTO refresh_details.time_refresh_cutoff FROM dual;
48
49 DBMS_OUTPUT.PUT_LINE('Refreshing ADB.'||CHR(10));
50 response_refresh := DBMS_CLOUD_OCI_DB_DATABASE.AUTONOMOUS_DATABASE_MANUAL_REFRESH(
51 credential_name => 'OCI$RESOURCE_PRINCIPAL',
52 autonomous_database_id => clone_adb_ocid,
53 region => clone_adb_region,
54 autonomous_database_manual_refresh_details => refresh_details);
55
56 wait_adb_state(clone_adb_region, clone_adb_ocid, 'AVAILABLE');
57 DBMS_OUTPUT.PUT_LINE('ADB is refreshed.'||CHR(10));
58
59 DBMS_OUTPUT.PUT_LINE('Stopping ADB.'||CHR(10));
60 response_stop := DBMS_CLOUD_OCI_DB_DATABASE.STOP_AUTONOMOUS_DATABASE(
61 credential_name => 'OCI$RESOURCE_PRINCIPAL',
62 autonomous_database_id => clone_adb_ocid,
63 region => clone_adb_region);
64
65 wait_adb_state(clone_adb_region, clone_adb_ocid, 'STOPPED');
66 DBMS_OUTPUT.PUT_LINE('ADB is stopped.'||CHR(10));
67
68 END;
69 /
プロシージャが作成されました。
SQL>
PL/SQLプロシージャrefresh_remote_cloneが作成できました。
早速実行してみます。
SQL> set serveroutput on
SQL> exec refresh_remote_clone;
PL/SQLプロシージャが正常に完了しました。
SQL>
コンソールでリフレッシュ可能クローンの詳細画面を確認すると、ステータスが「起動中」に変わりました。

その後、一瞬「使用可能」になり、すぐに「更新中」になりました。

しばらく待つと、また一瞬「使用可能」になり、すぐに「停止中」になりました。

少し待つと、「停止済」になりました。

作業リクエストを確認すると、起動→リフレッシュ→停止の一連の処理が行われたことが確認できました。

リフレッシュ・ポイントを確認してみると、リフレッシュ実行時間の直前の正時(0分)に更新されていることが確認できました。
(リフレッシュの実行が2022/12/7 23:49:49 UTCなので、リフレッシュ・ポイントは直前の正時である2022/12/7 23:00:00 UTC)になっています)

3. 自動実行の設定
PL/SQLプロシージャの自動実行にはDBMS_SCHEDULERを使用します。
ここでは、毎時5分にrefresh_remote_cloneプロシージャを実行してリフレッシュを自動実行するジョブrefresh_remote_clone_jobを作成します。
初回実行時間は日本時間の2022/12/8 9:05としました。
BEGIN
DBMS_SCHEDULER.CREATE_JOB (
job_name => 'REFRESH_REMOTE_CLONE_JOB',
job_type => 'STORED_PROCEDURE',
job_action => 'ADMIN.REFRESH_REMOTE_CLONE',
start_date => TO_TIMESTAMP_TZ('2022-12-8 09:05:00 +9:00','YYYY-MM-DD HH24:MI:SS TZH:TZM'),
repeat_interval => 'FREQ=HOURLY;INTERVAL=1',
auto_drop => FALSE,
enabled => TRUE
);
END;
/
SQL*Plusから実際に作成してみます。
SQL> BEGIN
2 DBMS_SCHEDULER.CREATE_JOB (
3 job_name => 'REFRESH_REMOTE_CLONE_JOB',
4 job_type => 'STORED_PROCEDURE',
5 job_action => 'ADMIN.REFRESH_REMOTE_CLONE',
6 start_date => TO_TIMESTAMP_TZ('2022-12-8 09:05:00 +9:00','YYYY-MM-DD HH24:MI:SS TZH:TZM'),
7 repeat_interval => 'FREQ=HOURLY;INTERVAL=1',
8 auto_drop => FALSE,
9 enabled => TRUE
10 );
11 END;
12 /
PL/SQLプロシージャが正常に完了しました。
SQL>
ジョブrefresh_remote_clone_jobが作成できました。
ジョブrefresh_remote_clone_jobがログを出力するように設定します。
BEGIN
DBMS_SCHEDULER.SET_ATTRIBUTE (
'REFRESH_REMOTE_CLONE_JOB', 'logging_level', DBMS_SCHEDULER.LOGGING_FULL);
END;
/
SQL> BEGIN
2 DBMS_SCHEDULER.SET_ATTRIBUTE (
3 'REFRESH_REMOTE_CLONE_JOB', 'logging_level', DBMS_SCHEDULER.LOGGING_FULL);
4 END;
5 /
PL/SQLプロシージャが正常に完了しました。
SQL>
ジョブrefresh_remote_clone_jobの設定内容を確認します。
col job_name for a25
col next_run_date for a35
col logging_level for a15
SELECT job_name, next_run_date, logging_level FROM all_scheduler_jobs
WHERE job_name = 'REFRESH_REMOTE_CLONE_JOB';
SQL> col job_name for a25
SQL> col next_run_date for a35
SQL> col logging_level for a15
SQL> SELECT job_name, NEXT_RUN_DATE, LOGGING_LEVEL FROM all_scheduler_jobs
2 WHERE job_name = 'REFRESH_REMOTE_CLONE_JOB';
JOB_NAME NEXT_RUN_DATE LOGGING_LEVEL
------------------------- ----------------------------------- ---------------
REFRESH_REMOTE_CLONE_JOB 22-12-08 09:05:00.000000 +09:00 FULL
1行が選択されました。
SQL>
しばらく待ってから、以下のSQLでジョブの実行状況を確認します。
col job_name for a25
col log_timestamp for a35
col status for a15
SELECT
job_name,
TO_CHAR(FROM_TZ(sys_extract_utc(log_date), 'UTC') at time zone 'Asia/Tokyo','YYYY/MM/DD HH24:Mi:SS') log_timestamp,
status
FROM user_scheduler_job_run_details
WHERE job_name = 'REFRESH_REMOTE_CLONE_JOB'
ORDER BY log_date;
SQL> col job_name for a25
SQL> col log_timestamp for a35
SQL> col status for a15
SQL> SELECT
2 job_name,
3 FROM_TZ(sys_extract_utc(log_date), 'UTC') at time zone 'Asia/Tokyo' timestamp,
4 status
5 FROM user_scheduler_job_run_details
6 WHERE job_name = 'REFRESH_REMOTE_CLONE_JOB'
7 ORDER BY log_date;
JOB_NAME LOG_TIMESTAMP STATUS
------------------------- ----------------------------------- ---------------
REFRESH_REMOTE_CLONE_JOB 2022/12/08 09:10:15 SUCCEEDED
REFRESH_REMOTE_CLONE_JOB 2022/12/08 10:09:44 SUCCEEDED
SQL>
ジョブが問題なく自動実行されていることが確認できました。
リフレッシュ可能クローンの作業リクエストを確認すると、指定した時間にジョブが自動実行されていることがわかります。

また、リフレッシュ・ポイントを確認すると、最後にジョブが実行された時刻(2022/12/8 10:05 JST)の直前の正時(2022/12/8 1:00:00 UTC = 2022/12/8 10:00 JST)になっていることがわかります。

まとめ
リフレッシュ可能クローンをDR目的の遠隔地バックアップとして利用する場合の起動、リフレッシュ、停止といった一連の処理を、PL/SQLのみで自動化することができました。
リフレッシュ可能クローンは差分のみの更新なので、フルクローンを作成する場合に比べて短時間でリフレッシュでき、ソース側のAutonomous Databaseの負荷も軽減できます。
参考資料
・Autonomous Databaseでのリフレッシュ可能クローンについて
・API Reference and Endpoints:ListAutonomousDatabaseRefreshableClones
・DBMS_CLOUD_OCI_DB_DATABASE.START_AUTONOMOUS_DATABASE
・DBMS_CLOUD_OCI_DB_DATABASE.STOP_AUTONOMOUS_DATABASE
・BMS_CLOUD_OCI_DB_DATABASE.AUTONOMOUS_DATABASE_MANUAL_REFRESH