はじめに

これはNekiを触ってみた記事の2本目です。Nekiの基本的な構成、4 shardにしたときのqueryの送信先選択、Scatterとコロケーションによるplanの変化は、第1回で説明しています。

今回は少し視点を変えて、すでにshardされたPostgreSQLを運用する側からNekiを見ます。特に知りたかったのは、1 shardにあるtableを別のshard groupへ移すとき、コピー、追従、整合性確認、read/writeの宛先切り替えをどこまで製品側が引き受けるのか、という点です。

NekiにはReshard workflowがあるようです。公式資料では、source shard上のtableをdestination shardsへ移し、移行中の変更をstreamingで追従させ、整合性確認後にread/write trafficを切り替える仕組みと説明されています。今回は40行だけの小さなtableで、workflowを最後まで進めてみました。

NekiはPlatform Previewです。この記事で示すのは2026年9月中旬の検証時点での挙動に限られます。ReshardのAPI、管理画面、制約、運用手順は今後変更される可能性があり、ここでの観測を将来の仕様として保証するものではありません。

Reshardで使う言葉

  • source: 移行元のshard groupです。
  • destinationまたはtarget: 移行先のshard groupです。
  • streaming: 初回コピー後、移行元の変更を追従するためのworkflowの段階です。今回確認したのはこの段階への遷移で、追加writeの追従自体は検証していません。
  • differ: sourceとtargetのrowを比較し、一致しているかを確認する仕組みです。
  • traffic cutover: read/writeの宛先をsourceからtargetへ切り替える操作です。以後は単に「切り替え」と呼びます。

先に結論

今回のReshardでは、停止状態でworkflowを作り、copyからstreamingへ進み、differで整合性を確認してからread/writeの宛先を切り替え、completeするところまで進められました。40行はdestination-1へ25行、destination-2へ15行に分かれ、differ完了時には40行すべてが一致していました。切り替え後もrouterから40行を読めました。

公式資料によれば、Nekiが引き受けるのはdestination schemaとstreamの準備、初回コピー、WAL由来の変更追従、進捗保持、source/targetの比較、read/writeの宛先切り替えのようです。一方で、sourceとtargetのデータ配置定義、分散キー、copyのiteration key、移行対象table、statusとerrorの確認、differが終わるまで切り替えないという判断は、利用者側に残るようです。

今回見た範囲では、管理操作の入口は操作ごとに異なっていました。databaseとshardの作成、database全体の削除はCLI、Reshard workflowはrouter SQLのNeki metafunction、監視画面はUIという分担のようです。今回はUIを使った監視画面の確認までは手が回っていません。

なぜ3 shardを使ったのか

まず、1 shardから2 shardへ分けるだけなのに、なぜ3 shard必要なのかを確認します。移行元をそのまま移行先として使えない事情があるようです。

この実験では、sourceかつauthoritativeなshardを1つ、destination shardを2つ使いました。各shardはprimary 1台とreplica 2台、routerはNKR-1です。events tableに40行だけ入れ、event_idをprimary key、tenant_idを分散先を決めるkeyにしました。

2-way splitに3 shardが必要になるのは、targetにsource shardを再利用しないためのようです。sourceのauthoritative shardはPostgreSQL catalog、system table、sequenceのauthorityとして残り、application tableの配置は新しい2 shard groupへ切り替わります。application dataの配置とcatalog上のauthorityは、移行の前後で別の意味を持つようなので、混同しないようにしました。

Reshard前にtableと2種類のkeyを準備する

移行を始める前に、どのtableをどのkeyで移すかを明示します。ここでのkeyの役割が、コピー順と移行先の選択で異なるのがポイントです。

Reshardの前に、eventsがsource groupにあり、targetで使うshard indexとしてxxhash_tenant_idを定義しました。tableは次のようにしています。

CREATE TABLE events (
  event_id bigint PRIMARY KEY,
  tenant_id bigint NOT NULL,
  payload text NOT NULL
);

ここではevent_idtenant_idの役割が異なります。event_idはcopy順を決めるために使うiteration keyで、tenant_idはdestinationを選ぶshard keyです。少なくとも今回の40行の移行では、この区別が必要でした。

開始前にworkflowの状態を確認する

いきなりコピーを始めず、まず移行workflowの形だけを作って確認できるのでしょうか。create_stoppedを使って状態を見てみます。

今回はReshardをrouter上のNeki metafunctionから作成しました。Platform Previewの時点では、CLI workflowやdashboardの設定画面からではなく、SQL connection経由で行う操作のようです。

SELECT __neki.reshard_create(
  'reshard_events',
  'postgres',
  'imported',
  '{
    "default_shard_index": "xxhash_tenant_id",
    "key_ranges": [
      {"shard_uid": "destination-1", "end": "80"},
      {"shard_uid": "destination-2", "start": "80"}
    ]
  }',
  'events_by_tenant',
  '{"create_stopped": true}'
);

create_stoppedを指定したことで、copyを始める前にworkflowの形を確認できました。この時点のstatusは次のとおりです。

phase: not_started
status: idle
traffic_state: not_switched

source shard、destination shard、stream数が意図どおりかを見てから開始できるので、短時間の検証でも扱いやすそうでした。

この状態から始められるなら、移行開始前に対象と行き先を見直す余地がありそうです。

初回コピー後、workflowはstreamingへ遷移した

開始後に知りたいのは、コピーがどこまで進んだかと、移行元の変更を追従する段階に入ったかです。workflowのstatusで確認してみます。

workflowを開始します。

SELECT __neki.workflow_start('reshard_events');

statusはnot_startedから進み、destinationごとのstreamがstreaming / runningになりました。40行のうちdestination-1に25行、destination-2に15行がcopyされています。これは小さなsampleなので分散の均等性を評価する数字ではありません。公式仕様はstreamingによる変更追従を説明していますが、本実験では追加writeを発生させてtargetへの追従を個別に検証してはいません。確認できたのはstreaming phaseへの遷移です。

保存したworkflow_statusの出力では、2つのdestinationに対して次の値が返っています。

destination 1: phase=streaming, status=running,
               total_rows_copied=25, total_tables_copied=1
destination 2: phase=streaming, status=running,
               total_rows_copied=15, total_tables_copied=1
traffic_state: not_switched

同じworkflow_startをもう一度実行すると、all streams ... are already runningというerrorになりました。少なくともこの状態では開始操作をそのまま再実行するものではなさそうで、次の判断前にstatusを読む必要がありそうです。

端的に言うと、進捗はdestinationごとに見られ、開始後はstatusを見ながら次の操作を選ぶ形になりそうです。

differでsourceとtargetを比較した

コピーされた行が本当に移行先と一致しているかは、行数だけでは分かりません。切り替え前にdifferで比較する意味を確かめます。

sourceとtargetの一貫性確認にはdifferを使いました。

SELECT __neki.differ_create('reshard_events', 'pre_cutover');
SELECT * FROM __neki.differ_status('reshard_events', 'pre_cutover');
SELECT * FROM __neki.differ_report('reshard_events', 'pre_cutover');

作成直後のreportはcomplete: falseでした。行数が0でmismatch: falseでも、完了前に一致しているとは言えません。完了後は次の結果でした。

status: completed
rows compared: 40
matching rows: 40
mismatched rows: 0
extra source rows: 0
extra target rows: 0
complete: true

少なくともこの40行については、differが完了したあとにsourceとtargetの行内容が一致していることを確認できました。大規模データや長時間更新での検証ではない点には注意が必要です。

今回の範囲では、complete: trueになるまで待つことが、切り替え判断の最低条件になりそうです。

read/writeの宛先を切り替え、移行を完了した

比較が終わったあと、applicationが使うtable名を変えずに移行先へ切り替えられるのでしょうか。read/writeの宛先を切り替えた直後の結果を確認します。

differ完了後に、readとwriteをまとめてtargetへ切り替えました。

SELECT * FROM __neki.workflow_switch_traffic('reshard_events');
SELECT __neki.workflow_complete('reshard_events');

結果はReads and Writes switchedで、complete後にrouterから実行したSELECT count(*) FROM eventsは40行を返しました。applicationから見たtable名を変えずに、read/writeの行き先を切り替えられたことになります。

Reads and Writes switched for workflow reshard_events

events_after_cutover
--------------------
40

ただし、切り替えを安全にする作業がなくなるわけではなさそうです。streamingが正常であること、differが完了していること、application側が切り替え時の制約を受け入れられることを確認してから実行するのがよさそうです。

端的には、table名を変えずに宛先を切り替えられそうですが、切り替えの可否を判断する責任までなくなるわけではなさそうです。

管理操作の入口を整理する

ここまでのReshardはrouter SQLで進めました。では、cluster作成や監視、replicaへの接続はどこから扱うのかを、今回触れた範囲だけ整理します。

今回確認した管理操作は、入口ごとに分かれていました。CLIで実際に再現できたのはdatabaseとshardの作成・database削除、data topology、physical shardの配置、logs、extension catalog、primaryまたはreplicaへのSQL routingです。configuration profileとrouterの操作はCLI commandの存在を確認しましたが、この調査では実行していません。

一方、Reshardの作成・開始・status・differ・traffic switch・completeは、router SQLのNeki metafunctionで行いました。公式資料にはtopology revisionの全routerへの可視化待ちもありますが、今回は実行していません。SQLとして履歴に残しやすい一方、workflowの状態遷移は把握しておく必要がありそうです。

metricsの時間グラフとQuery InsightsはUI中心のようです。pscale metrics reportを試すとdatabase engine "neki" is not supported by metrics reportと返りました。UIで確認できるという公式資料はありますが、今回はその画面を確認していません。UIに制約があったという意味ではなく、今回の検証対象に含めなかったためです。

pscale metrics reportの実行結果は次のとおりでした。

{
"status": "error",
"error": "database engine \"neki\" is not supported by metrics report"
}

今回見る限り、どの操作も同じ入口で行うというより、目的に応じてCLI、router SQL、UIを使い分ける形になりそうです。

replica・failover・cross-shard transactionで確認できたこと

shardが複数nodeで動くなら、read replicaや障害時の扱いも気になります。ただし今回は、readの経路を1回確認しただけで、障害試験までは行っていません。

各shardにはprimary 1台とreplica 2台がありました。replica targetへのreadはreplica: trueで実行でき、primaryへ書いた行を1回読めました。ただし、これはread-your-writesやmonotonic readを保証する検証ではありません。公式資料もreplica readにこれらの保証はないと説明しています。replica targetへのwriteはpermission errorでしたが、接続roleもreaderだったため、replicaだけが理由だとはこの実験だけでは断定できません。

{ "replica": true, "replica_count": 1 }

pq: permission denied for table replica_probe (42501)

公式資料では、primary障害時にはNeki adminがeligible replicaをpromoteしてshard topologyを更新すると説明されています。ただし、今回のCLIにはNeki専用の手動failoverを見つけられませんでした。通常Postgres向けのbranch switchoverもNekiには使えないようです。安全な操作面を確認できなかったため、障害を起こす実機試験は行っていません。

Previewの公式制約として、複数shardにまたがるtransactionのatomic commitはサポートされていないようです。shard内でPostgreSQL transactionを使えることと、複数PostgreSQL shardをまたいでatomicにcommitできることは別の話です。

端的には、replica readとfailoverの仕組みは用意されていそうですが、read整合性や障害時の具体的な運用手順は、この検証だけでは判断できません。

extensionにはmanaged serviceとしての制約がある

各shardがPostgreSQLなら、既存のextensionもそのまま使えるのでしょうか。catalogの状態を確認します。

configuration profileのcatalogでは、pg_pscale_utilspgextwlistが内部extensionとして有効で、vectorvectorscaleはavailableながら無効でした。各shardがPostgreSQLであっても、managed serviceとしてimageとallowlistの制約を受けます。

name             enabled  internal
pg_pscale_utils  true     true
pgextwlist       true     true
vector           false    false
vectorscale      false    false

既存systemのextensionに依存する場合は、対応の有無だけでなくversion、設定、各shardへの適用方法まで移行前に確認した方がよさそうです。

今回見えた責務分担

Reshardと運用についても、workflowがあることと判断が不要になることは別のようです。今回の観測と公式資料からは、次のような分担が見えてきました。

領域 Nekiが受け持つように見えたこと 実装者・運用者に残りそうなこと
データ移行 destinationの準備、初回コピー、streaming、source/targetの比較、read/writeの宛先切り替え 移行先のデータ配置定義、分散キー、iteration key、移行対象tableの決定
切り替え判断 differによる比較とworkflowの状態表示 completeを確認し、applicationを切り替えてよいか判断すること
日常の運用 CLI、router SQL、UIから使える操作を提供 必要な監視、read整合性、障害時手順、各入口の使い分けを決めること

これはNekiが分散を隠せていない、という話ではなさそうです。dataの意味や許容できる停止・整合性の条件はapplicationごとに異なるため、製品がworkflowを提供しても、最終的な設計と運用の判断は残るように見えます。

まとめ

NekiのReshardは、通常のPostgreSQL tableを新しいshardへ移すための運用workflowのようです。今回の小規模実験では、初回コピー、streaming、differ、read/writeの宛先切り替えという一連の状態遷移を確認できました。

ただし、今回見る限り、Nekiは分散を完全に隠すものではなさそうです。どのtableをどのkeyで移すか、いつ切り替えるか、一貫性をどのように判断するか、管理操作をCLI・router SQL・UIのどこで行うかは、利用者側に残るようです。

ordinary PostgreSQL shardの価値は、SQLやplannerの互換性だけではなさそうです。データ移動のworkflowを外側に持ちながら、第1回を含む今回の検証では、Nekiのshard上でPostgreSQLのSQL、EXPLAIN、index、catalogに関する知識を再利用できました。今回見た範囲でのNekiは、分散を不要にするというより、分散の機械的な部分を引き受け、設計と運用の判断を見える形で残す製品のように見えました。

この結論もPlatform Preview時点のものです。実運用に持ち込むなら、利用時点の公式ドキュメントを確認したうえで、対象データ量、更新頻度、障害時の手順、必要な監視を含めた検証を別途行った方がよさそうです。

参照資料