きり丸の技術日記

技術検証したり、資格等をここに残していきます。

Postgresで1つ前のレコードを取得する方法

※ 参考にした記事を見たら実装方法がわかる駄文です。
新規性は作りたかったけど無いです。


基本的には、Postgresで特定のレコードの1つ前のレコードを取得する方法については、こちらのかたの参考記事を見ていただければわかります。

www.atroom.info

参考先のブログが閉鎖した時のことを考えて、自分用のブログに残しておきます。

実装方法

WITH, LAG, OVERを使用する。
以下のbefore_idで1つ前のレコードの主キー(sequence_id)を取得できます。

WITH tmp AS (
    SELECT 
      sequence_id, 
      LAG(sequence_id, 1, null) OVER (ORDER BY sequence_id) AS before_id
    FROM target_table
    WHERE 
      /** 条件はここに設定する */
    ORDER BY sequence_id DESC
)
SELECT before_id FROM tmp WHERE sequence_id = /** sequence_id */

本当は、1つ前のレコードを一気に全部取得したかったのですが…。
WITH文も、LAG, OVERのWindow関数もよくわからない…。

性能問題もわかってないし不安なので、1つ前の主キーだけ取得して、主キーで検索をかけるようにしてます。

時間が無いなか、取り急ぎ動くから実装したっていう、典型的なダメエンジニアムーブをやってしまいました。
後で覚えないと…。


以降の文章はどのようなユースケースで使用しようとしたのか、とかそういう自分用のまとめです。

ユースケース

対向システムへの連携時のReq/Resに使用した電文を保存するテーブルを所持している。
ReqとResは違うレコードで保存する。

問題があった場合、Resレコードに書き込まれる。
基本的には問題があったらロールバックするが、対向システムによっては処理の重要度が低いのでエラーを握りつぶす。
(※ 社内用分析システムへの書き込みに失敗したからと言って、申込を失敗させる方が機会損失になる等)

ただし、分析できないことも問題なので、後でリトライさせたい。

なので、問題があったResの対になるReqを取得し、Reqの電文を元にリトライする。

※ そもそも、送信している電文が悪いというパターンは考えないことにする。


【ただの感想】
そもそも、対向システムが異常時のリトライ方法ってどのような設計にした方がいいんだろう。

メルカリはMicroserviceだから、そういう知見多そう。
LINEはJJUGのセッションだと、システム異常で相手にメッセージが送れないことのほうが問題だから、二重になってでもリトライしてメッセージを送るとか聞いた気がする。

sequence -1 ではダメなのか。

他のReq/Resと混ざる可能性があるため、sequence -1では基本的に保証できません。
上記のSQLでも基本的にはアウトなので、

      /** 条件はここに設定する */

としているところに、もっと絞れるような条件を指定してあげる必要があります。

正直な感想

こういうユースケースで使うものではないですね。

せめて、Req/Resが対になるようなキーを採番するとか。
もしくは、Req/Resで保存する先のテーブルを変えるとか。

RDBでのアンチパターンを踏みまくってる気がします。

まだ世に出回ってないシステムではあるんですけど、テストしている段階で500万レコードも書き込まれているようなログテーブルなので、商用で運用に耐えきれない気がする…。

この仕事が振られた時も、「技術的には可能です(やりたくありません)」とは答えたんですが。

こういう例外設計がちゃんとできるようになったら、一流エンジニアだと思うので、今後も頑張りたいです…。