带有 CockroachDB 的简单 CRUD 应用程序中的 org.hibernate.TransactionException

Posted

技术标签:

【中文标题】带有 CockroachDB 的简单 CRUD 应用程序中的 org.hibernate.TransactionException【英文标题】:org.hibernate.TransactionException in simple CRUD app with CockroachDB 【发布时间】:2018-08-26 14:33:16 【问题描述】:

以下使用 Spring Boot、Hibernate、JpaRepository、CockroachDB 和 Kotlin 的最小 CRUD 示例生成 org.springframework.orm.jpa.JpaSystemException / org.hibernate.TransactionException

有问题的实体 Thing 只有两个字段:

@Entity
data class Thing (
    @Id
    var id: Long,
    var value: String
)

为了保持这篇文章的简短,我将实际的源文件存储在 gists 中:

./src/main/kotlin/ThingService.kt

./src/main/resources/application.properties

./build.gradle.kts

./stress_test.py

使用这些文件,可以使用以下命令重现问题(在我的情况下是在 Ubuntu 16.04 上)。

下载并初始化 CockroachDB:

# download
wget -qO- https://binaries.cockroachdb.com/cockroach-v1.1.5.linux-amd64.tgz | tar xvz

# start
./cockroach-v1.1.5.linux-amd64/cockroach start --insecure
# leave terminal open in background

# init
cockroach sql --insecure -e "CREATE USER root WITH PASSWORD '123';"
cockroach sql --insecure -e "CREATE DATABASE things_db;"
cockroach sql --insecure -e "GRANT ALL ON DATABASE things_db TO root;"

运行数据服务:

gradle bootRun
# leave terminal open in background

运行压力测试:

python3 stress_test.py

stress_test.py 同时向服务发送PUT 请求和GET 请求(按值查找)。大多数请求都可以正常工作,但中间的输出如下所示:

PUT OK
find OK
PUT OK
find OK
find OK
find OK
PUT ERROR: "timestamp":"2018-03-17T16:00:24.616+0000","status":500,"error":"Internal Server Error","message":"Unable to commit against JDBC Connection; nested exception is org.hibernate.TransactionException: Unable to commit against JDBC Connection","path":"/thing/"
find OK
PUT OK

Logs of the Spring application 显示更多细节:

2018-03-17 17:00:24.615 ERROR 3547 --- [nio-8082-exec-6] o.a.c.c.C.[.[.[/].[dispatcherServlet]    : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is org.springframework.orm.jpa.JpaSystemException: Unable to commit against JDBC Connection; nested exception is org.hibernate.TransactionException: Unable to commit against JDBC Connection] with root cause

org.postgresql.util.PSQLException: ERROR: restart transaction: HandledRetryableTxnError: TransactionRetryError: retry txn (RETRY_SERIALIZABLE): "sql txn" id=1cb57665 key=/Table/51/1/11125601/0 rw=true pri=0.04354217 iso=SERIALIZABLE stat=PENDING epo=0 ts=1521302424.604752770,1 orig=1521302424.604725980,0 max=1521302424.604725980,0 wto=false rop=false seq=3

没有并发写入。所有写入都是严格顺序的。仅当并发读取起作用时才会出现此问题。但是我认为这不应该导致需要重试任何事务。 我的数据库连接配置是否有问题,或者可能是什么问题?

【问题讨论】:

运行jar的用户是否有写权限? @MikeTung:大多数对数据库的写入都成功了。仅当同时发送GET 请求时才会出现该错误。即便如此,也只有一些 PUTs 失败了。 【参考方案1】:

HandledRetryableTxnError 表示transaction should be retried.当在事务之间检测到冲突时,这会发生在SERIALIZABLE 隔离级别。

Cockroach 会自动执行一些重试,但不是全部重试,需要客户参与。

您可以在docs site 上找到多种语言的客户端重试示例,包括java。

【讨论】:

谢谢。我用一些能够实现客户端重试的魔术注释解决了这个问题。但是,我不明白为什么在我的情况下根本需要这样做(顺序写入,并发读取)。你知道为什么吗?【参考方案2】:

通过添加 org.springframework.retry 作为依赖项 (org.springframework.retry:spring-retry:1.2.2.RELEASE) 来解决它,导入所需的 annitations

import org.springframework.retry.annotation.EnableRetry
import org.springframework.retry.annotation.Retryable

替换

@SpringBootApplication

@SpringBootApplication
@EnableRetry

@PutMapping("/thing/")

@PutMapping("/thing/")
@Retryable

我不明白为什么在只进行严格的顺序写入(并发读取)时需要这样做,但至少它似乎有效。

【讨论】:

以上是关于带有 CockroachDB 的简单 CRUD 应用程序中的 org.hibernate.TransactionException的主要内容,如果未能解决你的问题,请参考以下文章

在 CockroachDB 中删除数据库中的所有表

如何在管理面板中为多行添加带有批量操作(如 CRUD)的复选框 [重复]

CockroachDB 支持全文搜索吗?

CockroachDB 中的 DESCRIBE TABLE 等效项?

如何加快 CockroachDB 中的插入性能

从 CockroachDB 中的“SELECT”返回随机行