Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Deep Dive into Reactive Relational Database Access

Igor Lozynskyi
September 28, 2019

Deep Dive into Reactive Relational Database Access

R2DBC as a reactive API for data access in SQL databases.
Presented at Java Day Odesa 2019 by Igor Lozynskyi.

Why reactive, why R2DBC? How to build PrestoDB R2DBC driver.

Igor Lozynskyi

September 28, 2019
Tweet

More Decks by Igor Lozynskyi

Other Decks in Technology

Transcript

  1. For whom this talk: / Want to build reactive apps?

    / Like to work with databases? / Want to learn about R2DBC? / I assume, you know what Reactive is
  2. Why Reactive? / New frontier for high-efficiency apps / Fundamentally

    non-blocking / Paired with asynchronous behaviors / Complex stream processing / Reactive Streams Standard / Pull-push back pressure
  3. Why Reactive? / New frontier for high-efficiency apps / Fundamentally

    non-blocking / Paired with asynchronous behaviors / Complex stream processing / Reactive Streams Standard / Pull-push back pressure
  4. ADBA / Async API for relational data access in Java

    / Implemented with CompletableFuture / Uses Java 9’s Flow API
  5. Criticism / Does not imply back pressure / Has only

    ADBA-over-JDBC implementation / Slow development / Authors have neglected Reactive approach for a long time / Tiny interest in async-only API
  6. R2DBC Design principles / Based on Reactive Streams Types and

    Patterns / Completely non-blocking, up to the DB / Utilize wire-protocol for non-blocking implementations / Divide Client API and Driver SPI / Shrink Driver SPI
  7. JDBC API vs R2DBC SPI / JDBC has the same

    API for: / Driver authors / Human users / Inhuman users like JPA, Jdbi, jOOQ, etc. / No-one likes using JDBC API / Many methods, most useless / Too low-level for human users / Too verbose for driver implementors
  8. R2DBC SPI - Connection / Batch createBatch() / Statement createStatement(String

    sql) / Publisher<Void> beginTransaction() / Publisher<Void> commitTransaction() / Publisher<Void> rollbackTransaction() / Publisher<Void> rollbackTransactionToSavepoint(String name) / Publisher<Void> setTransactionIsolationLevel(IsolationLevel level) / Publisher<Void> createSavepoint(String name) / Publisher<Void> releaseSavepoint(String name) / Publisher<Void> close()
  9. R2DBC SPI - Statement / Statement add() / Statement bind(Object

    identifier, Object value) / Statement bindNull(Object identifier, Class<?> type) / Publisher<? extends Result> execute() / Statement returnGeneratedValues(String... columns)
  10. R2DBC SPI - Result / Publisher<Integer> getRowsUpdated() / <T> Publisher<T>

    map(BiFunction<Row, RowMetadata, ? extends T> f)
  11. SPI: Simple select connectionFactory .create() .flatMapMany(conn -> conn.createStatement("SELECT currency, price

    FROM trades") .execute() .flatMap(result -> result .map((row, metadata) -> row.get("currency"))))
  12. SPI: Batch insert connectionFactory .create() .flatMapMany(conn -> conn.createStatement( “INSERT INTO

    trades (currency, market, price) “ + "VALUES (?, ?, ?)") .bind(0, "EUR").bind(1, "TD").bind(2, 7.0).add() .bind(0, "UAH").bind(1, "TX").bind(2, 6.0).add() .execute())
  13. SPI: Transactions connectionFactory .create() .flatMapMany(conn -> conn.beginTransaction() .thenMany(conn.createStatement( "INSERT INTO

    trades (currency, market, price) " + "VALUES (?, ?, ?)") .bind(0, "UAH").bind(1, "TX").bind(2, "B") .execute()) .delayUntil(p -> conn.commitTransaction()) .onErrorResume(t -> conn .rollbackTransaction() .then(Mono.error(t)))) try-with-resources
  14. R2DBC Client r2dbcClient .withHandle(handle -> handle.createUpdate( "INSERT INTO trades (currency,

    market, price) " + "VALUES ($1, $2, $3)") .bind("$1", "UAH").bind("$2", "TX").bind("$3", 3.4) .execute())
  15. R2DBC Client: Transactions r2dbcClient .inTransaction(handle -> handle.createUpdate( "INSERT INTO trades

    (currency, market, price) " + "VALUES ($1, $2, $3)") .bind("$1", "UAH").bind("$2", "TX").bind("$3", 3.4) .execute())
  16. Spring Data Repository public interface UsSalesR2dbcRepository extends R2dbcRepository<UsSalesDataDto, String> {

    @Query("select * from us_sales_by_districts") Flux<UsSalesDataDto> findAll(); @Query("select * from us_sales_by_districts, where code=:code") Mono<UsSalesDataDto> findById(@Param("code") String code); }
  17. public interface UsSalesR2dbcRepository extends R2dbcRepository<UsSalesDataDto, String> { @Query("select * from

    us_sales_by_districts") Flux<UsSalesDataDto> findAll(); @Query("select * from us_sales_by_districts, where code=:code") Mono<UsSalesDataDto> findById(@Param("code") String code); } Spring Data R2DBC
  18. Spring Data Repository & Transactions? @Component class TransactionalService { private

    CustomerRepository repository; @Transactional public Mono<Customer> save(Customer customer) { return repository.save(customer).map(it -> { if (it.firstname.equals("Dave")) { throw new IllegalStateException(); } else { return it; } }); } }
  19. Longer Queries / DBs rushing to deliver data / Back

    pressure may delay data delivery
  20. Longer Transactions / Back pressure may cause more contention /

    JDBC may be faster than R2DBC / R2DBC may impact DB internals
  21. Wire-protocol / Should allow data streaming / Should allow back

    pressure / Cancellation / Implement Reactive Streams semantics / Multiplexing? / RSocket?
  22. R2DBC Pros / New and shiny / Brings reactive to

    DB access / Active community, many connectors / Easy to implement drivers
  23. R2DBC Cons / Still not GA (current: 0.8 RC1) /

    No JPA (Hibernate/EclipseLink) / Reactive approach may not fit SQL at all / Reactive programming is under a thread
  24. Fibers/Coroutines Cons / Light-weight threads / Write blocking, synchronous code

    / Use JDBC/JPA / Don’t use FP, write imperative code / C#, JS has async/await / Go, Kotlin has green threads
  25. Why Reactive? vs Fibers/Coroutines / New frontier for high-efficiency apps

    / Fundamentally non-blocking / Paired with asynchronous behaviors / Complex stream processing / Pull-push back pressure / Reactive Streams Standard Kotlin Coroutine’s Flow Java / JS / C++ / C# … May be simulated with channels
  26. Summary / R2DBC won the battle against ADBA / Will

    have reactive DB access, soon / Spring Data drives R2DBC / Not for production yet? But soon! / It is time to try R2DBC! / Reactive vs Fibers: friends or foes?