ページ付けとソート順


バグ🐞


我々は、公共のリリースの前にDay Oneで我々の同期エンジンの次世代をテストしていました、そして、我々はおかしいバグを見つけました.最後のフェッチから変更されたエントリの大きなリストを取得するためのエンドポイントがあります.このエンドポイントはまた、どのようにコンテンツのない新しい携帯電話は、ジャーナルからすべてのエントリをプルダウンします.しかし、我々は新しいデバイスに私たちのジャーナルの内容のすべてを同期しようとしたとき、我々はエントリを逃していた!

シャーロック🔎


エンドポイントの内部動作を調べ、何が起きたかを確認しました.行方不明のエントリは、データベース内にあったので、問題ではなかった.行を取得し、エントリが表示されたクエリをテストしました.内部では、ページ内のデータベースからエントリのグループを取得し、エンドポイントにそれらを書き出します.したがって、ページ化といくつかの欠陥のあるロジックがあったに違いありません.我々は、ページのパターンを記録したが、すべてが完璧に見えた.
それからサーバによって返された応答を調べました.
我々は、エントリの右側の番号は、そのエンドポイントからプルダウンしていたが、いくつかのエントリのフィードから不足していたが判明し、他の人が繰り返された!
これはソートに問題があったという大きな指標でした.我々は、2回現れたエントリを全く示しなかったエントリと比較しました.確かに、私たちは、彼らが正確に同じ「同期日付」を持っているのを発見しました.
    builder->orderBy([|"sync_date asc"|]);

フィックス


私たちがしなければならなかったのは、ソート句にユニークな列を追加することです.
    builder->orderBy([|"sync_date asc, id asc"|]);
そして、問題は修正されました.

でもどうして?🤔


ページが終了すると、新しいページが取得されるたびに、同じクエリが再び実行されます.たびに、結果の小さなウィンドウが前のページによって取得された項目の数をスキップして取得されます.しかし、これが動作するためには、全体の結果セットはすべてのページ*に対して同じである必要があります.新しいページが取得されたときに結果セットが変更された場合、項目の削除または繰り返しのリスクを実行します.
私たちのケースでは、エントリの同期日付はかなりユニークに近いです.それはミリ秒の精度を持っている.そして、ほとんどの時間、2つのエントリが正確に同じミリセカンドで同期されていません.しかし、我々が現在本当のジャーナルデータのテストから知っているように、同じ同期日付でエントリがあります.これが起こると、それらの2つのエントリーが結果状態で返される順序は不安定であると考えられます.私たちの問い合わせは不安定なソートをしました.
それで、我々の結果セットの項目をスキップすることについての何かは、Postgresが毎回それらの2行の順序を逆にする原因になりました.彼らのうちの1人は逃しました.
ユニークな列をソートすると、ソートが安定したソートになりました.つまり、Postgresは常にどのようにスキップしたり制限してもクエリの結果をどのように順序付けするかを常に知っています.その代わりに、古い行をスキップすると、もはや順序を変更し、すべてのエントリが適切に結果セットに含まれていた.