Ein paar Worte zu R2DBC und PostgreSQL

In letzter Zeit sehe ich wieder, dass sich der Hype um reaktive Programmierung im Allgemeinen und reaktive Arbeit mit Datenbanken im Besonderen verschärft hat. Ich habe ein paar Sätze, die ich dazu sagen möchte.

Bild

Ich möchte Sie daran erinnern, was in früheren Serien passiert ist. Ende 2017 erschien Spring 5, in dem Unterstützung für reaktive Programmierung erschien. Insbesondere sprechen wir über WebFlux . Jeder fing an, diese Technologie (eine typische Hype-Kurve) scharf auszuprobieren, und dann erkannte die Branche, dass nicht jeder Reaktivismus brauchte. Wichtige Erkenntnisse:

  • Reaktive Programmierung ist schwieriger zu debuggen
  • Die Entwicklung reaktiver Systeme ist schwieriger als klassische, blockierende. Wenn Sie den Thread unbeabsichtigt blockieren, sind Sie fertig. Deshalb erschien das Projekt (einer der Autoren -bsideup )
  • Reaktive Programmierung und insbesondere Elastizität sind hauptsächlich bei hohen Lasten nach Netflix erforderlich. Wenn Sie 100 RPS haben, sollten Sie wahrscheinlich Code in einem blockierenden Stil schreiben - es ist billiger und einfacher

Darüber hinaus verstand jeder noch etwas. Damit sich die reaktive Programmierung wirklich entfalten kann, muss das gesamte System einschließlich Ihrer Datenbank reaktiv sein. Das Problem ist, dass die Datenbank möglicherweise nicht einmal über eine reaktive oder zumindest asynchrone API verfügt. Nun, oder eine solche API kann sein, aber es wird möglicherweise nicht in Ihrem Treiber unterstützt.

Tatsächlich entwickelt fast jeder um mich herum PostgreSQL-Systeme, wenn ich eine klassische relationale Datenbank benötige. Davon abgesehen die Standardauswahl. Wir arbeiten mit PostgreSQL mit dem alten (über 10 Jahre alten) JDBC-Treiber - pgjdbc . Dieser Treiber ist gut für alle. Es gibt praktisch keine Fehler. Dieser Treiber ist in der Leistung stark verbessert (danke dafür, einschließlichvladimirsitnikov) Pgjdbc hat jedoch einen schwerwiegenden Fehler: Die asynchrone API wird nicht unterstützt .

In den letzten Jahren hat ein großes Rennen begonnen, alternative Treiber für PostgreSQL zu entwickeln, einschließlich der Entwicklung einer asynchronen API. Sogar Oracle hat dies versucht , die Entwicklung jedoch bereits zugunsten des Loom- Projekts und seiner Fasern abgeschlossen . Aber dann überlegte Oracle es sich besser und fing wieder an, es zu tun .

Derzeit gibt es 3 Lösungen:

  1. https://github.com/jasync-sql/jasync-sql - asynchroner Treiber für PostgreSQL, geschrieben in Kotlin, wo Netty der Transport ist
  2. Die von Vert.x verwendete Lösung lautet https://github.com/mauricio/postgresql-async . Es ist bemerkenswert, dass das Projekt archiviert wird und der vorherige jasync-sql-Treiber trotzdem neu geschrieben wird (das Original ist jedoch in Scala und die neue Lösung in Kotlin geschrieben).
    Aktualisieren Ich wurde in den Kommentaren korrigiert, der Kunde von Vert.x lebt und fühlt sich gut in Benchmarks .
  3. Der Fahrer aus dem Spring-Team ist https://github.com/r2dbc/r2dbc-postgresql . Dieser Treiber stellt sofort eine reaktive API bereit (und nicht asynchron), und Netty wird wie in jasync-sql für den Transport verwendet.

Konzeptionell basieren diese Treiber auf ähnlichen Prinzipien. Alle verwenden ausschließlich vorhandene PostgreSQL-Funktionen:


Leider können Treiber das Wire-Protokoll Postgres nicht ändern (als ob Oleg Dokuk dies nicht möchte - https://www.youtube.com/watch?v=n9tL2I_Big8 ). Das heißt, Postgres benötigt weiterhin eine separate Verbindung für eine SELECT-Abfrage. Darüber hinaus erstellt Postgres für jede Verbindung einen neuen Prozess - https://www.postgresql.org/docs/current/tutorial-arch.html .

Es gibt jedoch gute Nachrichten. Änderungsanforderungen können stapelweise gesendet werden, ohne auf das Ergebnis zu warten (mithilfe der Pipeline). In diesem Fall ist die Verbindung nicht für jede Anforderung erforderlich.

Das heißt, zusammenzufassen. Reaktive (oder asynchrone) PG-Treiber verbessern die Situation in Anwendungen (wir verwenden jetzt Netty-basierte nicht blockierende E / A, ohne Thread auf Anfrage zu verschwenden), aber speziell auf der PostgreSQL-Seite - die Dinge sind nicht so gut (hoffen wir auf eine Verbesserung des Netzwerkprotokolls und der Datenbankarchitektur )

Welchen Treiber sollten Sie sich genauer ansehen? Wenn Sie wie ich Spring in Ihrer Arbeit verwenden, ist r2dbc-postgresql wahrscheinlich eine gute Wahl. Es ist wichtig, dass die Jungs von Pivotal in den neuesten Versionen ihres Frameworks den Treiber ziemlich gut in die üblichen Dinge integriert haben, zum Beispiel in Spring Data R2DBC(Dies ist immer noch RC). Daher müssen wir nicht mit einer Treiber-API auf niedriger Ebene und dem Verbindungspool arbeiten, wodurch die Wahrscheinlichkeit eines Fehlers in einem so komplexen Projekt verringert wird.

Das minimale Beispiel (das ich in der Dokumentation erfolgreich ausspioniert habe) mit Spring Data R2DBC kommt mir ziemlich bekannt vor:

1. Konfigurieren Sie die Anmeldeinformationen und den Verbindungspool:

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. Erstellen Sie eine Entität:

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. Erstellen Sie das übliche Repository:

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

4. Verwenden Sie das Repository:

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

Ein vollständiges Beispiel finden Sie in diesem Repository - https://github.com/Hixon10/spring-boot-r2dbc-postgresql-example .

Lassen Sie uns abschließend über das Wichtige sprechen: Müssen Sie r2dbc jetzt in Ihren Anwendungen verwenden? Meiner Meinung nach ist es zu früh. Ich würde warten, bis eine Reihe von Technologien (Treiber, Spring DATA) veröffentlicht werden, und die ersten Shys sammeln. Ich denke, dass es bereits 2021 möglich sein wird, diesen Fahrer genauer zu betrachten.

Der aktuelle Stand der Dinge - das gesamte reaktive System (Spring WebFlux + R2DBC-Datenbanktreiber) sieht sehr cool aus. Buchstäblich heuteOlegchirveröffentlichte einen Digest, in dem es einen Link zu einem Artikel mit Benchmarks gibt - https://technology.amis.nl/2020/04/10/spring-blocking-vs-non-blocking-r2dbc-vs-jdbc-and-webflux-vs- web-mvc / . Kurz gesagt - Reaktivität gewinnt.

All Articles