Django原子选择更新没有锁定表的递归调用?

时间:2017-05-26 09:41:24

标签: python sql django postgresql transactions

我有一个通过以下命令在PostgreSQL中创建和填充的表:

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

,现在从两个不同的终端(或shell)调用first_method(), 其中一个打印出以下内容:

In first method
Lock acquired in first_method()

第二个打印出以下内容:

In first method

因此,锁在这种情况下有效,但不能递归工作。

1 个答案:

答案 0 :(得分:3)

这就是锁的工作方式。在Row level locks

  

请注意,事务可以在同一行上保存冲突的锁,即使在不同的子事务中也是如此;

您的代码在单个事务中运行。 Postgres中的锁旨在防止与其他事务冲突。因此,单个事务可以多次获取相同的锁,但只要当前事务持有,其他事务就无法获取该锁。