几年前,我写过一篇关于复制冲突的文章,今天来聊聊它的小兄弟:保存冲突。
从名字上就能看出来,复制冲突是在两台服务器上修改同一个文档后产生的,而保存冲突则是在同一台服务器上修改同一个文档后产生的。
在 web 应用中修改一个文档,主要有两种方式:
- 以 ?EditDocument URL 命令打开文档,然后调用 form.submit() 保存当前文档
- 通过代理等后台代码的方式修改并保存文档
以下图的时间线场景为例:
由于代理的运行时间一般很短, 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是以同一个用户身份保存的,那么不会有冲突文档,结果是后保存的覆盖之前的。(这样也合理,同一个用户间的数据覆盖没问题,不同用户间就要保留两份文档,供管理员后续合并冲突文档时参考)