不同OracleDB Connections之间的共享事务

Ton*_*ino 25 java oracle junit transactions sql2o

经过几天调查这个问题后,我决定提交这个问题,因为在发生的事情中显然没有任何意义.

案子

我的计算机配置了本地Oracle Express数据库.我有一个带有几个JUnit测试的JAVA项目扩展了一个父类(我知道它不是一个"最佳实践"),它在@Before方法中打开一个OJDBC连接(使用10个连接的静态Hikari连接池)并滚动回到@After.

public class BaseLocalRollbackableConnectorTest {
private static Logger logger = LoggerFactory.getLogger(BaseLocalRollbackableConnectorTest.class);
protected Connection connection;

@Before
public void setup() throws SQLException{
    logger.debug("Getting connection and setting autocommit to FALSE");
    connection = StaticConnectionPool.getPooledConnection();
}

@After
public void teardown() throws SQLException{ 
    logger.debug("Rollback connection");
    connection.rollback();
    logger.debug("Close connection");
    connection.close();
}
Run Code Online (Sandbox Code Playgroud)

StacicConnectionPool

public class StaticConnectionPool {

private static HikariDataSource ds;

private static final Logger log = LoggerFactory.getLogger(StaticConnectionPool.class);

public static Connection getPooledConnection() throws SQLException {

    if (ds == null) {
        log.debug("Initializing ConnectionPool");
        HikariConfig config = new HikariConfig();
        config.setMaximumPoolSize(10);
        config.setDataSourceClassName("oracle.jdbc.pool.OracleDataSource");
        config.addDataSourceProperty("url", "jdbc:oracle:thin:@localhost:1521:XE");
        config.addDataSourceProperty("user", "MyUser");
        config.addDataSourceProperty("password", "MyPsw");
        config.setAutoCommit(false);
        ds = new HikariDataSource(config);

    }
    return ds.getConnection();

}
Run Code Online (Sandbox Code Playgroud)

}

这个项目有数百个测试(不是并行),使用这个连接(在localhost上)使用Sql2o执行查询(插入/更新和选择),但是只在外部管理连接的事务和clousure(通过上面的测试).数据库完全为空以进行ACID测试.

所以预期的结果是在DB中插入一些东西,然后进行断言然后回滚.以这种方式,第二次测试将找不到先前测试添加的任何数据以保持隔离级别.

问题 一起(顺序)运行所有测试,90%的时间它们正常工作.10%的一个或两个测试,随机,失败,因为以前的测试数据库中存在脏数据(例如,重复唯一).查看日志,正确完成以前测试的回滚.事实上,如果我检查数据库,它是空的)如果我在性能更高但同一个JDK(同一个Oracle DB XE)的服务器中执行此测试,则此故障率将增加到50%.

这很奇怪,我不知道,因为测试之间的连接不同,每次调用回滚.JDBC隔离级别是READ COMMITTED,因此即使我们使用相同的连接,即使使用相同的连接也不会产生任何问题.所以我的问题是:为什么会发生?你有什么主意吗?正如我所知,JDBC回滚是同步的还是在某些情况下它可以继续前进,即使它没有完全完成?

这些是我的主要数据库参数:进程100个会话172个事务189

Ton*_*ino 1

在从您的答案中确认我对单元测试中的回滚和事务行为并不生气之后,我深入检查了所有查询和所有可能的原因,幸运的是(是的,不幸的是......即使我为此感到羞耻,我也下定决心免费)一切按预期进行(交易、之前、之后等)。

有一些查询获取一些复杂视图的结果(并且从根本上深入配置到 DAO 层)来识别单行信息。此视图基于为了MAX of a TIMESTAMP识别特定事件的最新情况(在现实生活中,几个月后发生的事件)。

在准备数据库以进行单元测试时,每个测试都会按顺序添加这些事件。在某些情况下,当同一事务下的这些插入查询特别快时,同一毫秒内会添加与同一对象相关的更多事件(使用 JODA DateTime 手动添加 TIMESTAMP)并且日期的 MAX,返回两个或更多的价值。因此,解释了这样一个事实:在性能较高的计算机/服务器上,这种情况比速度较慢的计算机/服务器更频繁地发生。这个视图在更多的测试中使用,并且根据测试的不同,错误是不同的和随机的(NULL值添加为主键,重复的主键等)。

例如:在以下INSERT SELECT查询中,这个错误很明显:

INSERT INTO TABLE1 (ID,COL1,COL2,COL3) 
  SELECT :myId, T.VAL1, T.VAL2, T.VAL3 
  FROM MyView v 
  JOIN Table2 t on t.ID = v.ID
  WHERE ........
Run Code Online (Sandbox Code Playgroud)

之后将参数 myId 添加为 Sql2o 参数

我的视图是

SELECT ID, MAX(MDATE) FROM TABLEV WHERE.... GROUP BY ...
Run Code Online (Sandbox Code Playgroud)

当视图由于相同的 Max Date 至少返回 2 个结果时,它会失败,因为 ID 是固定的(由开始时的序列生成,但第二次使用参数存储)。这会产生违反的 PK 约束。

这只是一个案例,但由于这种随机行为而让我(和我的同事)发疯......

在这些事件插入之间添加 1 毫秒的睡眠,它是固定的。现在我们正在努力寻找不同的解决方案,尽管这种情况(用户在同一毫秒内交互两次)不可能在生产系统中发生,但重要的是不会像往常一样发生魔法!

现在你可以侮辱我了:)