使用与Alfresco的SERIALIZABLE事务隔离

时间:2015-11-27 17:06:29

标签: spring transactions alfresco

背景

Alfresco使用默认的数据库事务隔离,对于我们的Oracle数据库是READ_COMMITED。我正在开发一个项目,其中不可重复和幻像读取可能是一个主要问题,所以我正在研究使用SERIALIZABLE事务隔离来避免这种情况。

一方面,我们将拥有一个自定义API服务,它将更改分组为原子事务 - 基本上是对文档的CRUD操作。 另一方面,我们将有后台进程更新并行运行的这些文档的元数据。

这些操作将使用事务元数据查询,因为最终将一致的SOLR查询添加到组合中不会使问题进一步复杂化。

目标是在API服务运行时能够进行主要的元数据模型迁移。出于这个问题的目的,我将使用一个属性作为示例,但IRL会有很多这种变化。例如,我们目前有一个带有约束的元数据字段:mymodel:doctypes1。但我们需要将mymodel:doctypes1中的值重新映射到具有不同约束的新字段:mymodel:doctypes2。 (不要问我为什么,我无法控制这个决定,我个人质疑这种改变的智慧)。

我对READ_COMMITTED隔离的理解告诉我,在这种情况下,我们非常容易受到以下情况的影响:

  • 后台进程启动一个事务并读取其值 为MyModel:doctypes1。
  • API在mymodel:doctypes1中写入更改 在后台进程提交之前。
  • 后台进程更新了值 mymodel:doctypes2基于mymodel的原始值:doctypes1。

这两个值现在不一致:我认为这个错误被称为不可重复的读取。

问题

将Oracle数据库设置为SERIALIZABLE会阻止此问题吗? Alfresco正在使用Spring交易。我的理解是,使用Spring事务的可序列化tx隔离可以防止这个问题“透明地”发生。

有没有人有任何将Alfresco数据库设置为SERIALIZABLE的实际经验?你试图解决类似的问题吗?它有用吗?它对你有什么样的性能影响?

非常感谢您分享您的经历!

1 个答案:

答案 0 :(得分:2)

在Alfresco论坛上的Axel Faust通过指出RetryingTransactionHelper强制执行乐观锁定并在两个事务重叠时重试来帮助我解决这个问题。如果你对此感到好奇,我会推荐他的帖子。

https://forums.alfresco.com/forum/developer-discussions/repository-services/using-serializable-transaction-isolation-alfresco

只是为了确保我做了一个小模型来激发ConcurrencyException并检查它确实表现得正确。

我使用主类将同一节点的49个更新发送到线程池:

    for (int i=0; i < 50; i++) {
        TestIncrementer ti = new TestIncrementer(node);
        threadPoolExecuter.submit(ti);
    }

我的增量器只是读取现有的标题,随机睡觉,然后添加到它上面。

    int hash  = RetryingTransactionHelper.getActiveUserTransaction().hashCode();
    log.debug("Thread id: " + Thread.currentThread().getId() + " tx " + hash);
    String title = (String) nodeService.getProperty(nodeRef, ContentModel.PROP_TITLE);
    Thread.sleep(Math.round(Math.random()* 1000));
    nodeService.setProperty(nodeRef, ContentModel.PROP_TITLE, title + "1");
    log.debug("Title: " + title);

正如所料,我看到了并发异常和重试:

Transaction commit failed: 
   Thread: defaultAsyncAction4
   Txn:    UserTransaction[object=org.alfresco.util.transaction.SpringAwareUserTransaction@65b249f2, status=0]
   Iteration: 8
   Exception follows:
 org.springframework.dao.ConcurrencyFailureException: Failed to update node 126094

但他们正在重审。

最终的重试让节点只有49个聚合!这正是需要发生的事情。

结论是,如果将RetryingTransactionHelper用于后台进程,则更改数据库隔离绝对不是必需的,甚至也不可取。