#author("2018-04-16T14:33:42+00:00","default:haikikyou","haikikyou")
[[PostgreSQL/開発]]
#contents
* 概要 [#e195b03b]
- SIGALRMによる割り込みを多重化するためのライブラリである。
- 長時間クエリのタイムアウトを監視するStatementCancelHandlerなどの、PostgreSQL本来の機能に加え、ユーザー独自のタイムアウトを設定することも可能である。
&label(warn){参考};[[(utils/timeout.h) enum TimeoutId>https://git.postgresql.org/gitweb/?p=postgresql.git;a=blob;f=src/include/utils/timeout.h;h=5a2efc0dd94fc5daed3844408577f12b9ffa4f15;hb=ad4fb805ad08c86dd6389e6755081dfd7c864416#l23]] - &size(11){&color(gray){https://git.postgresql.org/gitweb/?p=postgresql.git;a=shortlog;h=refs/heads/REL_10_STABLE};};
#ref(./timeout-overview.png,100%)
- 複数のタイマーの管理は、アクティブなタイムアウトの存在と、最も近いタイムアウト時間に合わせてタイマーを設定することで実現している。
- タイムアウト情報は、all_timeoutという配列で管理されている。登録可能なタイマー数には制限がある。PostgreSQL10.0のソースを確認すると16(0~15)となっており、8個(0~7)が予約済みであるため、実質8個(8~15)のタイマーを登録可能である。
- 有効化されたタイムアウト設定は、active_timeoutsという配列に登録される。この配列は、all_timeouts要素の参照を保持する配列である。この配列で管理されるタイムアウト設定は、最もタイムアウト時間が直近のものから昇順に整列されている。つまり、active_timeouts[0]の要素が、最も直近に発火すべきタイマーとなっている。よって、timeoutライブラリは、タイマーの設定においてactive_timeouts[0]のfin_timeを呼べばよく、タイマー設定処理がシンプルになっている。
- タイマーが発火すると、MyLatchのフラグがセットされるため、MyLatchで待ち状態となっている処理は復帰する。また、indicatorというフラグがタイムアウト情報にセットされる。このフラグを見て(&code(){get_timeout_indicator};)、登録したタイマーが発火したか否かを判断できる。
- タイマーの発火で登録していたタイムアウト設定のハンドラーが呼ばれると、そのタイムアウト設定はactive_timeouts配列から削除される。従って、再びタイマースケジュールしたい場合は、enable_timeout_after関数などを呼びアクティブにする必要がある。
* 定義 [#z90736c7]
** 変数,エイリアス [#c2fa847d]
** マクロ [#ffb64d59]
** 列挙型 [#bd396b49]
*** TimeoutId [#r6754990]
タイムアウトの種別を示す、またタイムアウトを管理する配列のインデックスでもある。
|~定数名|~説明|h
|STARTUP_PACKET_TIMEOUT|PostgreSQLの予約タイムアウト定義|
|DEADLOCK_TIMEOUT|~|
|LOCK_TIMEOUT|~|
|STATEMENT_TIMEOUT|~|
|STANDBY_DEADLOCK_TIMEOUT|~|
|STANDBY_TIMEOUT|~|
|STANDBY_LOCK_TIMEOUT|~|
|IDLE_IN_TRANSACTION_SESSION_TIMEOUT|~|
|USER_TIMEOUT|ユーザーが独自に定義できるタイムアウトを示す。プラグインなどでRegisterTimeoutするときは、この定数を引数に指定する。|
|MAX_TIMEOUTS|タイムアウトの上限を示す定数。16である。|
*** TimeoutType [#z3e59a08]
|~定数名|~説明|h
|TMPARAM_AFTER|タイムアウト設定で、xx秒後を示すタイプ識別子|
|TMPARAM_AT|タイムアウト設定で、xx時間を示すタイプ識別子|
これらの定数は、複数のタイムアウト設定を有効にするenable_timeouts関数で用いる。~
enable_timeoutsには、EnableTimeoutParams構造体のオブジェクトを指定する。
** 構造体/共用体 [#t10c1dfa]
*** EnableTimeoutParams [#g1d72d85]
|~番号|~データ型|~フィールド|~説明|h
|1|TimeoutId|id|タイムアウトID|
|2|TimeoutType|type|タイムアウトがxx秒後またはxx時間かを示すタイプ識別子|
|3|int|delay_ms|typeがTMPARAM_AFTERの時の時間指定(msec)|
|4|TimestampTz|fin_time|typeがTMPARAM_ATの時の時間指定(msec)|
enable_timeouts関数で指定する構造体。
*** DisableTimeoutParams [#q1c335d3]
|~番号|~データ型|~フィールド|~説明|h
|1|TimeoutId|id|タイムアウトID|
|2|bool|keep_indicator|indicator(タイマが発火したか否かを示すフラグ)をリセットするか否か|
disable_timeouts関数で指定する構造体。
** 関数 [#v6487eee]
*** InitializeTimeouts [#v494f5f0]
#geshi(c){{{
extern void InitializeTimeouts(void);
}}}
- タイムアウト配列を初期化する。通常、バックエンドプロセスの初期化中に呼ばれるので、明示的に呼び出す必要はない。
*** RegisterTimeout [#p8012cf1]
#geshi(c){{{
// 引数1:タイムアウトID
// 引数2:タイムアウトハンドラ
// 戻り値:登録されたタイムアウトID
extern TimeoutId RegisterTimeout(TimeoutId id, timeout_handler_proc handler);
}}}
- タイムアウトを登録する。
- 戻り値でタイムアウトNoが返ってくるので、以降のタイムアウトAPIの呼び出し時にそれを用いる。
&label(sample){サンプル};
#geshi(c){{{
static TimeoutId MyTimeoutId = MAX_TIMEOUTS;
MyTimeoutId = RegisterTimeout(USER_TIMEOUT, my_alarm_handler);
}}}
*** reschedule_timeouts [#jc725c5f]
#geshi(c){{{
extern void reschedule_timeouts(void);
}}}
- アクティブなタイムアウト設定がある場合、リスケジュールする。
*** enable_timeout_after [#zf59480f]
#geshi(c){{{
// 引数1:タイムアウトID
// 引数2:タイムアウト設定(xxミリ秒後)
extern void enable_timeout_after(TimeoutId id, int delay_ms);
}}}
- xxミリ秒後にタイムアウトするようタイマー設定する。
*** enable_timeout_at [#b240660c]
#geshi(c){{{
// 引数1:タイムアウトID
// 引数2:タイムアウト設定(xxミリ秒後)
extern void enable_timeout_at(TimeoutId id, TimestampTz fin_time);
}}}
- xx時間にタイムアウトするようタイマー設定する。
*** enable_timeouts [#rb2edc12]
#geshi(c){{{
// 引数1:タイムアウト設定を保持するEnableTimeoutParams構造体配列のポインタ
// 引数2:配列の要素数
extern void enable_timeouts(const EnableTimeoutParams *timeouts, int count);
}}}
- 引数で指定されたタイムアウト設定をまとめて有効化する。
*** disable_timeout [#mb97c469]
#geshi(c){{{
// 引数1:タイムアウトID
// 引数2:indicatorフラグをリセットするか否か
extern void disable_timeout(TimeoutId id, bool keep_indicator);
}}}
- タイムアウトを無効化する。
*** disable_timeouts [#k9fe473d]
#geshi(c){{{
// 引数1:タイムアウト設定を保持するDisableTimeoutParams構造体配列のポインタ
// 引数2:配列の要素数
extern void disable_timeouts(const DisableTimeoutParams *timeouts, int count);
}}}
- 引数で指定されたタイムアウト設定をまとめて無効化する。
*** disable_all_timeouts [#e72ef4a4]
#geshi(c){{{
// 引数1:indicatorフラグをリセットするか否か
extern void disable_all_timeouts(bool keep_indicators);
}}}
- タイマーを停止し、全てのタイムアウトをアクティブなタイムアウト配列から削除する。
- タイムアウト配列の設定はそのまま残っており、アクティブなタイムアウト数を0にする。
*** get_timeout_indicator [#p315862e]
#geshi(c){{{
// 引数1:タイムアウトID
// 引数2:indicatorフラグをリセットするか否か
// 戻り値:indicatorの値
extern bool get_timeout_indicator(TimeoutId id, bool reset_indicator);
}}}
- indicatorの値を返す。一度リセットすると以降はfalseを返すようになる。
*** get_timeout_start_time [#x7dfd092]
#geshi(c){{{
// 引数1:タイムアウトID
// 戻り値:タイマ開始時間
extern TimestampTz get_timeout_start_time(TimeoutId id);
}}}
- タイムアウト設定時刻を返す。
*** get_timeout_finish_time [#sc8c0ce2]
#geshi(c){{{
// 引数1:タイムアウトID
// 戻り値:タイムアウト時間
extern TimestampTz get_timeout_finish_time(TimeoutId id);
}}}
- タイムアウトの終了時刻を返す。
* サンプルプログラム [#yb927eb0]
以下は、timeout.hの各種関数を拡張機能から呼び出すサンプルである。~
詳細は、添付の&ref(./my_timeout.zip);を参照。
#geshi(c){{
/*-------------------------------------------------------------------------
*
* timeout_test.c
* Timeout test code
*
* IDENTIFICATION
* contrib/timeout_test/pg_retire.c
*
*-------------------------------------------------------------------------
*/
#include "postgres.h"
#include "fmgr.h"
#include "utils/timeout.h"
PG_MODULE_MAGIC;
static TimeoutId MyTimeoutId = USER_TIMEOUT;
static void my_timeout_handler(void);
static void my_timeout_handler(void)
{
ereport(DEBUG3,
(errmsg("MyTimer fired!")));
}
PG_FUNCTION_INFO_V1(my_register_timeout);
Datum
my_register_timeout(PG_FUNCTION_ARGS)
{
if (MyTimeoutId != USER_TIMEOUT)
{
ereport(WARNING,
(errmsg("timeout is already set.")));
PG_RETURN_INT32(-1);
}
MyTimeoutId = RegisterTimeout(USER_TIMEOUT, my_timeout_handler);
PG_RETURN_INT32(MyTimeoutId);
}
PG_FUNCTION_INFO_V1(my_enable_timeout_after);
Datum
my_enable_timeout_after(PG_FUNCTION_ARGS)
{
int timeout_after = PG_GETARG_INT32(0);
enable_timeout_after(MyTimeoutId, timeout_after);
PG_RETURN_BOOL(true);
}
PG_FUNCTION_INFO_V1(my_enable_timeout_at);
Datum
my_enable_timeout_at(PG_FUNCTION_ARGS)
{
int timeout_at = PG_GETARG_INT32(0);
enable_timeout_at(MyTimeoutId, timeout_at);
PG_RETURN_BOOL(true);
}
PG_FUNCTION_INFO_V1(my_disable_timeout);
Datum
my_disable_timeout(PG_FUNCTION_ARGS)
{
disable_timeout(MyTimeoutId, true);
PG_RETURN_BOOL(true);
}
PG_FUNCTION_INFO_V1(my_get_timeout_indicator);
Datum
my_get_timeout_indicator(PG_FUNCTION_ARGS)
{
bool indicator;
indicator = get_timeout_indicator(MyTimeoutId, false);
PG_RETURN_BOOL(indicator);
}
PG_FUNCTION_INFO_V1(my_get_timeout_start_time);
Datum
my_get_timeout_start_time(PG_FUNCTION_ARGS)
{
int64 start_time;
start_time = get_timeout_start_time(MyTimeoutId);
PG_RETURN_INT64(start_time);
}
PG_FUNCTION_INFO_V1(my_get_timeout_finish_time);
Datum
my_get_timeout_finish_time(PG_FUNCTION_ARGS)
{
int64 finish_time;
finish_time = get_timeout_finish_time(MyTimeoutId);
PG_RETURN_INT64(finish_time);
}
}}
実行結果は、以下のようになる。
#geshi(sql){{{
postgres=# CREATE EXTENSION my_timeout;
CREATE EXTENSION
postgres=# SELECT my_register_timeout(); -- Register my timer.
my_register_timeout
---------------------
8
(1 row)
postgres=# SELECT my_enable_timeout_after(5000); -- A timer will be fired after 5 seconds.
my_enable_timeout_after
-------------------------
t
(1 row)
postgres=# SELECT my_get_timeout_indicator(); -- Check whether timer fired.
my_get_timeout_indicator
--------------------------
t
(1 row)
postgres=# SELECT my_get_timeout_start_time(); -- Show start-time of the timer.
my_get_timeout_start_time
-------------------------------
2018-04-16 00:17:37.234418+09
(1 row)
postgres=# SELECT my_get_timeout_finish_time(); -- Show finish-time of the timer.
my_get_timeout_finish_time
-------------------------------
2018-04-16 00:17:42.234418+09
(1 row)
postgres=# DROP EXTENSION my_timeout;
DEBUG: drop auto-cascades to function my_register_timeout()
DEBUG: drop auto-cascades to function my_enable_timeout_after(integer)
DEBUG: drop auto-cascades to function my_enable_timeout_at(integer)
DEBUG: drop auto-cascades to function my_disable_timeout()
DEBUG: drop auto-cascades to function my_get_timeout_indicator()
DEBUG: drop auto-cascades to function my_get_timeout_start_time()
DEBUG: drop auto-cascades to function my_get_timeout_finish_time()
DROP EXTENSION
}}}
* 参考・関連 [#tdfd2e8b]
- [[utils/timeout.h>https://git.postgresql.org/gitweb/?p=postgresql.git;a=blob;f=src/include/utils/timeout.h;h=5a2efc0dd94fc5daed3844408577f12b9ffa4f15;hb=ad4fb805ad08c86dd6389e6755081dfd7c864416]] - &size(11){&color(gray){https://git.postgresql.org/gitweb/?p=postgresql.git;a=shortlog;h=refs/heads/REL_10_STABLE};};
- [[utils/misc/timeout.c>https://git.postgresql.org/gitweb/?p=postgresql.git;a=blob;f=src/backend/utils/misc/timeout.c;h=d7fc040ad31da8669a2e237a9787f51942955dee;hb=ad4fb805ad08c86dd6389e6755081dfd7c864416]] - &size(11){&color(gray){https://git.postgresql.org/gitweb/?p=postgresql.git;a=shortlog;h=refs/heads/REL_10_STABLE};};
- [[Timeout test code - my_timeout>https://github.com/moritoru81/pg_misc/tree/master/my_timeout]] - &size(11){&color(gray){on https://github.com/moritoru81/pg_misc/};};
&label(rel){関連};
#related