【问题标题】:Concurrency protection is not working并发保护不起作用
【发布时间】:2017-12-08 10:34:05
【问题描述】:

我在这里吃香蕉。随着我尝试实现并发保护。

所以我发现有两种方法可以做到这一点。 乐观 - 基本上我们在记录中添加一个版本字段,每次保存都会增加它。因此,如果我当前的版本字段与光盘上的不同,则意味着某些内容已被修改并且我给出了错误。

而悲观的方法意味着我们只是锁定了一条记录,并且无法编辑。

我为 Django 实现了两种方式以及并发包,但我在 SQLLite 和 Postgres 上使用 Heroku 没有任何效果。

下面是我的视图方法,它有两种方法

为了悲观,我使用 select_for_update 锁定事务原子 并且为了乐观,我使用并发 django 包,在每次编辑时增加我的版本字段,并与 on_disc 值进行简单比较

我做错了什么?请帮忙?

@login_required
    def lease_edit(request,pk,uri):
        logger = logging.getLogger(__name__)
        title = 'Edit Lease'
        uri = _get_redirect_url(request, uri)


        with transaction.atomic():

            post = get_object_or_404(Lease.objects.select_for_update(), pk=pk)

            if request.method == "POST":
                form = LeaseForm(request.POST, instance=post)
                if form.is_valid():
                    lease = form.save(commit=False)
                    on_disc= Lease.objects.get(pk=post.id)
                    if on_disc.version > post.version:
                        messages.add_message(request, messages.ERROR, str(lease.id) + "-Error object was modified by another user. Please reopen object.")
                        return redirect(uri)


                    lease.save()
                    messages.add_message(request, messages.SUCCESS, str(lease.id) + "--SUCCESS Object saved successfully")
                    return redirect(uri)
            else:
                form = LeaseForm(instance=post)
            #lease = post.lease 
            return render(request, 'object_edit.html', {'form': form, 'title':title, 'extend': EXTEND})

为了测试,我只需打开 2 个具有相同记录的浏览器并尝试并行编辑(我使用相同的用户)

【问题讨论】:

  • 在您进行编辑时,代码未在服务器上运行,因此事务不会在那里等待您编辑任何内容。它仅在您更新时才处于活动状态,因此在锁定、更新和释放锁定之间自然不会出现错误,除非您非常幸运并且同时运行两个请求。至于光盘版本号检查不起作用不能确切地说出原因。代码是从浏览器获取 ID 还是在 POST 上从数据库中读取,正如我所看到的那样?
  • 为了乐观,我收到了关于另一个问题的评论。stackoverflow.com/questions/47692858/…“可能虽然您的数据库本身将项目锁定在另一个事务中,并且您的事务隔离设置阻止了此进程看到更新值。”但我不知道如何处理它。
  • 但是我是否正确阅读了代码:您使用 select for update 从数据库中获取 post,然后保存,然后获取 on_disc 并进行比较?因为这些都不是来自 previous 请求,它具有 previous 值。这两个都来自 this 请求并且将始终匹配。您必须将值发送到浏览器并将其取回并将该值与当前值进行比较。
  • @Sami Kuhmonen 对于我编辑时的悲观主义者,它应该被锁定。因为我在实际加载表单之前触发了 select_for_update。至少这是我的假设
  • 不,您在更新请求时触发它。整个代码同时运行并结束。当您编辑数据时,事务不会挂起。没有什么可以让它等你。它锁定、加载、解锁。然后你编辑、发布数据,它会再次锁定、更新、解锁。它不会阻止多个请求为所欲为。

标签: django postgresql heroku concurrency


【解决方案1】:

问题在于网站的运作方式。目前您的锁定工作如下:

  • GET 请求加载数据
    • 您锁定行、加载、发送到浏览器、解锁
  • 您在浏览器上编辑数据
  • POST 数据到服务器
    • 您锁定行、更新、解锁

这里没有任何东西可以保持行锁定。多个实例可以向服务器发送数据,因为在每次请求后该行都被解锁。这是 Web 系统通常的工作方式,每个请求都是独立的,并且必须以不同的方式进行锁定。

至于行版本控制,如果我正确理解代码,您不会将当前行号发送到浏览器,而是在更新时阅读它。这再次意味着您获得了正确的号码。您应该将当前号码发送到浏览器并在 POST 上将该号码与数据库中的当前号码进行比较。这样,如果有什么变化,它就会检测到它。

我可能对这部分有误,因为我不熟悉 Django,但这是我对代码功能的理解。我假设form 可能具有当前行版本,但它没有与任何东西进行比较,只有数据库中的当前数字和更新后的数字(这也可能会改变数字)。

【讨论】:

  • 我还没有让它工作。由于我在从表单中获取版本时遇到问题。但我想你是对的。谢谢
猜你喜欢
  • 1970-01-01
  • 2015-10-08
  • 2010-09-24
  • 2019-02-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多