【问题标题】:Using SERIALIZABLE transaction isolation with Alfresco在 Alfresco 中使用 SERIALIZABLE 事务隔离
【发布时间】:2015-12-04 19:55:54
【问题描述】:

背景

Alfresco 使用默认的数据库事务隔离,对于我们的 Oracle 数据库,它是 READ_COMMITED。我正在从事一个项目,其中不可重复和幻读可能是一个主要问题,因此我正在考虑使用 SERIALIZABLE 事务隔离来避免这种情况。

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

这些操作将使用事务元数据查询,以免通过将最终一致的 SOLR 查询添加到组合中而使事情进一步复杂化。

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

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

  • 后台进程启动事务并读取 我的模型:doctypes1。
  • API 将更改写入 mymodel:doctypes1 在后台进程提交之前。
  • 后台进程更新值 mymodel:doctypes2 基于 mymodel:doctypes1 的原始值。

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

问题

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

有没有人有任何将 Alfresco 数据库设置为 SERIALIZABLE 的实际经验?您是否试图解决类似的问题?它奏效了吗?它对您产生了什么样的性能影响?

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

【问题讨论】:

  • 如果您将 Oracle 与 Alfresco 结合使用,那么您使用的是付费的 Alfresco One Enterprise。这意味着您会得到支持 - 只需给他们打个电话/提出一张票,他们就会让您与少数真正了解这些东西的 Alfresco 工程师取得联系!

标签: spring transactions alfresco


【解决方案1】:

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 用于后台进程,那么更改数据库隔离绝对没有必要,甚至也不可取。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-06-28
    • 1970-01-01
    • 2011-09-30
    • 2015-06-14
    • 1970-01-01
    相关资源
    最近更新 更多