org.hibernate.TransactionException 在带有 CockroachDB 的简单 CRUD 应用程序中
org.hibernate.TransactionException in simple CRUD app with CockroachDB
以下使用 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
)
为了简短 post,我将实际的源文件存储在要点中:
./src/main/kotlin/ThingService.kt
./src/main/resources/application.properties
使用这些文件,可以使用以下命令重现问题(在我的例子中是 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
没有并发写入。所有写入都是严格顺序的。只有在并发读取开始起作用时才会出现此问题。但是我认为这不应该导致需要重试任何交易。
我的数据库连接配置有问题吗,或者可能是什么问题?
HandledRetryableTxnError 表示transaction should be retried。当在事务之间检测到冲突时,这发生在 SERIALIZABLE
隔离级别。
Cockroach 会自动执行一些重试,但不是全部,需要客户端参与。
通过添加 org.springframework.retry
作为依赖项 (org.springframework.retry:spring-retry:1.2.2.RELEASE
) 来解决它,导入所需的动画
import org.springframework.retry.annotation.EnableRetry
import org.springframework.retry.annotation.Retryable
并替换
@SpringBootApplication
和
@SpringBootApplication
@EnableRetry
和
@PutMapping("/thing/")
和
@PutMapping("/thing/")
@Retryable
我不明白为什么仅在严格顺序写入(并发读取)时才需要这样做,但至少它似乎有效。
以下使用 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
)
为了简短 post,我将实际的源文件存储在要点中:
./src/main/kotlin/ThingService.kt
./src/main/resources/application.properties
使用这些文件,可以使用以下命令重现问题(在我的例子中是 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
没有并发写入。所有写入都是严格顺序的。只有在并发读取开始起作用时才会出现此问题。但是我认为这不应该导致需要重试任何交易。 我的数据库连接配置有问题吗,或者可能是什么问题?
HandledRetryableTxnError 表示transaction should be retried。当在事务之间检测到冲突时,这发生在 SERIALIZABLE
隔离级别。
Cockroach 会自动执行一些重试,但不是全部,需要客户端参与。
通过添加 org.springframework.retry
作为依赖项 (org.springframework.retry:spring-retry:1.2.2.RELEASE
) 来解决它,导入所需的动画
import org.springframework.retry.annotation.EnableRetry
import org.springframework.retry.annotation.Retryable
并替换
@SpringBootApplication
和
@SpringBootApplication
@EnableRetry
和
@PutMapping("/thing/")
和
@PutMapping("/thing/")
@Retryable
我不明白为什么仅在严格顺序写入(并发读取)时才需要这样做,但至少它似乎有效。