Lanzar Predator - Repositorios de datos precompilados


Hoy, el equipo de Micronaut en Object Computing Inc (OCI) presentó Predator , un nuevo proyecto de código abierto cuyo objetivo es mejorar significativamente el tiempo de ejecución y el rendimiento (desde la memoria) del acceso a datos para microservicios y aplicaciones sin servidor, sin perder productividad en comparación con herramientas como GORM y Spring Data.


Historial de herramientas de acceso a datos


Podemos rastrear el historial de la plantilla de repositorio de datos desde 2004, cuando Ruby on Rails lanzó el subsistema ActiveRecord, una API que revolucionó nuestra comprensión del acceso a los datos en términos de productividad del desarrollador.


En 2007, el equipo de Grails introdujo por primera vez una API similar a ActiveRecord para JVM - GORM (parte de Grails). GORM se basó en la naturaleza dinámica de Groovy para implementar métodos de búsqueda además de Hibernate y proporcionó los mismos beneficios de productividad a los usuarios de JVM.


Dado que GORM depende del lenguaje Groovy, en 2011 se creó un proyecto Spring Data que permitió a los desarrolladores de Java definir métodos de búsqueda, como findByTitle , en la interfaz e implementar automáticamente la lógica de consulta en tiempo de ejecución.


Cómo funcionan las herramientas de acceso a datos


Todas las implementaciones mencionadas usan la misma plantilla, que es construir un metamodelo de entidades de proyecto en tiempo de ejecución que modela las relaciones entre sus clases de entidad. En Spring Data es un MappingContext, y en GORM también se llama MappingContext. Se construyen escaneando clases usando la reflexión. (La similitud en los nombres no es accidental aquí. En 2010, trabajé con el equipo de Spring Data para intentar recrear GORM para Java, en un proyecto que finalmente se convirtió en lo que hoy se llama Spring Data)


Este metamodelo se usa para transformar una expresión de búsqueda, como bookRepository.findByTitle("The Stand") , en un modelo de consulta abstracta en tiempo de ejecución utilizando una combinación de análisis de expresiones regulares y lógica de marco. Necesitamos un modelo de consulta abstracta porque el dialecto objetivo de la consulta es diferente para cada base de datos (SQL, JPA-QL, Cypher, Bson, etc.)


Soporte de repositorio de Micronaut


Desde el lanzamiento de Micronaut hace poco más de un año, la principal característica que nos faltaba era "GORM para Java" o soporte de Spring Data. Muchos desarrolladores están enamorados de la productividad que proporcionan estas herramientas, así como de la facilidad de definir las interfaces que implementa el marco. Diría que la mayor parte del éxito de Grails y Spring Boot se puede atribuir a GORM y Spring Data respectivamente.


Para los usuarios de Micronaut que usaban Groovy, teníamos soporte GORM desde el primer día, y los usuarios de Java y Kotlin no tenían nada, porque necesitaban implementar repositorios por su cuenta.


Sería técnicamente posible, y francamente más fácil, simplemente agregar un módulo para Micronaut que configuraría Spring Data. Sin embargo, siguiendo este camino, proporcionaríamos un subsistema implementado utilizando todos los métodos que Micronaut intentó evitar: uso generalizado de proxies, reflexión y alto consumo de memoria.


¡Presentamos Predator!


Predator, abreviatura de repositorios de datos precalculados, utiliza la API de Micronaut para compilar antes de la ejecución (AoT, por adelantado) para transferir un findByTitle de entidades y convertir expresiones de búsqueda (como findByTitle ) al SQL o JPA-QL apropiado en su compilador . Como resultado, la consulta ejecuta una capa de tiempo de ejecución del programa muy delgada sin reflejo, y solo le queda ejecutar la consulta y devolver los resultados.


El resultado es abrumador ... el arranque en frío se reduce significativamente, obtenemos un consumo de memoria sorprendentemente bajo y una mejora notable en el rendimiento.


Hoy abrimos el código fuente de Predator bajo la licencia Apache 2, vendrá con dos implementaciones iniciales (más características planificadas para el futuro) para JPA (basado en Hibernate) y para SQL con JDBC.


La implementación de JDBC me agrada más, ya que es completamente independiente de la reflexión, no utiliza proxies y carga de clase dinámica para su nivel de acceso a datos, lo que conduce a un mejor rendimiento. La capa de tiempo de ejecución es tan ligera que incluso el código de repositorio equivalente escrito a mano no se ejecutará más rápido.


Depredador de rendimiento


Dado que Predator no necesita ejecutar ninguna transformación de consulta en tiempo de ejecución, la ganancia de rendimiento es significativa. En el mundo de la utilización de la computación en la nube, donde paga la cantidad de tiempo que se ejecuta su aplicación o la ejecución de una sola función, los desarrolladores a menudo pierden de vista el rendimiento de sus mecanismos de acceso a datos.


La siguiente tabla resume las diferencias de rendimiento que se pueden esperar para una expresión de búsqueda simple, como findByTitle , en comparación con otras implementaciones. Todas las pruebas se realizaron utilizando un banco de pruebas en el Xeon iMac Pro de 8 núcleos en las mismas condiciones, las pruebas están abiertas y se pueden encontrar en el repositorio :


ImplementaciónOperaciones por segundo
Depredador JDBC225K operaciones / seg.
Depredador jpa130K operaciones / seg.
Spring data jpa90K operaciones / seg.
GORM JPA50K operaciones / seg.
Spring Data JDBCBuscadores no admitidos

Sí, lo leíste bien. Con Predator JDBC, puede esperar un aumento de rendimiento de casi 4X sobre GORM y 2.5X sobre Spring Data.


E incluso si usa Predator JPA, puede contar con más de 2 veces las mejoras de rendimiento en comparación con GORM y hasta un 40% de aumento en comparación con Spring Data JPA.


Observe la diferencia en el tamaño de la pila de ejecución cuando usa Predator en comparación con las alternativas:


Depredador



Predator JPA:



Datos de primavera:



GORM:



Predator JDBC usa solo 15 cuadros hasta el momento en que se ejecuta su solicitud, mientras que Predator JPA usa 30 (principalmente debido a Hibernate), en comparación con más de 50 cuadros de pila en Spring Data o GORM. Y todo gracias a los mecanismos Micronaut AOP que no utilizan la reflexión.


Las razas de pila más cortas también simplifican la depuración de la aplicación. Una de las ventajas de hacer la mayor parte del trabajo durante la compilación es que se pueden detectar errores antes de que se inicie la aplicación, lo que mejora en gran medida la experiencia del desarrollador. Recibimos errores de compilación inmediatamente en lugar de errores de tiempo de ejecución para los errores más comunes.


Comprobaciones de tiempo de compilación


La mayoría de las implementaciones de la plantilla de repositorio se basan únicamente en la realización de todas las operaciones en tiempo de ejecución. Esto significa que si el desarrollador comete un error al definir la interfaz del repositorio, los errores no serán visibles hasta que la aplicación se inicie realmente.


Esto nos roba algunos de los beneficios de Java para la verificación de tipos y tenemos poca experiencia en datos. Este no es el caso con Predator. Considere el siguiente ejemplo:


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

Aquí BookRepository una solicitud a un objeto llamado Book , que tiene una propiedad de title . Desafortunadamente, hay un error en esta declaración: findByTile método findByTile lugar de findByTitle . En lugar de ejecutar este código, Predator no permitirá que su código se compile con un mensaje de error informativo:


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

Muchos aspectos de Predator se verifican en tiempo de compilación, cuando es posible, para garantizar que un error de tiempo de ejecución no sea causado por una declaración de repositorio incorrecta.


Predator JDBC y GraalVM Substrate


Otra razón por la que Predator debería estar contento es que es compatible con las imágenes nativas de sustrato GraalVM y no requiere conversiones complejas de código de bytes durante la compilación, a diferencia de las de Hibernate en GraalVM.


Al eliminar por completo la reflexión y los proxys dinámicos de la capa de acceso a datos, Predator simplifica enormemente la creación de aplicaciones que funcionan con datos que se ejecutan en GraalVM.


La aplicación de ejemplo Predator JDBC se ejecuta en Substrate sin problemas y le permite crear una imagen nativa mucho más pequeña (¡25 MB menos!) De lo que Hibernate necesita para funcionar, gracias a una capa de tiempo de ejecución mucho más delgada.


Vimos el mismo resultado cuando implementamos la compilación de reglas de Validación de Bean para Micronaut 1.2. El tamaño de la imagen nativa disminuyó en 10 MB, tan pronto como eliminamos la dependencia del Validador de Hibernate, y el tamaño JAR en 2 MB.


La ventaja aquí es obvia: al hacer más trabajo durante la compilación y crear tiempos de ejecución más compactos, obtienes una imagen nativa más pequeña y un archivo JAR, lo que conduce a microservicios más pequeños y fáciles de implementar cuando se implementa a través de Docker. El futuro de los frameworks Java son compiladores más potentes y tiempos de ejecución más pequeños y ligeros.


Depredador y el futuro


Recién estamos empezando a trabajar con Predator y estamos extremadamente satisfechos con las oportunidades que se abren.


Inicialmente, comenzamos con soporte para JPA y SQL, pero en el futuro puede esperar soporte para MongoDB, Neo4J, Reactive SQL y otras bases de datos. Afortunadamente, este trabajo es mucho más simple porque la mayoría de Predator se basa realmente en el código fuente GORM, y podemos reutilizar la lógica GORM para Neo4J y la lógica GORM para MongoDB para lanzar estas implementaciones más rápido de lo que espera.


Predator es la culminación de combinar los diversos bloques de construcción en Micronaut Core que hicieron posible implementarlo, desde las API de AoT, que también se utilizan para generar documentación Swagger, hasta el soporte relativamente nuevo de Bean Introspection, que le permite analizar objetos en tiempo de ejecución sin reflexión.


Micronaut proporciona bloques de construcción para cosas increíbles. Predator es una de esas cosas, y estamos empezando a trabajar en algunas de las características prometedoras de Micronaut 1.0.


ACTUALIZACIÓN: Después del anuncio impactante, el asesino de Spring Data pasó a llamarse Micronaut Data: https://micronaut-projects.imtqy.com/micronaut-data/1.0.x/guide/

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


All Articles