web-dev-qa-db-ja.com

共有リソースのリースメカニズムを設計する

クライアントC1とC2によって使用されるデータベースに共有リソースがあります。リソースがC1によって読み取られる前に、C1はリソースをリース済みとしてマークし、C2がリソースがビジーであることを認識できるようにする必要があります。 C1はいくつかの作業を行い、完了するとリースを更新してリソースをC2で使用できるようにします。私はリースを使用しているので、Cxのホストシステムがクラッシュした場合、別のCが時間Tの後にリソースを使用できるようになります。

フローは次のようになります。

データベーストランザクションの開始

  1. C1はリソースを読み取り、リース時間をチェックしてcurrent time >= leasetime + Tを確認します。

  2. C1は、現在の時刻のタイムスタンプをリース列に書き込みます

データベーストランザクションの終了

  1. C1がリソースの使用を開始します

  2. C2はリソースを読み取り、リース時間をチェックしますが、current time < leasetime + Tであるため、リソースはビジーです

  3. C2はエラーを返します

  4. C1が終了し、リースにcurrent time - Tを書き込みます

  5. C2は再試行し(ステップ1-3)、リソースをリースできるようになります

C1とC2は通信できません。

私の素朴な実装は、リソースにホストシステムからのタイムスタンプをタグ付けして、リースとしてマークすることです。 C2がリソースを読み取ろうとすると、タイムスタンプが時間T以内であり、リソースがビジーであることを意味します。その後、C2はユーザーにエラーを返し、再試行するように求めます。このソリューションは、C1とC2のホストシステムのシステム時間に依存し、その時間が異なる場合があるため、機能しません。

私が考えていたのは、データベースの時間を使用して、リソースがリースされているか、自由に使用できるかを計算することでした。しかし、時間に依存することはまだ悪い解決策のように思えます。

C1が終了するまでCnがリソースを使用しないようにするには、どうすればよいですか?

2
span

C1はケースを押しつぶしません

dbトランザクションを開始します(必ず 明示的なロック を使用してください!)

  1. C1はリソースを読み取り、_is_busy = false_をチェックします

  2. C1は_is_busy = true_と_leased_at_を現在のタイムスタンプで書き込みます

データベーストランザクションの終了

  1. C1がリソースの使用を開始します

  2. C2はリソースを読み取り、_is_busy_がtrueであることを確認します

  3. C2はエラーを返します

  4. C1が終了し、_is_busy = false_を書き込みます

  5. C2は再試行し(ステップ1-3)、リソースをリースできるようになります

C1はケースを押しつぶします

データベーストランザクションを開始します

  1. C1はリソースを読み取り、_is_busy = false_をチェックします

  2. C1は_is_busy = true_と_leased_at_を現在のタイムスタンプで書き込みます

データベーストランザクションの終了

  1. C1クラッシュ

  2. C2はリソースを読み取り、_is_busy_がtrueであることを確認します

  3. C2はエラーを返します

  4. いくつかの個別のスレッド/プロセス/サービス/データベース自体( pgAgent のようなsmth)は、_is_busy_の値をチェックすることにより、_leased_at_が長時間ロックされているかどうかを監視します。クエリはupdate table set is_busy = false where is_busy = true and leased_at < now() - interval '15 minutes'のようになります

  5. C2は再試行し(ステップ1-3)、15分でリソースをリースできます

1
Zapadlo