我正在尝试很多新想法(DDD,Event Sourcing和CQRS)并将RethinkDB评估为域事件的潜在数据存储。在DDD中,聚合是一组协同工作以提供特定行为的对象。每个聚合都是事务/一致性边界。聚合的根是一个提供API并隐藏内部实现的对象。
要持久化聚合,通常建议使用乐观锁定。我们的想法是在聚合中拥有版本号属性,当需要保存聚合时,我们会检查以确保数据库中聚合的版本与应用程序中读取/更新的聚合版本相匹配。这保证了在此期间没有人改变聚合并防止覆盖其他改变。
显然,这种版本检查不仅仅发生在应用层(想想多个应用服务器场景)。应用程序需要来自数据存储的支持,以进行考虑此版本号的原子更新。
这是一个使用RethinkDB Ruby API的简单实现。
我创建了一个名为'applicants'的表,其中包含一条记录
"id": "6b3b57a7-3ba8-4322-873e-1d6c8333daae" ,
"name": "Homer Simpson" ,
"updated_at": Mon Dec 28 2015 12:05:40 GMT+05:30 ,
"version": 1
以下是我并行运行两次的示例测试代码
require 'rethinkdb'
include RethinkDB::Shortcuts
conn = r.connect(:host => 'localhost',
:port => 28015,
:db => 'test')
def update_applicant(conn, current_version)
result = r.table('applicants').get('6b3b57a7-3ba8-4322-873e-1d6c8333daae').update{ |applicant|
r.branch(
applicant['version'].eq(current_version),
{updated_at: Time.now, version: current_version + 1},
{}
)
}.run(conn)
fail 'optimistic locking failure' if result['unchanged'] == 1
rescue => e
puts "optimistic locking failure: #{current_version}"
current_version = r.table('applicants').get('6b3b57a7-3ba8-4322-873e-1d6c8333daae').run(conn)['version']
retry
end
(1..100).each { |version| update_applicant(conn, version) }
conn.close
这似乎有效,但我想确保在生产环境中没有竞争条件和此方法的其他问题。我假设更新是一个原子操作,并且在更新中使用分支仍然保持原子。
我正在寻找RethinkDB开发者/用户的一些验证和建议。感谢。
答案 0 :(得分:4)
update
始终是一个原子操作,除非你传递non_atomic: true
标志(如果更新包含非确定性操作,有时需要这样做),这样代码对我来说是安全的。