Pull to refresh

Пара слов про R2DBC и PostgreSQL

Reading time4 min
Views28K
В последнее время я опять вижу, что усилился хайп вокруг реактивного программирования в общем, и реактивной работе с Базами данных — в частности. У меня есть пара фраз, которые я бы хотел сказать по этому поводу.

image

Напомню, что было в предыдущих серииях. В конце 2017 вышел Spring 5, в котором появилась поддержка реактивного программирования. В частности, речь идет про WebFlux. Все стали резко пробовать эту технологию (типичная кривая хайпа), а потом в индустрии появилось понимание, что реактивщина нужна не всем. Главные усвоенные уроки:

  • Реактивное программирование более сложно в отладке
  • Разрабатывать реактивные системы — труднее, чем классические, блокирующиеся; если нечаенно заблокируешь thread — тебе конец. Именно поэтому и появился проект (один из авторов — bsideup )
  • Реактивное программирование, а в частности — эластичность, нужно в основном на больших нагрузках а-ля Netflix. Если у вас 100 RPS, наверное, вам следует писать код в блокирующем стиле — так дешевле и проще

Кроме того, все поняли ещё одну штуку. Для того чтобы реактивное программирование действительно раскрылось, нужно чтобы вся система была реактивной, включая вашу база данных. Проблема в том, что у БД может вовсе и не быть реактивного, или хотя бы асинхронного API. Ну, или такое API может быть, но оно может быть не поддержено в вашем драйвере.

Собственно, почти все вокруг меня сейчас разрабатывают системы на PostgreSQL, когда нужна классическая реляционная База данных. Это, как говорится, выбор по умолчанию. С PostgreSQL мы работаем с помощью старого (ему больше 10 лет) JDBC драйвера — pgjdbc. Этот драйвер всем хорош. Там практически нет багов. Этот драйвер сильно улучшен по производительности (спасибо за это в том числе и vladimirsitnikov ). Но в pgjdbc есть один фатальный недостаток — нет поддержки асинхронного API.

В последние годы началась большая гонка по разработке альтернативных драйверов для PostgreSQL, в которых предполагалась разработка асинхронного API. Даже Oracle попытался это сделать, но уже закрыл разработку в пользу проекта Loom и его Fibers. Но потом Oracle одумалась и начала делать это опять.

Прямо сейчас есть 3 решения:

  1. https://github.com/jasync-sql/jasync-sql — асинхронный драйвер для PostgreSQL, написанный на Kotlin, где транспортом выступает Netty
  2. Решение, которое используют Vert.xhttps://github.com/mauricio/postgresql-async. Примечательно, что проект заархивирован, и предыдущий драйвер jasync-sql переписывает как раз таки именно его (правда, оригинал написан на Scala, а новое решение — на Kotlin).
    Update. Меня поправили в комментариях, клиент от Vert.x жив и чувствует себя хорошо в бенчмарках.
  3. Драйвер от команды Spring — https://github.com/r2dbc/r2dbc-postgresql. Этот драйвер сразу предоставляет реактивное API (а не асинхронное), а для транспорта, как и в jasync-sql, используется Netty.

Концептуально, эти драйверы построены на похожих принципах. Все они используют исключительно существующие возможности PostgreSQL:


К сожалению, драйверы не могут поменять Wire protocol Postgres (как бы этого не хотел Олег Докука — https://www.youtube.com/watch?v=n9tL2I_Big8). То есть, Postgres всё ещё требует отдельный Connection для SELECT запроса. Кроме того, Postgres всё также создает новый процесс для каждого Connection — https://www.postgresql.org/docs/current/tutorial-arch.html.

Однако есть и хорошие новости. Модифицирующие запросы можно посылать пачкой, не дожидаясь результата (с помощью pipeline). Для такого случая не требуется Connection на каждый запрос.

То есть, подытожим. Реактивные (или асинхронные) PG драйверы улучшают ситуацию в приложениях (мы теперь используем Netty based Non-blocking IO, не тратя по thread на запрос), но конкретно на стороне PostgreSQL — дела не так хороши (будем надеяться на улучшение сетевого протокола и архитектуры БД).

На какой из драйверов стоит посмотреть более пристально? Если вы, как и я, используете Spring в работе — то, вероятно, r2dbc-postgresql — хороший выбор. Важно, что в последних версиях своего фреймворка, ребята из Pivotal довольно хорошо синтегрировали драйвер с привычными вещами, например, с Spring Data R2DBC (это пока ещё RC). В результате, нам не придется работать с низкоуровневым API драйвера и Пулом соединений, что понижает вероятность допустить ошибку в таком сложном проекте.

Минимальный пример (который я успешно подсмотрел в документации) использования Spring Data R2DBC выглядит довольно привычно:

1. Настраиваем credentials и connection pool:

spring.r2dbc.username=postgres
spring.r2dbc.password=P4$$W0RddD
spring.r2dbc.url=r2dbc:postgresql://localhost:5432/r2dbc
spring.r2dbc.pool.enabled=true
spring.r2dbc.pool.initial-size=10
spring.r2dbc.pool.max-idle-time=1m
spring.r2dbc.pool.max-size=30
spring.data.r2dbc.repositories.enabled=true

2. Создаем сущность:

public class Customer {
    @Id
    private Long id;

    private String firstName;

    private String lastName;

    public Customer(String firstName, String lastName) {
        this.firstName = firstName;
        this.lastName = lastName;
    }
}

3. Создаем привычный нам Репозиторий:

public interface CustomerRepository extends ReactiveCrudRepository<Customer, Long> {
}

4. Используем репозиторий:

@RestController
@RequestMapping("/")
public class CustomerController {

    private CustomerRepository customerRepository;

    @PostMapping("/customer")
    Mono<Void> create(@RequestBody Publisher<Customer> customerFlux) {
        return customerRepository.saveAll(customerFlux).then();
    }

    @GetMapping("/customer")
    public Flux<Customer> list() {
        return customerRepository.findAll();
    }

    @GetMapping("/customer/{id}")
    public Mono<Customer> findById(@PathVariable Long id) {
        return customerRepository.findById(id);
    }
}

Полный пример можно найти в этом репозитории — https://github.com/Hixon10/spring-boot-r2dbc-postgresql-example.

Напоследок, давайте поговорим о важном: нужно ли использовать r2dbc сейчас в ваших приложениях? На мой взгляд — пока рановато. Я бы подождал, когда набор технологий (драйвер, Spring DATA) зарелизятся, и собирут первые шишки. Я думаю, что в 2021 году уже можно будет смотреть на этот драйвер более пристально.

Текущее состояние дел — целиком реактивная система (Spring WebFlux + R2DBC database driver) выглядит очень круто. Буквально сегодня olegchir опубликовал дайджест, в котором есть ссылка на статью с бенчмарками — https://technology.amis.nl/2020/04/10/spring-blocking-vs-non-blocking-r2dbc-vs-jdbc-and-webflux-vs-web-mvc/. В кратце — реактивщина побеждает.
Tags:
Hubs:
+11
Comments8

Articles