メインコンテンツにスキップ
バージョン: 1.x

同期エンジンの仕組み

v1.10.0の新機能

このページでは、WCPOS v1.10.0 で導入された同期エンジンについて説明します。それ以前のバージョンは、画面ごとに異なるレプリケーションモデルを使用しています。ページ末尾のv1.10.0での変更点をご覧ください。

WCPOSはローカルファーストです。すべての画面はデバイス上のデータベースに対して読み書きを行い、同期エンジンがそのデータベースとWooCommerceストアをバックグラウンドで収束させ続けます。このページでは、エンジンが何を、いつ取得するかをどのように判断し、売上がどのようにサーバーへ戻るのかを説明します。開発者、インテグレーター、そしてPOSがホスティングに与える負荷を理解したいストアオーナーに役立つ詳細度でまとめています。

店舗とレジ係ごとに1つのエンジン​

各デバイス上のPOSは、サイト、店舗、レジ係の組み合わせごとに1つの同期エンジンを実行します。エンジンは独自のローカルデータベースを管理するため、店舗またはレジ係を切り替えると、共有データを絞り込むのではなく、データ領域全体が切り替わります。レジ係ごとの分離は意図的な設計です。同じデバイスを使う2人のレジ係がローカルデータを共有することはありません。

対象の切り替えはトランザクションとして行われます。新しい店舗またはレジ係のデータ領域を読み込み、準備が整ってから確定します。エラーが発生した場合は通知されるため、2つの店舗の間で中途半端な切り替え状態になることはありません。

アップグレード時にローカルデータベースがその場で移行されることはありません。アプリは新しいデータベースを作成し、常に正となるコピーを保持しているサーバーからダウンロードし直します(v1.9からのアップグレードを参照)。

Proのマルチストア構成では、すべての同期リクエストが対象のストアを識別します。そのため、レジで編集した価格はWebストアの価格ではなく、そのストアの価格を更新します。

バックグラウンドで実行される処理​

エンジンがスケジュールに従って実行する処理はすべてレーンと呼ばれます。レーンは名前と上限を持つバックグラウンド処理単位です。レーンは3つのグループに分かれます:

グループレーン既定の実行間隔
プル変更チェック(変更シグナル)10秒〜5分(同期プリセットで設定)
最近の注文、商品カタログのシード、参照データのシード約5分
顧客のトリクル(アイドル時のみ)約5分
プッシュ書き込みドレイン — キューに入ったローカルの変更を送信約10秒
メンテナンス整合性および削除の監査、サーバー合計数の更新数分〜約17分ごと

どのレーンにも共通する特性が2つあります:

  • 各レーンには1回の実行あたりのリクエスト上限があります。 1回の実行でリクエスト数が無制限に増えるレーンはありません。大規模な処理は、再開可能なカーソルを使って上限付きのバッチに分割されます。これは中核となる設計上の不変条件であり、同期のパフォーマンスで詳しく説明しています。
  • メンテナンスはキャッシャーに道を譲ります。 監査やバックグラウンドの事前読み込みは、レジが販売できる状態になるまでの対話的な処理の後に実行され、その前に実行されることはありません。また、サーバーに負荷の兆候が見られたときに最初に一時停止されるのもこれらの処理です。

上記の間隔は既定値です。チェック間隔とリクエストあたりのレコード数は、POSの ストアヘルス → パフォーマンス からデバイスごとに販売者が調整できます。ストアヘルスをご覧ください。

POSが変更を検知する仕組み​

エンジンは、変更の有無を確認するためにデータを再ダウンロードしません。サーバーが変更ログを保持し、POSは軽量な変更チェックをポーリングして、前回の位置から変更があったかどうかだけを確認します。

  • 1回のチェックで8つのコレクションを確認します。商品、バリエーション、税率、顧客、クーポン、カテゴリー、ブランド、タグが対象です。注文は意図的に変更チェックから除外されています。注文の最新状態は専用の最近の注文レーンで取得されるため、商品と注文の更新頻度は異なる場合があります。
  • アイドル状態のレジにかかるコストはほとんどありません。 エンジンは条件付きリクエストを送信し、変更がなければサーバーは本文のない304 Not Modifiedを1回返します。静かな店舗では、チェックごとに小さな応答が1回返る状態に落ち着きます。
  • チェックの間隔には±20%のジッターを加えます。そのため、同じサイトにある複数のレジのチェック時刻が分散され、サーバーに一斉にリクエストが集中するのを防ぎます。
  • アイドル時の減衰: 10分間操作がないと、チェック間隔が延びます(下限は60秒)。タッチ、キー入力、バーコードスキャンの受理といった実際の操作があれば、すぐに通常の間隔に戻り、即座にキャッチアップのチェックが実行されます。減衰は間隔を延ばすだけで、設定値より速くポーリングすることはありません。
  • 数日間閉じられていたレジは、履歴を再生するのではなくベースラインを取り直します。 変更ログがレジの最後の位置から離れすぎている場合、すべての行を再生すると数百件のリクエストが必要になります。そこでエンジンはカーソルを先頭までジャンプさせ、デバイスがすでに保持しているデータの現在のサーバー上の状態を確認し直します。これにより、コストはレジが離れていた期間ではなく、ローカルコピーのサイズに比例します。

画面がデータを取得する仕組み​

v1.10.0では、画面ごとに同期を実行しません。画面は検索語、フィルター、並べ替え、ページなどの表示内容を宣言し、それにリクエストが必要かどうかをエンジンが判断します。 各宣言は次の3つのいずれかになります:

  • 取得(Fetched) — エンジンが宣言を満たすために通信を行いました。
  • ローカルで応答(Served locally) — デバイスがすでに答えを保持していたか、直近の同一の取得結果でカバーできました。
  • 上書き(Superseded) — 画面が先へ進み(スクロールした、フィルターを変更した)、より新しい宣言に置き換えられました。これは通常の動作であり、エラーではありません。

ローカルだけで答えられるフィルターがサーバーまで届くことはありません。また、宣言が成功したからといって、そのコレクションが完全にダウンロードされたことにはなりません。完全性は別途追跡されます(次のセクション)。

すべてが先読みでダウンロードされるわけではなく、起動時にデバイス上に何があるかはコレクションによって異なります。

  1. シードされるもの — 商品は上限付きのカタログシードによって充填されます。税率は起動時に取得されます(税率がなければPOSはカートの計算ができません)。
  2. オンデマンド+アイドル時のトリクル — 顧客には先読みのシードがありません。顧客のトリクルはアイドルの間隔ごとに小さなバッチをダウンロードし、キャッシャーが操作中のときは完全にスキップします。新規または変更された顧客は変更チェックを通じて届きます。
  3. 初回オープン時に取得されるもの — カテゴリー、タグ、ブランド、クーポンは、キャッシャーが最初に開いたときに取得されます。誰も開かないコレクションが、リクエストを発生させることは一切ありません。

バリエーション選択画面では、開くたびに価格と在庫も更新されます。そのため、読み込み済みのバリエーションはすぐに表示され、何日も前の古い在庫が表示されることもありません。

「すべてダウンロードされていますか?」— 誠実なカバレッジ​

*「5,000件中1,240件の商品」*のような表示には、分母となるサーバー側の合計数が必要です。エンジンはコレクションごとに合計数を管理し(およそ15分ごとに更新されます。通常のsyncレスポンスにすでに合計数が含まれているため、専用のリクエストが必要になることはめったになく、たいていコストはかかりません)、次の厳格な誠実性のルールに従います。

  • サーバー合計数が古い、または取得できない場合は*確認中…*と表示され、ローカルの件数がひそかに代用されることはありません。ローカルの分母を使えば常に100%と表示され、この数値が明らかにすべきギャップそのものを隠してしまうからです。
  • エンジンが完全性を保証できない場合、判定は不明となり、UIはその数値をローカルの件数として表示します。
  • 注文のカバレッジバーは、サーバー上のすべての注文履歴に対して測定されます。一方でレジは意図的に未処理の注文と最近の注文のみを保持します。そのため、正常なレジでも注文は部分的と表示されますが、これは設計どおりです。

これらの数値は ストアヘルス → データベース に表示されます。あわせて、最初の商品がデバイスに届いた時点で切り替わる販売準備完了のマイルストーンも表示されます。オフラインであっても妨げにはなりません。オフラインで販売できることこそが目的だからです。ストアヘルスをご覧ください。

変更がストアに戻る仕組み​

ローカルでの書き込み(売上、商品の編集、顧客の更新)はすべて、まずデバイス上の永続的な送信キューに入ります。そしてドレインレーンが数秒ごとにキューをWooCommerceへ送信します。これがオフライン販売を安全にしている仕組みです。接続がない状態で計上した売上はキューに留まり、接続が戻った時点で送信されます。

重要な詳細は次のとおりです。

  • カートへの書き込みは注文ごとに直列化されます。 スキャナーで短時間に連続読み取りしても、1件ずつ適用されます。同じ商品を繰り返し追加した場合は、2つ目の行をキューに追加するのではなく、既存の行に統合します。
  • 注文の確認応答ではサーバー側のコピーを採用します。 WooCommerceは注文の作成時に明細行へIDを割り当てます。エンジンは確認応答された注文を採用するため、以降の更新は重複行を追加するのではなく、それらの明細行に一致します。採用は保守的に行われ、サーバーがまだ認識していないローカルの編集を上書きすることはなく、注文にのみ適用されます。
  • 注文の書き込み競合は1回だけ自動復旧します。 注文の書き込みがサーバー上の新しいコピーと競合すると、エンジンは整合させて1回だけ再試行します。それでも解決できない場合は、無言でループせず、ユーザーが解決できるよう競合を通知します。
  • サーバーが恒久的に拒否した書き込みは、自動で再試行されません。 サーバーが返した理由とともに保留され、店舗の健全性 → データベースに*"変更がサーバーに届きませんでした"*と表示されます。明示的な操作として再送信(現在のレコードからリクエストを再構築するため、その後の修正が反映されます)と破棄が用意されています。自動再試行は行われず、復旧操作は常に明示的に実行します。店舗管理者向けの手順は店舗の健全性を参照してください。

複数のブラウザータブ​

同じ店舗を複数のタブで開いてウェブPOSを使用できます。どのタブからも販売を処理でき、書き込みは共有キューに追加されますが、店舗とレジ係の各スコープで送信を担当するタブは1つだけです。そのタブを閉じると、ブラウザーが自動的に次のタブを担当にします。異なるレジ係でログインした2つのタブは別々のスコープとなり、それぞれ独自のキューを管理します。

ローカルストレージ​

ウェブ版とデスクトップアプリは、OPFS(Origin Private File System)ワーカーを通じてデータを保存します。iOS版とAndroid版は、ファイルシステムエンジンを使って同じディスク上の形式を使用します。4つのプラットフォームすべてで共通のストレージ形式と破損復旧ツールを使用します(以前のバージョンでは、ウェブにIndexedDB、ネイティブアプリにSQLiteを使用していました)。

ローカル店舗データが読み取れない場合、WCPOSはチェックアウトをブロックして再読み込みを促します。破損したデータで販売を続けて注文を失うことはありません。また、その状態でバーコードをスキャンすると、無反応になるのではなくエラーが明確に表示されます。復旧ツールがデータを再構築する前に、破損した店舗データによって販売が気付かれずに失われることを防ぎます。

クエリ(セレクター、並べ替え、ページ)はデータベース層内で実行されるため、表示中のページの行だけがアプリに渡されます。注文10,000件の合成データを使ったテストでは、ライブ購読中の書き込み1回あたりの更新コストが約27 msから約0.06 msに減りました。

v1.9からのアップグレード​

v1.10.0はローカルデータベースを移行せず、コールド再同期を実行します。

  1. 初回起動時、アプリは新しいローカルデータベースを開き、ストアからダウンロードし直します。すべてのデバイスで一度だけ、完全な再ダウンロードが発生します。
  2. データが失われることはありません。同期されるすべてのデータについて、正となるコピーはWooCommerceサーバーです。未送信の変更は、古いデータが整理される前に送信されるか、画面に表示されます。アップグレードによって未送信の売上が失われることはありません。
  3. アプリとプラグインは足並みをそろえてリリースされます。 v1.10.0のクライアントは、プラグインのv2同期APIを使用します。どちらか一方だけをアップグレードすると、リクエストは rest_no_route エラーで失敗します。このエラーは「もう一方も更新してください」という意味であり、インストールが壊れているわけではありません。

v1.10.0での変更点​

v1.9.xv1.10.0
同期の単位マウントされた画面クエリごとに1つのレプリケーションストリームサイト、店舗、レジ係ごとに1つのエンジン
取得のきっかけ画面のマウント画面が必要なものを宣言すること
変更検出定期ポーリングと1時間ごとの完全監査条件付き304応答を使う変更ログカーソル
サーバー合計数追跡なしコレクションごとの合計数と、誠実な*確認中…*の状態
失敗した書き込み不透明に再試行永続キュー、目に見える復旧パネル、ひそかな再試行なし
複数タブのウェブ調整なしスコープごとに送信担当を1つ選出
ローカル検索単語の前方一致部分一致(3文字以上)
ストレージIndexedDB(Web)、SQLite(ネイティブ)全プラットフォームでOPFS形式のストレージ
同期のチューニング固定ストアヘルス内のデバイスごとのプリセットとダイヤル

エンジンの背後にあるパフォーマンスの考え方(リクエスト上限、サーバー負荷時のバックオフ、実測値)については、同期のパフォーマンスをご覧ください。