代码之家  ›  专栏  ›  技术社区  ›  Marcus Whybrow

Django中的多道程序设计,写入数据库

  •  0
  • Marcus Whybrow  · 技术社区  · 14 年前

    介绍

    我有以下代码,用于检查数据库中是否存在类似的模型,如果不存在,则创建新模型:

    class BookProfile():
    
        # ...
    
        def save(self, *args, **kwargs):
    
            uniqueConstraint = {'book_instance': self.book_instance, 'collection': self.collection}
    
            # Test for other objects with identical values
            profiles = BookProfile.objects.filter(Q(**uniqueConstraint) & ~Q(pk=self.pk))
    
            # If none are found create the object, else fail.
            if len(profiles) == 0:
                super(BookProfile, self).save(*args, **kwargs)
            else:
                raise ValidationError('A Book Profile for that book instance in that collection already exists')
    

    我首先构建我的约束,然后搜索一个具有我正在强制执行的那些值的模型,这些值必须是唯一的 Q(**uniqueConstraint) . 此外,我还确保如果save方法正在更新且未插入,则不会发现 查找时的对象 其他 相似对象 ~Q(pk=self.pk) .

    我应该提到我在实现软删除时 objects 管理器,只显示未删除的对象),这就是为什么我必须检查自己,而不是依赖 unique_together 错误。

    问题

    是的,这是引言。我的问题是,当多个相同的对象以快速(或几乎同时)连续的方式保存时,有时会同时添加这两个对象,即使第一个对象被添加时会阻止第二个对象。

    我已经在shell中测试了代码,每次运行它都会成功。因此,我的假设是,假设我们有两个对象被添加到对象A和对象B中。 save() 被召唤。然后保存对象B的进程在处理器上得到一些时间。对象B运行相同的测试,但尚未添加对象A,因此对象B被添加到数据库中。然后,对象A重新获得处理器的控制权,并且已经运行了它的测试,即使数据库中有相同的对象B,它也会添加它,不管怎样。

    我的想法

    我担心可能涉及到多道程序设计的原因是,每个对象A和对象都是通过API保存视图添加的,因此每次保存都会向视图发出请求,因此 对对象进行多次顺序保存的单个请求。

    可能是Apache为每个请求创建了一个进程,从而导致了我认为我看到的问题。正如您所期望的那样,问题只在有时发生,这是多道程序设计或多道处理错误的特征。

    如果是这种情况,是否有一种方法可以进行测试并设置 保存() 方法一个关键部分,这样测试和集合之间就不会发生过程切换?

    3 回复  |  直到 14 年前
        1
  •  1
  •   Community PPrice    7 年前

    根据您所描述的,假设多个Apache进程是问题的根源似乎是合理的。如果将Apache限制为单个工作进程,您是否能够复制它?

    也许本文中的建议有助于: How to lock a critical section in Django?

    另一种方法是使用队列。您只需将要保存的对象粘贴到队列中,然后让另一个进程进行实际保存。这样就可以保证对象是按顺序处理的。如果您的应用程序依赖于在返回响应时保存对象,那么这不会很好地工作,除非您还让请求进程等待结果(例如,观察完成的队列)。

    更新 您可能会发现此信息很有用。Dumpleton先生在列出考虑因素方面做得比我在这里总结的要好得多:

    http://code.google.com/p/modwsgi/wiki/ProcessesAndThreading

    http://code.google.com/p/modwsgi/wiki/ConfigurationGuidelines 特别是 定义流程组 第节。

    http://code.google.com/p/modwsgi/wiki/QuickConfigurationGuide 委派到守护进程 部分

    http://code.google.com/p/modwsgi/wiki/IntegrationWithDjango 在页面底部查找以以下内容开头的文本部分:

    现在,传统智慧在 Django一直认为 只能用于单件 线程服务器。这意味着 使用单线程的Apache Unix系统和 避免多线程“worker” 英里/分钟。

    读到最后一页。

        2
  •  1
  •   Community PPrice    7 年前

    我找到了一个我认为可行的解决方案:

    import threading
    
    def save(self, *args, **kwargs):
    
        lock = threading.Lock()
        lock.acquire()
        try:
            # Test and Set Code
        finally:
            lock.release()
    

    它不会像 that decorator 到目前为止,我还没有再看到错误。

    除非有人能说这不是一个正确的解决方案,否则我认为这是可行的。

    更新

    The accepted answer 是这种变化的灵感。

    我缝了我的印象,锁是某种特殊的巫术,是正常逻辑豁免。这里 lock = threading.Lock() 每次运行,从而实例化新的 解锁 可随时获取的锁。

    为此,我需要一个中央锁,但是如果没有线程一直在运行的话,这会不会成功呢?答案是使用文件锁 this answer 接受答案中提到的stackoverflow问题。

    以下是根据我的情况修改的解决方案:

    代码

    下面是我修改的 DjangoLock . 我希望保持与django根相关的锁,为此,我将一个自定义变量放入 settings.py 文件。

    # locks.py
    
    import os
    import fcntl
    from django.conf import settings
    
    class DjangoLock:
    
        def __init__(self, filename):
            self.filename = os.path.join(settings.LOCK_DIR, filename)
            self.handle = open(self.filename, 'w')
    
        def acquire(self):
            fcntl.flock(self.handle, fcntl.LOCK_EX)
    
        def release(self):
            fcntl.flock(self.handle, fcntl.LOCK_UN)
    
        def __del__(self):
            self.handle.close()
    

    现在附加的 LOCK_DIR 设置变量:

    # settings.py
    
    import os
    PATH = os.path.abspath(os.path.dirname(__file__))
    
    # ...
    
    LOCK_DIR = os.path.join(PATH, 'locks')
    

    这将把锁放在一个名为 locks 相对于Django项目的根。在我的例子中,请确保您授予Apache写访问权:

    sudo chown www-data locks
    

    最后,用法与以前基本相同:

    import locks
    
    def save(self, *args, **kwargs):
    
        lock = locks.DjangoLock('ClassName')
        lock.acquire()
        try:
            # Test and Set Code
        finally:
            lock.release()
    

    这就是我现在使用的实现,它的接缝工作得非常好。感谢所有为实现这一目标做出贡献的人。

        3
  •  0
  •   Luiz C.    14 年前

    您需要对保存方法使用同步。我还没试过,但是 here 是一个可以用来装饰的人。