Quelques mots sur R2DBC et PostgreSQL

Récemment, je constate à nouveau que le battage médiatique s'est intensifié autour de la programmation réactive en général, et du travail réactif avec les bases de données en particulier. J'ai quelques phrases que j'aimerais dire à ce sujet.

image

Permettez-moi de vous rappeler ce qui s'est passé dans la série précédente. Fin 2017, Spring 5 est sorti, dans lequel le support de la programmation réactive est apparu. En particulier, nous parlons de WebFlux . Tout le monde a commencé à essayer cette technologie (une courbe de battage médiatique typique), puis l'industrie a réalisé que tout le monde n'avait pas besoin de réactivisme. Principaux enseignements tirés:

  • La programmation réactive est plus difficile à déboguer
  • Le développement de systèmes réactifs est plus difficile que les systèmes classiques, bloquants; si vous bloquez involontairement le fil, vous avez terminé. C’est pourquoi le projet est apparu (l’un des auteurs -bsideup )
  • Une programmation réactive, et en particulier l'élasticité, est nécessaire principalement à des charges élevées à la Netflix. Si vous avez 100 RPS, vous devriez probablement écrire du code dans un style de blocage - c'est moins cher et plus facile

De plus, tout le monde a compris une chose. Pour que la programmation réactive se déroule vraiment, vous devez que tout le système soit réactif, y compris votre base de données. Le problème est que la base de données peut même ne pas avoir d'API réactive, ou du moins asynchrone. Eh bien, une telle API peut l'être, mais elle peut ne pas être prise en charge dans votre pilote.

En fait, presque tout le monde autour de moi développe des systèmes PostgreSQL lorsque j'ai besoin d'une base de données relationnelle classique. Cela étant dit, le choix par défaut. Nous travaillons avec PostgreSQL en utilisant l'ancien pilote JDBC (plus de 10 ans) - pgjdbc . Ce pilote est bon pour tout le monde. Il n'y a pratiquement pas de bugs. Ce pilote a considérablement amélioré ses performances (merci pour cela, y comprisvladimirsitnikov) Mais pgjdbc a une faille fatale - il n'y a pas de support pour l'API asynchrone .

Ces dernières années, une grande course a commencé pour développer des pilotes alternatifs pour PostgreSQL, qui comprenaient le développement d'une API asynchrone. Même Oracle a essayé de le faire, mais a déjà fermé le développement en faveur du projet Loom et de ses fibres . Mais ensuite, Oracle y a réfléchi et a recommencé .

Il existe actuellement 3 solutions:

  1. https://github.com/jasync-sql/jasync-sql - pilote asynchrone pour PostgreSQL, écrit en Kotlin, où Netty est le transport
  2. La solution utilisée par Vert.x est https://github.com/mauricio/postgresql-async . Il est à noter que le projet est archivé et que le pilote jasync-sql précédent réécrit tout de même (cependant, l'original est écrit en Scala et la nouvelle solution est écrite en Kotlin).
    Mise à jour J'ai été corrigé dans les commentaires, le client de Vert.x est vivant et se sent bien dans les benchmarks .
  3. Le pilote de l'équipe Spring est https://github.com/r2dbc/r2dbc-postgresql . Ce pilote fournit immédiatement une API réactive (et non asynchrone), et Netty est utilisée pour le transport, comme dans jasync-sql.

Conceptuellement, ces pilotes sont construits sur des principes similaires. Tous utilisent exclusivement des fonctionnalités PostgreSQL existantes:


Malheureusement, les pilotes ne peuvent pas modifier le protocole Wire Postgres (comme si Oleg Dokuk ne voulait pas - https://www.youtube.com/watch?v=n9tL2I_Big8 ). Autrement dit, Postgres nécessite toujours une connexion distincte pour une requête SELECT. En outre, Postgres crée également un nouveau processus pour chaque connexion - https://www.postgresql.org/docs/current/tutorial-arch.html .

Cependant, il y a de bonnes nouvelles. Les demandes de modification peuvent être envoyées par lots sans attendre le résultat (en utilisant le pipeline). Dans ce cas, la connexion n'est pas requise pour chaque demande.

Autrement dit, pour résumer. Les pilotes PG réactifs (ou asynchrones) améliorent la situation dans les applications (nous utilisons maintenant des E / S non bloquantes basées sur Netty, sans gaspiller de thread sur une demande), mais spécifiquement du côté de PostgreSQL - les choses ne sont pas si bonnes (espérons une amélioration du protocole réseau et de l'architecture de la base de données )

Quel pilote devriez-vous regarder de plus près? Si vous, comme moi, utilisez Spring dans votre travail, alors r2dbc-postgresql est probablement un bon choix. Il est important que dans les dernières versions de leur framework, les gars de Pivotal aient assez bien intégré le pilote avec les choses habituelles, par exemple, avec Spring Data R2DBC(c'est toujours RC). Par conséquent, nous n'avons pas à travailler avec l'API de pilote de bas niveau et le pool de connexions, ce qui réduit les risques d'erreur dans un projet aussi complexe.

L'exemple minimum (que j'ai réussi à espionner dans la documentation) utilisant Spring Data R2DBC semble assez familier:

1. Configurez les informations d'identification et le pool de connexions:

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. Créez une entité:

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. Créez le référentiel habituel:

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

4. Utilisez le référentiel:

@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);
    }
}

Un exemple complet peut être trouvé dans ce référentiel - https://github.com/Hixon10/spring-boot-r2dbc-postgresql-example .

Enfin, parlons de l'important: avez-vous besoin d'utiliser r2dbc maintenant dans vos applications? À mon avis - c'est trop tôt. J'attendrais la sortie d'un ensemble de technologies (driver, Spring DATA), et récupérer les premiers shys. Je pense qu'en 2021, il sera déjà possible de regarder ce pilote de plus près.

L'état actuel des choses - l'ensemble du système réactif (pilote de base de données Spring WebFlux + R2DBC) a l'air très cool. Littéralement aujourd'huiolegchira publié un condensé dans lequel il existe un lien vers un article avec des références - https://technology.amis.nl/2020/04/10/spring-blocking-vs-non-blocking-r2dbc-vs-jdbc-and-webflux-vs- web-mvc / . En bref - la réactivité l'emporte.

All Articles