2017-05-26 63 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中的锁定旨在防止与其他事务发生冲突。因此,单个事务可以多次获取相同的锁,但是只要其被当前事务持有,其他事务就不能获取该锁。