7
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

PostgreSQLで勧告的ロックをしたいのでUUIDをBIGINTに変換する

Last updated at Posted at 2022-07-16

目的

排他制御のためだけに Redis 渋々使ってませんか?データベース単独でアドバイザリーロックできるよ!という記事をみて、ちょうどポイントを賞品に交換するというストアードプロシージャが複雑なロックが必要になったので、勧告的ロックを使ってみました。

関数

pg_try_advisory_xact_lock_sharedの引数は1つのBIGINTまたは2つのINTになります。自分はテーブルの主キーをUUIDにしているのでそのまま使うことができません。なんらかの方法で変換する必要があります。色々探したところ以下のようなコードを見つけました。

('x' || translate(gen_random_uuid()::TEXT, '-', ''))::bit(64)::BIGINT

UUIDは128bitでBIGINTは64bitなので、半分捨ててしまうことになりますが、本人が同時にポイント交換させないという目的には十分だと考えました。

コード

賞品を交換する雰囲気のストアードプロシージャは以下のようになります。

CREATE OR REPLACE FUNCTION exchange_item (
  p_user_uuid UUID DEFAULT NULL
  ,p_item_uuid UUID DEFAULT NULL
) AS $FUNCTION$
DECLARE
BEGIN
  -- 勧告的ロック
  IF NOT pg_try_advisory_xact_lock(
    ('x' || translate(p_user_uuid::TEXT, '-', ''))::bit(64)::BIGINT
  ) THEN
    RAISE SQLSTATE 'U0001' USING MESSAGE = 'advisory lock';
  END IF;

  -- 複雑な状況チェック

  -- ポイント交換処理
END;
$FUNCTION$ LANGUAGE plpgsql;

追記

このままのコードだとシステム全体で1っか所でしか使えないので、複数で使いたい場合、数字を足せばよいです。
例えば4か所で使うなら使う場所ごとに+1, +2, +3, +4すれば同じuser_uuidでも同時に使えます。
同じ人に同時に処理させたくないという目的ならこれで問題無いと思います。

IF NOT pg_try_advisory_xact_lock(
  ('x' || translate(p_user_uuid::TEXT, '-', ''))::bit(64)::BIGINT + 1
) THEN
  RAISE SQLSTATE 'U0001' USING MESSAGE = 'advisory lock';
END IF;
7
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
7
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?