保存冲突的那些事儿

几年前,我写过一篇关于复制冲突的文章,今天来聊聊它的小兄弟:保存冲突。

从名字上就能看出来,复制冲突是在两台服务器上修改同一个文档后产生的,而保存冲突则是在同一台服务器上修改同一个文档后产生的。

在 web 应用中修改一个文档,主要有两种方式:

  1. 以 ?EditDocument URL 命令打开文档,然后调用 form.submit() 保存当前文档
  2. 通过代理等后台代码的方式修改并保存文档

以下图的时间线场景为例:

由于代理的运行时间一般很短, 3、4 两个代理在时间线上可以考虑为点,所以理论上他们之间的时间是不会有交叉的,也就是两个代理保存同一个文档一般不会出现保存冲突

通过 ?EditDocument 方式一般持续时间较长,从打开文档到点击保存按钮这一段时间都是其处理时间,这期间出现保存冲突的可能性很大。例如:在用户A编辑期间,代理C保存了同一条文档,那么用户A点击保存按钮的时候,就会产生冲突文档。同样的,用户B编辑期间,用户A通过用户A点击保存按钮修改了同一条文档,用户B点击保存按钮的时候,就会产生冲突文档。

那么如何避免保存冲突的产生呢?NotesDocument.lock 方法就是针对这个问题的,但是很遗憾此方法在 web 端无法使用。所以我们需要自己实现一个文档锁的逻辑:每次进入编辑状态时加锁,离开编辑状态后解锁;未取得的锁的情况下不允许进入编辑状态。这样就能保证同时只有一人编辑文档,避免了保存冲突的产生。这里有一个思路供参考:Distributed Document Locking Web Style

再回到上面的一个问题,代理的运行时间短,一般不会导致冲突文档。如果代理的运行时间很长呢,两个代理保存同一个文档会不会产生冲突文档?为此我写了如下代码做测试:

Sub Initialize
Dim s As New NotesSession
Dim db As NotesDatabase
Dim doc As NotesDocument
MsgBox “agent start”
Set db = s.Currentdatabase
Set doc = db.Getdocumentbyunid(“FC71D0667744C41D48257D6C0011AB7F”)
Dim count As Integer
count  = doc.count(0)
MsgBox count
Sleep(5)
doc.count = count + 1
doc.agentname = “1”
MsgBox doc.save(true,true)
MsgBox “agent end”
End Sub

为了拉长运行时间,我在中间加了 Sleep(5)。连续运行两次代理,测试结果如下:

  • doc.save(false,false):后一次保存失败,文档未保存,返回值为 false
  • doc.save(false,true):后一次保存为前一次的答复文档(注意不是冲突文档),返回值为 true
  • doc.save(true,true):后一次保存覆盖前一次保存的内容,前一次的内容丢失,返回值为 true(第二个参数随便传true/false,没有影响)

总结一下:web 端通过 ?EditDocument 方式编辑文档时,很容易出现保存冲突,注意用文档那个锁的方式来避免;代理保存文档时,一般不会出现冲突,但如果 getDocument 至 doc.save 之间的代码逻辑很复杂、运行时间长,也要注意处理数据覆盖的问题。

今天是 2014 年的最后一天了,祝大家新年快乐!保存冲突系列还有一篇没写完,明年继续。

Edit:经测试发现,如果用户A和代理C是以同一个用户身份保存的,那么不会有冲突文档,结果是后保存的覆盖之前的。(这样也合理,同一个用户间的数据覆盖没问题,不同用户间就要保留两份文档,供管理员后续合并冲突文档时参考)

2 评论

  1. 请教个问题,notesdocument的save方法第一个参数如果是true,是覆盖保存;domino有默认保存,就是没有任何save代码的情况下也能默认保存,这种默认保存是一个什么方式,是覆盖保存,还是保存成冲突文档

    1. 你说的就是时间线中“3通过代理C保存”后,“1用户A再通过EditDocument保存”的情况。这时1这里会保存成冲突文档。