【发布时间】: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