개발 일지

R2DBC history 및 issue

북극곰은콜라 2023. 4. 21. 15:01
반응형


개요

Reactive Relational Database Connectivity
SQL database에서 Reactive programming을 지원하기 위해 디자인된 새로운 Open Specification
R2DBC를 쉽게 사용할 수 있도록 ORM의 사용법을 참고하여 설계하였으나, ORM Framework에서 제공하는 다양한 기능을 제공하지 않음. 

 


특징

Based on the Reactive Streams specification. 
 - provides a fully-reactive non-blocking API.

Works with relational databases.
 - JDBC의 Blocking 특성과 달리, R2DBC를 사용하면 Reactive API를 사용하여 SQL database로 작업할 수 있다.

Supports scalable solutions.
 - Reactive Stream을 사용하면 기존의 "연결 당 하나의 스레드"모델에서보다 강력하고 확장 가능한 접근 방식이 가능하다.

Provides an open specification.
 - R2DBC 는 open specification이며 드라이버 공급업체가 spec을 구현하고 클라이언트가 사용할 SPI(Service Provider Interface) 제공합니다.
 - R2DBC는 최소한의 SPI를 목표로 한다. 

 


구현체

clickhouse-r2dbc - R2DBC wrapper of async Java client for ClickHouse.
cloud-spanner-r2dbc - driver for Google Cloud Spanner.
jasync-sql - R2DBC wrapper for Java & Kotlin Async Database Driver for MySQL and PostgreSQL (written in Kotlin).
oracle-r2dbc - native driver implemented for Oracle.
r2dbc-h2 - native driver implemented for H2 as a test database.
r2dbc-mariadb - native driver implemented for MariaDB.
r2dbc-mssql - native driver implemented for Microsoft SQL Server.
r2dbc-mysql - native driver implemented for MySQL (previously mirromutth/r2dbc-mysql).
r2dbc-postgresql - native driver implemented for PostgreSQL.

 


SPI History ~ 1.0

0.8

  • initial version

0.9

  • Extended transaction definitions
  • Improved Bind Parameter declararation
  • Consumption of OUT Parameters
  • Consumption of Result Segments
  • Lifecycle extension
  • Refinement of Option
  • Refinement of RowMetadata
  • Lock Wait and Statement Timeouts
  • Refined Exceptions for Readable.get(…) and Statement.bind(…)

1.0

  • Revised Result.getRowsUpdated() signature
    • 1.0 스펙 중 이전 버전이랑 하위호환이 안되게 하는 주범, update 시 update row를 Long -> Integer로 변경해서, update(save) 시 classCastException이 발생한다.
  • Removed RowMetadata.getColumnNames() method
  • TCK Extensions

 


Spring Data R2DBC

Spring Data R2DBC version imported R2DBC SPI version 주요 기능
1.0.0 0.8 initial major version
1.1.0 0.8.1 r2dbcEntityTemplate 기능 추가
query derivation 추가
1.2.0 0.8.3 r2dbcAuditing 추가
@Value support
support Oracle R2DBC Driver
1.3.0 0.8.4 Query By Example 기능 추가
1.4.0 0.8.5 upgrade dependency
1.5.0 0.9.1 upgrade dependency
3.0.0 1.0.0 spring boot 3 대응
Spring Boot Starter Data R2DBC version Spring Data R2DBC version
2.3.2 1.1.2
2.4.0 1.2.1
2.5.0 1.3.1
2.6.0 1.4.0
2.7.0 1.5.0
3.0.0 3.0.0

 


MySQL, MariaDB Driver

관련 드라이버

DB Type Driver develop by Desc
Mysql jasync-sql jasync-sql 코틀린으로 개발된 드라이버
MySQL, PostgreSQL 지원
r2dbc-mysql mirrormutth 최초의 mysql r2dbc driver 구현체
현재는 개발 중단
r2dbc-mysql asyncer-io mysql 5.5, 5.6, 5.7, 8.0 호환
r2dbc-spi 1.0까지 호환
MariaDB mariadb-connector-r2dbc mariadb-corp MariaDB 공식 r2dbc 지원 드라이버

 

MySQL 계열 사용 시 공식드라이버인 MariaDB를 사용하는 것이 안정적 일 듯하다

 


주저리주저리

  • 신규 프로젝트에서 R2DBC 사용 도중 update 시 driver에서 오류가 올라와서 부랴부랴 알아봄.
  • SPI 1.0 버전이 나온 것까진 알고 있었는데 interface상 하위호환이 안될 줄은 상상도 못 함...
  • 일단 버전에 맞는 mariaDB 구현체를 사용해서 해결한 상태.
  • 이슈 해결 후 조사한 게 억울해서 블로그에 정리

이슈 stacktrace

20230421 14:59:18.570 [db-sql-netty-thread-2] ERROR o.s.b.a.w.r.e.AbstractErrorWebExceptionHandler - [695cf522-1]  500 Server Error for HTTP PUT "/dev/data" 
java.lang.ClassCastException: class java.lang.Long cannot be cast to class java.lang.Integer (java.lang.Long and java.lang.Integer are in module java.base of loader 'bootstrap')
	at java.base/java.util.stream.Collectors.lambda$summingInt$19(Collectors.java:673)
	Suppressed: reactor.core.publisher.FluxOnAssembly$OnAssemblyException: 
Error has been observed at the following site(s):
	*__checkpoint ⇢ Handler org.springframework.web.reactive.function.server.RouterFunctionDsl$PUT$2@10647471 [DispatcherHandler]
	*__checkpoint ⇢ com.pbear.mainfunctionalserver.common.log.LoggingWebFilter [DefaultWebFilterChain]
	*__checkpoint ⇢ HTTP PUT "/dev/data" [ExceptionHandlingWebHandler]
Original Stack Trace:
		at java.base/java.util.stream.Collectors.lambda$summingInt$19(Collectors.java:673)
		at reactor.core.publisher.MonoStreamCollector$StreamCollectorSubscriber.onNext(MonoStreamCollector.java:132)
		at reactor.core.publisher.FluxFlatMap$FlatMapMain.tryEmitScalar(FluxFlatMap.java:489)
		at reactor.core.publisher.FluxFlatMap$FlatMapMain.onNext(FluxFlatMap.java:422)
		at reactor.core.publisher.FluxMap$MapSubscriber.onNext(FluxMap.java:122)
		at reactor.core.publisher.FluxOnErrorResume$ResumeSubscriber.onNext(FluxOnErrorResume.java:79)
		at reactor.core.publisher.FluxMap$MapSubscriber.onNext(FluxMap.java:122)
		at reactor.core.publisher.FluxConcatMap$ConcatMapImmediate.innerNext(FluxConcatMap.java:282)
		at reactor.core.publisher.FluxConcatMap$ConcatMapInner.onNext(FluxConcatMap.java:863)
		at reactor.core.publisher.Operators$MonoSubscriber.complete(Operators.java:1839)
		at reactor.core.publisher.MonoCompletionStage.lambda$subscribe$0(MonoCompletionStage.java:96)
		at java.base/java.util.concurrent.CompletableFuture.uniHandle(CompletableFuture.java:930)
		at java.base/java.util.concurrent.CompletableFuture$UniHandle.tryFire(CompletableFuture.java:907)
		at java.base/java.util.concurrent.CompletableFuture.postComplete(CompletableFuture.java:506)
		at java.base/java.util.concurrent.CompletableFuture.complete(CompletableFuture.java:2073)
		at com.github.jasync.sql.db.util.FutureUtilsKt.success(FutureUtils.kt:16)
		at com.github.jasync.sql.db.mysql.MySQLConnection.succeedQueryPromise$lambda-12(MySQLConnection.kt:361)
		at java.base/java.util.Optional.ifPresent(Optional.java:183)
		at com.github.jasync.sql.db.mysql.MySQLConnection.succeedQueryPromise(MySQLConnection.kt:360)
		at com.github.jasync.sql.db.mysql.MySQLConnection.onOk(MySQLConnection.kt:227)
		at com.github.jasync.sql.db.mysql.codec.MySQLConnectionHandler.channelRead0(MySQLConnectionHandler.kt:125)
		at io.netty.channel.SimpleChannelInboundHandler.channelRead(SimpleChannelInboundHandler.java:99)
		at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:444)
		at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:420)
		at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:412)
		at io.netty.handler.codec.ByteToMessageDecoder.fireChannelRead(ByteToMessageDecoder.java:346)
		at io.netty.handler.codec.ByteToMessageDecoder.channelRead(ByteToMessageDecoder.java:318)
		at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:444)
		at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:420)
		at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:412)
		at io.netty.channel.DefaultChannelPipeline$HeadContext.channelRead(DefaultChannelPipeline.java:1410)
		at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:440)
		at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:420)
		at io.netty.channel.DefaultChannelPipeline.fireChannelRead(DefaultChannelPipeline.java:919)
		at io.netty.channel.nio.AbstractNioByteChannel$NioByteUnsafe.read(AbstractNioByteChannel.java:166)
		at io.netty.channel.nio.NioEventLoop.processSelectedKey(NioEventLoop.java:788)
		at io.netty.channel.nio.NioEventLoop.processSelectedKeysOptimized(NioEventLoop.java:724)
		at io.netty.channel.nio.NioEventLoop.processSelectedKeys(NioEventLoop.java:650)
		at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:562)
		at io.netty.util.concurrent.SingleThreadEventExecutor$4.run(SingleThreadEventExecutor.java:997)
		at io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74)
		at java.base/java.lang.Thread.run(Thread.java:834)

REFERENCE

https://r2dbc.io/

https://docs.spring.io/spring-data/r2dbc/docs/current/reference/html/

https://r2dbc.io/spec/1.0.0.RELEASE/spec/html/#changes

https://stackoverflow.com/questions/74972672/java-lang-integer-cannot-be-cast-to-class-java-lang-long-in-r2dbc-mysql-model-cl

https://github.com/spring-projects/spring-data-commons/wiki/Release-Trains

 

 

 

 

반응형

'개발 일지' 카테고리의 다른 글

브라우저와 Redirect (feat. XMLHttpRequest)  (0) 2023.05.19
Redis 설치 (ubuntu)  (0) 2023.04.26
Mysql data directory 변경  (0) 2023.04.19
Gradle 이란  (0) 2023.04.18
Flutter & Dart 개발환경 구성 (feat. IntelliJ)  (0) 2023.04.16