2017-05-26 37 views
0

我有其經由以下命令創建和PostgreSQL中填充的表:Django原子選擇更新不鎖定表的遞歸調用?

CREATE TABLE my_lock (
    id integer, 
    CONSTRAINT id_pkey PRIMARY KEY (id) 
) ; 

INSERT INTO my_lock VALUES (1) ; 
INSERT INTO my_lock VALUES (2) ; 

該表由以下的Django模型

from django.db import models 
from django.db import transaction 

class MyLock(models.Model): 
    class Meta(object): 
     db_table = 'my_lock' 

接着表示,我有以下幾種方法:

from contextlib import contextmanager 

@contextmanager 
def acquire_lock(): 
    with transaction.atomic(): 
     lock = MyLock.objects.select_for_update().filter(id=1).first() 
     yield lock 


def first_method(): 
    print "In first method" 
    with acquire_lock(): 
     print "Lock acquired in first_method()" 
     second_method() 


def second_method(): 
    print "In second method" 
    first_method() 

acquire_lock()方法是一個Python生成器,它運行一個SELECT FOR UPDATE在事務中查詢。這應該對id = 1的行進行鎖定,並且由於調用yield lock時事務未完成,請持續保持該鎖定。

因此,如果我們調用first_method(),以下輸出應該打印:

In first method 
Lock acquired in first_method() 
In second method 
In first method 

然而,在現實中上調用first_method(),下面會打印:

In first method 
Lock acquired in first_method() 
In second method 
In first method 
Lock acquired in first_method() 
In second method 
In first method 
Lock acquired in first_method() 
In second method 
In first method 
Lock acquired in first_method() 
In second method 

(這一直持續到RuntimeError: maximum recursion depth exceeded

我在這裏錯過了一些東西。這怎麼會發生? PostgreSQL中的行鎖如何被多次獲取?

編輯:

如果我改變first_method()到:

def first_method(): 
    print "In first method" 
    with acquire_lock(): 
     print "Lock acquired in first_method()" 
     i = 1 
     while True: 
      i = i + 1 
      i = i - 1 

和,現在就從兩個不同終端(或殼),first_method() 其中一個打印如下:

In first method 
Lock acquired in first_method() 

第二個打印以下內容:

In first method 

因此,鎖在這種情況下起作用,但不能遞歸地工作。

回答

2

這就是鎖的工作原理。在Row level locks

請注意,即使在不同的子事務中,事務也可以在同一行上保存衝突的鎖;

您的代碼在單個事務中運行。 Postgres中的鎖定旨在防止與其他事務發生衝突。因此,單個事務可以多次獲取相同的鎖,但是隻要其被當前事務持有,其他事務就不能獲取該鎖。