Starten Sie Predator - Precompiled Data Repositories


Heute hat das Micronaut- Team von Object Computing Inc (OCI) Predator vorgestellt , ein neues Open-Source-Projekt, dessen Ziel es ist, die Laufzeit und Leistung (aus dem Speicher) des Datenzugriffs für Microservices und serverlose Anwendungen erheblich zu verbessern, ohne die Produktivität zu beeinträchtigen im Vergleich zu Tools wie GORM und Spring Data.


Verlauf der Datenzugriffstools


Wir können den Verlauf der Datenrepository-Vorlage seit 2004 verfolgen, als Ruby on Rails das ActiveRecord-Subsystem herausbrachte, eine API, die unser Verständnis des Datenzugriffs im Hinblick auf die Entwicklerproduktivität revolutionierte.


2007 führte das Grails-Team erstmals eine ActiveRecord-ähnliche API für die JVM - GORM (Teil von Grails) ein. GORM verließ sich bei der Implementierung von Suchmethoden zusätzlich zu Hibernate auf die Dynamik von Groovy und bot JVM-Benutzern dieselben Produktivitätsvorteile.


Da GORM von der Sprache Groovy abhängt, wurde 2011 ein Spring Data-Projekt erstellt, mit dem Java-Entwickler Suchmethoden wie findByTitle in der Benutzeroberfläche definieren und zur Laufzeit automatisch die Abfragelogik implementieren konnten.


Funktionsweise von Datenzugriffstools


Alle genannten Implementierungen verwenden dieselbe Vorlage, mit der zur Laufzeit ein Metamodell von Projektentitäten erstellt wird, das die Beziehungen zwischen Ihren Entitätsklassen modelliert. In Spring Data ist es ein MappingContext und in GORM wird es auch als MappingContext bezeichnet. Sie werden durch Scannen von Klassen unter Verwendung von Reflexion konstruiert. (Die Ähnlichkeit bei der Benennung ist hier nicht zufällig. 2010 habe ich mit dem Spring Data-Team zusammengearbeitet, um zu versuchen, GORM für Java neu zu erstellen, an einem Projekt, das sich heute schließlich in Spring Data verwandelte.)


Dieses Metamodell wird dann verwendet, um einen bookRepository.findByTitle("The Stand") wie bookRepository.findByTitle("The Stand") zur Laufzeit mithilfe einer Kombination aus Parsing regulärer Ausdrücke und Framework-Logik in ein abstraktes Abfragemodell bookRepository.findByTitle("The Stand") . Wir benötigen ein abstraktes Abfragemodell, da der Zieldialekt der Abfrage für jede Datenbank unterschiedlich ist (SQL, JPA-QL, Cypher, Bson usw.).


Unterstützung für Micronaut Repository


Seit dem Start von Micronaut vor etwas mehr als einem Jahr wurde hauptsächlich nach "GORM für Java" oder Spring Data-Unterstützung gefragt. So viele Entwickler sind von der Produktivität dieser Tools sowie von der einfachen Definition der vom Framework implementierten Schnittstellen begeistert. Ich würde sagen, dass der größte Teil des Erfolgs von Grails und Spring Boot auf GORM bzw. Spring Data zurückzuführen ist.


Für Micronaut-Benutzer, die Groovy verwenden, hatten wir vom ersten Tag an GORM-Unterstützung, und Java- und Kotlin-Benutzer hatten nichts mehr zu tun, da sie Repositorys selbst implementieren mussten.


Es wäre technisch möglich und ehrlich gesagt einfacher, einfach ein Modul für Micronaut hinzuzufügen, mit dem Spring Data konfiguriert werden kann. Auf diesem Weg würden wir jedoch ein Subsystem bereitstellen, das mit allen Methoden implementiert wurde, die Micronaut zu vermeiden versuchte: weit verbreitete Verwendung von Proxys, Reflexion und hoher Speicherverbrauch.


Wir stellen Predator vor!


Predator, kurz für Precomputed Data Repositories, verwendet die Micronaut-API zum Kompilieren vor der Ausführung (AoT, vorzeitig), um ein Metamodell von Entitäten zu übertragen und findByTitle (wie findByTitle ) in das entsprechende SQL oder JPA-QL in Ihrem Compiler zu konvertieren . Infolgedessen führt die Abfrage eine sehr dünne Programmlaufzeitschicht ohne Reflexion aus, und es bleibt nur, dass sie die Abfrage ausführt und die Ergebnisse zurückgibt.


Das Ergebnis ist überwältigend ... der Kaltstart wird deutlich reduziert, wir erhalten einen erstaunlich niedrigen Speicherverbrauch und eine deutliche Leistungsverbesserung.


Heute öffnen wir den Quellcode für Predator unter der Apache 2-Lizenz. Er wird mit zwei ersten Implementierungen (weitere Funktionen für die Zukunft geplant) für JPA (basierend auf Hibernate) und für SQL mit JDBC geliefert.


Die JDBC-Implementierung gefällt mir am besten, da sie völlig unabhängig von der Reflexion ist und keine Proxys und kein dynamisches Laden von Klassen für Ihren Datenzugriff verwendet, was zu einer verbesserten Leistung führt. Die Laufzeitschicht ist so leicht, dass selbst der von Hand geschriebene äquivalente Repository-Code nicht schneller ausgeführt wird.


Performance Predator


Da Predator zur Laufzeit keine Abfragetransformationen ausführen muss, ist der Leistungsgewinn erheblich. In der Welt der Cloud-Computing-Nutzung, in der Sie für die Ausführungszeit Ihrer Anwendung oder für die Ausführung einer einzelnen Funktion bezahlen, verlieren Entwickler häufig die Leistung ihrer Datenzugriffsmechanismen aus den Augen.


In der folgenden Tabelle sind die Leistungsunterschiede zusammengefasst, die für einen einfachen findByTitle wie findByTitle im Vergleich zu anderen Implementierungen zu erwarten sind. Alle Tests wurden mit einem Prüfstand auf dem 8-Kern-Xeon iMac Pro unter den gleichen Bedingungen durchgeführt. Die Tests sind offen und befinden sich im Repository :


ImplementierungOperationen pro Sekunde
Predator JDBC225 K Ops / Sek
Predator jpa130 K Ops / Sek
Federdaten jpa90 K Ops / Sek
GORM JPA50 K Ops / Sek
Federdaten JDBCFinder werden nicht unterstützt

Ja, du hast es richtig gelesen. Mit Predator JDBC können Sie eine fast 4-fache Leistungssteigerung gegenüber GORM und eine 2,5-fache Steigerung gegenüber Spring Data erwarten.


Und selbst wenn Sie Predator JPA verwenden, können Sie mit mehr als der zweifachen Leistungsverbesserung im Vergleich zu GORM und einer Steigerung von bis zu 40% im Vergleich zu Spring Data JPA rechnen.


Sehen Sie sich den Unterschied in der Größe des Ausführungsstapels bei Verwendung von Predator im Vergleich zu Alternativen an:


Predator:



Predator JPA:



Federdaten:



GORM:



Predator JDBC verwendet bis zur Ausführung Ihrer Anforderung nur 15 Frames, während Predator JPA 30 (hauptsächlich aufgrund von Hibernate) verwendet, verglichen mit mehr als 50 Stack-Frames in Spring Data oder GORM. Und das alles dank der AOP-Mechanismen von Micronaut, die keine Reflexion verwenden.


Kürzere Stackraces vereinfachen auch das Debuggen von Anwendungen. Einer der Vorteile der meisten Arbeiten während der Kompilierung besteht darin, dass Fehler vor dem Start der Anwendung erkannt werden können, was die Erfahrung des Entwicklers erheblich verbessert. Wir erhalten sofort Kompilierungsfehler anstelle von Laufzeitfehlern für die häufigsten Fehler.


Zeitprüfungen kompilieren


Die meisten Implementierungen der Repository-Vorlage basieren ausschließlich auf der Ausführung aller Vorgänge zur Laufzeit. Dies bedeutet, dass, wenn der Entwickler beim Definieren der Schnittstelle des Repositorys einen Fehler macht, Fehler erst sichtbar werden, wenn die Anwendung tatsächlich gestartet wird.


Dies raubt uns einige der Vorteile von Java für die Typprüfung und wir haben schlechte Datenerfahrung. Dies ist bei Predator nicht der Fall. Betrachten Sie das folgende Beispiel:


 @JdbcRepository(dialect = Dialect.H2) public interface BookRepository extends CrudRepository<Book, Long> { Book findByTile(String t); } 

Hier BookRepository wir eine Anfrage an ein Objekt namens Book deklariert, das eine title Eigenschaft hat. Leider gibt es einen Fehler in dieser Deklaration: Wir haben die findByTile Methode anstelle von findByTitle . Anstatt diesen Code auszuführen, lässt Predator Ihren Code nicht mit einer informativen Fehlermeldung kompilieren:


 Error:(9, 10) java: Unable to implement Repository method: BookRepository.findByTile(String title). Cannot use [Equals] criterion on non-existent property path: tile 

Viele Aspekte von Predator werden nach Möglichkeit zur Kompilierungszeit überprüft, um sicherzustellen, dass ein Laufzeitfehler nicht durch eine falsche Repository-Deklaration verursacht wird.


Predator JDBC und GraalVM Substrat


Ein weiterer Grund, warum Predator sich freuen sollte, ist, dass es sofort mit nativen GraalVM-Substratbildern kompatibel ist und im Gegensatz zu Halnate auf GraalVM keine komplexen Bytecode-Konvertierungen während der Erstellung erfordert.


Durch die vollständige Eliminierung von Reflexionen und dynamischen Proxys aus der Datenzugriffsschicht vereinfacht Predator die Erstellung von Anwendungen, die mit Daten arbeiten, die auf GraalVM ausgeführt werden, erheblich.


Die Predator JDBC-Beispielanwendung kann problemlos auf Substrate ausgeführt werden und ermöglicht es Ihnen, dank einer viel dünneren Laufzeitschicht ein viel kleineres natives Image (25 MB weniger!) Zu erstellen, als der Ruhezustand funktionieren muss.


Wir haben das gleiche Ergebnis gesehen, als wir die Zusammenstellung der Bean-Validierungsregeln für Micronaut 1.2 implementiert haben. Die native Bildgröße verringerte sich um 10 MB, sobald wir die Abhängigkeit vom Hibernate Validator und die JAR-Größe um 2 MB entfernt hatten.


Der Vorteil liegt auf der Hand: Wenn Sie während der Kompilierung mehr Arbeit leisten und kompaktere Laufzeiten erstellen, erhalten Sie ein kleineres natives Image und eine JAR-Datei, was zu kleineren und einfacher zu implementierenden Microservices bei der Bereitstellung über Docker führt. Die Zukunft von Java-Frameworks sind leistungsfähigere Compiler und kleinere, leichtere Laufzeiten.


Raubtier und die Zukunft


Wir fangen gerade erst an, mit Predator zusammenzuarbeiten, und sind äußerst zufrieden mit den Möglichkeiten, die sich daraus ergeben.


Zunächst beginnen wir mit der Unterstützung von JPA und SQL, aber in Zukunft können Sie Unterstützung für MongoDB, Neo4J, Reactive SQL und andere Datenbanken erwarten. Glücklicherweise ist dieser Job viel einfacher, da der größte Teil von Predator tatsächlich auf dem GORM-Quellcode basiert und wir die GORM-Logik für Neo4J und die GORM-Logik für MongoDB wiederverwenden können, um diese Implementierungen schneller als erwartet freizugeben.


Predator ist der Höhepunkt der Kombination der verschiedenen Bausteine ​​in Micronaut Core, die die Implementierung ermöglichten, von den AoT-APIs, die auch zum Generieren der Swagger-Dokumentation verwendet werden, bis zur relativ neuen Bean Introspection-Unterstützung, mit der Sie Objekte zur Laufzeit ohne Reflexion analysieren können.


Micronaut bietet Bausteine ​​für erstaunliche Dinge. Predator ist eine solche Sache, und wir fangen gerade erst an, an einigen der vielversprechenden Funktionen von Micronaut 1.0 zu arbeiten.


UPDATE: Nach der schockierenden Ankündigung wurde der Spring Data-Killer in Micronaut Data umbenannt: https://micronaut-projects.imtqy.com/micronaut-data/1.0.x/guide/

Source: https://habr.com/ru/post/de460839/


All Articles