2016-05-05 9 views
5

状況によっては、シリアライズされたアクセスを必要とする機能があります。これは、アドバイザリロックを使用する良いケースのようです。しかし、かなり重い負荷の下で、私はシリアル化されたアクセスが発生していないと私は機能に同時にアクセスを見ていることを発見している。Postgresのアドバイザリロック機能で同時実行が可能

この機能の目的は、イベントの「在庫管理」を提供することです。意味することは、イベントが過度に過多にならないように、特定のイベントに対するチケットの同時購入を制限することを意図しています。これらは、アプリケーション/データベース内で使用される唯一のアドバイザリロックです。

イベントには、eventTicketMax値よりも多くのチケットが存在することがあります。これは、アドバイザリロックのために可能であるようには思われません。少量(またはロック取得後にpg_sleepなどの手作業で導入された遅延)でテストすると、正常に動作します。

  • ジャンゴ1.8.1(django.db.backends.postgresql_psycopg2ワット/ CONN_MAX_AGE 300)
  • PGBouncer 1.7.2(セッションモード)
  • のPostgres:

    CREATE OR REPLACE FUNCTION createTicket(
         userId int, 
         eventId int, 
         eventTicketMax int 
        ) RETURNS integer AS $$ 
         DECLARE insertedId int; 
         DECLARE numTickets int; 
        BEGIN 
          -- first get the event lock 
          PERFORM pg_advisory_lock(eventId); 
    
          -- make sure we aren't over ticket max 
          numTickets := (SELECT count(*) FROM api_ticket 
           WHERE event_id = eventId and status <> 'x'); 
    
          IF numTickets >= eventTicketMax THEN 
           -- raise an exception if this puts us over the max 
           -- and bail 
           PERFORM pg_advisory_unlock(eventId); 
           RAISE EXCEPTION 'Maximum entries number for this event has been reached.'; 
          END IF; 
    
          -- create the ticket 
          INSERT INTO api_ticket (
           user_id, 
           event_id, 
           created_ts 
          ) 
          VALUES (
           userId, 
           eventId, 
           now() 
          ) 
          RETURNING id INTO insertedId; 
    
          -- update the ticket count 
          UPDATE api_event SET ticket_count = numTickets + 1 WHERE id = eventId; 
    
          -- release the event lock 
          PERFORM pg_advisory_unlock(eventId); 
    
         RETURN insertedId; 
        END; 
        $$ LANGUAGE plpgsql; 
    

    は、ここに私の環境設定ですAmazonで9.3.10 RDS

チューニングを試した追加の変数:

  • 設定CONN_MAX_AGE pgbouncerを削除し、私のテストではDB

に直接接続

  • 0に、私は、イベントが売られ過ぎた場合には、チケットが大きく異なるWebサーバから購入した、ということに気づきました私は、共有セッションについて面白いビジネスはないとは思っていますが、私は確かに言うことができません。

  • 答えて

    3

    PERFORM pg_advisory_unlock(eventId)が実行されるとすぐに、別のセッションがそのロックを取得できますが、セッション#1のINSERTはまだコミットされていないため、セッション#2のCOUNT(*)にカウントされず、 。

    アドバイザリロック戦略を維持する場合は、セッションレベルではなくトランザクションレベルのアドバイザリロック(pg_advisory_xact_lock)を使用する必要があります。これらのロックはCOMMIT時に自動的に解放されます。

    また、トランザクション分離レベルが重要であることにも注意してください。デフォルトでは、postgresはREPEATABLE READを使用しています。そして、Djangoはそれを離れないことが一目瞭然です。 COUNT(*)は、トランザクションが開始された状態ではなく、最後にコミットされた状態を反映することが重要です。

    +0

    はい、これはまさに問題です。トランザクションレベルのロックに切り替えると、問題は解決しました。ありがとうございました! –

    関連する問題