不知何故,我总是在星期五得到这些.
我之前的问题是关于同样的问题,但我现在可以稍微缩小一点:
我整天都在玩这个,试图理解它.我有一个带有lock_version列的表,由此指定:
add_column :jobs, :lock_version, :integer, :default=>0
Run Code Online (Sandbox Code Playgroud)
我做这样的事情:
foo = job.create!
first = Job.find(foo.id)
second = Job.find(foo.id)
Run Code Online (Sandbox Code Playgroud)
然后我验证第一个和第二个引用相同的对象 - 它们的ID是相同的,我使用mysql命令行工具在数据库中看到该行.
first.some_attribute_field = 'first'
second.some_attribute_field = 'second'
first.save
second.save
Run Code Online (Sandbox Code Playgroud)
到目前为止没问题.我正确得到一个ActiveRecord :: StaleObjectError异常. 但是:
first = Job.find(foo.id)
second = Job.find(foo.id)
first.some_attribute_field = 'first'
second.some_attribute_field = 'second'
first.save
second.save
Run Code Online (Sandbox Code Playgroud)
......没有任何反应.事实证明,我唯一一次得到正确(抛出异常)行为是第一次和第二次的lock_version为0.然而,在第一次保存之后,它再次为0.究竟是什么呢?
我正在使用ruby 1.8.6和活动记录2.2.2
谢谢...
我正在尝试为Django选择一个REST框架,这将允许我轻松地使用ETag进行乐观锁定.我打算检查Django-活塞和Django Rest Framework库,但我对任何非GPL解决方案持开放态度(公司许可要求阻止我使用它们).
我的应用程序是以JSON/YAML形式从SQLAlchemy模型(不是Django模型)中销售数据,并且模拟ETag问题与Django Rest Framework一起正常工作.但是,我看不到在我的视图上应用ETag标头的简单方法.
对于我的观点,我想这样做:
给定响应,轻松地将ETag添加到我发送成功的响应标头中.这必须由我计算,因为它将取决于模型; 它不足以散列响应值或类似的东西.
在POST/PUT上,确保我收到的ETag与我发出的ETag匹配,或拒绝请求.
这是第一步给我带来一些麻烦; 我不确定哪个REST框架会使这个最简单,我也不确定实现它的最佳方法是什么.
我正在实现一个自定义NSIncrementalStore子类,它使用关系数据库进行持久存储.我仍在努力的一件事是支持乐观锁定.
(可以在下面的问题中跳过这个冗长的描述)
我分析了Core Data的SQLite增量存储如何通过检查它生成的SQL日志来解决这个问题,并得出以下结论:
数据库中的每个实体表都有一个Z_OPT列,它指示从1(初始插入)开始修改此实体(行)的特定实例的次数.
每次修改托管对象时,其相应数据库行中的Z_OPT值都会递增.
存储维护NSIncrementalStoreNode实例的缓存(在Core Data docs中称为行缓存),每个实例的版本属性等于由托管对象的行上的先前SELECT或UPDATE SQL查询返回的Z_OPT值.
当从NSManagedObjectContext返回托管对象时(例如,通过对其执行NSFetchRequest),MOC会创建包含此版本号的此对象的快照.
修改或删除对象时,Core Data通过比较缓存的行和对象快照的版本,确保它未在上下文之外被修改或删除.当-save:在对象所属的上下文上调用时,所有这些都会发生.如果版本不同,则基于集合并策略检测并处理合并冲突.
保存MOC时,为每个修改/删除的对象调用-newValuesForObjectWithID:withContext:error:方法,后者又返回带有版本号的NSIncrementalStoreNode.然后将此版本与快照的版本进行比较,如果它们不同,则保存将因适当的合并冲突而失败(至少使用默认合并策略).
这个简单的用例适用于我的商店,因为-newValuesForObjectWithID:withContext:error:首先检查行缓存,如果在其他上下文中使用相同的商店实例同时修改了该对象,那么这就足够了.如果是这种情况,则缓存包含具有更高版本号的更新行,该行足以检测冲突.
但是,我如何检测到底层数据库已经在我的商店外被修改,可能是由使用相同数据库文件的其他应用程序或其他商店实例?我知道这是一个不常见的边缘情况,但Core Data正确处理它,我宁愿做同样的事情.
Core Data的商店使用这些SQL查询来更新/删除对象的行:
UPDATE ZFOO SET Z_OPT=Y, (...) WHERE (...) AND Z_OPT=X
DELETE FROM ZFOO WHERE (...) AND Z_OPT=X
Run Code Online (Sandbox Code Playgroud)
其中:
X - 商店最后知道的版本号(来自缓存)
Y - 新版本号
如果此类查询失败(没有行受影响),则会在商店的缓存中更新该行,并将其版本与先前缓存的版本进行比较.
我的问题是:自定义NSIncrementalStore如何通知Core Data一些更新/删除/锁定的对象发生了乐观锁定失败?这是这是唯一能告诉大家,当它处理店里NSSaveChangesRequest …
我打算将Node用于我的下一个项目,因为它似乎非常适合我正在解决的问题.
我需要弄清楚的一件事是乐观和悲观的锁定.http://guides.rubyonrails.org/active_record_querying.html#locking-records-for-update
比如说,用户有一个余额1.两个节点实例同时提出请求撤销1.这两个请求可以同时通过足够余额的检查,从而使两次成功提款导致余额为-1.
Rails提供了Postgres事务和锁定功能的包装器.Node是否存在某种类似的包装器?
你如何在Node/Postgresql中解决这个问题?
我知道这是一个数据库功能,但我想知道Node或某些库是否提供了远离编写原始SQL查询的抽象概念?
我有一个复杂的实体(我们称之为Thing),这是在SQL Server中表示为很多表:一个父表dbo.Thing与多个子表dbo.ThingBodyPart,dbo.ThingThought等等.我们已经使用一个单一的实现乐观并发rowversion上柱dbo.Thing,使用UPDATE OUTPUT INTO的技术.这一直很有效,直到我们添加了一个触发器dbo.Thing.我正在寻找选择不同方法的建议,因为我相信我目前的方法无法修复.
这是我们目前的代码:
CREATE PROCEDURE dbo.UpdateThing
@id uniqueidentifier,
-- ...
-- ... other parameters describing what to update...
-- ...
@rowVersion binary(8) OUTPUT
AS
BEGIN TRANSACTION;
BEGIN TRY
-- ...
-- ... update lots of Thing's child rows...
-- ...
DECLARE @t TABLE (
[RowVersion] binary(8) NOT NULL
);
UPDATE dbo.Thing
SET ModifiedUtc = sysutcdatetime()
OUTPUT INSERTED.[RowVersion] INTO @t
WHERE
Id …Run Code Online (Sandbox Code Playgroud) t-sql sql-server triggers stored-procedures optimistic-locking
我正在学习rails,并阅读乐观的锁定.我添加lock_version类型的列integer到我的articles表.
但是现在每当我第一次尝试更新记录时,我都会遇到StaleObjectError异常.
这是我的迁移:
class AddLockVersionToArticle < ActiveRecord::Migration
def change
add_column :articles, :lock_version, :integer
end
end
Run Code Online (Sandbox Code Playgroud)
当我尝试通过rails控制台更新文章时:
article = Article.first
=> #<Article id: 1, title: "Ccccc", text: "dfdsfsdfsdf", created_at: "2015-02-20 21:58:45", updated_at: "2015-02-25 20:03:12", lock_version: 0>
Run Code Online (Sandbox Code Playgroud)
我这样做:
article.title = "new title"
article.save
Run Code Online (Sandbox Code Playgroud)
我明白了:
(0.3ms) begin transaction
(0.3ms) UPDATE "articles" SET "title" = 'dwdwd', "updated_at" = '2015-02-25 20:40:36.537876', "lock_version" = 1 WHERE ("articles"."id" = 1 AND "articles"."lock_version" = 0)
(0.1ms) …Run Code Online (Sandbox Code Playgroud) 在我目前的项目中,我使用Spring Data JPA和Hibernate,但认为这是一个更普遍的问题,也应该涵盖"普通"JPA.
我不确定OptimisticLockException在使用时应该如何处理@Version.
由于我的应用程序是如何工作的一些关系,有CascadeType.PERSIST和CascadeType.REFRESH,其他人也有CascadeType.MERGE.
OptimisticLockException据我所知,在服务层处理这个问题不会特别有效,CascadeType.MERGE因为那时违规实体可能是需要由另一个服务处理的实体(我每个实体类都有一个服务).
问题是我正在创建一个框架,因此没有服务上面的层,所以我可以将其"委托"给我的框架用户,但这似乎"软弱和懒惰".
如果发生OptimisticLockException,如何获取导致该问题的实体以及更改了哪些字段?
是的,我可以打电话getEntity()但如何将其转换为正确的类型,特别是如果使用CascadeType.MERGE?该实体可以是多种类型,因此instanceof可以想到if/switch,但这看起来像地狱一样难看.
一旦我有正确的类型,我需要获得版本之间的所有差异,不包括某些字段,如版本本身或lastModifiedDate.
在我的脑海中还有HTTP 409,它表示如果冲突响应应该包含冲突的字段.
所有这些都有"最佳实践模式"吗?
我想乐观地“锁定”一段代码。伪代码如下:
revision = GET('lock_key') # default as 0
{
<<block of code>>
}
new_revision = INCR('lock_key')
if new_revision != revision + 1:
raise Exception # now retry or whatever
Run Code Online (Sandbox Code Playgroud)
这对我来说很好,因为 INCR 和 GET 都是原子的。你觉得这种方法有什么问题吗?
在提交当前事务之前,hibernate 检查该行的版本时,它应该发出一条 sqlselect语句来获取该行。
假设在发出该select语句后,hibernate 发现行版本没有改变,因此它应该继续提交事务。
我想知道 hibernate 如何确保在选择行和提交当前事务之间的时间段内没有任何其他事务会更新行更改其版本号?休眠可以做的唯一可能的事情似乎是使用悲观锁定的行版本选择Select ... For Update或具有这种隔离级别的事务,它将锁定正在读取的行。
如果我的想法是真的:
那么休眠乐观锁实际上确实对其操作使用了悲观锁,尽管悲观锁被持有的时间很短,因为事务将在此之后立即提交。
否则我们在行版本检查和提交之间有一个很短的时间段,可能会发生竞争条件。
请分享您的想法。
sql hibernate race-condition optimistic-locking pessimistic-locking
美好的一天,我了解什么是可序列化隔离级别以及它与REPEATABLE READPostgres 中的有何不同。可串行化事务能够检测读写周期,因此只有第一次提交才会成功。
Hibernate's考虑到这一点,使用基于行版本控制的乐观锁定是否有意义?行版本控制的行为方式完全相同,如果版本列已更新,则将引发 Java 异常,从而回滚事务。此外,根据Postgres wiki,如果某些更新是在应用程序级代码之外完成的(例如 psql 运行的纯 sql 查询),则必须创建触发器。因此,以我的拙见,可序列化级别是乐观锁定的替代品,是这样还是有一些用例您更喜欢乐观锁定?