PostgreSQL

pgpool + PostgreSQLの構成においてテーブルをロックしているPostgreSQLのSQLをキャンセルしたい

この文書はPostgreSQL11.7とpgpool4.1.1の話題です。

なんらかの理由でテーブルをロックしたままのPostgreSQLのSQLをキャンセルしてロックを開放したい場合以下の関数を使います。


この関数は成功するとtrueを返すのですが、trueが返ってきたからといって、キャンセルできる(プロセスを終了できる)わけではありません。
以下に例を示します。
ap2=# select l.pid , a.backend_start from (select pid from pg_locks group by pid) l,pg_stat_activity a where l.pid=a.pid order by query_start;
  pid  |         backend_start
-------+-------------------------------
 10673 | 2020-05-06 14:06:57.721961+09
 18222 | 2020-05-06 15:52:12.091632+09
 28546 | 2020-05-06 16:30:04.77062+09
(3 rows)

ap2=# SELECT pg_cancel_backend(10673);
 pg_cancel_backend
-------------------
 t
(1 row)

ap2=# SELECT pg_cancel_backend(18222);
 pg_cancel_backend
-------------------
 t
(1 row)

ap2=# select l.pid , a.backend_start from (select pid from pg_locks group by pid) l,pg_stat_activity a where l.pid=a.pid order by query_start;
  pid  |         backend_start
-------+-------------------------------
 10673 | 2020-05-06 14:06:57.721961+09
 18222 | 2020-05-06 15:52:12.091632+09
 28546 | 2020-05-06 16:30:04.77062+09
(3 rows)
10673と18222のプロセスを終了したいのですが、終了できません。
pg_terminate_backend(pid int)を使うと終了できるのですが、pgpoolを使っている場合、pg_terminate_backendを使うとpgpoolがPostgreSQLを切り離すので、サービスが中断します。

ErogameScapeにおいて、上記のようにpg_cancel_backendがきかないのは以下のような状況のときのようでした。

Apacheにアクセス
php-fpmがpgpoolにクエリを発行
pgpoolがPostgreSQLにクエリを発行
クエリの実行に時間がかかる
一定時間たってもphp-fpmから応答がないのでApacheがtimeoutとしてユーザーに504を返す
このあとpsコマンドで確認すると
pgpoolはidleもしくはidle in transaction
PostgreSQLはSELECTだったりUPDATEだったりpgpoolが発行したSQLを実行している状態

以下、例を示します。
[root@sakura ap2]# cat /var/log/httpd/access_log | grep update
127.0.0.1 - - [08/May/2020:04:40:01 +0900] "GET /~ap2/ero/toukei_kaiseki/update_userreview_display_log.php HTTP/1.1" 504 247 "-" "Wget/1.19.5 (linux-gnu)" 60027802
127.0.0.1 - - [08/May/2020:04:41:02 +0900] "GET /~ap2/ero/toukei_kaiseki/update_userreview_display_log.php HTTP/1.1" 504 247 "-" "Wget/1.19.5 (linux-gnu)" 60060326
127.0.0.1 - - [08/May/2020:04:42:04 +0900] "GET /~ap2/ero/toukei_kaiseki/update_userreview_display_log.php HTTP/1.1" 200 462 "-" "Wget/1.19.5 (linux-gnu)" 6428
[root@sakura ap2]# cat /var/lib/pgsql/11/log/postgresql-Fri.log
2020-05-08 04:41:10.718 JST [28908] [ap2] LOG:  duration: 8271.792 ms  execute : UPDATE userreview SET display_unique_count = coalesce( display_unique_count , 0 ) + $1 WHERE uid = $2 AND game = $3 ;
ap2=# select * from (select pid from pg_locks group by pid) l,pg_stat_activity a where l.pid=a.pid order by query_start;
  pid  |  datid   | datname |  pid  | usesysid | usename | application_name | client_addr | client_hostname | client_port |
        backend_start         |          xact_start           |          query_start          |         state_change
  | wait_event_type | wait_event | state  | backend_xid | backend_xmin |
     query                                                         |  backend_type
-------+----------+---------+-------+----------+---------+------------------+-------------+-----------------+-------------+-
------------------------------+-------------------------------+-------------------------------+-----------------------------
--+-----------------+------------+--------+-------------+--------------+----------------------------------------------------
-------------------------------------------------------------------+----------------
 28908 | 10934808 | ap2     | 28908 |    16385 | ap2     |                  | 127.0.0.1   |                 |       55878 |
2020-05-08 04:41:01.103872+09 | 2020-05-08 04:41:02.407331+09 | 2020-05-08 04:41:02.446477+09 | 2020-05-08 04:41:02.446478+0
9 | Client          | ClientRead | active |    17877103 |              | UPDATE userreview SET display_unique_count = coales
ce( display_unique_count , 0 ) + $1 WHERE uid = $2 AND game = $3 ; | client backend
この状態で
SELECT pg_cancel_backend(28908);
しても、28908を終了させることができません。

28908を使っているpgpoolのプロセスを殺したところ、28908を終了させることができました。
pcp_proc_infoコマンドで28908を使っているpgpoolのプロセスIDを確認します。
[ap2@sakura ~]$ pcp_proc_info -h /var/run/postgresql/ -U pgpool | grep 28908
Password:
ap2 ap2 2020-05-08 04:36:00 2020-05-08 04:41:01 3 0 2 28908 1 27546 0
28908と27546のプロセスを確認します。
[ap2@sakura ~]$ ps ax | grep 27546
 8121 pts/0    S+     0:00 grep --color=auto 27546
27546 ?        S      0:00 pgpool: ap2 ap2 127.0.0.1(58634) idle in transaction
[ap2@sakura ~]$ ps ax | grep 28908
 8134 pts/0    S+     0:00 grep --color=auto 28908
28908 ?        Ss     0:00 postgres: ap2 ap2 127.0.0.1(55878) UPDATE
27546をkillします。
※確か、kill 27546では殺せなくて、kill -KILL 27546で殺しました。
これで、28908のプロセスも開放されて、ロックされていたテーブルもロックが解除されました。

※そもそも、なぜpostgresがSELECTやUPDATEのまま止まってしまうのかの原因は未だ分かりません…

pg_basebackup: ベースバックアップの初期化中 - チェックポイントの完了を待機中です

pg_basebackupを実行しても、pg_basebackupがはじまらない場合は、チェックポイントの完了を待っている可能性があります。
-bash-4.1$ pg_basebackup -r 10M -X s -S replication_slot -h erogamescape.dyndns.org -p 5432 -U replication_user -D /var/lib/pgsql/11/data --write-recovery-conf -P -v
pg_basebackup: ベースバックアップの初期化中 - チェックポイントの完了を待機中です
チェックポイントの完了を待てない場合は、チェックポイントを実施すれば、すぐにpg_basebackupが開始されます。
ap2=# CHECKPOINT;
LOG:  duration: 5910.506 ms  statement: CHECKPOINT;
CHECKPOINT

pg_basebackup: チェックポイントが完了しました
pg_basebackup: 先行書き込みログの開始ポイント: タイムライン26上の55/363790E8
ただし、チェックポンイトの処理は負荷がかかるので、業務との兼ね合いでCHECKPOINTするかしないかになります。
ちなみにpg_basebackupのオプションに「--checkpoint=fast」をつけると、pg_basebackupを実行した際にすぐにCHECKPOINTを実行します。

以下参考文書です。

muninがPostgreSQLから情報をとれなくなるほど負荷があがる

ここ1ヶ月ほどmuninがPostgreSQLから情報をとれなくなるほど負荷があがることが1日に1,2回程度発生していました。
以下の赤い四角で囲った部分です。
グラフがぽっかり抜けています。
001
スロークエリがあると想定してログを取得するも、問題となるようなクエリはありませんでした。
実行に時間がかかっているクエリはたくさんあったのですが、いずれのクエリも平常時はms単位で応答を返すものでした。
が、この事象が発生するときは、応答に5秒かかる…というような状態でした。
sarを実行すると以下のような出力が得られました。

06時40分01秒     CPU     %user     %nice   %system   %iowait    %steal     %idle
06時41分01秒     all      7.36      2.01      3.00      0.58      0.00     87.05
06時42分01秒     all      4.46      0.00      1.79      0.32      0.00     93.43
06時43分01秒     all      4.87      0.00      1.55      0.43      0.00     93.15
06時44分01秒     all      6.98      0.00      2.56      0.26      0.00     90.19
06時45分01秒     all     13.59      0.00      5.06      0.24      0.00     81.11
06時46分01秒     all     45.36      1.99     17.29      0.30      0.00     35.06
06時47分01秒     all     74.13      0.00     25.38      0.00      0.00      0.49
06時48分01秒     all     74.97      0.00     24.87      0.00      0.00      0.16
06時49分02秒     all     76.15      0.00     23.46      0.00      0.00      0.39
06時50分01秒     all     44.06      0.00     14.09      0.08      0.00     41.77
06時51分01秒     all     14.55      1.98      5.38      0.90      0.00     77.19
06時52分01秒     all      9.73      0.00      2.82      0.31      0.00     87.14
※sarはCentOSの場合、デフォルトでは10分に1回記録するようになっていますが、10分だと問題が発生したときにログを見るのがつらいので1分に変更すべきだと思います。ちなみに、この問題が発生したとき、DBサーバーは10分間隔だったで…1分に変更しました。

userとsystemでCPU時間を100%使いつくしている状態です。
負荷が高いのはPostgreSQLを動かしているサーバーで、Apacheを動かしているサーバーの負荷は問題ありませんでした。
以上から、PostgreSQLでなにか起こっているに違いないと思って、いろいろ切り分けたのですが、PostgreSQLのログを見ても何もわかりませんでした。

何か多くのアクセスがきているのが原因かもしれないと思い、Apacheのログを見たのですが、何かが起こっている06時46分と何も起こってない時間のアクセス数はあまりかわらず、多くのアクセスがきているわけではなさそうでした。

アクセス数はかわらないのですが、どこからアクセスがきているのか?を集計したところ、bingbotが多くをしめていました。
bingbotにしぼって06時45分のログを見たのが以下です。
207.46.13.34	[30/Apr/2020:06:45:27+0900] GET /~ap2/ero/toukei_kaiseki/soukan.php?game=6151 HTTP/1.1	200	49991
40.77.167.157	[30/Apr/2020:06:45:30+0900] GET /~ap2/ero/toukei_kaiseki/soukan.php?game=19208 HTTP/1.1	200	51751
40.77.167.157	[30/Apr/2020:06:45:22+0900] GET /~ap2/ero/toukei_kaiseki/soukan.php?game=2830 HTTP/1.1	200	50266
207.46.13.34	[30/Apr/2020:06:45:22+0900] GET /~ap2/ero/toukei_kaiseki/soukan.php?game=9955 HTTP/1.1	200	51289
40.77.167.157	[30/Apr/2020:06:45:24+0900] GET /~ap2/ero/toukei_kaiseki/soukan.php?game=2041 HTTP/1.1	200	50478
40.77.167.186	[30/Apr/2020:06:45:39+0900] GET /~ap2/ero/toukei_kaiseki/soukan.php?game=21480 HTTP/1.1	504	247
40.77.167.186	[30/Apr/2020:06:45:39+0900] GET /~ap2/ero/toukei_kaiseki/soukan.php?game=20671 HTTP/1.1	504	247
40.77.167.186	[30/Apr/2020:06:45:39+0900] GET /~ap2/ero/toukei_kaiseki/soukan.php?game=20632 HTTP/1.1	504	247
207.46.13.34	[30/Apr/2020:06:45:43+0900] GET /~ap2/ero/toukei_kaiseki/soukan.php?game=16545 HTTP/1.1	504	247
soukan.phpにそこそこな頻度でアクセスした結果、504を返しています。
soukan.phpはあるゲームに似た評価をされているゲームを表示するスクリプトです。
このスクリプトは作りが悪くて…SQLを1万回程度実行するスクリプトです。
SQL自体は0.006秒程度で応答を返すのですが、それを1万回実行するので…応答がかえるまでに60秒程度かかります。
実行に60秒程度かかるスクリプトをだいたい5秒おきに実行されてしまったので負荷が高くなったようでした。

自分は、DBのネックで負荷があがる場合は、iowaitが高くなると思い込んでいたのですが、1つのスクリプトで応答時間は短いけどSQLを大量に発行して処理が追いつかなくなる場合は、userやsystemが高くなる…ということを知りました。

本格対処はsoukan.phpを書き換えることなのですが、暫定対処として、bingbotをブロックすることにしました。

CHECK制約はCASE式も使える

※今日までCHECK制約はCASE式も使えることを知らなかったので…メモします。

SQLの本やドキュメントのCHECK制約の説明は、制約の内容がとても単純なものになっていると思います。
例えば、PostgreSQLのドキュメントの場合、
CREATE TABLE products (
    product_no integer,
    name text,
    price numeric CHECK (price > 0)
);
のようにpriceが0を超える値だけ許可という単純なものです。
CKECKの()の中身はboolを返す式だったらOKですので、CASE式が使えます。

ErogameScapeではユーザーさんが任意のゲームについて、動作したOSを記載する項目があります。
OSを入力するとともに、1:動作、2:条件付きで動作、3:動作不可・インストール不可を選択します。

OSに「Wine on macOS」を追加することになったのですが、「Wine on macOS」は組み合わせがたくさんあるので、「2:条件付きで動作」のみ選択可として、memo列に組み合わせを書いて頂くことにしました。

「WIne on macOS」を選択した場合は「2:条件付きで動作」だけを許容したいです。
これをCHECK制約として追加すると以下のようになります。
ap2=# SELECT * FROM platformlist ;
 id  |       name       | sort | model
-----+------------------+------+-------
   1 | Windows98        |  100 | PC
   2 | Windows2000      |  200 | PC
   3 | WindowsXP        |  300 | PC
   4 | WindowsVista     |  400 | PC
   5 | Windows7(32bit)  |  500 | PC
   6 | Windows7(64bit)  |  600 | PC
   7 | Windows8(32bit)  |  700 | PC
   8 | Windows8(64bit)  |  800 | PC
 164 | Windows10(32bit) |  900 | PC
 165 | Windows10(64bit) | 1000 | PC
 317 | Wine on macOS    |  650 | PC
 
 ap2=# \d support_platform
                       テーブル "public.support_platform"
    列     |             型              | 照合順序 | Null 値を許容 | デフォルト
-----------+-----------------------------+----------+---------------+------------
 uid       | text                        |          | not null      |
 game      | integer                     |          | not null      |
 platform  | integer                     |          | not null      |
 operation | integer                     |          | not null      |
 memo      | text                        |          |               |
 timestamp | timestamp without time zone |          | not null      | now()
インデックス:
    "support_platform_pkey" PRIMARY KEY, btree (uid, game, platform)
外部キー制約:
    "support_platform_game_fkey" FOREIGN KEY (game) REFERENCES gamelist(id)
    "support_platform_platform_fkey" FOREIGN KEY (platform) REFERENCES platformlist(id)
    "support_platform_uid_fkey" FOREIGN KEY (uid) REFERENCES myuser(uid) ON UPDATE CASCADE ON DELETE CASCADE

凡例
operation
 1 : 動作
 2 : 条件付きで動作
 3 : 動作不可・インストール不可

ap2=# ALTER TABLE support_platform
              ADD CONSTRAINT support_platform_operation_check
      CHECK (CASE
               WHEN platform = 317
                  THEN operation = 2
           END);
ap2=# INSERT INTO support_platform (uid,game,platform,operation) VALUES ('ap2',1,1,1);
INSERT 0 1
ap2=# INSERT INTO support_platform (uid,game,platform,operation) VALUES ('ap2',1,317,2);
INSERT 0 1
ap2=# INSERT INTO support_platform (uid,game,platform,operation) VALUES ('ap2',1,317,1);
ERROR:  new row for relation "support_platform" violates check constraint "support_platform_operation_check"
DETAIL:  Failing row contains (ap2, 1, 317, 1, null, 2019-09-30 19:45:35.259753).


double precision型の0.5をroundで四捨五入すると0になる

PostgreSQL11.5の話題です。
double precision型の0.5をroundで四捨五入すると1になって欲しいところ0になります。
ap2=# SELECT round( CAST( 0.5 AS double precision ) ) ;
 round
-------
     0
double precision型はfloat8(倍精度浮動小数点数)なので、誤差が生じるのは分かるのですが…10進数の0.5は2進数で誤差無く表現できると思っているので、なぜroundで四捨五入すると0になるのか分かりませんでした。
ap2=# SELECT round( 0.5 ) AS "round( 0.5 )" , pg_typeof( 0.5 ) AS "pg_typeof( 0.5 )";
-[ RECORD 1 ]----+--------
round( 0.5 )     | 1
pg_typeof( 0.5 ) | numeric
numeric型でしたら、ちゃんと四捨五入して1になるので、numericに型変換すればOKです。
知らずにdouble precision型の数字に対してroundしてしまうこともあるかと思うので、roundするときはとりあえずnumericに型変換しておこうと思いました。

気がついた契機は以下のとおりです。
ErogameScapeでは中央値を計算することが多いのですが、PERCENTILE_CONT関数というとても便利な関数が追加されていたので、SQLを書き換えていました。

gamelistテーブルにはmedianという列があって、ゲームの中央値を格納しています。
このmedidnの列が、どういうわけか…INTEGER型で定義されています(本当に、どういうわけでしょうか…)

あるゲームに62点と63点がつけられた場合、中央値は62.5点になります。
が、medianがINTEGER型になってしまっているので、62.5を四捨五入して63を格納することにしています。

62点と63点の中央値を求めるSQLは以下のとおりです。
ap2=# SELECT PERCENTILE_CONT(0.5) WITHIN GROUP (ORDER BY tokuten) AS "62と63の中央値をPERCENTILE_CONT関数で求める"
ap2-#   FROM (
ap2(#          SELECT 62 AS tokuten
ap2(#           UNION ALL
ap2(#          SELECT 63 AS tokuten
ap2(#        ) AS t
ap2-# ;
-[ RECORD 1 ]-------------------------------+-----
62と63の中央値をPERCENTILE_CONT関数で求める | 62.5
これをroundすると…
ap2=# SELECT round( PERCENTILE_CONT(0.5) WITHIN GROUP (ORDER BY tokuten) ) AS "63になって欲しいけど62になる"
ap2-#   FROM (
ap2(#          SELECT 62 AS tokuten
ap2(#           UNION ALL
ap2(#          SELECT 63 AS tokuten
ap2(#        ) AS t;
-[ RECORD 1 ]----------------+---
63になって欲しいけど62になる | 62
PERCENTILE_CONT(0.5) WITHIN GROUP (ORDER BY tokuten)の戻り値の型を調べると、
ap2=# SELECT pg_typeof(PERCENTILE_CONT(0.5) WITHIN GROUP (ORDER BY tokuten)) AS "戻り値の型はdouble precision"
ap2-#   FROM (
ap2(#          SELECT 62 AS tokuten
ap2(#           UNION ALL
ap2(#          SELECT 63 AS tokuten
ap2(#        ) AS t;
-[ RECORD 1 ]----------------+-----------------
戻り値の型はdouble precision | double precision
double precisionなので、62.5をroundで四捨五入すると62になってしまいます。
そこで、PERCENTILE_CONTの戻り値をnumericに型変換してからroundで四捨五入します。
ap2=# SELECT round( CAST( PERCENTILE_CONT(0.5) WITHIN GROUP (ORDER BY tokuten) AS numeric ) ) AS "numericにCASTしてからなら63になってくれる"
ap2-#   FROM (
ap2(#          SELECT 62 AS tokuten
ap2(#           UNION ALL
ap2(#          SELECT 63 AS tokuten
ap2(#        ) AS t;
-[ RECORD 1 ]-----------------------------+---
numericにCASTしてからなら63になってくれる | 63
double precisionの0.5をroundで四捨五入すると0になる根本理由を知りたいのですが…浮動小数点のことから勉強し直さないと駄目でまったく感覚がなくなっているので…諦めることにしました…

WHERE句でPOVをいくつかに絞る場合など、多くの場合、ORよりINの方が速い

「WHERE句でPOVをいくつかに絞る場合など、多くの場合、ORよりINの方が速いです」とユーザーさんからコメントを頂いたので、なんでそうなるのかな?と思いましたので、EXPLAINで実行計画を確認してみました。

まず、たくさんのPOVをORで指定した場合の実行計画は以下のとおりです。
ap2=# EXPLAIN SELECT '<img  src="http://pics.dmm.co.jp/digital/pcgame/' || gamelist.dmm || '/' || gamelist.dmm || 'ps.jpg">' AS 画像 ,'' || gamelist.gamename || '' AS タイトル ,gamelist.sellday as 発売日 ,CASE WHEN brandlist.url IS NOT NULL THEN '' || brandlist.brandname || '' ELSE brandlist.brandname || ' (解散)' END AS ブランド ,gamelist.median as 中央値 ,gamelist.count2 as データ数 ,(CASE WHEN gamelist.dmm_subsc IS NOT NULL THEN '有' END) AS DMMプレミアム ,SUM(CASE WHEN povgroups.pov=33 THEN 1 ELSE 0 END) AS 萌ゲー ,SUM(CASE WHEN povgroups.pov=10 THEN 1 ELSE 0 END) AS コメディ ,SUM(CASE WHEN povgroups.pov=57 THEN 1 ELSE 0 END) AS シナリオいい ,SUM(CASE WHEN povgroups.pov=193 THEN 1 ELSE 0 END) AS 主人公強い ,SUM(CASE WHEN povgroups.pov=50 THEN 1 ELSE 0 END) AS ロリ ,SUM(CASE WHEN povgroups.pov=314 THEN 1 ELSE 0 END) AS 戦う女の子 ,SUM(CASE WHEN povgroups.pov=369 THEN 1 ELSE 0 END) AS 女装 ,SUM(CASE WHEN povgroups.pov=379 THEN 1 ELSE 0 END) AS 主人公モテモテ ,SUM(CASE WHEN povgroups.pov=1 THEN 1 ELSE 0 END) AS おかず ,SUM(CASE WHEN povgroups.pov=13 THEN 1 ELSE 0 END) AS 主人公素敵 ,SUM(CASE WHEN povgroups.pov=216 THEN 1 ELSE 0 END) AS アナルSEX ,SUM(CASE WHEN povgroups.pov=250 THEN 1 ELSE 0 END) AS 陵辱レイプ ,SUM(CASE WHEN povgroups.pov=124 THEN 1 ELSE 0 END) AS 女の子に襲 ,SUM(CASE WHEN povgroups.pov=3 THEN 1 ELSE 0 END) AS ゲーム性有 ,SUM(CASE WHEN povgroups.pov=34 THEN 1 ELSE 0 END) AS キャラ取巻く世界観 ,SUM(CASE WHEN povgroups.pov=235 THEN 1 ELSE 0 END) AS 奇人変人 ,SUM(CASE WHEN povgroups.pov=72 THEN 1 ELSE 0 END) AS 主人公ダメ ,SUM(CASE WHEN povgroups.pov=528 THEN 1 ELSE 0 END) AS 個別ルートダメ FROM (gamelist INNER JOIN brandlist ON gamelist.brandname = brandlist.id) INNER JOIN povgroups ON gamelist.ID=povgroups.game WHERE gamelist.median >= 70 AND ((gamelist.count2 >=80 AND gamelist.sellday >= '2008-01-01') OR (gamelist.count2 >=40 AND gamelist.sellday >= CURRENT_DATE - interval '6 month')) AND (povgroups.pov=33 OR povgroups.pov=10 OR povgroups.pov=57 OR povgroups.pov=193 OR povgroups.pov=50 OR povgroups.pov=314 OR povgroups.pov=369 OR povgroups.pov=379 OR povgroups.pov=1 OR povgroups.pov=13 OR povgroups.pov=216 OR povgroups.pov=250 OR povgroups.pov=124 OR povgroups.pov=3 OR povgroups.pov=34 OR povgroups.pov=235 OR povgroups.pov=72 OR povgroups.pov=528) GROUP BY gamelist.sellday,gamelist.count2,gamelist.median,gamelist.dmm,gamelist.id,brandlist.url,brandlist.brandname,gamelist.dmm_subsc ORDER BY gamelist.sellday DESC,gamelist.count2 DESC;

                    QUERY PLAN

----------------------------------------------------------------------------------------------------------------------------
----------------------------------------------------------------------------------------------------------------------------
---------------------------------------------------
 Sort  (cost=65952.58..65973.13 rows=8217 width=355)
   Sort Key: gamelist.sellday DESC, gamelist.count2 DESC
   ->  GroupAggregate  (cost=64206.29..65418.30 rows=8217 width=355)
         Group Key: gamelist.id, brandlist.url, brandlist.brandname
         ->  Sort  (cost=64206.29..64226.83 rows=8217 width=126)
               Sort Key: gamelist.id, brandlist.url, brandlist.brandname
               ->  Hash Join  (cost=11426.17..63672.01 rows=8217 width=126)
                     Hash Cond: (povgroups.game = gamelist.id)
                     ->  Bitmap Heap Scan on povgroups  (cost=9403.99..60155.36 rows=376612 width=8)
                           Recheck Cond: ((pov = 33) OR (pov = 10) OR (pov = 57) OR (pov = 193) OR (pov = 50) OR (pov = 314)
 OR (pov = 369) OR (pov = 379) OR (pov = 1) OR (pov = 13) OR (pov = 216) OR (pov = 250) OR (pov = 124) OR (pov = 3) OR (pov
= 34) OR (pov = 235) OR (pov = 72) OR (pov = 528))
                           ->  BitmapOr  (cost=9403.99..9403.99 rows=408734 width=0)
                                 ->  Bitmap Index Scan on povgroups_pov  (cost=0.00..344.66 rows=18164 width=0)
                                       Index Cond: (pov = 33)
                                 ->  Bitmap Index Scan on povgroups_pov  (cost=0.00..391.88 rows=20726 width=0)
                                       Index Cond: (pov = 10)
                                 ->  Bitmap Index Scan on povgroups_pov  (cost=0.00..1231.64 rows=65495 width=0)
                                       Index Cond: (pov = 57)
                                 ->  Bitmap Index Scan on povgroups_pov  (cost=0.00..386.18 rows=20500 width=0)
                                       Index Cond: (pov = 193)
                                 ->  Bitmap Index Scan on povgroups_pov  (cost=0.00..357.75 rows=18842 width=0)
                                       Index Cond: (pov = 50)
                                 ->  Bitmap Index Scan on povgroups_pov  (cost=0.00..351.49 rows=18541 width=0)
                                       Index Cond: (pov = 314)
                                 ->  Bitmap Index Scan on povgroups_pov  (cost=0.00..106.05 rows=5549 width=0)
                                       Index Cond: (pov = 369)
                                 ->  Bitmap Index Scan on povgroups_pov  (cost=0.00..439.66 rows=23364 width=0)
                                       Index Cond: (pov = 379)
                                 ->  Bitmap Index Scan on povgroups_pov  (cost=0.00..532.97 rows=28339 width=0)
                                       Index Cond: (pov = 1)
                                 ->  Bitmap Index Scan on povgroups_pov  (cost=0.00..785.58 rows=41754 width=0)
                                       Index Cond: (pov = 13)
                                 ->  Bitmap Index Scan on povgroups_pov  (cost=0.00..106.05 rows=5549 width=0)
                                       Index Cond: (pov = 216)
                                 ->  Bitmap Index Scan on povgroups_pov  (cost=0.00..292.88 rows=15526 width=0)
                                       Index Cond: (pov = 250)
                                 ->  Bitmap Index Scan on povgroups_pov  (cost=0.00..106.05 rows=5549 width=0)
                                       Index Cond: (pov = 124)
                                 ->  Bitmap Index Scan on povgroups_pov  (cost=0.00..106.05 rows=5549 width=0)
                                       Index Cond: (pov = 3)
                                 ->  Bitmap Index Scan on povgroups_pov  (cost=0.00..1071.77 rows=56979 width=0)
                                       Index Cond: (pov = 34)
                                 ->  Bitmap Index Scan on povgroups_pov  (cost=0.00..632.53 rows=33614 width=0)
                                       Index Cond: (pov = 235)
                                 ->  Bitmap Index Scan on povgroups_pov  (cost=0.00..360.01 rows=19144 width=0)
                                       Index Cond: (pov = 72)
                                 ->  Bitmap Index Scan on povgroups_pov  (cost=0.00..106.05 rows=5549 width=0)
                                       Index Cond: (pov = 528)
                     ->  Hash  (cost=2015.70..2015.70 rows=518 width=122)
                           ->  Hash Join  (cost=473.47..2015.70 rows=518 width=122)
                                 Hash Cond: (gamelist.brandname = brandlist.id)
                                 ->  Bitmap Heap Scan on gamelist  (cost=262.19..1797.30 rows=518 width=81)
                                       Recheck Cond: (median >= 70)
                                       Filter: (((count2 >= 80) AND (sellday >= '2008-01-01'::date)) OR ((count2 >= 40) AND
(sellday >= (('now'::cstring)::date - '6 mons'::interval))))
                                       ->  Bitmap Index Scan on tokuten_index  (cost=0.00..262.06 rows=8237 width=0)
                                             Index Cond: (median >= 70)
                                 ->  Hash  (cost=152.79..152.79 rows=4679 width=49)
                                       ->  Seq Scan on brandlist  (cost=0.00..152.79 rows=4679 width=49)
(57 行)
pov1つ1つにBitmap Index Scan on povgroups_povが動いています。
->  BitmapOr  (cost=9403.99..9403.99 rows=408734 width=0)
  ->  Bitmap Index Scan on povgroups_pov  (cost=0.00..344.66 rows=18164 width=0)
    Index Cond: (pov = 33)
  ->  Bitmap Index Scan on povgroups_pov  (cost=0.00..391.88 rows=20726 width=0)
    Index Cond: (pov = 10)
以下略

一方、たくさんのPOVをINで指定した場合の実行計画は以下のとおりです。
ap2=# EXPLAIN SELECT '<img src="http://pics.dmm.co.jp/digital/pcgame/' || gamelist.dmm || '/' || gamelist.dmm || 'ps.jpg">' AS 画像 ,'' || gamelist.gamename || '' AS タイトル ,gamelist.sellday as 発売日 ,CASE WHEN brandlist.url IS NOT NULL THEN '' || brandlist.brandname || '' ELSE brandlist.brandname || ' (解散)' END AS ブランド ,gamelist.median as 中央値 ,gamelist.count2 as データ数 ,(CASE WHEN gamelist.dmm_subsc IS NOT NULL THEN '有' END) AS DMMプレミアム ,SUM(CASE WHEN povgroups.pov=33 THEN 1 ELSE 0 END) AS 萌ゲー ,SUM(CASE WHEN povgroups.pov=10 THEN 1 ELSE 0 END) AS コメディ ,SUM(CASE WHEN povgroups.pov=57 THEN 1 ELSE 0 END) AS シナリオいい ,SUM(CASE WHEN povgroups.pov=193 THEN 1 ELSE 0 END) AS 主人公強い ,SUM(CASE WHEN povgroups.pov=50 THEN 1 ELSE 0 END) AS ロリ ,SUM(CASE WHEN povgroups.pov=314 THEN 1 ELSE 0 END) AS 戦う女の子 ,SUM(CASE WHEN povgroups.pov=369 THEN 1 ELSE 0 END) AS 女装 ,SUM(CASE WHEN povgroups.pov=379 THEN 1 ELSE 0 END) AS 主人公モテモテ ,SUM(CASE WHEN povgroups.pov=1 THEN 1 ELSE 0 END) AS おかず ,SUM(CASE WHEN povgroups.pov=13 THEN 1 ELSE 0 END) AS 主人公素敵 ,SUM(CASE WHEN povgroups.pov=216 THEN 1 ELSE 0 END) AS アナルSEX ,SUM(CASE WHEN povgroups.pov=250 THEN 1 ELSE 0 END) AS 陵辱レイプ ,SUM(CASE WHEN povgroups.pov=124 THEN 1 ELSE 0 END) AS 女の子に襲 ,SUM(CASE WHEN povgroups.pov=3 THEN 1 ELSE 0 END) AS ゲーム性有 ,SUM(CASE WHEN povgroups.pov=34 THEN 1 ELSE 0 END) AS キャラ取巻く世界観 ,SUM(CASE WHEN povgroups.pov=235 THEN 1 ELSE 0 END) AS 奇人変人 ,SUM(CASE WHEN povgroups.pov=72 THEN 1 ELSE 0 END) AS 主人公ダメ ,SUM(CASE WHEN povgroups.pov=528 THEN 1 ELSE 0 END) AS 個別ルートダメ FROM (gamelist INNER JOIN brandlist ON gamelist.brandname = brandlist.id) INNER JOIN povgroups ON gamelist.ID=povgroups.game WHERE gamelist.median >= 70 AND ((gamelist.count2 >=80 AND gamelist.sellday >= '2008-01-01') OR (gamelist.count2 >=40 AND gamelist.sellday >= CURRENT_DATE - interval '6 month')) AND (povgroups.pov IN (33,10,57,193,50,314,369,379,1,13,216,250,124,3,34,235,72,528 )) GROUP BY gamelist.sellday,gamelist.count2,gamelist.median,gamelist.dmm,gamelist.id,brandlist.url,brandlist.brandname,gamelist.dmm_subsc ORDER BY gamelist.sellday DESC,gamelist.count2 DESC;
                                                                                    QUERY PLAN

----------------------------------------------------------------------------------------------------------------------------
-------------------------------------------------------
 Sort  (cost=54480.58..54502.87 rows=8918 width=355)
   Sort Key: gamelist.sellday DESC, gamelist.count2 DESC
   ->  HashAggregate  (cost=53471.84..53895.45 rows=8918 width=355)
         Group Key: gamelist.id, brandlist.url, brandlist.brandname
         ->  Hash Join  (cost=9425.55..52602.34 rows=8918 width=126)
               Hash Cond: (povgroups.game = gamelist.id)
               ->  Bitmap Heap Scan on povgroups  (cost=7403.37..48958.22 rows=408734 width=8)
                     Recheck Cond: (pov = ANY ('{33,10,57,193,50,314,369,379,1,13,216,250,124,3,34,235,72,528}'::integer[]))
                     ->  Bitmap Index Scan on povgroups_pov  (cost=0.00..7301.18 rows=408734 width=0)
                           Index Cond: (pov = ANY ('{33,10,57,193,50,314,369,379,1,13,216,250,124,3,34,235,72,528}'::integer
[]))
               ->  Hash  (cost=2015.70..2015.70 rows=518 width=122)
                     ->  Hash Join  (cost=473.47..2015.70 rows=518 width=122)
                           Hash Cond: (gamelist.brandname = brandlist.id)
                           ->  Bitmap Heap Scan on gamelist  (cost=262.19..1797.30 rows=518 width=81)
                                 Recheck Cond: (median >= 70)
                                 Filter: (((count2 >= 80) AND (sellday >= '2008-01-01'::date)) OR ((count2 >= 40) AND (selld
ay >= (('now'::cstring)::date - '6 mons'::interval))))
                                 ->  Bitmap Index Scan on tokuten_index  (cost=0.00..262.06 rows=8237 width=0)
                                       Index Cond: (median >= 70)
                           ->  Hash  (cost=152.79..152.79 rows=4679 width=49)
                                 ->  Seq Scan on brandlist  (cost=0.00..152.79 rows=4679 width=49)
(20 行)
->  Bitmap Index Scan on povgroups_pov  (cost=0.00..7301.18 rows=408734 width=0)
 Index Cond: (pov = ANY ('{33,10,57,193,50,314,369,379,1,13,216,250,124,3,34,235,72,528}'::integer[]))
と、一回のBitmap Index Scan on povgroups_pov でpovを探しています。

ORでつなぐよりINを使った方が速いです。

psqlにおけるタブによるテーブル名等の補完の候補の表示について

psqlではクエリを書いている途中でタブを押すとテーブル名等が補完されます。
例えば以下のとおりです。
ap2=# SELECT * FROM game
game_banner_getchu_widesky         gamegrouplist                      gamelist_masterup_update_id_seq
game_banner_getchu_widesky_id_seq  gamegrouplist_id_seq               gamelist_sellday_update
game_music                         gamelist                           gamelist_sellday_update_id_seq
game_store_sp                      gamelist_id_seq
game_store_sp_id_seq               gamelist_masterup_update
PostgreSQLを以下の設定にすると、タブを押したときにどのように補完候補が抽出されているかのクエリが表示されます。

postgresql.conf
log_statement = 'all'
client_min_messages = log
SELECT * FROM gameli


LOG:  statement: SELECT pg_catalog.quote_ident(c.relname) FROM pg_catalog.pg_class c WHERE c.relkind IN ('r', 'S', 'v', 'm', 'f', 'p') AND substring(pg_catalog.quote_ident(c.relname),1,6)='gameli' AND pg_catalog.pg_table_is_visible(c.oid) AND c.relnamespace <> (SELECT oid FROM pg_catalog.pg_namespace WHERE nspname = 'pg_catalog')
UNION
SELECT pg_catalog.quote_ident(n.nspname) || '.' FROM pg_catalog.pg_namespace n WHERE substring(pg_catalog.quote_ident(n.nspname) || '.',1,6)='gameli' AND (SELECT pg_catalog.count(*) FROM pg_catalog.pg_namespace WHERE substring(pg_catalog.quote_ident(nspname) || '.',1,6) = substring('gameli',1,pg_catalog.length(pg_catalog.quote_ident(nspname))+1)) > 1
UNION
SELECT pg_catalog.quote_ident(n.nspname) || '.' || pg_catalog.quote_ident(c.relname) FROM pg_catalog.pg_class c, pg_catalog.pg_namespace n WHERE c.relnamespace = n.oid AND c.relkind IN ('r', 'S', 'v', 'm', 'f', 'p') AND substring(pg_catalog.quote_ident(n.nspname) || '.' || pg_catalog.quote_ident(c.relname),1,6)='gameli' AND substring(pg_catalog.quote_ident(n.nspname) || '.',1,6) = substring('gameli',1,pg_catalog.length(pg_catalog.quote_ident(n.nspname))+1) AND (SELECT pg_catalog.count(*) FROM pg_catalog.pg_namespace WHERE substring(pg_catalog.quote_ident(nspname) || '.',1,6) = substring('gameli',1,pg_catalog.length(pg_catalog.quote_ident(nspname))+1)) = 1
LIMIT 1000;
このSQLを実行してみると以下の結果が得られます。
           quote_ident
---------------------------------
 gamelist_sellday_update
 gamelist
 gamelist_sellday_update_id_seq
 gamelist_masterup_update_id_seq
 gamelist_id_seq
 gamelist_masterup_update
(6 行)
補完候補ってクエリで抽出していたんだなあ…とちょっと感動したのでメモしておきます。

[解決]FATAL: terminating connection due to administrator command

環境
PostgreSQL 11.4

ここ3日くらいPostgreSQLで意図しない「FATAL:  terminating connection due to administrator command」が発生してpgpoolから切り離されて困っています。

PostgreSQLのログ
2019-07-19 07:50:01.632 JST [27138] FATAL:  terminating connection due to administrator command

PostgreSQLの、ログの出力をdebug3、実行されたSQLをすべて出力している状態で現象が再現したのですが、ログを見ても原因が分かりません…

postgresql.conf
log_min_messages = debug3
log_statement = 'all'

「FATAL:  terminating connection due to administrator command」となる直接の原因は、おそらく「SELECT pg_terminate_backend(pid)」が実行されていることです。
※とても参考にさせて頂いています。とてもすごい資料です。
2019-07-19 07:50:01.615 JST [10453] LOG:  statement: SELECT pg_terminate_backend(pid)
          FROM pg_stat_activity
         WHERE pid IN (
                            SELECT l.pid
                              FROM ( SELECT pid FROM pg_locks GROUP BY pid ) l
                                 , pg_stat_activity a
                             WHERE l.pid = a.pid
                               AND now() - query_start > '5minutes'::INTERVAL
                      )
        ;
このクエリの結果、クエリが実行されてから5分を超えているプロセスにpg_terminate_backendしにいっています。
「FATAL:  terminating connection due to administrator command」を出力しているのは、プロセス番号7813なので、プロセス番号7813を抜き出すと以下の通りでした。

PostgreSQLのログ
2019-07-19 07:50:01.621 JST [7813] LOG:  statement: BEGIN
2019-07-19 07:50:01.626 JST [7813] LOG:  statement:
        SELECT tag
             , uid
                         , game
                         , comment
          FROM userreview_with_tag uwg
         WHERE EXISTS ( SELECT *
                          FROM userreview_tag u
                                                                         WHERE u.uid  = uwg.uid
                                                                           AND u.game = uwg.game
                                                                                 AND timestamp >= ( SELECT timestamp FROM userreview_tag_last_check_timestamp )
                                                                 )
        ;
        2019-07-19 07:50:01.630 JST [7813] LOG:  statement:
        SELECT uid
             , game
                         , tags
         FROM userreview_tag u
        WHERE timestamp >= ( SELECT timestamp FROM userreview_tag_last_check_timestamp )
2019-07-19 07:50:01.632 JST [7813] FATAL:  terminating connection due to administrator command


5minutesな設定でクエリを解放しそうなPostgreSQLの設定が「max_standby_streaming_delay」で、デフォルトは30秒なのですが、当時の自分がなんとなく300秒に設定していました。

userreview_tagテーブル/userreview_tag_last_check_timestampテーブルを長時間ロックしそうな処理はしていない…と思うのですが、してるのかな…


2019/07/19追記

pgpoolのログ
Jul 19 04:20:01 erogamescape13 pgpool[25274]: [2975989-1] 2019-07-19 04:20:01: pid 25274: LOG:  reading and processing packets
Jul 19 04:20:01 erogamescape13 pgpool[25274]: [2975989-2] 2019-07-19 04:20:01: pid 25274: DETAIL:  postmaster on DB node 1 was shutdown by administrative command
Jul 19 04:20:01 erogamescape13 pgpool[25274]: [2975990-1] 2019-07-19 04:20:01: pid 25274: LOG:  received degenerate backend request for node_id: 1 from pid [25274]
Jul 19 04:20:01 erogamescape13 pgpool[10368]: [449-1] 2019-07-19 04:20:01: pid 10368: LOG:  new IPC connection received
Jul 19 04:20:01 erogamescape13 pgpool[10368]: [450-1] 2019-07-19 04:20:01: pid 10368: LOG:  watchdog received the failover command from local pgpool-II on IPC interface
Jul 19 04:20:01 erogamescape13 pgpool[10368]: [451-1] 2019-07-19 04:20:01: pid 10368: LOG:  watchdog is processing the failover command [DEGENERATE_BACKEND_REQUEST] received from local pgpool-II on IPC interface
Jul 19 04:20:01 erogamescape13 pgpool[10368]: [452-1] 2019-07-19 04:20:01: pid 10368: LOG:  we have got the consensus to perform the failover
Jul 19 04:20:01 erogamescape13 pgpool[10368]: [452-2] 2019-07-19 04:20:01: pid 10368: DETAIL:  1 node(s) voted in the favor
Jul 19 04:20:01 erogamescape13 pgpool[10367]: [2976456-1] 2019-07-19 04:20:01: pid 10367: LOG:  Pgpool-II parent process has received failover request
Jul 19 04:20:01 erogamescape13 pgpool[10368]: [453-1] 2019-07-19 04:20:01: pid 10368: LOG:  new IPC connection received
Jul 19 04:20:01 erogamescape13 pgpool[10368]: [454-1] 2019-07-19 04:20:01: pid 10368: LOG:  received the failover indication from Pgpool-II on IPC interface
Jul 19 04:20:01 erogamescape13 pgpool[10368]: [455-1] 2019-07-19 04:20:01: pid 10368: LOG:  watchdog is informed of failover start by the main process
Jul 19 04:20:01 erogamescape13 pgpool[10367]: [2976457-1] 2019-07-19 04:20:01: pid 10367: LOG:  starting degeneration. shutdown host 192.168.0.13(5432)

2019/07/24追記

おそらく解決しました。
2013年9月9日から2013年9月15日に発生した障害について(続き)のときに対策として、以下のクエリをcronで5分おきに実行していました。
SELECT pg_terminate_backend(pid)
          FROM pg_stat_activity
         WHERE pid IN (
                            SELECT l.pid
                              FROM ( SELECT pid FROM pg_locks GROUP BY pid ) l
                                 , pg_stat_activity a
                             WHERE l.pid = a.pid
                               AND now() - query_start > '5minutes'::INTERVAL
                      )
        ;
pg_locksテーブルに存在するクエリで、query_startから5分たったものはpg_terminate_backendで強制的にプロセスを落とすようにしていました。
pg_terminate_backendを実行すると、PostgreSQLのログに「FATAL:  terminating connection due to administrator command」が記録されます。また、pg_terminate_backend()関数の値が定数ではない場合、pgpoolから切り離されます。

5分も実行しているクエリなんかあるのかな…と思っていたら
  • BEGINではじまるトランザクションで5分以上かかる処理が存在する
  • ユーザーさんが5分を超えるクエリを実行することがある。いまいちなSQLの場合は、いつまでも処理が終わらずにCPU使用率が100%にはりつく※ErogameScapeは誰でもクエリを実行できるようにしています。
ことがあることが分かったので、長い間実行しているクエリは殺す必要があります
twitterで「pg_terminate_backend」ではなく「pg_cancel_backend」を使用した方がよいと教わりましたので、「pg_cancel_backend」に置換しました。

あるブランドのゲームにつけられた得点の中央値を求める

SELECT b.id
     , b.brandname
     , b.kind
     , b.lost
     , PERCENTILE_CONT(0.5) WITHIN GROUP (ORDER BY tokuten) AS median
     , cast ( STDDEV(tokuten) AS INTEGER ) AS stddev
     , count(tokuten) AS count
  FROM userreview u
 INNER JOIN gamelist g
         ON u.game = g.id
 INNER JOIN brandlist b
         ON g.brandname = b.id
 WHERE b.id = 1
 GROUP BY b.id
        , b.brandname
        , b.kind
        , b.lost;
 id | brandname |    kind     | lost | median | stddev | count
----+-----------+-------------+------+--------+--------+-------
  1 | âge(age)  | CORPORATION | f    |     81 |     15 | 13807
PERCENTILE_CONT関数について
順序集合集約関数というものが9.4のときに追加されていて、中央値をたった一行で求められるようになっていました…(以前はウインドウ関数を使う必要がありました。それ以前は、とてもいろいろ書く必要がありました)

wal_keep_segments

wal_keep_segmentsはリンク先のマニュアルに書いてある通り、
ストリーミングレプリケーションにおいて、スタンバイサーバが過去のファイルセグメントを取得する必要がある場合に備え、pg_walディレクトリに保持しておくファイルセグメント数の最小値を指定します。
です。デフォルトは0です。0だからpg_walディレクトリの配下に何も生成されないかというとそうではなく、最低min_wal_sizeの設定だけpg_walファイルが生成されます。

例えば以下のようなになります。
[root@erogamescape14 pg_wal]# ls -lh
合計 97M
-rw------- 1 postgres postgres  386  5月 30 23:22 2019 0000000A.history
-rw------- 1 postgres postgres  431  6月 26 09:06 2019 0000000B.history
-rw------- 1 postgres postgres  16M  6月 26 10:00 2019 0000000B00000013000000D7
-rw------- 1 postgres postgres  16M  6月 26 10:02 2019 0000000B00000013000000D8
-rw------- 1 postgres postgres  16M  6月 26 09:06 2019 0000000B00000013000000D9
-rw------- 1 postgres postgres  16M  6月 26 09:07 2019 0000000B00000013000000DA
-rw------- 1 postgres postgres  16M  6月 26 09:30 2019 0000000B00000013000000DB
-rw------- 1 postgres postgres  16M  6月 26 09:30 2019 0000000B00000013000000DC
drwx------ 2 postgres postgres 4.0K  6月 26 09:07 2019 archive_status
ErogameScapeではストリーミングレプリケーションを使用しており、PostgreSQLのマイナーバージョンアップの際にはレプリケーションスロットを使用するのでwal_keep_segmentsは0にしていました。

ところが手順を間違えて、primaryのPostgreSQLにレプリケーションスロットを作る前に、standbyのPostgreSQLを止めてしまい、yum updateしている際に6つのWALファイルが全部新しくなってしまった結果、standbyを組み込もうとした際に「もうWALファイルがありません」的なメッセージがでてしまって、組み込めませんでした…
泣く泣く、pg_basebackupからスタートしました。

ErogameScapeでそんなに更新があるのか?と思うかもしれませんが(私もそう思っていました)、統計用のテーブルは全部のデータをDELETEして、INSERTするので、その統計用のテーブルを作り直す際にWALがたくさん生成されます。

以上から、手順を間違えることも考慮してwal_keep_segmentsを適当に設定しておくことにしました。
HDDは余裕があるので、えいやで50(walファイルは1つ16MBなので、50個で800MB)にしました。

実際、どの程度必要なのか…については、実運用環境で試せるなら、pg_walディレクトリのWALファイルの生成時刻を見ればいいのかな…と思います。
[root@erogamescape14 pg_wal]# ls -lh
合計 257M
-rw------- 1 postgres postgres  386  5月 30 23:22 2019 0000000A.history
-rw------- 1 postgres postgres  431  6月 26 09:06 2019 0000000B.history
-rw------- 1 postgres postgres  16M  6月 26 10:00 2019 0000000B00000013000000D7
-rw------- 1 postgres postgres  16M  6月 26 10:06 2019 0000000B00000013000000D8
-rw------- 1 postgres postgres  16M  6月 26 11:07 2019 0000000B00000013000000D9
-rw------- 1 postgres postgres  16M  6月 26 12:10 2019 0000000B00000013000000DA
-rw------- 1 postgres postgres  16M  6月 26 12:30 2019 0000000B00000013000000DB
-rw------- 1 postgres postgres  16M  6月 26 12:49 2019 0000000B00000013000000DC
-rw------- 1 postgres postgres  16M  6月 26 14:07 2019 0000000B00000013000000DD
-rw------- 1 postgres postgres  16M  6月 26 15:30 2019 0000000B00000013000000DE
-rw------- 1 postgres postgres  16M  6月 26 15:30 2019 0000000B00000013000000DF
-rw------- 1 postgres postgres  16M  6月 26 16:17 2019 0000000B00000013000000E0
-rw------- 1 postgres postgres  16M  6月 26 17:30 2019 0000000B00000013000000E1
-rw------- 1 postgres postgres  16M  6月 26 18:30 2019 0000000B00000013000000E2
-rw------- 1 postgres postgres  16M  6月 26 18:30 2019 0000000B00000013000000E3
-rw------- 1 postgres postgres  16M  6月 26 18:55 2019 0000000B00000013000000E4
-rw------- 1 postgres postgres  16M  6月 26 19:26 2019 0000000B00000013000000E5
-rw------- 1 postgres postgres  16M  6月 26 19:41 2019 0000000B00000013000000E6
drwx------ 2 postgres postgres 4.0K  6月 26 09:07 2019 archive_status
見ると、10時に0000000B00000013000000D7が生成されて、19時41分の時点で0000000B00000013000000Eです。16個あれば9時間持つので、standbyを9時間止めててもwal_keep_segmentsは16だったら、まあ…pg_basebackupなしに組み込めるかなあと想定できます。

記事検索