プランナは、クエリ中に実行される操作をパラレル安全(parallel safe)、パラレル制限(parallel restricted)、パラレル非安全(parallel unsafe)に分類します。
パラレル安全操作は、パラレルクエリとコンフリクトしない操作です。
パラレル制限操作は、パラレルクエリを利用中に、パラレルワーカー中では実行できないが、リーダーによって実行できる操作です。
したがって、パラレル制限操作は、Gather
ノードより下では決して実行されませんが、Gather
ノードを含むプランの別の場所では実行されるかもしれません。
パラレル非安全操作は、パラレルクエリ利用中に、リーダも含めて実行できない操作です。
クエリがパラレル非安全なものを含む場合は、クエリ中でのパラレルクエリの利用は全くできなくなります。
次の操作は常にパラレル制限です。
共通テーブル式(CTE)のスキャン
一時テーブルのスキャン
外部テーブルのスキャン。
外部データラッパがIsForeignScanParallelSafe
APIを持ち、パラレル安全を返す場合を除く。
InitPlan
あるいはSubPlan
へのアクセス。
プランナは、自動的にはユーザ定義関数や集約がパラレル安全か、パラレル制限か、あるいはパラレル非安全かを決定することはできません。
この関数が潜在的に実行する可能性のあるすべての操作を予測することが、このために要求されるからです。
一般的には、これは停止性問題と同等で、それ故に不可能です。
思いつく限りにおいては、終了すると思われる単純な関数においてさえ、私達は予測をしようとは思いません。
なぜなら、そうした予測は高価でエラーを起こしやすいからです。
その代わりに、そうではないとマークされない限り、すべてのユーザ定義関数は、パラレル非安全と見なされます。
CREATE FUNCTIONあるいはALTER FUNCTIONを使用するときは、
適当なPARALLEL SAFE
、PARALLEL RESTRICTED
、PARALLEL UNSAFE
を指定することによってマーキングを行うことができます。
CREATE AGGREGATEを利用するときは、対応する値にしたがって、SAFE
、RESTRICTED
、UNSAFE
のどれかをPARALLEL
オプションに指定します。
データベースに書き込むか、シーケンスにアクセスするか、あるいはトランザクションの状態を一時的にであっても変更する(たとえばエラーを捕捉するためにEXCEPTION
ブロック確立するPL/pgsql関数)、恒久的な設定変更を行う関数あるいは集約は、PARALLEL UNSAFE
とマークされなければなりません。
同様に、一時テーブル、クライアントの接続状態、カーソル、準備された文、システムがワーカーの間で同期できないその他のバックエンドローカルな状態にアクセスする関数あるいは集約は、PARALLEL RESTRICTED
とマークされなければなりません。
たとえば、setseed
とrandom
は、最後の理由により、パラレル制限です。
一般的に制限あるいは非安全な関数が安全とラベル付されたり、実際には非安全なのに制限付きとラベル付されると、パラレルクエリの中で使用される際に、エラーを生じたり、間違った結果を生成するかもしれません。
誤ったラベル付をされると、C言語関数は理論的にはまったく未定義の振る舞いを示すことがあります。
システムは任意のCコードから身を守るすべがないからです。
しかしもっとも起こりえる可能性としては、他の関数のよりも悪いということはなさそうです。
もし自信がないなら、たぶんその関数をUNSAFE
とラベル付するのが最善でしょう。
パラレルワーカーの中で実行される関数がリーダーが獲得していないロックを獲得する場合、たとえばクエリ中で参照されていないテーブルに対して問い合わせを実行する場合などは、これらのロックはトランザクションが終了した時点ではなく、ワーカーが終了する際に解放されます
もしあなたがこれを行う関数を作成し、こうした振る舞いの違いがあなたにとって重要ならば、関数がリーダーの中だけで実行されることを保証するために、関数をPARALLEL RESTRICTED
とマーク付けしてください。
より良いプランを得るために、プランナがクエリの中で実行されるパラレル制限な関数や集約の評価の遅延を考慮することはないことに注意してください。
したがって、たとえばあるテーブルに適用されるWHERE
句がパラレル制限であるときに、クエリプランナはGather
ノードの下にそのテーブルのスキャンを配置することを考慮しません。
ある場合においては、クエリ中のパラレル部分におけるテーブルのスキャンを含むようにして、WHERE
句の評価を遅らせ、Gather
ノード上で実行されるようにすることも可能でしょう(そしてその方が効率が良いことさえあります)。
しかし、プランナはそうしたことは行いません。