SlideShare a Scribd company logo
CustomScan APIを使い倒して俺様DBエンジンを実装する
ヘテロDB株式会社
チーフアーキテクト 兼 CEO
海外 浩平 <kaigai@heterodb.com>
CustomScan APIとは?
postgres=# explain select sum(lo_revenue), d_year, p_brand1
from flineorder, date1, part
where lo_orderdate = d_datekey
and lo_partkey = p_partkey
and p_category = 'MFGR#12’
group by d_year, p_brand1;
QUERY PLAN
-----------------------------------------------------------------------------------------------------
HashAggregate (cost=3656703.22..3656773.22 rows=7000 width=22)
Group Key: date1.d_year, part.p_brand1
-> Custom Scan (GpuPreAgg) (cost=3656580.72..3656650.72 rows=7000 width=22)
Reduction: GroupBy (Global+Local [nrooms: 1974])
Group keys: d_year, p_brand1
Combined GpuJoin: enabled
-> Custom Scan (GpuJoin) on flineorder (cost=16028.65..3638534.69 rows=28873638 width=18)
Outer Scan: flineorder (cost=0.00..10277198.49 rows=720040849 width=12)
Depth 1: GpuHashJoin(nrows 720040849...28873638)
HashSize: 1135.95KB
HashKeys: flineorder.lo_partkey
JoinQuals: (flineorder.lo_partkey = part.p_partkey)
Depth 2: GpuHashJoin(nrows 28873638...28873638)
HashSize: 442.11KB
HashKeys: flineorder.lo_orderdate
JoinQuals: (flineorder.lo_orderdate = date1.d_datekey)
referenced: lo_partkey, lo_orderdate, lo_revenue
files0: /opt/pgdata14/f_lineorder.arrow (read: 8.05GB, size: 81.81GB)
-> Seq Scan on part (cost=0.00..5426.00 rows=8020 width=14)
Filter: (p_category = 'MFGR#12'::bpchar)
-> Seq Scan on date1 (cost=0.00..72.56 rows=2556 width=8)
(21 rows)
見覚えのない
実行計画があるなぁ…。
PostgreSQL Conference Japan 2022 ~CustomScan APIを使い倒して俺様DBエンジンを実装する~
2
CustomScan APIとは?
▌概要
 エグゼキュータの一部を拡張モジュールによる
実装で置き換えたいときに利用するAPI
 拡張モジュールの開発者が利用するもので、
SQL経由でユーザが利用するものではない。
 PostgreSQL v9.5 (2016) で最初のバージョンが
提供されるようになった。
▌利点
 独自にSQL実行エンジンを強化したい場合でも、
PostgreSQLからのフォークを行わずに済む。
 自分が興味のある部分“だけ”を置き換える事が
できるので、開発規模を抑える事ができる。
IndexScan on table-Y
CustomScan (MyScan) on table-X
CustomScan (MyJoin)
Agg (Group By)
NestLoop
SeqScan on table-Z
PostgreSQL Conference Japan 2022 ~CustomScan APIを使い倒して俺様DBエンジンを実装する~
3
CustomScan APIとFDW(Foreign Data Wrapper)の違い
▌FDW: Foreign Data Wrapper
 外部のデータをあたかもPostgreSQLのテーブルで
あるかのように読み書きするためのAPI
 何を(What?)スキャンするか。
▌CustomScan API
 PostgreSQLのテーブルを異なる方法でスキャン
するためのAPI
✓ 実装上、キャッシュを参照するなどの手法もアリ
 どのように(How?)スキャンするか
Foreign
Table
Foreign
Table
Foreign
Table
Foreign
Table
CSV
mysql_fdw oracle_fdw file_fdw arrow_fdw
MySQL
データベース
Oracle
データベース
CSV
ファイル
Arrow
ファイル PostgreSQL
テーブル
SeqScan IndexScan CacheScan
FpgaScan
DpuScan
GpuScan
columnar
cache
PostgreSQL Conference Japan 2022 ~CustomScan APIを使い倒して俺様DBエンジンを実装する~
4
HeteroDB社について
▌会社概要
 商号 ヘテロDB株式会社
 設立 2017年7月4日
 資本金 999万円
 所在地 品川区北品川5-5-15
大崎ブライトコア4F
 事業内容
高速データベース製品の開発・販売
GPU&DB領域の技術コンサルティング
 受賞など
✓ IPA 未踏アドバンスト事業 第一期生(2017)
✓ GPU Technology Conference Japan 2017、Inception Award受賞
✓ 東京都ベンチャー技術大賞 技術奨励賞(2022)
▌代表者プロフィール
 海外 浩平|KaiGai Kohei
OSS開発者コミュニティにおいて、
PostgreSQLやLinux kernelの開発に
15年以上従事。主にセキュリティ・
FDWなどの技術領域に貢献する。
本日のテーマ CustomScan API や、
GPUを用いたSQL高速化モジュール・PG-Stromの開発者。
筑波大学情報学類卒(2001)、同大学院経営・政策科学研究
科修了(2003)、日本電気株式会社勤務(2003-2017)の後、
HeteroDB社を創業。
 受賞など
✓ IPA未踏ソフト「天才プログラマ―」認定(2006)
✓ OSS推進フォーラム OSS貢献者賞(2014)
✓ 日本情報処理学会 喜安記念業績賞(2014)
ヘテロジニアスコンピューティング技術をデータベース領域に適用し、
誰もが使いやすく、シンプルで高速なデータ解析基盤を提供する。
PostgreSQL Conference Japan 2022 ~CustomScan APIを使い倒して俺様DBエンジンを実装する~
5
CustomScan APIの利用例 – Microsoft Azure Cosmos DB
出典: https://learn.microsoft.com/ja-jp/azure/cosmos-db/postgresql/tutorial-shard
PostgreSQL Conference Japan 2022 ~CustomScan APIを使い倒して俺様DBエンジンを実装する~
6
CustomScan APIの利用例 – Google AlloyDB
出典: https://cloud.google.com/blog/ja/products/databases/alloydb-for-postgresql-columnar-engine
PostgreSQL Conference Japan 2022 ~CustomScan APIを使い倒して俺様DBエンジンを実装する~
7
CustomScan APIの利用例 – HeteroDB PG-Strom
postgres=# explain select sum(lo_revenue), d_year, p_brand1
from flineorder, date1, part
where lo_orderdate = d_datekey
and lo_partkey = p_partkey
and p_category = 'MFGR#12’
group by d_year, p_brand1;
QUERY PLAN
-----------------------------------------------------------------------------------------------------
HashAggregate (cost=2631106.55..2631176.55 rows=7000 width=22)
Group Key: date1.d_year, part.p_brand1
-> Custom Scan (GpuPreAgg) (cost=2630984.05..2631054.05 rows=7000 width=22)
Reduction: GroupBy (Global+Local [nrooms: 1974])
Group keys: d_year, p_brand1
Combined GpuJoin: enabled
GPU Preference: GPU0 (NVIDIA A100-PCIE-40GB)
-> Custom Scan (GpuJoin) on flineorder (cost=14190.72..2612938.03 rows=28873638 width=18)
Outer Scan: flineorder (cost=0.00..10277198.49 rows=720040849 width=12)
Depth 1: GpuHashJoin(nrows 720040849...28873638)
HashSize: 1135.95KB
HashKeys: flineorder.lo_partkey
JoinQuals: (flineorder.lo_partkey = part.p_partkey)
Depth 2: GpuHashJoin(nrows 28873638...28873638)
HashSize: 442.11KB
HashKeys: flineorder.lo_orderdate
JoinQuals: (flineorder.lo_orderdate = date1.d_datekey)
GPU Preference: GPU0 (NVIDIA A100-PCIE-40GB)
GPUDirect SQL: enabled
referenced: lo_partkey, lo_orderdate, lo_revenue
files0: /opt/pgdata14/f_lineorder.arrow (read: 8.05GB, size: 81.81GB)
-> Seq Scan on part (cost=0.00..5426.00 rows=8020 width=14)
Filter: (p_category = 'MFGR#12'::bpchar)
-> Seq Scan on date1 (cost=0.00..72.56 rows=2556 width=8)
(24 rows)
PostgreSQL Conference Japan 2022 ~CustomScan APIを使い倒して俺様DBエンジンを実装する~
8
CustomScan APIの利用例 – FUJITSU Software Enterprise Postgres 9.5
出典: https://software.fujitsu.com/jp/manual/manualfiles/m160008/j2ul2119/05z200/j2119-00-13-01-00.html
PostgreSQL Conference Japan 2022 ~CustomScan APIを使い倒して俺様DBエンジンを実装する~
9
SamsungのSmart-SSD開発チーム(?)の講演でも
PostgreSQL Conference Japan 2022 ~CustomScan APIを使い倒して俺様DBエンジンを実装する~
10
PostgreSQL向けExtensionの開発
PostgreSQL Conference Japan 2022 ~CustomScan APIを使い倒して俺様DBエンジンを実装する~
11
最小構成での PostgreSQL 拡張モジュールのビルド
$ ls
Makefile
ctidscan.h
ctidscan.c
ctidscan.sql
ctidscan.control
$ cat Makefile
MODULES = ctidscan
EXTENSION = ctidscan
DATA = ctidscan.sql
PG_CONFIG = pg_config
PGXS := $(shell $(PG_CONFIG) --pgxs)
include $(PGXS)
$ cat ctidscan.control
# ctidscan extension
comment = 'example module for custom-scan api’
default_version = '1.0'
module_pathname = '$libdir/ctidscan'
relocatable = true
✓ PostgreSQL向け拡張モジュールは、メジャー
バージョン跨ぎのバイナリ互換性の保証なし。
✓ ビルド時の ./configure 次第で変わるパラメータ
例:BLCKSZの定義やAssertの有無、構造体のレイアウトも
✓ ターゲットとなるPostgreSQLに同梱の pg_config を
利用してビルドオプションの同一性を担保する。
PostgreSQL Conference Japan 2022 ~CustomScan APIを使い倒して俺様DBエンジンを実装する~
12
拡張モジュールが制御を獲得するタイミング
#include “postgres.h”
:
:
void
_PG_init(void)
{
DefineCustomBoolVariable("enable_ctidscan",
"Enables use of ctidscan”,
NULL,
&enable_ctidscan,
true,
PGC_USERSET,
GUC_NOT_IN_SAMPLE,
NULL, NULL, NULL);
:
/* registration of the hook to add alternative path */
set_rel_pathlist_next = set_rel_pathlist_hook;
set_rel_pathlist_hook = SetCtidScanPath;
}
✓ PostgreSQLが拡張モジュールをロードすると、
まず _PG_init() 関数を呼び出す。
✓ 拡張モジュールをロードするタイミングは…
✓ その拡張モジュールで実装されているSQL関数の呼び出し。
✓ (shared|local|session)_preload_libraries パラメー
タによる指定。
✓ SQLのLOADコマンド
➔ CustomScan APIを使用する拡張モジュールの場合は、
*_preload_libraries パラメータを使うのが自然。
✓ 拡張モジュールで制御を獲得するには?
✓ “C”で実装されたSQL関数の実行
➔ 演算子やSQL関数、インデックスの評価やFDWなど、
大半のケースではこちら。
✓ フック、コールバックで登録されたタイミング
➔ CustomScan APIでは、一連の処理の中でSQL関数を必要と
する場面がないため、_PG_init() でフックやコールバックの
登録を済ませておく。
PostgreSQL Conference Japan 2022 ~CustomScan APIを使い倒して俺様DBエンジンを実装する~
13
比較的シンプルな CustomScan のサンプル
PostgreSQL Conference Japan 2022 ~CustomScan APIを使い倒して俺様DBエンジンを実装する~
14
▌https://github.com/kaigai/ctidscan
 検索条件に ctid システム列(*)に対する大小比較を含む
場合、明らかにマッチしないブロックを読み飛ばす。
 PostgreSQL v14で対応した TidRangeScan と同等だが、
CustomScan APIの提案時には 意味のある拡張モジュール
だった。
✓ (*) ctidシステム列は、タプルの存在するブロック番号と
アイテム番号を示す。
PostgreSQLはどのように実行計画を作るのか
PostgreSQL Conference Japan 2022 ~CustomScan APIを使い倒して俺様DBエンジンを実装する~
15
PostgreSQLがクエリを処理する流れ
SQL Parser
Query Optimizer
Query Executor
Buffer
Manager
SQLクエリ
パース木
クエリ実行計画
クエリ 実行結果
 SQL構文を分解し、取り扱いやすい
内部データ形式(パース木)に変換
 文法エラーの検出
 統計情報を元にコスト値を算出
 最も合理的と予想される実行計画を作成する。
 クエリ実行計画に基づいて、
ScanやJoin、Sortなどの処理を実行する。
 PostgreSQL内部のインフラを使用
PostgreSQL Conference Japan 2022 ~CustomScan APIを使い倒して俺様DBエンジンを実装する~
16
Transaction
Control
Resource
Management
IPC, Shared
Memory
クエリの実行をサポートする
多種多様な内部インフラが
用意されている。
PostgreSQLはどのように実行計画を作るか
t0 JOIN t1の候補パス
Scan
t0
Scan
t1
Join
t0,t1
統計情報)
nrows: 120万行
width: 80
インデックス:なし
候補パス
HashJoin
cost=4000
候補パス
MergeJoin
cost=12000
候補パス
NestLoop
cost=99999
候補パス
Parallel
Hash Join
cost=3000
候補パス
GpuJoin
cost=2500
WINNER!
PostgreSQLビルトインの実行パス
拡張モジュールによる提案
(PostgreSQL v9.5以降)
(PostgreSQL v9.6以降)
GpuJoin
t0,t1
統計情報)
nrows: 4000行
width: 120
インデックス:id列
PostgreSQL Conference Japan 2022 ~CustomScan APIを使い倒して俺様DBエンジンを実装する~
17
PostgreSQLがクエリを処理する流れ
PostgreSQL Conference Japan 2022 ~CustomScan APIを使い倒して俺様DBエンジンを実装する~
18
▌リレーション(Scan, Join, ...)に紐づいた
候補パス(Path構造体)を作成する。
✓ Pathには実行コストや、実行結果の出力順
(ソートされているか否か)などを保持する。
✓ ビルトインの実行計画の他に CustomPath を作成
する事ができれば、ここに突っ込む。
▌オプティマイザは最もコストの安い候補パスを
選択し、Path ⇒ Plan 構造体に変換する。
✓ Pathと異なり、Plan構造体は copyObject などの
内部APIを用いて操作できる。
▌式表現の書き換え
✓ 場合によっては、オプティマイザにヒントを与える
必要がある(custom_scan_tlist)
SQL Parser
Query Optimizer
Query Executor
SQLクエリ
パース木
クエリ実行計画
クエリ 実行結果
set_rel_pathlist_hook による実行計画の登録
PostgreSQL Conference Japan 2022 ~CustomScan APIを使い倒して俺様DBエンジンを実装する~
19
/* Hook for plugins to get control in set_rel_pathlist() */
typedef void (*set_rel_pathlist_hook_type) (PlannerInfo *root,
RelOptInfo *rel,
Index rti,
RangeTblEntry *rte);
extern PGDLLIMPORT set_rel_pathlist_hook_type
set_rel_pathlist_hook;
static void
SetCtidScanPath(PlannerInfo *root, RelOptInfo *baserel,
Index rtindex, RangeTblEntry *rte)
{
/* call the secondary custom-scan provider */
if (set_rel_pathlist_next)
set_rel_pathlist_next(root, baserel, rtindex, rte);
:
/*
* 1. リレーション種別や検索条件をチェック
* 2. CustomPathを作成し、CustomPathMethods を登録
* 3. add_path()を使ってパスを登録
*/
✓ set_rel_pathlist_hook はいつ呼ばれるのか?
➔ 特定のテーブルに対して、オプティマイザが
スキャンする方法を検討するタイミング。
✓ ビルトインのスキャン方法
• SeqScan
• IndexScan
• BitmapHeapScan
• TidScan
• ForeignScan
➔ ここに割り込んで、『俺々CustomScan』の方が
より効率的にこのテーブルをスキャンできると
いう事を教えてやる。
CustomPath構造体
PostgreSQL Conference Japan 2022 ~CustomScan APIを使い倒して俺様DBエンジンを実装する~
20
typedef struct Path
{
NodeTag type;
NodeTag pathtype;
RelOptInfo *parent;
PathTarget *pathtarget;
ParamPathInfo *param_info;
:
Cardinality rows;
Cost startup_cost;
Cost total_cost;
List *pathkeys;
} Path;
typedef struct CustomPath
{
Path path;
uint32 flags;
List *custom_paths;
List *custom_private;
const struct CustomPathMethods *methods;
} CustomPath;
▌CustomPath構造体
 『俺ならこれぐらいの処理コストで実行できる』を
PostgreSQLのオプティマイザに教えてやるための
データ構造。
 Path中の rows, startup_cost, total_cost で
処理コストを表現する。
 methods には、次工程で Path --> Plan への変換を
行うコールバック関数などを設定する。
 flagsには当該パスの特徴を指定することができる。
✓ CUSTOMPATH_SUPPORT_BACKWARD_SCAN
✓ CUSTOMPATH_SUPPORT_MARK_RESTORE
✓ CUSTOMPATH_SUPPORT_PROJECTION
RelOptInfoから条件句を抜き出す
PostgreSQL Conference Japan 2022 ~CustomScan APIを使い倒して俺様DBエンジンを実装する~
21
static void
SetCtidScanPath(PlannerInfo *root,
RelOptInfo *baserel,
Index rtindex, RangeTblEntry *rte)
{
:
/* walk on the restrict info */
foreach (lc, baserel->baserestrictinfo)
{
RestrictInfo *rinfo = lfirst(lc);
if (IS_SUPPORTED_EXPR(rino->clause))
dev_quals = lappend(dev_quals, rinfo);
}
if (dev_quals == NIL)
return;
:
cpath = makeNode(CustomPath);
cpath->custom_private = ctid_quals;
cpath->methods = &ctidscan_path_methods;
:
add_path(baserel, (Path *)cpath);
}
▌RelOptInfo
 FROM行に列挙されたリレーションを表現する構造体
 このリレーションをスキャンする Path の一覧
 基本的な統計情報
✓ ブロック数、レコード数、ALL_VISIBLEの割合
 検索に利用できるインデックスの情報
 リレーションをスキャンする条件句
 被参照列のビットマップ
 パーティションの子テーブル一覧
選ばれた Path から Plan を生成する(1/2)
PostgreSQL Conference Japan 2022 ~CustomScan APIを使い倒して俺様DBエンジンを実装する~
22
typedef struct CustomPathMethods
{
:
Plan *(*PlanCustomPath)(PlannerInfo *root,
RelOptInfo *rel,
CustomPath *best_path,
List *tlist,
List *clauses,
List *custom_plans);
:
}
typedef struct CustomScan
{
Scan scan;
uint32 flags;
List *custom_plans; /* list of Plan nodes */
List *custom_exprs; /* expressions nodes */
List *custom_private; /* private data */
List *custom_scan_tlist; /* optional tlist */
Bitmapset *custom_relids; /* RTIs by this can */
const struct CustomScanMethods *methods;
} CustomScan;
 PathからPlanを生成する
✓ 先に登録した CustomPath のコストが最も小さく、
オプティマイザに選択された場合、method に登録
した PlanCustomPath 関数がコールされる。
✓ CustomPathの内容をベースに、CustomPlanを作成する。
 オプティマイザから渡される情報
✓ tlist ターゲットリスト
✓ clauses 検索条件句
✓ custom_plans 既に Plan に変換された下位ノード
(custom_pathsをセットした場合)
 CustomScanに含めるもの
✓ 疑似ターゲットリスト(custom_scan_tlist)
✓ プライベートデータ(expressionとそれ以外)
✓ CustomScan --> CustomScanState に変換するための関数
コールバック
custom_scan_tlist の使い方(1/2)
PostgreSQL Conference Japan 2022 ~CustomScan APIを使い倒して俺様DBエンジンを実装する~
23
postgres=# explain verbose
select count(*), sum(lo_revenue), lo_orderdate % 10000
from lineorder
where lo_orderdate between 19950101 and 19950331
group by 3;
QUERY PLAN
-----------------------------------------------------------------------------------------------------
HashAggregate (cost=1155141.38..1155177.47 rows=2406 width=44)
Output: count(*), sum(lo_revenue), ((lo_orderdate % 10000))
Group Key: ((lineorder.lo_orderdate % 10000))
-> Custom Scan (GpuScan) on public.lineorder (cost=11355.56..1140071.00 rows=2009384 width=10)
Output: ((lo_orderdate % 10000)), lo_revenue
GPU Projection: (lineorder.lo_orderdate % 10000), lineorder.lo_revenue
GPU Filter: ((lineorder.lo_orderdate >= 19950101) AND (lineorder.lo_orderdate <= 19950331))
GPU Preference: GPU0 (NVIDIA A100-PCIE-40GB)
Kernel Source: /var/lib/pgdata/pgsql_tmp/pgsql_tmp_strom_3737600.12.gpu
(9 rows)
custom_scan_tlist の使い方(2/2)
PostgreSQL Conference Japan 2022 ~CustomScan APIを使い倒して俺様DBエンジンを実装する~
24
tuple tuple
SeqScan
Projection
CustomScan
Projection
tuple
CustomScan
Projection’
次工程(JOIN、GROUP BYなど)
tuple”
tuple
tuple
tuple” tuple” tuple”
テーブル定義通りの
データ構造を持つタプル
EXPLAIN VERBOSEコマンドで
Output: に表示された列から
構成されるタプル
GPU Device Projection
CPU
GPU
SSD
Smart-SSD
2種類のプライベート領域 custom_exprs と custom_privates
PostgreSQL Conference Japan 2022 ~CustomScan APIを使い倒して俺様DBエンジンを実装する~
25
▌後工程での式表現(expression)書き換え
 式表現がある場合や、複数の入力から入ってきた
データの取扱いを簡便にするため、実行計画を作成
した後に setrefs.c で式表現の書き換えを行う。
 例えば、「tbl_x の a列」「tbl_y.b % 1000の計算結果」と
いう形で表現されていた式表現を「INNER側の0番目」
「OUTER側の0番目」といった形に書き換える。
▌CustomScanのプライベート領域に式表現を
含める場合
 setrefs.c による書き換えを行ってほしい場合
➔ custom_exprs に保存する
 setrefs.c による書き換えを行わない場合
➔ custom_privates に保存する
SeqScan
CustomScan
JOIN tbl_x.a = tbl_y.b % 1000
on tbl_x
on tbl_y
output: tbl_y.b % 1000, tbl_y.c
output: tbl_x.a, tbl_y.c
SeqScan
CustomScan
JOIN INNER[0] = OUTER[0]
on tbl_x
on tbl_y
output: tbl_y.b % 1000, tbl_y.c
output: INNER[0], OUTER[1]
setrefs.c
による書き換え
パラレルスキャン用の CustomPath
PostgreSQL Conference Japan 2022 ~CustomScan APIを使い倒して俺様DBエンジンを実装する~
26
▌パラレルスキャンに必要な事
✓ 条件句に出現する関数・演算子が、ワーカーノードでも
動作するものとして宣言されている。
(immutable or stable かつ parallel_safe)
✓ テーブル上のスキャンすべきブロックを、他のワーカー
と協調して特定する事ができる。
 重複して同じブロックを読み出さない
 共有メモリ(後述のDSM)を利用して、ワーカーノードと
読出し位置を調整する。
▌CustomPathに必要な事
✓ parallel_safe = true
✓ parallel_aware = true
✓ parallel_workers > 0
✓ add_partial_path() を用いて登録する。
PG backend PG worker-1 PG worker-2
エグゼキュータを理解する
PostgreSQL Conference Japan 2022 ~CustomScan APIを使い倒して俺様DBエンジンを実装する~
27
PostgreSQLがクエリを処理する流れ
PostgreSQL Conference Japan 2022 ~CustomScan APIを使い倒して俺様DBエンジンを実装する~
28
▌実行計画(Plan)を元に、
実行時情報(PlanState)を生成する。
▌上位のノードから呼び出されたら、
有効な1行、またはNULLを返す。
▌実行終了時にリソースを開放する。
▌パラレルクエリの場合、共有メモリ(DSM: Dynamic
Shared Memory)の初期化とアタッチを行う。
SQL Parser
Query Optimizer
Query Executor
SQLクエリ
パース木
クエリ実行計画
クエリ 実行結果
実行時情報の生成(1/3)
PostgreSQL Conference Japan 2022 ~CustomScan APIを使い倒して俺様DBエンジンを実装する~
29
typedef struct CustomScan
{
Scan scan;
uint32 flags;
List *custom_plans; /* list of Plan nodes */
List *custom_exprs; /* expressions nodes */
List *custom_private; /* private data */
List *custom_scan_tlist; /* optional tlist */
Bitmapset *custom_relids; /* RTIs by this can */
const struct CustomScanMethods *methods;
} CustomScan;
typedef struct CustomScanMethods
{
const char *CustomName;
Node *(*CreateCustomScanState)(CustomScan *cscan);
} CustomScanMethods;
 CreateCustomScanState
➔ CustomScanに紐づく唯一のコールバック
 役割:CustomScanを元に、CustomScanStateまたは
構造体の先頭にCustomScanStateを含む実行時情報
を割り当てる。
MyOwnScanState
それ以外に独自に
必要な実行時情報
CustomScanState
カスタムスキャンに
必要な実行時情報
ScanState
テーブルスキャンに
必要な実行時情報
PlanState
全ての実行ノードに
共通のフィールド
⚫ Estate(共通の実行時情報)の参照
⚫ 入力行 ➔ 出力行の変換(Projection)
⚫ 出力行の型定義
⚫ INNER/OUTERノードへの参照
⚫ オープンしている Relation ハンドラ
⚫ 現在のスキャン位置情報
⚫ 実行時に呼び出されるコールバック
関数(methods)
⚫ 下位ノードのリスト(あれば)
⚫ その他、実行時に必要な情報
(GPUのデバイス管理情報、
ワーカースレッドのID、など)
実行時情報の生成(2/3)
PostgreSQL Conference Japan 2022 ~CustomScan APIを使い倒して俺様DBエンジンを実装する~
30
typedef struct CustomExecMethods
{
const char *CustomName;
/* Required executor methods */
void (*BeginCustomScan) (CustomScanState *node,
EState *estate,
int eflags);
TupleTableSlot *
(*ExecCustomScan)(CustomScanState *node);
void (*EndCustomScan) (CustomScanState *node);
void (*ReScanCustomScan) (CustomScanState *node);
:
:
void (*ExplainCustomScan) (CustomScanState *node,
List *ancestors,
ExplainState *es);
} CustomExecMethods;
▌BeginCustomScan
 出力行/入力行の型定義情報を初期化
 入力行 ➔ 出力行への変換(Projection)を初期化
 その他の実行時情報を初期化
▌ExecCustomScan
 初回実行時に
 実行結果から一行を返却する。
もしくは終端に達した場合はNULL
▌EndCustomScan
 リソースを開放する。
▌ReScanCustomScan
 もう一度スキャンをやり直す。
▌ExplainCustomScan
 EXPLAINで追加的な情報を出力する場合、
ここで追加する。
実行時情報の生成(3/3)
PostgreSQL Conference Japan 2022 ~CustomScan APIを使い倒して俺様DBエンジンを実装する~
31
void ExecInitCustomScan(...)
{
if (scanrelid > 0) {
scan_rel = ExecOpenScanRelation(...);
css->ss.ss_currentRelation = scan_rel;
}
:
if (cscan->custom_scan_tlist != NIL || scan_rel == NULL) {
scan_tupdesc = ExecTypeFromTL(cscan->custom_scan_tlist);
ExecInitScanTupleSlot(estate, &css->ss, scan_tupdesc,
&TTSOpsVirtual);
tlistvarno = INDEX_VAR;
} else {
ExecInitScanTupleSlot(estate, &css->ss,
RelationGetDescr(scan_rel),
&TTSOpsVirtual);
tlistvarno = scanrelid;
}
:
/* Init result slot, type and projection */
ExecInitResultTupleSlotTL(&css->ss.ps, &TTSOpsVirtual);
ExecAssignScanProjectionInfoWithVarno(&css->ss,
tlistvarno);
:
css->methods->BeginCustomScan(css, estate, eflags);
}
tuple
CustomScan
Projection’
tuple”
GPU Device Projection
tuple
CustomScan
Projection
tuple
次工程(JOIN、GROUP BYなど)
tuple” tuple”
custom_scan_tlist なしの
初期化パターン
custom_scan_tlist ありの
初期化パターン
※ 結局、自前で初期化を
やり直す方が多いが…。
TupleTableSlot構造体について
PostgreSQL Conference Japan 2022 ~CustomScan APIを使い倒して俺様DBエンジンを実装する~
32
typedef struct TupleTableSlot
{
NodeTag type;
uint16 tts_flags; /* Boolean states */
AttrNumber tts_nvalid; /* # of valid values */
const TupleTableSlotOps *const tts_ops;
TupleDesc tts_tupleDescriptor; /* 列定義情報 */
Datum *tts_values; /* 列ごとの値 */
bool *tts_isnull; /* NULL値フラグ */
MemoryContext tts_mcxt;
ItemPointerData tts_tid; /* stored tuple's tid */
Oid tts_tableOid; /* table oid of tuple */
} TupleTableSlot;
typedef struct HeapTupleTableSlot
{
TupleTableSlot base;
HeapTuple tuple; /* physical tuple */
uint32 off; /* state for deforming */
HeapTupleData tupdata; /* optional workspace */
} HeapTupleTableSlot;
▌TupleTableSlot構造体の役割
 列定義情報(TupleDesc)と、そのデータ形式に沿った
一行分のデータを保持する。
➔ 各実行ノード間でデータの受け渡しに用いる。
 PostgreSQL v12 のプラガブルストレージ機構のマージと
共に、多様なデータ構造を定義できるようになった。
▌TTSOpsVirtualの場合
 有効長tts_nvalid の配列tts_values/tts_isnullに
列単位で展開された値が入る。
 最も汎用的な形式である一方、物理テーブルをスキャン
した場合、タプルを展開しないといけない。
▌物理タプルをそのまま使う場合は?
 HeapTupleTableSlot か、さらに Buffer へのポインタを
付加した BufferHeapTupleTableSlot を利用すれば、
TupleTableSlot に物理タプルを格納できる。
 table_slot_callbacks(relation) を使えば、
テーブルの TupleTableSlotOps を取得できる。
(v12以降、特定の物理タプル形式を前提にできない)
エグゼキュータの構造
PostgreSQL Conference Japan 2022 ~CustomScan APIを使い倒して俺様DBエンジンを実装する~
33
BeginCustomScan
ExecutorStart
initPlan
上位ノードの初期化
ExecInitCustomScan
ExecCustomScan
ExecutorRun
ExecutePlan
上位ノードの実行
ExecCustomScan
EndCustomScan
ExecutorEnd
ExecEndPlan
上位ノードの終了処理
ExecEndCustomScan
拡張モジュールで
実装すべき箇所
どのような実装手段でも構わないので、
✓ 指定されたテーブルを読み出す
✓ 条件句に合致したタプルだけを返す
✓ 直上のノードが「入力値」として
期待するデータ型のタプルを返す。
ことを守っていればよい。
GPU
列キャッシュ
SIMD命令
量子
コンピュータ
Smart-SSD
パラレルクエリの対応(1/2)
PostgreSQL Conference Japan 2022 ~CustomScan APIを使い倒して俺様DBエンジンを実装する~
34
postgres=# explain verbose
select count(*), lo_orderdate % 10000
from lineorder
where lo_orderdate between 19950101 and 19950331
group by 2;
QUERY PLAN
--------------------------------------------------------------------------------------------
Finalize HashAggregate (cost=726605.39..726635.47 rows=2406 width=12)
Output: count(*), ((lo_orderdate % 10000))
Group Key: ((lineorder.lo_orderdate % 10000))
-> Gather (cost=726070.06..726581.33 rows=4812 width=12)
Output: ((lo_orderdate % 10000)), (PARTIAL count(*))
Workers Planned: 2
-> Partial HashAggregate (cost=725070.06..725100.13 rows=2406 width=12)
Output: ((lo_orderdate % 10000)), PARTIAL count(*)
Group Key: ((lineorder.lo_orderdate % 10000))
-> Parallel Custom Scan (GpuScan) on public.lineorder (cost=29652.44..720883.84 rows=837243 width=4)
Output: ((lo_orderdate % 10000))
GPU Projection: (lineorder.lo_orderdate % 10000)
GPU Filter: ((lineorder.lo_orderdate >= 19950101) AND (lineorder.lo_orderdate <= 19950331))
GPU Preference: GPU0 (NVIDIA A100-PCIE-40GB)
Kernel Source: /var/lib/pgdata/pgsql_tmp/pgsql_tmp_strom_3870445.12.gpu
Kernel Binary: /var/lib/pgdata/pgsql_tmp/pgsql_tmp_strom_3870445.13.ptx
(16 rows)
Gatherがワーカーを立ち上げ、
ワーカーから返された結果も含め、
上位ノードへ結果を返す。
パラレルクエリの対応(2/2)
PostgreSQL Conference Japan 2022 ~CustomScan APIを使い倒して俺様DBエンジンを実装する~
35
typedef struct CustomExecMethods
{
const char *CustomName;
:
/* optional: needed if parallel execution */
Size (*EstimateDSMCustomScan)(CustomScanState *node,
ParallelContext *pcxt);
void (*InitializeDSMCustomScan)(CustomScanState *node,
ParallelContext *pcxt,
void *coordinate);
void (*ReInitializeDSMCustomScan)(CustomScanState *node,
ParallelContext *pcxt,
void *coordinate);
void (*InitializeWorkerCustomScan)(CustomScanState *node,
shm_toc *toc,
void *coordinate);
void (*ShutdownCustomScan) (CustomScanState *node);
:
} CustomExecMethods;
Gatherノードの動き
① 下位ノードの共有メモリ(DSM)必要量を計算
✓ EstimateDSMCustomScanが呼ばれる
② IPC用の共有メモリを確保し、各ノードごとに初期化
✓ InitializeDSMCustomScanが呼ばれる
③ Partial Aggregate以下を実行するワーカプロセスを起動
④ 各ワーカで初期化済みの共有メモリをアタッチする。
✓ InitializeWorkerCustomScan が呼ばれる
⑤ バックエンド・各ワーカーで終端までスキャンを行う
⑥ 各ワーカープロセスの終了
⑦ 共有メモリを開放する前に、 シャットダウン処理を実行
✓ ShutdownCustomScan が呼ばれる。
⑧ エグゼキュータの終了処理
✓ EndCustomScanが呼ばれる
Gather
Partial Aggregate
Hash Join
CustomScan
SeqScan
Partial Aggregate
Hash Join
CustomScan
SeqScan
Partial Aggregate
Hash Join
CustomScan
SeqScan
共有メモリ
(DSM)
PostgreSQL Conference Japan 2022 ~CustomScan APIを使い倒して俺様DBエンジンを実装する~
36
JOINとGROUP BY
JOINとGROUP BY(1/2)
PostgreSQL Conference Japan 2022 ~CustomScan APIを使い倒して俺様DBエンジンを実装する~
37
HashJoin
Projection
CustomScan
Projection
tuple
tuple
tuple
tuple
tuple” tuple”
次工程(他のJOIN、GROUP BYなど)
どのような実装手段でも構わないので、
✓ 要求された下位ノードからタプルを読み出す。
✓ 結合条件に従ってJOIN処理を行い、直上のノードが
「入力値」として期待するデータ型のタプルを返す。
✓ 集約式に従ってGROUP BY処理を行い、直上のノードが
「入力値」として期待するデータ型のタプルを返す。
ことを守っていればよい。
JOINとGROUP BY(2/2)
PostgreSQL Conference Japan 2022 ~CustomScan APIを使い倒して俺様DBエンジンを実装する~
38
/* Hook for plugins to get control in
add_paths_to_joinrel() */
typedef void (*set_join_pathlist_hook_type)
(PlannerInfo *root,
RelOptInfo *joinrel,
RelOptInfo *outerrel,
RelOptInfo *innerrel,
JoinType jointype,
JoinPathExtraData *extra);
extern PGDLLIMPORT set_join_pathlist_hook_type
set_join_pathlist_hook;
/* Hook for plugins to get control when
grouping_planner() plans upper rels */
typedef void (*create_upper_paths_hook_type)
(PlannerInfo *root,
UpperRelationKind stage,
RelOptInfo *input_rel,
RelOptInfo *output_rel,
void *extra);
extern PGDLLIMPORT create_upper_paths_hook_type
create_upper_paths_hook;
▌JOINに対する Path の追加
 set_join_pathlist_hook を利用する。
 joinrelは、outerrelとinnerrelを結合した表を
意味する RelOptInfo で、ここに CustomPath を追加す
る。
▌GROUP BYに対する Path の追加
 SCAN/JOINが一通り終わった後、より上位の実行計画を
積み上げるという意味で UPPER-PATH と呼んでいる。
 create_upper_paths_hookを利用する。
 UpperRelationKindは以下の8種類
✓ UPPERREL_SETOP
✓ UPPERREL_PARTIAL_GROUP_AGG
✓ UPPERREL_GROUP_AGG
✓ UPPERREL_WINDOW
✓ UPPERREL_PARTIAL_DISTINCT
✓ UPPERREL_DISTINCT
✓ UPPERREL_ORDERED
✓ UPPERREL_FINAL
下位ノードを持つ CustomPath の作成
PostgreSQL Conference Japan 2022 ~CustomScan APIを使い倒して俺様DBエンジンを実装する~
39
typedef struct CustomPath
{
Path path;
uint32 flags; /* mask of CUSTOMPATH_* flags */
List *custom_paths; /* list of child Path */
List *custom_private;
const struct CustomPathMethods *methods;
} CustomPath;
typedef struct CustomPathMethods
{
const char *CustomName;
/* Convert Path to a Plan */
Plan *(*PlanCustomPath) (PlannerInfo *root,
RelOptInfo *rel,
CustomPath *best_path,
List *tlist,
List *clauses,
List *custom_plans);
:
}
typedef struct CustomScan
{
Scan scan;
uint32 flags;
List *custom_plans; /* list of Plan nodes */
List *custom_exprs;
List *custom_private;
List *custom_scan_tlist;
Bitmapset *custom_relids;
CustomScanMethods *methods;
} CustomScan;
void
BeginCustomScan(CustomScanState *node, ...)
{
CustomScan *cscan = (CustomScan *)node->ss.ps->plan;
foreach (lc, cscan->custom_plans)
{
Plan *child_plan = lfirst(lc);
child_ps = ExecInitNode(child_plan, ...);
:
PostgreSQL Conference Japan 2022 ~CustomScan APIを使い倒して俺様DBエンジンを実装する~
40
CustomScan APIの拡張 ~ 非同期APIへの対応
FDWにおける非同期API(1/2)
PostgreSQL Conference Japan 2022 ~CustomScan APIを使い倒して俺様DBエンジンを実装する~
41
子テーブル①
(Foreign Table)
子テーブル②
(Foreign Table)
子テーブル③
(Foreign Table)
子テーブル④
(Foreign Table)
リモートサーバー
Gather
Append
Foreign Scan on ①
Foreign Scan on ②
Foreign Scan on ③
Foreign Scan on ④
パーティションテーブル
nworkers=2
backend
worker-1
worker-2
FDWにおける非同期API(1/2)
PostgreSQL Conference Japan 2022 ~CustomScan APIを使い倒して俺様DBエンジンを実装する~
42
子テーブル①
(Foreign Table)
子テーブル②
(Foreign Table)
子テーブル③
(Foreign Table)
子テーブル④
(Foreign Table)
リモートサーバー
Gather
Append
Foreign Scan on ①
Foreign Scan on ②
Foreign Scan on ③
Foreign Scan on ④
パーティションテーブル
nworkers=2
worker-1
FDWにおける非同期API(2/2)
PostgreSQL Conference Japan 2022 ~CustomScan APIを使い倒して俺様DBエンジンを実装する~
43
/* Support functions for asynchronous execution */
bool IsForeignPathAsyncCapable(ForeignPath *path);
ForeignPathが非同期実行をサポートしているかどうかを
オプティマイザに通知する
void ForeignAsyncRequest(AsyncRequest *areq);
リモートサーバから非同期に受け取ったタプルを一つ、
呼び出し元へ返す、またはareq->callback_pending=trueを
セットして結果待ちである事を呼び出し元に知らせる、あるい
はスロットにNULLをセットして終端に達した事を知らせる。
void ForeignAsyncConfigureWait(AsyncRequest *areq);
結果待ちである場合、WaitEventSetWait()の対象となる
ファイルディスクリプタを追加する。
(読み出し可能となった時点でプロセスが起床する)
void ForeignAsyncNotify(AsyncRequest *areq);
新たにリモートから処理結果を受け取ったら、そのタプルを
一つ、呼び出し元へ返す。
① ForeignAsyncRequest(初回)を通じて、リモートサーバに
実行開始のリクエスト。
Append
Foreign Scan on ①
Foreign Scan on ②
Foreign Scan on ③
SeqScan on ④
FDWにおける非同期API(2/2)
PostgreSQL Conference Japan 2022 ~CustomScan APIを使い倒して俺様DBエンジンを実装する~
44
/* Support functions for asynchronous execution */
bool IsForeignPathAsyncCapable(ForeignPath *path);
ForeignPathが非同期実行をサポートしているかどうかを
オプティマイザに通知する
void ForeignAsyncRequest(AsyncRequest *areq);
リモートサーバから非同期に受け取ったタプルを一つ、
呼び出し元へ返す、またはareq->callback_pending=trueを
セットして結果待ちである事を呼び出し元に知らせる、あるい
はスロットにNULLをセットして終端に達した事を知らせる。
void ForeignAsyncConfigureWait(AsyncRequest *areq);
結果待ちである場合、WaitEventSetWait()の対象となる
ファイルディスクリプタを追加する。
(読み出し可能となった時点でプロセスが起床する)
void ForeignAsyncNotify(AsyncRequest *areq);
新たにリモートから処理結果を受け取ったら、そのタプルを
一つ、呼び出し元へ返す。
① ForeignAsyncRequest(初回)を通じて、リモートサーバに
実行開始のリクエスト。
② ForeignAsyncConfigureWaitにより同期イベントをセット
Append
Foreign Scan on ①
Foreign Scan on ②
Foreign Scan on ③
SeqScan on ④
FDWにおける非同期API(2/2)
PostgreSQL Conference Japan 2022 ~CustomScan APIを使い倒して俺様DBエンジンを実装する~
45
/* Support functions for asynchronous execution */
bool IsForeignPathAsyncCapable(ForeignPath *path);
ForeignPathが非同期実行をサポートしているかどうかを
オプティマイザに通知する
void ForeignAsyncRequest(AsyncRequest *areq);
リモートサーバから非同期に受け取ったタプルを一つ、
呼び出し元へ返す、またはareq->callback_pending=trueを
セットして結果待ちである事を呼び出し元に知らせる、あるい
はスロットにNULLをセットして終端に達した事を知らせる。
void ForeignAsyncConfigureWait(AsyncRequest *areq);
結果待ちである場合、WaitEventSetWait()の対象となる
ファイルディスクリプタを追加する。
(読み出し可能となった時点でプロセスが起床する)
void ForeignAsyncNotify(AsyncRequest *areq);
新たにリモートから処理結果を受け取ったら、そのタプルを
一つ、呼び出し元へ返す。
① ForeignAsyncRequest(初回)を通じて、リモートサーバに
実行開始のリクエスト。
② ForeignAsyncConfigureWaitにより同期イベントをセット
③ リモート実行中に、同期実行できるものを実行
Append
Foreign Scan on ①
Foreign Scan on ②
Foreign Scan on ③
SeqScan on ④
FDWにおける非同期API(2/2)
PostgreSQL Conference Japan 2022 ~CustomScan APIを使い倒して俺様DBエンジンを実装する~
46
/* Support functions for asynchronous execution */
bool IsForeignPathAsyncCapable(ForeignPath *path);
ForeignPathが非同期実行をサポートしているかどうかを
オプティマイザに通知する
void ForeignAsyncRequest(AsyncRequest *areq);
リモートサーバから非同期に受け取ったタプルを一つ、
呼び出し元へ返す、またはareq->callback_pending=trueを
セットして結果待ちである事を呼び出し元に知らせる、あるい
はスロットにNULLをセットして終端に達した事を知らせる。
void ForeignAsyncConfigureWait(AsyncRequest *areq);
結果待ちである場合、WaitEventSetWait()の対象となる
ファイルディスクリプタを追加する。
(読み出し可能となった時点でプロセスが起床する)
void ForeignAsyncNotify(AsyncRequest *areq);
新たにリモートから処理結果を受け取ったら、そのタプルを
一つ、呼び出し元へ返す。
① ForeignAsyncRequest(初回)を通じて、リモートサーバに
実行開始のリクエスト。
② ForeignAsyncConfigureWaitにより同期イベントをセット
③ リモート実行中に、同期実行できるものを実行
④ 結果が返ってきていれば、ForeignAsyncNotifyを通じ
て結果を取得。一行を返す。
Append
Foreign Scan on ①
Foreign Scan on ②
Foreign Scan on ③
SeqScan on ④
CustomScan APIにおける非同期処理APIの追加(1/2)
PostgreSQL Conference Japan 2022 ~CustomScan APIを使い倒して俺様DBエンジンを実装する~
47
▌ForeignScan の非同期APIとの違いは?
 ほとんどない。
 CustomScanが非同期APIをサポートするかどうか、
CUSTOMPATH_SUPPORT_ASYNC_EXECUTIONフラグを用いてオプ
(専用のコールバックは設けていない)
▌追加するコールバックは
 void CustomScanAsyncRequest(AsyncRequest *areq);
 void CustomScanAsyncConfigureWait(AsyncRequest *areq);
 void CustomScanAsyncNotify(AsyncRequest *areq);
➔ 使い方は FDW のケースと全く同一
CustomScan APIにおける非同期処理APIの追加(2/2)
PostgreSQL Conference Japan 2022 ~CustomScan APIを使い倒して俺様DBエンジンを実装する~
48
Computational Storage
SSDドライブ上にFPGAやARMプロセッサを
搭載し、ストレージドライブ上でユーザの
開発したソフトウェアを実行する事ができる。
Flashストレージの業界では最もホットな話題の一つ。
仮に2U/24ドライブのサーバにフル実装したら、
24ノードのワーカーが走っているのと同じ。☺
子テーブル①
(Heap Table)
子テーブル②
(Heap Table)
子テーブル③
(Heap Table)
子テーブル④
(Heap Table)
パーティションテーブル
shared
buffer
Filesystem
NVME
Block
System
参照
SELECT + Index
書き込み
INSERT/UPDATE/DELETE
CustomScan APIにおける非同期処理APIの追加(2/2)
PostgreSQL Conference Japan 2022 ~CustomScan APIを使い倒して俺様DBエンジンを実装する~
49
Computational Storage
SSDドライブ上にFPGAやARMプロセッサを
搭載し、ストレージドライブ上でユーザの
開発したソフトウェアを実行する事ができる。
Flashストレージの業界では最もホットな話題の一つ。
仮に2U/24ドライブのサーバにフル実装したら、
24ノードのワーカーが走っているのと同じ。☺
子テーブル①
(Heap Table)
子テーブル②
(Heap Table)
子テーブル③
(Heap Table)
子テーブル④
(Heap Table)
パーティションテーブル
shared
buffer
Filesystem
NVME
Block
System
検索・集計
SELECT + Scan
まとめ
 CustomScan APIとは
✓ FDWが「何を」テーブルとして見せるのかに対して、テーブルを「どのように」処理するかを
プラガブルにするための機構。
✓ PostgreSQL v9.5でサポート。PostgreSQLを母体とするソリューションで割と使われている。
 オプティマイザ
✓ set_rel_pathlist_hook やset_join_pathlist_hookを用いて、独自の CustomPath を追加する。
✓ 上位ノードに結果を返す際の列定義情報(custom_scan_tlist)に注意する。
 エグゼキュータ
✓ BeginCustomScanで初期化、ExecCustomScanで実行、EndCustomScanで終了
✓ 上位ノードに結果を返す際の列定義情報(TupleDesc)に注意。必ず一致していないとダメ。
✓ 本来の SCAN や JOIN の処理と同じ結果を返しさえすれば、どのような方法で実装してもよい。
✓ パラレルクエリの場合は、共有メモリ(DSM)を介して他のワーカーと協調する。
 今後の機能強化
✓ ForeignScan同様の非同期処理用のAPIを提案中
✓ Smart-SSDやSmart-NICでSQLを処理する際に、非同期処理を記述できるようになる。
PostgreSQL Conference Japan 2022 ~CustomScan APIを使い倒して俺様DBエンジンを実装する~
50
オモシロ技術を、カタチにする。

More Related Content

PPTX
PostgreSQLのfull_page_writesについて(第24回PostgreSQLアンカンファレンス@オンライン 発表資料)
PDF
PostgreSQL13でのレプリケーション関連の改善について(第14回PostgreSQLアンカンファレンス@オンライン)
PDF
まずやっとくPostgreSQLチューニング
PDF
カスタムプランと汎用プラン
PDF
YugabyteDBの実行計画を眺める(NewSQL/分散SQLデータベースよろず勉強会 #3 発表資料)
PDF
PostgreSQL13でのpg_basebackupの改善について(第13回PostgreSQLアンカンファレンス@オンライン)
PPTX
PostgreSQLの統計情報について(第26回PostgreSQLアンカンファレンス@オンライン 発表資料)
PDF
PostgreSQLのリカバリ超入門(もしくはWAL、CHECKPOINT、オンラインバックアップの仕組み)
PostgreSQLのfull_page_writesについて(第24回PostgreSQLアンカンファレンス@オンライン 発表資料)
PostgreSQL13でのレプリケーション関連の改善について(第14回PostgreSQLアンカンファレンス@オンライン)
まずやっとくPostgreSQLチューニング
カスタムプランと汎用プラン
YugabyteDBの実行計画を眺める(NewSQL/分散SQLデータベースよろず勉強会 #3 発表資料)
PostgreSQL13でのpg_basebackupの改善について(第13回PostgreSQLアンカンファレンス@オンライン)
PostgreSQLの統計情報について(第26回PostgreSQLアンカンファレンス@オンライン 発表資料)
PostgreSQLのリカバリ超入門(もしくはWAL、CHECKPOINT、オンラインバックアップの仕組み)

What's hot (20)

PPTX
祝!PostgreSQLレプリケーション10周年!徹底紹介!!
PPTX
PostgreSQLモニタリングの基本とNTTデータが追加したモニタリング新機能(Open Source Conference 2021 Online F...
PDF
PGOを用いたPostgreSQL on Kubernetes入門(PostgreSQL Conference Japan 2022 発表資料)
PPTX
PostgreSQL開発コミュニティに参加しよう! ~2022年版~(Open Source Conference 2022 Online/Kyoto 発...
PDF
PG-REXで学ぶPacemaker運用の実例
PPTX
オンライン物理バックアップの排他モードと非排他モードについて ~PostgreSQLバージョン15対応版~(第34回PostgreSQLアンカンファレンス...
PPTX
フックを使ったPostgreSQLの拡張機能を作ってみよう!(第33回PostgreSQLアンカンファレンス@オンライン 発表資料)
PDF
PostgreSQLレプリケーション10周年!徹底紹介!(PostgreSQL Conference Japan 2019講演資料)
PDF
統計情報のリセットによるautovacuumへの影響について(第39回PostgreSQLアンカンファレンス@オンライン 発表資料)
PPTX
監査要件を有するシステムに対する PostgreSQL 導入の課題と可能性
PPTX
スケールアウトするPostgreSQLを目指して!その第一歩!(NTTデータ テクノロジーカンファレンス 2020 発表資料)
PDF
PostgreSQLの運用・監視にまつわるエトセトラ
PDF
PostgreSQLアーキテクチャ入門(INSIGHT OUT 2011)
PDF
pg_dbms_statsの紹介
PDF
pg_hint_planを知る(第37回PostgreSQLアンカンファレンス@オンライン 発表資料)
PPTX
PostgreSQLのロール管理とその注意点(Open Source Conference 2022 Online/Osaka 発表資料)
PDF
Kubernetesのしくみ やさしく学ぶ 内部構造とアーキテクチャー
PDF
Inside vacuum - 第一回PostgreSQLプレ勉強会
PDF
Azure Database for PostgreSQL 入門 (PostgreSQL Conference Japan 2021)
PDF
pg_walinspectについて調べてみた!(第37回PostgreSQLアンカンファレンス@オンライン 発表資料)
祝!PostgreSQLレプリケーション10周年!徹底紹介!!
PostgreSQLモニタリングの基本とNTTデータが追加したモニタリング新機能(Open Source Conference 2021 Online F...
PGOを用いたPostgreSQL on Kubernetes入門(PostgreSQL Conference Japan 2022 発表資料)
PostgreSQL開発コミュニティに参加しよう! ~2022年版~(Open Source Conference 2022 Online/Kyoto 発...
PG-REXで学ぶPacemaker運用の実例
オンライン物理バックアップの排他モードと非排他モードについて ~PostgreSQLバージョン15対応版~(第34回PostgreSQLアンカンファレンス...
フックを使ったPostgreSQLの拡張機能を作ってみよう!(第33回PostgreSQLアンカンファレンス@オンライン 発表資料)
PostgreSQLレプリケーション10周年!徹底紹介!(PostgreSQL Conference Japan 2019講演資料)
統計情報のリセットによるautovacuumへの影響について(第39回PostgreSQLアンカンファレンス@オンライン 発表資料)
監査要件を有するシステムに対する PostgreSQL 導入の課題と可能性
スケールアウトするPostgreSQLを目指して!その第一歩!(NTTデータ テクノロジーカンファレンス 2020 発表資料)
PostgreSQLの運用・監視にまつわるエトセトラ
PostgreSQLアーキテクチャ入門(INSIGHT OUT 2011)
pg_dbms_statsの紹介
pg_hint_planを知る(第37回PostgreSQLアンカンファレンス@オンライン 発表資料)
PostgreSQLのロール管理とその注意点(Open Source Conference 2022 Online/Osaka 発表資料)
Kubernetesのしくみ やさしく学ぶ 内部構造とアーキテクチャー
Inside vacuum - 第一回PostgreSQLプレ勉強会
Azure Database for PostgreSQL 入門 (PostgreSQL Conference Japan 2021)
pg_walinspectについて調べてみた!(第37回PostgreSQLアンカンファレンス@オンライン 発表資料)
Ad

Similar to 20221111_JPUG_CustomScan_API (20)

PDF
PostgreSQL v9.5の新機能~CustomScan/Join Interface
PDF
[Cloud OnAir] 最新アップデート Google Cloud データ関連ソリューション 2020年5月14日 放送
PDF
20180920_DBTS_PGStrom_JP
PDF
PostgreSQL最新動向 ~カラムナストアから生成AI連携まで~ (Open Source Conference 2025 Tokyo/Spring ...
PPTX
Azure Cosmos DB を使った クラウドネイティブアプリケーションの 設計パターン
PDF
SSDとGPUがPostgreSQLを加速する【OSC.Enterprise】
PPTX
Cosmos DB 入門 multi model multi API編
PDF
20170127 JAWS HPC-UG#8
PPTX
Azure Database for MySQL PostgreSQLを使って運用の手間を省きませんか?
PDF
20181212 - PGconf.ASIA - LT
PDF
PostgreSQL 12の話
PPTX
Azure Cosmos DB を使った高速分散アプリケーションの設計パターン
PDF
PostgreSQL17対応版 EXPLAINオプションについて (第49回PostgreSQLアンカンファレンス@東京 発表資料)
PDF
[Japan Tech summit 2017] DAL 005
PPT
Inside mobage platform
PDF
PL/CUDA - Fusion of HPC Grade Power with In-Database Analytics
PPTX
機械学習 / Deep Learning 大全 (5) Tool編
PDF
CDP Night #1 静的コンテンツ配信編
PPTX
PostgreSQL14の pg_stat_statements 改善(第23回PostgreSQLアンカンファレンス@オンライン 発表資料)
PDF
RailsエンジニアのためのSQLチューニング速習会
PostgreSQL v9.5の新機能~CustomScan/Join Interface
[Cloud OnAir] 最新アップデート Google Cloud データ関連ソリューション 2020年5月14日 放送
20180920_DBTS_PGStrom_JP
PostgreSQL最新動向 ~カラムナストアから生成AI連携まで~ (Open Source Conference 2025 Tokyo/Spring ...
Azure Cosmos DB を使った クラウドネイティブアプリケーションの 設計パターン
SSDとGPUがPostgreSQLを加速する【OSC.Enterprise】
Cosmos DB 入門 multi model multi API編
20170127 JAWS HPC-UG#8
Azure Database for MySQL PostgreSQLを使って運用の手間を省きませんか?
20181212 - PGconf.ASIA - LT
PostgreSQL 12の話
Azure Cosmos DB を使った高速分散アプリケーションの設計パターン
PostgreSQL17対応版 EXPLAINオプションについて (第49回PostgreSQLアンカンファレンス@東京 発表資料)
[Japan Tech summit 2017] DAL 005
Inside mobage platform
PL/CUDA - Fusion of HPC Grade Power with In-Database Analytics
機械学習 / Deep Learning 大全 (5) Tool編
CDP Night #1 静的コンテンツ配信編
PostgreSQL14の pg_stat_statements 改善(第23回PostgreSQLアンカンファレンス@オンライン 発表資料)
RailsエンジニアのためのSQLチューニング速習会
Ad

More from Kohei KaiGai (20)

PDF
20221116_DBTS_PGStrom_History
PDF
20211112_jpugcon_gpu_and_arrow
PDF
20210928_pgunconf_hll_count
PDF
20210731_OSC_Kyoto_PGStrom3.0
PDF
20210511_PGStrom_GpuCache
PDF
20210301_PGconf_Online_GPU_PostGIS_GiST_Index
PDF
20201128_OSC_Fukuoka_Online_GPUPostGIS
PDF
20201113_PGconf_Japan_GPU_PostGIS
PDF
20201006_PGconf_Online_Large_Data_Processing
PDF
20200828_OSCKyoto_Online
PDF
20200806_PGStrom_PostGIS_GstoreFdw
PDF
20200424_Writable_Arrow_Fdw
PDF
20191211_Apache_Arrow_Meetup_Tokyo
PDF
20191115-PGconf.Japan
PDF
20190926_Try_RHEL8_NVMEoF_Beta
PDF
20190925_DBTS_PGStrom
PDF
20190909_PGconf.ASIA_KaiGai
PDF
20190516_DLC10_PGStrom
PDF
20190418_PGStrom_on_ArrowFdw
PDF
20190314 PGStrom Arrow_Fdw
20221116_DBTS_PGStrom_History
20211112_jpugcon_gpu_and_arrow
20210928_pgunconf_hll_count
20210731_OSC_Kyoto_PGStrom3.0
20210511_PGStrom_GpuCache
20210301_PGconf_Online_GPU_PostGIS_GiST_Index
20201128_OSC_Fukuoka_Online_GPUPostGIS
20201113_PGconf_Japan_GPU_PostGIS
20201006_PGconf_Online_Large_Data_Processing
20200828_OSCKyoto_Online
20200806_PGStrom_PostGIS_GstoreFdw
20200424_Writable_Arrow_Fdw
20191211_Apache_Arrow_Meetup_Tokyo
20191115-PGconf.Japan
20190926_Try_RHEL8_NVMEoF_Beta
20190925_DBTS_PGStrom
20190909_PGconf.ASIA_KaiGai
20190516_DLC10_PGStrom
20190418_PGStrom_on_ArrowFdw
20190314 PGStrom Arrow_Fdw

20221111_JPUG_CustomScan_API

  • 2. CustomScan APIとは? postgres=# explain select sum(lo_revenue), d_year, p_brand1 from flineorder, date1, part where lo_orderdate = d_datekey and lo_partkey = p_partkey and p_category = 'MFGR#12’ group by d_year, p_brand1; QUERY PLAN ----------------------------------------------------------------------------------------------------- HashAggregate (cost=3656703.22..3656773.22 rows=7000 width=22) Group Key: date1.d_year, part.p_brand1 -> Custom Scan (GpuPreAgg) (cost=3656580.72..3656650.72 rows=7000 width=22) Reduction: GroupBy (Global+Local [nrooms: 1974]) Group keys: d_year, p_brand1 Combined GpuJoin: enabled -> Custom Scan (GpuJoin) on flineorder (cost=16028.65..3638534.69 rows=28873638 width=18) Outer Scan: flineorder (cost=0.00..10277198.49 rows=720040849 width=12) Depth 1: GpuHashJoin(nrows 720040849...28873638) HashSize: 1135.95KB HashKeys: flineorder.lo_partkey JoinQuals: (flineorder.lo_partkey = part.p_partkey) Depth 2: GpuHashJoin(nrows 28873638...28873638) HashSize: 442.11KB HashKeys: flineorder.lo_orderdate JoinQuals: (flineorder.lo_orderdate = date1.d_datekey) referenced: lo_partkey, lo_orderdate, lo_revenue files0: /opt/pgdata14/f_lineorder.arrow (read: 8.05GB, size: 81.81GB) -> Seq Scan on part (cost=0.00..5426.00 rows=8020 width=14) Filter: (p_category = 'MFGR#12'::bpchar) -> Seq Scan on date1 (cost=0.00..72.56 rows=2556 width=8) (21 rows) 見覚えのない 実行計画があるなぁ…。 PostgreSQL Conference Japan 2022 ~CustomScan APIを使い倒して俺様DBエンジンを実装する~ 2
  • 3. CustomScan APIとは? ▌概要  エグゼキュータの一部を拡張モジュールによる 実装で置き換えたいときに利用するAPI  拡張モジュールの開発者が利用するもので、 SQL経由でユーザが利用するものではない。  PostgreSQL v9.5 (2016) で最初のバージョンが 提供されるようになった。 ▌利点  独自にSQL実行エンジンを強化したい場合でも、 PostgreSQLからのフォークを行わずに済む。  自分が興味のある部分“だけ”を置き換える事が できるので、開発規模を抑える事ができる。 IndexScan on table-Y CustomScan (MyScan) on table-X CustomScan (MyJoin) Agg (Group By) NestLoop SeqScan on table-Z PostgreSQL Conference Japan 2022 ~CustomScan APIを使い倒して俺様DBエンジンを実装する~ 3
  • 4. CustomScan APIとFDW(Foreign Data Wrapper)の違い ▌FDW: Foreign Data Wrapper  外部のデータをあたかもPostgreSQLのテーブルで あるかのように読み書きするためのAPI  何を(What?)スキャンするか。 ▌CustomScan API  PostgreSQLのテーブルを異なる方法でスキャン するためのAPI ✓ 実装上、キャッシュを参照するなどの手法もアリ  どのように(How?)スキャンするか Foreign Table Foreign Table Foreign Table Foreign Table CSV mysql_fdw oracle_fdw file_fdw arrow_fdw MySQL データベース Oracle データベース CSV ファイル Arrow ファイル PostgreSQL テーブル SeqScan IndexScan CacheScan FpgaScan DpuScan GpuScan columnar cache PostgreSQL Conference Japan 2022 ~CustomScan APIを使い倒して俺様DBエンジンを実装する~ 4
  • 5. HeteroDB社について ▌会社概要  商号 ヘテロDB株式会社  設立 2017年7月4日  資本金 999万円  所在地 品川区北品川5-5-15 大崎ブライトコア4F  事業内容 高速データベース製品の開発・販売 GPU&DB領域の技術コンサルティング  受賞など ✓ IPA 未踏アドバンスト事業 第一期生(2017) ✓ GPU Technology Conference Japan 2017、Inception Award受賞 ✓ 東京都ベンチャー技術大賞 技術奨励賞(2022) ▌代表者プロフィール  海外 浩平|KaiGai Kohei OSS開発者コミュニティにおいて、 PostgreSQLやLinux kernelの開発に 15年以上従事。主にセキュリティ・ FDWなどの技術領域に貢献する。 本日のテーマ CustomScan API や、 GPUを用いたSQL高速化モジュール・PG-Stromの開発者。 筑波大学情報学類卒(2001)、同大学院経営・政策科学研究 科修了(2003)、日本電気株式会社勤務(2003-2017)の後、 HeteroDB社を創業。  受賞など ✓ IPA未踏ソフト「天才プログラマ―」認定(2006) ✓ OSS推進フォーラム OSS貢献者賞(2014) ✓ 日本情報処理学会 喜安記念業績賞(2014) ヘテロジニアスコンピューティング技術をデータベース領域に適用し、 誰もが使いやすく、シンプルで高速なデータ解析基盤を提供する。 PostgreSQL Conference Japan 2022 ~CustomScan APIを使い倒して俺様DBエンジンを実装する~ 5
  • 6. CustomScan APIの利用例 – Microsoft Azure Cosmos DB 出典: https://learn.microsoft.com/ja-jp/azure/cosmos-db/postgresql/tutorial-shard PostgreSQL Conference Japan 2022 ~CustomScan APIを使い倒して俺様DBエンジンを実装する~ 6
  • 7. CustomScan APIの利用例 – Google AlloyDB 出典: https://cloud.google.com/blog/ja/products/databases/alloydb-for-postgresql-columnar-engine PostgreSQL Conference Japan 2022 ~CustomScan APIを使い倒して俺様DBエンジンを実装する~ 7
  • 8. CustomScan APIの利用例 – HeteroDB PG-Strom postgres=# explain select sum(lo_revenue), d_year, p_brand1 from flineorder, date1, part where lo_orderdate = d_datekey and lo_partkey = p_partkey and p_category = 'MFGR#12’ group by d_year, p_brand1; QUERY PLAN ----------------------------------------------------------------------------------------------------- HashAggregate (cost=2631106.55..2631176.55 rows=7000 width=22) Group Key: date1.d_year, part.p_brand1 -> Custom Scan (GpuPreAgg) (cost=2630984.05..2631054.05 rows=7000 width=22) Reduction: GroupBy (Global+Local [nrooms: 1974]) Group keys: d_year, p_brand1 Combined GpuJoin: enabled GPU Preference: GPU0 (NVIDIA A100-PCIE-40GB) -> Custom Scan (GpuJoin) on flineorder (cost=14190.72..2612938.03 rows=28873638 width=18) Outer Scan: flineorder (cost=0.00..10277198.49 rows=720040849 width=12) Depth 1: GpuHashJoin(nrows 720040849...28873638) HashSize: 1135.95KB HashKeys: flineorder.lo_partkey JoinQuals: (flineorder.lo_partkey = part.p_partkey) Depth 2: GpuHashJoin(nrows 28873638...28873638) HashSize: 442.11KB HashKeys: flineorder.lo_orderdate JoinQuals: (flineorder.lo_orderdate = date1.d_datekey) GPU Preference: GPU0 (NVIDIA A100-PCIE-40GB) GPUDirect SQL: enabled referenced: lo_partkey, lo_orderdate, lo_revenue files0: /opt/pgdata14/f_lineorder.arrow (read: 8.05GB, size: 81.81GB) -> Seq Scan on part (cost=0.00..5426.00 rows=8020 width=14) Filter: (p_category = 'MFGR#12'::bpchar) -> Seq Scan on date1 (cost=0.00..72.56 rows=2556 width=8) (24 rows) PostgreSQL Conference Japan 2022 ~CustomScan APIを使い倒して俺様DBエンジンを実装する~ 8
  • 9. CustomScan APIの利用例 – FUJITSU Software Enterprise Postgres 9.5 出典: https://software.fujitsu.com/jp/manual/manualfiles/m160008/j2ul2119/05z200/j2119-00-13-01-00.html PostgreSQL Conference Japan 2022 ~CustomScan APIを使い倒して俺様DBエンジンを実装する~ 9
  • 10. SamsungのSmart-SSD開発チーム(?)の講演でも PostgreSQL Conference Japan 2022 ~CustomScan APIを使い倒して俺様DBエンジンを実装する~ 10
  • 11. PostgreSQL向けExtensionの開発 PostgreSQL Conference Japan 2022 ~CustomScan APIを使い倒して俺様DBエンジンを実装する~ 11
  • 12. 最小構成での PostgreSQL 拡張モジュールのビルド $ ls Makefile ctidscan.h ctidscan.c ctidscan.sql ctidscan.control $ cat Makefile MODULES = ctidscan EXTENSION = ctidscan DATA = ctidscan.sql PG_CONFIG = pg_config PGXS := $(shell $(PG_CONFIG) --pgxs) include $(PGXS) $ cat ctidscan.control # ctidscan extension comment = 'example module for custom-scan api’ default_version = '1.0' module_pathname = '$libdir/ctidscan' relocatable = true ✓ PostgreSQL向け拡張モジュールは、メジャー バージョン跨ぎのバイナリ互換性の保証なし。 ✓ ビルド時の ./configure 次第で変わるパラメータ 例:BLCKSZの定義やAssertの有無、構造体のレイアウトも ✓ ターゲットとなるPostgreSQLに同梱の pg_config を 利用してビルドオプションの同一性を担保する。 PostgreSQL Conference Japan 2022 ~CustomScan APIを使い倒して俺様DBエンジンを実装する~ 12
  • 13. 拡張モジュールが制御を獲得するタイミング #include “postgres.h” : : void _PG_init(void) { DefineCustomBoolVariable("enable_ctidscan", "Enables use of ctidscan”, NULL, &enable_ctidscan, true, PGC_USERSET, GUC_NOT_IN_SAMPLE, NULL, NULL, NULL); : /* registration of the hook to add alternative path */ set_rel_pathlist_next = set_rel_pathlist_hook; set_rel_pathlist_hook = SetCtidScanPath; } ✓ PostgreSQLが拡張モジュールをロードすると、 まず _PG_init() 関数を呼び出す。 ✓ 拡張モジュールをロードするタイミングは… ✓ その拡張モジュールで実装されているSQL関数の呼び出し。 ✓ (shared|local|session)_preload_libraries パラメー タによる指定。 ✓ SQLのLOADコマンド ➔ CustomScan APIを使用する拡張モジュールの場合は、 *_preload_libraries パラメータを使うのが自然。 ✓ 拡張モジュールで制御を獲得するには? ✓ “C”で実装されたSQL関数の実行 ➔ 演算子やSQL関数、インデックスの評価やFDWなど、 大半のケースではこちら。 ✓ フック、コールバックで登録されたタイミング ➔ CustomScan APIでは、一連の処理の中でSQL関数を必要と する場面がないため、_PG_init() でフックやコールバックの 登録を済ませておく。 PostgreSQL Conference Japan 2022 ~CustomScan APIを使い倒して俺様DBエンジンを実装する~ 13
  • 14. 比較的シンプルな CustomScan のサンプル PostgreSQL Conference Japan 2022 ~CustomScan APIを使い倒して俺様DBエンジンを実装する~ 14 ▌https://github.com/kaigai/ctidscan  検索条件に ctid システム列(*)に対する大小比較を含む 場合、明らかにマッチしないブロックを読み飛ばす。  PostgreSQL v14で対応した TidRangeScan と同等だが、 CustomScan APIの提案時には 意味のある拡張モジュール だった。 ✓ (*) ctidシステム列は、タプルの存在するブロック番号と アイテム番号を示す。
  • 15. PostgreSQLはどのように実行計画を作るのか PostgreSQL Conference Japan 2022 ~CustomScan APIを使い倒して俺様DBエンジンを実装する~ 15
  • 16. PostgreSQLがクエリを処理する流れ SQL Parser Query Optimizer Query Executor Buffer Manager SQLクエリ パース木 クエリ実行計画 クエリ 実行結果  SQL構文を分解し、取り扱いやすい 内部データ形式(パース木)に変換  文法エラーの検出  統計情報を元にコスト値を算出  最も合理的と予想される実行計画を作成する。  クエリ実行計画に基づいて、 ScanやJoin、Sortなどの処理を実行する。  PostgreSQL内部のインフラを使用 PostgreSQL Conference Japan 2022 ~CustomScan APIを使い倒して俺様DBエンジンを実装する~ 16 Transaction Control Resource Management IPC, Shared Memory クエリの実行をサポートする 多種多様な内部インフラが 用意されている。
  • 17. PostgreSQLはどのように実行計画を作るか t0 JOIN t1の候補パス Scan t0 Scan t1 Join t0,t1 統計情報) nrows: 120万行 width: 80 インデックス:なし 候補パス HashJoin cost=4000 候補パス MergeJoin cost=12000 候補パス NestLoop cost=99999 候補パス Parallel Hash Join cost=3000 候補パス GpuJoin cost=2500 WINNER! PostgreSQLビルトインの実行パス 拡張モジュールによる提案 (PostgreSQL v9.5以降) (PostgreSQL v9.6以降) GpuJoin t0,t1 統計情報) nrows: 4000行 width: 120 インデックス:id列 PostgreSQL Conference Japan 2022 ~CustomScan APIを使い倒して俺様DBエンジンを実装する~ 17
  • 18. PostgreSQLがクエリを処理する流れ PostgreSQL Conference Japan 2022 ~CustomScan APIを使い倒して俺様DBエンジンを実装する~ 18 ▌リレーション(Scan, Join, ...)に紐づいた 候補パス(Path構造体)を作成する。 ✓ Pathには実行コストや、実行結果の出力順 (ソートされているか否か)などを保持する。 ✓ ビルトインの実行計画の他に CustomPath を作成 する事ができれば、ここに突っ込む。 ▌オプティマイザは最もコストの安い候補パスを 選択し、Path ⇒ Plan 構造体に変換する。 ✓ Pathと異なり、Plan構造体は copyObject などの 内部APIを用いて操作できる。 ▌式表現の書き換え ✓ 場合によっては、オプティマイザにヒントを与える 必要がある(custom_scan_tlist) SQL Parser Query Optimizer Query Executor SQLクエリ パース木 クエリ実行計画 クエリ 実行結果
  • 19. set_rel_pathlist_hook による実行計画の登録 PostgreSQL Conference Japan 2022 ~CustomScan APIを使い倒して俺様DBエンジンを実装する~ 19 /* Hook for plugins to get control in set_rel_pathlist() */ typedef void (*set_rel_pathlist_hook_type) (PlannerInfo *root, RelOptInfo *rel, Index rti, RangeTblEntry *rte); extern PGDLLIMPORT set_rel_pathlist_hook_type set_rel_pathlist_hook; static void SetCtidScanPath(PlannerInfo *root, RelOptInfo *baserel, Index rtindex, RangeTblEntry *rte) { /* call the secondary custom-scan provider */ if (set_rel_pathlist_next) set_rel_pathlist_next(root, baserel, rtindex, rte); : /* * 1. リレーション種別や検索条件をチェック * 2. CustomPathを作成し、CustomPathMethods を登録 * 3. add_path()を使ってパスを登録 */ ✓ set_rel_pathlist_hook はいつ呼ばれるのか? ➔ 特定のテーブルに対して、オプティマイザが スキャンする方法を検討するタイミング。 ✓ ビルトインのスキャン方法 • SeqScan • IndexScan • BitmapHeapScan • TidScan • ForeignScan ➔ ここに割り込んで、『俺々CustomScan』の方が より効率的にこのテーブルをスキャンできると いう事を教えてやる。
  • 20. CustomPath構造体 PostgreSQL Conference Japan 2022 ~CustomScan APIを使い倒して俺様DBエンジンを実装する~ 20 typedef struct Path { NodeTag type; NodeTag pathtype; RelOptInfo *parent; PathTarget *pathtarget; ParamPathInfo *param_info; : Cardinality rows; Cost startup_cost; Cost total_cost; List *pathkeys; } Path; typedef struct CustomPath { Path path; uint32 flags; List *custom_paths; List *custom_private; const struct CustomPathMethods *methods; } CustomPath; ▌CustomPath構造体  『俺ならこれぐらいの処理コストで実行できる』を PostgreSQLのオプティマイザに教えてやるための データ構造。  Path中の rows, startup_cost, total_cost で 処理コストを表現する。  methods には、次工程で Path --> Plan への変換を 行うコールバック関数などを設定する。  flagsには当該パスの特徴を指定することができる。 ✓ CUSTOMPATH_SUPPORT_BACKWARD_SCAN ✓ CUSTOMPATH_SUPPORT_MARK_RESTORE ✓ CUSTOMPATH_SUPPORT_PROJECTION
  • 21. RelOptInfoから条件句を抜き出す PostgreSQL Conference Japan 2022 ~CustomScan APIを使い倒して俺様DBエンジンを実装する~ 21 static void SetCtidScanPath(PlannerInfo *root, RelOptInfo *baserel, Index rtindex, RangeTblEntry *rte) { : /* walk on the restrict info */ foreach (lc, baserel->baserestrictinfo) { RestrictInfo *rinfo = lfirst(lc); if (IS_SUPPORTED_EXPR(rino->clause)) dev_quals = lappend(dev_quals, rinfo); } if (dev_quals == NIL) return; : cpath = makeNode(CustomPath); cpath->custom_private = ctid_quals; cpath->methods = &ctidscan_path_methods; : add_path(baserel, (Path *)cpath); } ▌RelOptInfo  FROM行に列挙されたリレーションを表現する構造体  このリレーションをスキャンする Path の一覧  基本的な統計情報 ✓ ブロック数、レコード数、ALL_VISIBLEの割合  検索に利用できるインデックスの情報  リレーションをスキャンする条件句  被参照列のビットマップ  パーティションの子テーブル一覧
  • 22. 選ばれた Path から Plan を生成する(1/2) PostgreSQL Conference Japan 2022 ~CustomScan APIを使い倒して俺様DBエンジンを実装する~ 22 typedef struct CustomPathMethods { : Plan *(*PlanCustomPath)(PlannerInfo *root, RelOptInfo *rel, CustomPath *best_path, List *tlist, List *clauses, List *custom_plans); : } typedef struct CustomScan { Scan scan; uint32 flags; List *custom_plans; /* list of Plan nodes */ List *custom_exprs; /* expressions nodes */ List *custom_private; /* private data */ List *custom_scan_tlist; /* optional tlist */ Bitmapset *custom_relids; /* RTIs by this can */ const struct CustomScanMethods *methods; } CustomScan;  PathからPlanを生成する ✓ 先に登録した CustomPath のコストが最も小さく、 オプティマイザに選択された場合、method に登録 した PlanCustomPath 関数がコールされる。 ✓ CustomPathの内容をベースに、CustomPlanを作成する。  オプティマイザから渡される情報 ✓ tlist ターゲットリスト ✓ clauses 検索条件句 ✓ custom_plans 既に Plan に変換された下位ノード (custom_pathsをセットした場合)  CustomScanに含めるもの ✓ 疑似ターゲットリスト(custom_scan_tlist) ✓ プライベートデータ(expressionとそれ以外) ✓ CustomScan --> CustomScanState に変換するための関数 コールバック
  • 23. custom_scan_tlist の使い方(1/2) PostgreSQL Conference Japan 2022 ~CustomScan APIを使い倒して俺様DBエンジンを実装する~ 23 postgres=# explain verbose select count(*), sum(lo_revenue), lo_orderdate % 10000 from lineorder where lo_orderdate between 19950101 and 19950331 group by 3; QUERY PLAN ----------------------------------------------------------------------------------------------------- HashAggregate (cost=1155141.38..1155177.47 rows=2406 width=44) Output: count(*), sum(lo_revenue), ((lo_orderdate % 10000)) Group Key: ((lineorder.lo_orderdate % 10000)) -> Custom Scan (GpuScan) on public.lineorder (cost=11355.56..1140071.00 rows=2009384 width=10) Output: ((lo_orderdate % 10000)), lo_revenue GPU Projection: (lineorder.lo_orderdate % 10000), lineorder.lo_revenue GPU Filter: ((lineorder.lo_orderdate >= 19950101) AND (lineorder.lo_orderdate <= 19950331)) GPU Preference: GPU0 (NVIDIA A100-PCIE-40GB) Kernel Source: /var/lib/pgdata/pgsql_tmp/pgsql_tmp_strom_3737600.12.gpu (9 rows)
  • 24. custom_scan_tlist の使い方(2/2) PostgreSQL Conference Japan 2022 ~CustomScan APIを使い倒して俺様DBエンジンを実装する~ 24 tuple tuple SeqScan Projection CustomScan Projection tuple CustomScan Projection’ 次工程(JOIN、GROUP BYなど) tuple” tuple tuple tuple” tuple” tuple” テーブル定義通りの データ構造を持つタプル EXPLAIN VERBOSEコマンドで Output: に表示された列から 構成されるタプル GPU Device Projection CPU GPU SSD Smart-SSD
  • 25. 2種類のプライベート領域 custom_exprs と custom_privates PostgreSQL Conference Japan 2022 ~CustomScan APIを使い倒して俺様DBエンジンを実装する~ 25 ▌後工程での式表現(expression)書き換え  式表現がある場合や、複数の入力から入ってきた データの取扱いを簡便にするため、実行計画を作成 した後に setrefs.c で式表現の書き換えを行う。  例えば、「tbl_x の a列」「tbl_y.b % 1000の計算結果」と いう形で表現されていた式表現を「INNER側の0番目」 「OUTER側の0番目」といった形に書き換える。 ▌CustomScanのプライベート領域に式表現を 含める場合  setrefs.c による書き換えを行ってほしい場合 ➔ custom_exprs に保存する  setrefs.c による書き換えを行わない場合 ➔ custom_privates に保存する SeqScan CustomScan JOIN tbl_x.a = tbl_y.b % 1000 on tbl_x on tbl_y output: tbl_y.b % 1000, tbl_y.c output: tbl_x.a, tbl_y.c SeqScan CustomScan JOIN INNER[0] = OUTER[0] on tbl_x on tbl_y output: tbl_y.b % 1000, tbl_y.c output: INNER[0], OUTER[1] setrefs.c による書き換え
  • 26. パラレルスキャン用の CustomPath PostgreSQL Conference Japan 2022 ~CustomScan APIを使い倒して俺様DBエンジンを実装する~ 26 ▌パラレルスキャンに必要な事 ✓ 条件句に出現する関数・演算子が、ワーカーノードでも 動作するものとして宣言されている。 (immutable or stable かつ parallel_safe) ✓ テーブル上のスキャンすべきブロックを、他のワーカー と協調して特定する事ができる。  重複して同じブロックを読み出さない  共有メモリ(後述のDSM)を利用して、ワーカーノードと 読出し位置を調整する。 ▌CustomPathに必要な事 ✓ parallel_safe = true ✓ parallel_aware = true ✓ parallel_workers > 0 ✓ add_partial_path() を用いて登録する。 PG backend PG worker-1 PG worker-2
  • 27. エグゼキュータを理解する PostgreSQL Conference Japan 2022 ~CustomScan APIを使い倒して俺様DBエンジンを実装する~ 27
  • 28. PostgreSQLがクエリを処理する流れ PostgreSQL Conference Japan 2022 ~CustomScan APIを使い倒して俺様DBエンジンを実装する~ 28 ▌実行計画(Plan)を元に、 実行時情報(PlanState)を生成する。 ▌上位のノードから呼び出されたら、 有効な1行、またはNULLを返す。 ▌実行終了時にリソースを開放する。 ▌パラレルクエリの場合、共有メモリ(DSM: Dynamic Shared Memory)の初期化とアタッチを行う。 SQL Parser Query Optimizer Query Executor SQLクエリ パース木 クエリ実行計画 クエリ 実行結果
  • 29. 実行時情報の生成(1/3) PostgreSQL Conference Japan 2022 ~CustomScan APIを使い倒して俺様DBエンジンを実装する~ 29 typedef struct CustomScan { Scan scan; uint32 flags; List *custom_plans; /* list of Plan nodes */ List *custom_exprs; /* expressions nodes */ List *custom_private; /* private data */ List *custom_scan_tlist; /* optional tlist */ Bitmapset *custom_relids; /* RTIs by this can */ const struct CustomScanMethods *methods; } CustomScan; typedef struct CustomScanMethods { const char *CustomName; Node *(*CreateCustomScanState)(CustomScan *cscan); } CustomScanMethods;  CreateCustomScanState ➔ CustomScanに紐づく唯一のコールバック  役割:CustomScanを元に、CustomScanStateまたは 構造体の先頭にCustomScanStateを含む実行時情報 を割り当てる。 MyOwnScanState それ以外に独自に 必要な実行時情報 CustomScanState カスタムスキャンに 必要な実行時情報 ScanState テーブルスキャンに 必要な実行時情報 PlanState 全ての実行ノードに 共通のフィールド ⚫ Estate(共通の実行時情報)の参照 ⚫ 入力行 ➔ 出力行の変換(Projection) ⚫ 出力行の型定義 ⚫ INNER/OUTERノードへの参照 ⚫ オープンしている Relation ハンドラ ⚫ 現在のスキャン位置情報 ⚫ 実行時に呼び出されるコールバック 関数(methods) ⚫ 下位ノードのリスト(あれば) ⚫ その他、実行時に必要な情報 (GPUのデバイス管理情報、 ワーカースレッドのID、など)
  • 30. 実行時情報の生成(2/3) PostgreSQL Conference Japan 2022 ~CustomScan APIを使い倒して俺様DBエンジンを実装する~ 30 typedef struct CustomExecMethods { const char *CustomName; /* Required executor methods */ void (*BeginCustomScan) (CustomScanState *node, EState *estate, int eflags); TupleTableSlot * (*ExecCustomScan)(CustomScanState *node); void (*EndCustomScan) (CustomScanState *node); void (*ReScanCustomScan) (CustomScanState *node); : : void (*ExplainCustomScan) (CustomScanState *node, List *ancestors, ExplainState *es); } CustomExecMethods; ▌BeginCustomScan  出力行/入力行の型定義情報を初期化  入力行 ➔ 出力行への変換(Projection)を初期化  その他の実行時情報を初期化 ▌ExecCustomScan  初回実行時に  実行結果から一行を返却する。 もしくは終端に達した場合はNULL ▌EndCustomScan  リソースを開放する。 ▌ReScanCustomScan  もう一度スキャンをやり直す。 ▌ExplainCustomScan  EXPLAINで追加的な情報を出力する場合、 ここで追加する。
  • 31. 実行時情報の生成(3/3) PostgreSQL Conference Japan 2022 ~CustomScan APIを使い倒して俺様DBエンジンを実装する~ 31 void ExecInitCustomScan(...) { if (scanrelid > 0) { scan_rel = ExecOpenScanRelation(...); css->ss.ss_currentRelation = scan_rel; } : if (cscan->custom_scan_tlist != NIL || scan_rel == NULL) { scan_tupdesc = ExecTypeFromTL(cscan->custom_scan_tlist); ExecInitScanTupleSlot(estate, &css->ss, scan_tupdesc, &TTSOpsVirtual); tlistvarno = INDEX_VAR; } else { ExecInitScanTupleSlot(estate, &css->ss, RelationGetDescr(scan_rel), &TTSOpsVirtual); tlistvarno = scanrelid; } : /* Init result slot, type and projection */ ExecInitResultTupleSlotTL(&css->ss.ps, &TTSOpsVirtual); ExecAssignScanProjectionInfoWithVarno(&css->ss, tlistvarno); : css->methods->BeginCustomScan(css, estate, eflags); } tuple CustomScan Projection’ tuple” GPU Device Projection tuple CustomScan Projection tuple 次工程(JOIN、GROUP BYなど) tuple” tuple” custom_scan_tlist なしの 初期化パターン custom_scan_tlist ありの 初期化パターン ※ 結局、自前で初期化を やり直す方が多いが…。
  • 32. TupleTableSlot構造体について PostgreSQL Conference Japan 2022 ~CustomScan APIを使い倒して俺様DBエンジンを実装する~ 32 typedef struct TupleTableSlot { NodeTag type; uint16 tts_flags; /* Boolean states */ AttrNumber tts_nvalid; /* # of valid values */ const TupleTableSlotOps *const tts_ops; TupleDesc tts_tupleDescriptor; /* 列定義情報 */ Datum *tts_values; /* 列ごとの値 */ bool *tts_isnull; /* NULL値フラグ */ MemoryContext tts_mcxt; ItemPointerData tts_tid; /* stored tuple's tid */ Oid tts_tableOid; /* table oid of tuple */ } TupleTableSlot; typedef struct HeapTupleTableSlot { TupleTableSlot base; HeapTuple tuple; /* physical tuple */ uint32 off; /* state for deforming */ HeapTupleData tupdata; /* optional workspace */ } HeapTupleTableSlot; ▌TupleTableSlot構造体の役割  列定義情報(TupleDesc)と、そのデータ形式に沿った 一行分のデータを保持する。 ➔ 各実行ノード間でデータの受け渡しに用いる。  PostgreSQL v12 のプラガブルストレージ機構のマージと 共に、多様なデータ構造を定義できるようになった。 ▌TTSOpsVirtualの場合  有効長tts_nvalid の配列tts_values/tts_isnullに 列単位で展開された値が入る。  最も汎用的な形式である一方、物理テーブルをスキャン した場合、タプルを展開しないといけない。 ▌物理タプルをそのまま使う場合は?  HeapTupleTableSlot か、さらに Buffer へのポインタを 付加した BufferHeapTupleTableSlot を利用すれば、 TupleTableSlot に物理タプルを格納できる。  table_slot_callbacks(relation) を使えば、 テーブルの TupleTableSlotOps を取得できる。 (v12以降、特定の物理タプル形式を前提にできない)
  • 33. エグゼキュータの構造 PostgreSQL Conference Japan 2022 ~CustomScan APIを使い倒して俺様DBエンジンを実装する~ 33 BeginCustomScan ExecutorStart initPlan 上位ノードの初期化 ExecInitCustomScan ExecCustomScan ExecutorRun ExecutePlan 上位ノードの実行 ExecCustomScan EndCustomScan ExecutorEnd ExecEndPlan 上位ノードの終了処理 ExecEndCustomScan 拡張モジュールで 実装すべき箇所 どのような実装手段でも構わないので、 ✓ 指定されたテーブルを読み出す ✓ 条件句に合致したタプルだけを返す ✓ 直上のノードが「入力値」として 期待するデータ型のタプルを返す。 ことを守っていればよい。 GPU 列キャッシュ SIMD命令 量子 コンピュータ Smart-SSD
  • 34. パラレルクエリの対応(1/2) PostgreSQL Conference Japan 2022 ~CustomScan APIを使い倒して俺様DBエンジンを実装する~ 34 postgres=# explain verbose select count(*), lo_orderdate % 10000 from lineorder where lo_orderdate between 19950101 and 19950331 group by 2; QUERY PLAN -------------------------------------------------------------------------------------------- Finalize HashAggregate (cost=726605.39..726635.47 rows=2406 width=12) Output: count(*), ((lo_orderdate % 10000)) Group Key: ((lineorder.lo_orderdate % 10000)) -> Gather (cost=726070.06..726581.33 rows=4812 width=12) Output: ((lo_orderdate % 10000)), (PARTIAL count(*)) Workers Planned: 2 -> Partial HashAggregate (cost=725070.06..725100.13 rows=2406 width=12) Output: ((lo_orderdate % 10000)), PARTIAL count(*) Group Key: ((lineorder.lo_orderdate % 10000)) -> Parallel Custom Scan (GpuScan) on public.lineorder (cost=29652.44..720883.84 rows=837243 width=4) Output: ((lo_orderdate % 10000)) GPU Projection: (lineorder.lo_orderdate % 10000) GPU Filter: ((lineorder.lo_orderdate >= 19950101) AND (lineorder.lo_orderdate <= 19950331)) GPU Preference: GPU0 (NVIDIA A100-PCIE-40GB) Kernel Source: /var/lib/pgdata/pgsql_tmp/pgsql_tmp_strom_3870445.12.gpu Kernel Binary: /var/lib/pgdata/pgsql_tmp/pgsql_tmp_strom_3870445.13.ptx (16 rows) Gatherがワーカーを立ち上げ、 ワーカーから返された結果も含め、 上位ノードへ結果を返す。
  • 35. パラレルクエリの対応(2/2) PostgreSQL Conference Japan 2022 ~CustomScan APIを使い倒して俺様DBエンジンを実装する~ 35 typedef struct CustomExecMethods { const char *CustomName; : /* optional: needed if parallel execution */ Size (*EstimateDSMCustomScan)(CustomScanState *node, ParallelContext *pcxt); void (*InitializeDSMCustomScan)(CustomScanState *node, ParallelContext *pcxt, void *coordinate); void (*ReInitializeDSMCustomScan)(CustomScanState *node, ParallelContext *pcxt, void *coordinate); void (*InitializeWorkerCustomScan)(CustomScanState *node, shm_toc *toc, void *coordinate); void (*ShutdownCustomScan) (CustomScanState *node); : } CustomExecMethods; Gatherノードの動き ① 下位ノードの共有メモリ(DSM)必要量を計算 ✓ EstimateDSMCustomScanが呼ばれる ② IPC用の共有メモリを確保し、各ノードごとに初期化 ✓ InitializeDSMCustomScanが呼ばれる ③ Partial Aggregate以下を実行するワーカプロセスを起動 ④ 各ワーカで初期化済みの共有メモリをアタッチする。 ✓ InitializeWorkerCustomScan が呼ばれる ⑤ バックエンド・各ワーカーで終端までスキャンを行う ⑥ 各ワーカープロセスの終了 ⑦ 共有メモリを開放する前に、 シャットダウン処理を実行 ✓ ShutdownCustomScan が呼ばれる。 ⑧ エグゼキュータの終了処理 ✓ EndCustomScanが呼ばれる Gather Partial Aggregate Hash Join CustomScan SeqScan Partial Aggregate Hash Join CustomScan SeqScan Partial Aggregate Hash Join CustomScan SeqScan 共有メモリ (DSM)
  • 36. PostgreSQL Conference Japan 2022 ~CustomScan APIを使い倒して俺様DBエンジンを実装する~ 36 JOINとGROUP BY
  • 37. JOINとGROUP BY(1/2) PostgreSQL Conference Japan 2022 ~CustomScan APIを使い倒して俺様DBエンジンを実装する~ 37 HashJoin Projection CustomScan Projection tuple tuple tuple tuple tuple” tuple” 次工程(他のJOIN、GROUP BYなど) どのような実装手段でも構わないので、 ✓ 要求された下位ノードからタプルを読み出す。 ✓ 結合条件に従ってJOIN処理を行い、直上のノードが 「入力値」として期待するデータ型のタプルを返す。 ✓ 集約式に従ってGROUP BY処理を行い、直上のノードが 「入力値」として期待するデータ型のタプルを返す。 ことを守っていればよい。
  • 38. JOINとGROUP BY(2/2) PostgreSQL Conference Japan 2022 ~CustomScan APIを使い倒して俺様DBエンジンを実装する~ 38 /* Hook for plugins to get control in add_paths_to_joinrel() */ typedef void (*set_join_pathlist_hook_type) (PlannerInfo *root, RelOptInfo *joinrel, RelOptInfo *outerrel, RelOptInfo *innerrel, JoinType jointype, JoinPathExtraData *extra); extern PGDLLIMPORT set_join_pathlist_hook_type set_join_pathlist_hook; /* Hook for plugins to get control when grouping_planner() plans upper rels */ typedef void (*create_upper_paths_hook_type) (PlannerInfo *root, UpperRelationKind stage, RelOptInfo *input_rel, RelOptInfo *output_rel, void *extra); extern PGDLLIMPORT create_upper_paths_hook_type create_upper_paths_hook; ▌JOINに対する Path の追加  set_join_pathlist_hook を利用する。  joinrelは、outerrelとinnerrelを結合した表を 意味する RelOptInfo で、ここに CustomPath を追加す る。 ▌GROUP BYに対する Path の追加  SCAN/JOINが一通り終わった後、より上位の実行計画を 積み上げるという意味で UPPER-PATH と呼んでいる。  create_upper_paths_hookを利用する。  UpperRelationKindは以下の8種類 ✓ UPPERREL_SETOP ✓ UPPERREL_PARTIAL_GROUP_AGG ✓ UPPERREL_GROUP_AGG ✓ UPPERREL_WINDOW ✓ UPPERREL_PARTIAL_DISTINCT ✓ UPPERREL_DISTINCT ✓ UPPERREL_ORDERED ✓ UPPERREL_FINAL
  • 39. 下位ノードを持つ CustomPath の作成 PostgreSQL Conference Japan 2022 ~CustomScan APIを使い倒して俺様DBエンジンを実装する~ 39 typedef struct CustomPath { Path path; uint32 flags; /* mask of CUSTOMPATH_* flags */ List *custom_paths; /* list of child Path */ List *custom_private; const struct CustomPathMethods *methods; } CustomPath; typedef struct CustomPathMethods { const char *CustomName; /* Convert Path to a Plan */ Plan *(*PlanCustomPath) (PlannerInfo *root, RelOptInfo *rel, CustomPath *best_path, List *tlist, List *clauses, List *custom_plans); : } typedef struct CustomScan { Scan scan; uint32 flags; List *custom_plans; /* list of Plan nodes */ List *custom_exprs; List *custom_private; List *custom_scan_tlist; Bitmapset *custom_relids; CustomScanMethods *methods; } CustomScan; void BeginCustomScan(CustomScanState *node, ...) { CustomScan *cscan = (CustomScan *)node->ss.ps->plan; foreach (lc, cscan->custom_plans) { Plan *child_plan = lfirst(lc); child_ps = ExecInitNode(child_plan, ...); :
  • 40. PostgreSQL Conference Japan 2022 ~CustomScan APIを使い倒して俺様DBエンジンを実装する~ 40 CustomScan APIの拡張 ~ 非同期APIへの対応
  • 41. FDWにおける非同期API(1/2) PostgreSQL Conference Japan 2022 ~CustomScan APIを使い倒して俺様DBエンジンを実装する~ 41 子テーブル① (Foreign Table) 子テーブル② (Foreign Table) 子テーブル③ (Foreign Table) 子テーブル④ (Foreign Table) リモートサーバー Gather Append Foreign Scan on ① Foreign Scan on ② Foreign Scan on ③ Foreign Scan on ④ パーティションテーブル nworkers=2 backend worker-1 worker-2
  • 42. FDWにおける非同期API(1/2) PostgreSQL Conference Japan 2022 ~CustomScan APIを使い倒して俺様DBエンジンを実装する~ 42 子テーブル① (Foreign Table) 子テーブル② (Foreign Table) 子テーブル③ (Foreign Table) 子テーブル④ (Foreign Table) リモートサーバー Gather Append Foreign Scan on ① Foreign Scan on ② Foreign Scan on ③ Foreign Scan on ④ パーティションテーブル nworkers=2 worker-1
  • 43. FDWにおける非同期API(2/2) PostgreSQL Conference Japan 2022 ~CustomScan APIを使い倒して俺様DBエンジンを実装する~ 43 /* Support functions for asynchronous execution */ bool IsForeignPathAsyncCapable(ForeignPath *path); ForeignPathが非同期実行をサポートしているかどうかを オプティマイザに通知する void ForeignAsyncRequest(AsyncRequest *areq); リモートサーバから非同期に受け取ったタプルを一つ、 呼び出し元へ返す、またはareq->callback_pending=trueを セットして結果待ちである事を呼び出し元に知らせる、あるい はスロットにNULLをセットして終端に達した事を知らせる。 void ForeignAsyncConfigureWait(AsyncRequest *areq); 結果待ちである場合、WaitEventSetWait()の対象となる ファイルディスクリプタを追加する。 (読み出し可能となった時点でプロセスが起床する) void ForeignAsyncNotify(AsyncRequest *areq); 新たにリモートから処理結果を受け取ったら、そのタプルを 一つ、呼び出し元へ返す。 ① ForeignAsyncRequest(初回)を通じて、リモートサーバに 実行開始のリクエスト。 Append Foreign Scan on ① Foreign Scan on ② Foreign Scan on ③ SeqScan on ④
  • 44. FDWにおける非同期API(2/2) PostgreSQL Conference Japan 2022 ~CustomScan APIを使い倒して俺様DBエンジンを実装する~ 44 /* Support functions for asynchronous execution */ bool IsForeignPathAsyncCapable(ForeignPath *path); ForeignPathが非同期実行をサポートしているかどうかを オプティマイザに通知する void ForeignAsyncRequest(AsyncRequest *areq); リモートサーバから非同期に受け取ったタプルを一つ、 呼び出し元へ返す、またはareq->callback_pending=trueを セットして結果待ちである事を呼び出し元に知らせる、あるい はスロットにNULLをセットして終端に達した事を知らせる。 void ForeignAsyncConfigureWait(AsyncRequest *areq); 結果待ちである場合、WaitEventSetWait()の対象となる ファイルディスクリプタを追加する。 (読み出し可能となった時点でプロセスが起床する) void ForeignAsyncNotify(AsyncRequest *areq); 新たにリモートから処理結果を受け取ったら、そのタプルを 一つ、呼び出し元へ返す。 ① ForeignAsyncRequest(初回)を通じて、リモートサーバに 実行開始のリクエスト。 ② ForeignAsyncConfigureWaitにより同期イベントをセット Append Foreign Scan on ① Foreign Scan on ② Foreign Scan on ③ SeqScan on ④
  • 45. FDWにおける非同期API(2/2) PostgreSQL Conference Japan 2022 ~CustomScan APIを使い倒して俺様DBエンジンを実装する~ 45 /* Support functions for asynchronous execution */ bool IsForeignPathAsyncCapable(ForeignPath *path); ForeignPathが非同期実行をサポートしているかどうかを オプティマイザに通知する void ForeignAsyncRequest(AsyncRequest *areq); リモートサーバから非同期に受け取ったタプルを一つ、 呼び出し元へ返す、またはareq->callback_pending=trueを セットして結果待ちである事を呼び出し元に知らせる、あるい はスロットにNULLをセットして終端に達した事を知らせる。 void ForeignAsyncConfigureWait(AsyncRequest *areq); 結果待ちである場合、WaitEventSetWait()の対象となる ファイルディスクリプタを追加する。 (読み出し可能となった時点でプロセスが起床する) void ForeignAsyncNotify(AsyncRequest *areq); 新たにリモートから処理結果を受け取ったら、そのタプルを 一つ、呼び出し元へ返す。 ① ForeignAsyncRequest(初回)を通じて、リモートサーバに 実行開始のリクエスト。 ② ForeignAsyncConfigureWaitにより同期イベントをセット ③ リモート実行中に、同期実行できるものを実行 Append Foreign Scan on ① Foreign Scan on ② Foreign Scan on ③ SeqScan on ④
  • 46. FDWにおける非同期API(2/2) PostgreSQL Conference Japan 2022 ~CustomScan APIを使い倒して俺様DBエンジンを実装する~ 46 /* Support functions for asynchronous execution */ bool IsForeignPathAsyncCapable(ForeignPath *path); ForeignPathが非同期実行をサポートしているかどうかを オプティマイザに通知する void ForeignAsyncRequest(AsyncRequest *areq); リモートサーバから非同期に受け取ったタプルを一つ、 呼び出し元へ返す、またはareq->callback_pending=trueを セットして結果待ちである事を呼び出し元に知らせる、あるい はスロットにNULLをセットして終端に達した事を知らせる。 void ForeignAsyncConfigureWait(AsyncRequest *areq); 結果待ちである場合、WaitEventSetWait()の対象となる ファイルディスクリプタを追加する。 (読み出し可能となった時点でプロセスが起床する) void ForeignAsyncNotify(AsyncRequest *areq); 新たにリモートから処理結果を受け取ったら、そのタプルを 一つ、呼び出し元へ返す。 ① ForeignAsyncRequest(初回)を通じて、リモートサーバに 実行開始のリクエスト。 ② ForeignAsyncConfigureWaitにより同期イベントをセット ③ リモート実行中に、同期実行できるものを実行 ④ 結果が返ってきていれば、ForeignAsyncNotifyを通じ て結果を取得。一行を返す。 Append Foreign Scan on ① Foreign Scan on ② Foreign Scan on ③ SeqScan on ④
  • 47. CustomScan APIにおける非同期処理APIの追加(1/2) PostgreSQL Conference Japan 2022 ~CustomScan APIを使い倒して俺様DBエンジンを実装する~ 47 ▌ForeignScan の非同期APIとの違いは?  ほとんどない。  CustomScanが非同期APIをサポートするかどうか、 CUSTOMPATH_SUPPORT_ASYNC_EXECUTIONフラグを用いてオプ (専用のコールバックは設けていない) ▌追加するコールバックは  void CustomScanAsyncRequest(AsyncRequest *areq);  void CustomScanAsyncConfigureWait(AsyncRequest *areq);  void CustomScanAsyncNotify(AsyncRequest *areq); ➔ 使い方は FDW のケースと全く同一
  • 48. CustomScan APIにおける非同期処理APIの追加(2/2) PostgreSQL Conference Japan 2022 ~CustomScan APIを使い倒して俺様DBエンジンを実装する~ 48 Computational Storage SSDドライブ上にFPGAやARMプロセッサを 搭載し、ストレージドライブ上でユーザの 開発したソフトウェアを実行する事ができる。 Flashストレージの業界では最もホットな話題の一つ。 仮に2U/24ドライブのサーバにフル実装したら、 24ノードのワーカーが走っているのと同じ。☺ 子テーブル① (Heap Table) 子テーブル② (Heap Table) 子テーブル③ (Heap Table) 子テーブル④ (Heap Table) パーティションテーブル shared buffer Filesystem NVME Block System 参照 SELECT + Index 書き込み INSERT/UPDATE/DELETE
  • 49. CustomScan APIにおける非同期処理APIの追加(2/2) PostgreSQL Conference Japan 2022 ~CustomScan APIを使い倒して俺様DBエンジンを実装する~ 49 Computational Storage SSDドライブ上にFPGAやARMプロセッサを 搭載し、ストレージドライブ上でユーザの 開発したソフトウェアを実行する事ができる。 Flashストレージの業界では最もホットな話題の一つ。 仮に2U/24ドライブのサーバにフル実装したら、 24ノードのワーカーが走っているのと同じ。☺ 子テーブル① (Heap Table) 子テーブル② (Heap Table) 子テーブル③ (Heap Table) 子テーブル④ (Heap Table) パーティションテーブル shared buffer Filesystem NVME Block System 検索・集計 SELECT + Scan
  • 50. まとめ  CustomScan APIとは ✓ FDWが「何を」テーブルとして見せるのかに対して、テーブルを「どのように」処理するかを プラガブルにするための機構。 ✓ PostgreSQL v9.5でサポート。PostgreSQLを母体とするソリューションで割と使われている。  オプティマイザ ✓ set_rel_pathlist_hook やset_join_pathlist_hookを用いて、独自の CustomPath を追加する。 ✓ 上位ノードに結果を返す際の列定義情報(custom_scan_tlist)に注意する。  エグゼキュータ ✓ BeginCustomScanで初期化、ExecCustomScanで実行、EndCustomScanで終了 ✓ 上位ノードに結果を返す際の列定義情報(TupleDesc)に注意。必ず一致していないとダメ。 ✓ 本来の SCAN や JOIN の処理と同じ結果を返しさえすれば、どのような方法で実装してもよい。 ✓ パラレルクエリの場合は、共有メモリ(DSM)を介して他のワーカーと協調する。  今後の機能強化 ✓ ForeignScan同様の非同期処理用のAPIを提案中 ✓ Smart-SSDやSmart-NICでSQLを処理する際に、非同期処理を記述できるようになる。 PostgreSQL Conference Japan 2022 ~CustomScan APIを使い倒して俺様DBエンジンを実装する~ 50