【実務・中級編】 MulticornとカスタムFDW – PostgreSQL

やあ。また会ったね。今日は少しニッチだけど、知っておくと「あ、こいつできるな」と思われるような、PostgreSQLの奥の手について話そうか。

テーマは「Multicorn」だ。

PostgreSQLは強力なRDBMSだけど、実務では「PostgreSQLの中にあるデータ」だけで完結することなんてまずないよね。S3にあるログファイル、Redisのキャッシュ、あるいは社内の怪しげなREST API……。これらをいちいちPythonでスクレイピングしてDBに流し込んで……なんてやってたら、夜が明けてしまう。

そこで登場するのが「外部データラッパー(FDW)」だ。今回は、PythonでサクッとFDWを書ける神ライブラリ「Multicorn」を使って、PostgreSQLを「何でも屋」に変える方法を伝授するよ。

—

なぜ「Multicorn」なのか?

普通、PostgreSQLで自作のFDWを作ろうと思ったら、C言語でゴリゴリ書く必要がある。これは正直、生産性が悪いし、メモリリークのリスクを考えると気が重いよね。

Multicornは、その名の通り「多角形」のパワーで、PythonだけでFDWを書けるようにしてくれる。要は「Pythonのクラスを書くだけで、PostgreSQLのテーブルとして振る舞う」ことができるんだ。

実際に手を動かしてみよう:CSVをテーブルとして扱う

まずは簡単な例からいこう。ファイルシステム上のCSVを、まるでローカルのテーブルかのように`SELECT`するFDWだ。

1. インストール

環境によるけど、基本的にはPostgreSQLの拡張としてインストールする。

Ubuntu系の例
sudo apt-get install postgresql-14-python3-multicorn

2. Pythonでラッパーを書く

Multicornのクラスを継承して、`execute`メソッドを実装するだけだ。

from multicorn import ForeignDataWrapper
import csv

class SimpleCSVFDW(ForeignDataWrapper):
def __init__(self, options, columns):
super(SimpleCSVFDW, self).__init__(options, columns)
self.path = options[‘path’]
self.columns = columns

def execute(self, quals, columns):
with open(self.path, ‘r’) as f:
reader = csv.DictReader(f)
for row in reader:
yield row

これをサーバーの適切なパス(`PYTHONPATH`が通っている場所)に置く。

3. PostgreSQL側で宣言する

ここが面白いところだよ。SQLでPythonのコードを呼び出すんだ。

CREATE EXTENSION multicorn;

CREATE SERVER my_csv_server FOREIGN DATA WRAPPER multicorn
OPTIONS (wrapper ‘my_module.SimpleCSVFDW’);

CREATE FOREIGN TABLE my_csv_table (
id integer,
name text
) SERVER my_csv_server
OPTIONS (path ‘/tmp/data.csv’);

これだけで、`SELECT FROM my_csv_table;` と打てば、PythonがCSVをパースして結果を返してくれる。SQLのJOINだって、WHERE句によるフィルタリングだって、PostgreSQLのエンジンが勝手に最適化して処理してくれる。最高だろ?

—

実務で意識すべき「ハマりどころ」

さて、ここからは現場のエンジニアとしての忠告だ。Multicornは便利だけど、魔法の杖じゃない。以下の点には注意してくれ。

1. パフォーマンスは「Python次第」

当たり前だけど、Pythonの処理速度がボトルネックになる。数百万件のデータをループで回すような実装をすると、PostgreSQLのプロセスごと重くなる。

  • 解決策: 可能なら`quals`(WHERE句の条件)をPython側で受け取り、APIのリクエストやファイル検索時に「絞り込み」を先に行うこと。これを「プッシュダウン」と呼ぶ。これをやらないと、全件取得してからPostgreSQL側でフィルタリングすることになり、ネットワークとメモリが死ぬ。

2. ネットワーク越しのデータソースに注意

REST APIを叩くFDWを作るなら、タイムアウト対策は必須だ。DBのコネクションがAPIのレスポンス待ちで埋め尽くされると、サイト全体がダウンする。コネクションプールや非同期処理、あるいは適度なキャッシング層を間に挟む設計を心がけてほしい。

3. トランザクションの整合性

FDWは「外部」を見に行くわけだから、PostgreSQLのACID特性を完全に保証するのは難しい。読み取り専用として使う分にはいいけど、書き込み(INSERT/UPDATE)を実装する場合は、エラーハンドリングを死ぬほど丁寧にやってくれ。

—

最後に:データベースエンジニアの視点

僕は、「データベースはデータの『ハブ』であるべきだ」と思っている。

アプリケーション側で何でもかんでもデータを結合するのではなく、SQLという強力なインターフェースで、バラバラにあるデータソースをひとまとめにする。Multicornを使えば、その設計が驚くほどスマートになる。

「APIを叩くためのバッチ処理」を書いて管理するくらいなら、Multicornでテーブルとしてマッピングして、`VIEW`で綺麗に整える。これだけで、運用コストは劇的に下がるはずだ。

もし「こんなデータソースを繋ぎたいんだけど、どう設計すればいい?」という悩みがあれば、いつでも相談してくれ。技術の引き出しは、開けっ放しにしておくのが一番だ。

それじゃあ、またコードの世界で会おう。健闘を祈るよ。

コメント

タイトルとURLをコピーしました