
Hari ini, tim Micronaut di Object Computing Inc (OCI) memperkenalkan Predator , sebuah proyek open source baru yang tujuannya adalah untuk secara signifikan meningkatkan runtime dan kinerja (dari memori) akses data untuk layanan-layanan microser dan serverless, tanpa kehilangan produktivitas dibandingkan dengan alat seperti GORM dan Spring Data.
Riwayat Alat Akses Data
Kita bisa melacak sejarah templat repositori data sejak 2004, ketika Ruby on Rails keluar dengan subsistem ActiveRecord, sebuah API yang merevolusi pemahaman kita tentang akses data dalam hal produktivitas pengembang.
Pada 2007, tim Grails pertama kali memperkenalkan API seperti ActiveRecord untuk JVM - GORM (bagian dari Grails). GORM mengandalkan sifat dinamis Groovy untuk menerapkan metode pencarian di atas Hibernate dan memberikan manfaat produktivitas yang sama kepada pengguna JVM.
Karena GORM bergantung pada bahasa Groovy, proyek Data Spring dibuat pada 2011 yang memungkinkan pengembang Java untuk menentukan metode pencarian, seperti findByTitle
, di antarmuka, dan secara otomatis menerapkan logika kueri saat runtime.
Cara kerja alat akses data
Semua implementasi yang disebutkan menggunakan templat yang sama, yaitu untuk membangun metamodel entitas proyek pada saat run time yang memodelkan hubungan antara kelas entitas Anda. Di Spring Data, ini adalah MappingContext, dan dalam GORM juga disebut MappingContext. Mereka dibangun dengan memindai kelas menggunakan refleksi. (Kesamaan dalam penamaan tidak disengaja di sini. Pada 2010, saya bekerja dengan tim Data Spring untuk mencoba menciptakan GORM untuk Java, pada proyek yang akhirnya berubah menjadi apa yang disebut Data Musim Semi hari ini)
Metamodel ini kemudian digunakan untuk mengubah ekspresi pencarian, seperti bookRepository.findByTitle("The Stand")
, menjadi model kueri abstrak saat runtime menggunakan kombinasi parsing ekspresi reguler dan kerangka logika. Kami membutuhkan model permintaan abstrak karena dialek target dari permintaan berbeda untuk setiap basis data (SQL, JPA-QL, Cypher, Bson, dll.)
Dukungan Repositori Micronaut
Sejak meluncurkan Micronaut lebih dari setahun yang lalu, fitur utama yang hilang yang kami tanyakan adalah "GORM for Java" atau dukungan Data Musim Semi. Begitu banyak pengembang yang jatuh cinta pada produktivitas yang disediakan alat-alat ini, serta kemudahan mendefinisikan antarmuka yang diterapkan kerangka kerja. Saya akan mengatakan bahwa sebagian besar kesuksesan Grails dan Spring Boot masing-masing dapat dikaitkan dengan GORM dan Spring Data.
Untuk pengguna Micronaut yang menggunakan Groovy, kami mendapat dukungan GORM sejak hari pertama, dan pengguna Java dan Kotlin tidak memiliki apa-apa, karena mereka perlu mengimplementasikan repositori sendiri.
Secara teknis akan mungkin, dan terus terang lebih mudah, cukup menambahkan modul untuk Micronaut yang akan mengkonfigurasi Spring Data. Namun, mengikuti jalur ini, kami akan menyediakan subsistem yang diimplementasikan menggunakan semua metode yang coba dihindari oleh Micronaut: meluasnya penggunaan proxy, refleksi, dan konsumsi memori yang tinggi.
Memperkenalkan Predator!
Predator, kependekan dari Precomputed Data Repository, menggunakan Micronaut API untuk mengkompilasi sebelum eksekusi (AoT, sebelum waktu) untuk mentransfer model meta entitas dan mengubah ekspresi pencarian (seperti findByTitle
) ke dalam SQL atau JPA-QL yang sesuai untuk kompiler Anda . Akibatnya, kueri mengeksekusi lapisan runtime program yang sangat tipis tanpa refleksi, dan hanya tinggal menjalankan kueri dan mengembalikan hasilnya.
Hasilnya luar biasa ... awal yang dingin berkurang secara signifikan, kami mendapatkan konsumsi memori yang sangat rendah dan peningkatan kinerja yang tajam.
Hari ini kita membuka kode sumber untuk Predator di bawah lisensi Apache 2, itu akan datang dengan dua implementasi awal (lebih banyak fitur yang direncanakan untuk masa depan) untuk JPA (berdasarkan Hibernate) dan untuk SQL dengan JDBC.
Implementasi JDBC paling menyenangkan bagi saya, karena sepenuhnya independen dari refleksi, tidak menggunakan proxy dan pemuatan kelas dinamis untuk tingkat akses data Anda, yang mengarah pada peningkatan kinerja. Lapisan runtime sangat ringan sehingga kode repositori yang ditulis oleh tangan tidak akan mengeksekusi lebih cepat.
Predator Kinerja
Karena Predator tidak perlu menjalankan transformasi kueri apa pun pada waktu berjalan, keuntungan kinerja menjadi signifikan. Dalam dunia pemanfaatan komputasi awan, di mana Anda membayar jumlah waktu yang dijalankan aplikasi Anda atau untuk pelaksanaan fungsi tunggal, pengembang sering kali tidak melihat kinerja mekanisme akses data mereka.
Tabel berikut ini merangkum perbedaan kinerja yang dapat diharapkan untuk ekspresi pencarian sederhana, seperti findByTitle
, dibandingkan dengan implementasi lainnya. Semua tes dilakukan dengan menggunakan bangku tes pada 8-core Xeon iMac Pro dalam kondisi yang sama, tes terbuka dan dapat ditemukan di repositori :
Ya, Anda membacanya dengan benar. Dengan Predator JDBC, Anda dapat mengharapkan peningkatan kinerja hampir 4X dari GORM dan 2.5X dari Data Spring.
Dan bahkan jika Anda menggunakan Predator JPA, Anda dapat mengandalkan lebih dari 2X peningkatan kinerja lebih dari GORM dan hingga 40% dibandingkan Spring Data JPA.
Lihatlah perbedaan ukuran tumpukan eksekusi ketika menggunakan Predator dibandingkan dengan alternatif:
Predator:

Predator JPA:

Data Musim Semi:

GORM:

Predator JDBC hanya menggunakan 15 frame sampai saat permintaan Anda dieksekusi, sementara Predator JPA menggunakan 30 frame (terutama karena Hibernate), dibandingkan dengan 50+ stack frame di Spring Data atau GORM. Dan semua berkat mekanisme AOP Micronaut yang tidak menggunakan refleksi.
Stacker pendek juga menyederhanakan debugging aplikasi. Salah satu keuntungan melakukan sebagian besar pekerjaan selama kompilasi adalah bahwa kesalahan dapat dideteksi sebelum aplikasi diluncurkan, yang sangat meningkatkan pengalaman pengembang. Kami segera mendapatkan kesalahan kompilasi alih-alih kesalahan runtime untuk kesalahan paling umum.
Kompilasi pemeriksaan waktu
Sebagian besar implementasi templat repositori hanya mengandalkan menjalankan semua operasi saat runtime. Ini berarti bahwa jika pengembang membuat kesalahan dalam mendefinisikan antarmuka repositori, kesalahan tidak akan terlihat sampai aplikasi benar-benar diluncurkan.
Ini merampas beberapa manfaat Java untuk pengecekan tipe dan kami memiliki pengalaman data yang buruk. Ini tidak terjadi dengan Predator. Perhatikan contoh berikut:
@JdbcRepository(dialect = Dialect.H2) public interface BookRepository extends CrudRepository<Book, Long> { Book findByTile(String t); }
Di sini BookRepository
kami mendeklarasikan permintaan ke objek bernama Book
, yang memiliki properti title
. Sayangnya, ada kesalahan dalam deklarasi ini: kami menamai metode findByTile
alih-alih findByTitle
. Alih-alih menjalankan kode ini, Predator tidak akan mengizinkan kode Anda untuk dikompilasi dengan pesan kesalahan informatif:
Error:(9, 10) java: Unable to implement Repository method: BookRepository.findByTile(String title). Cannot use [Equals] criterion on non-existent property path: tile
Banyak aspek Predator diperiksa pada waktu kompilasi, bila memungkinkan, untuk memastikan bahwa kesalahan runtime tidak disebabkan oleh deklarasi repositori yang salah.
Predator JDBC dan GraalVM Substrat
Alasan lain Predator harus senang adalah bahwa ia berada di luar kotak yang kompatibel dengan gambar asli GraalVM dan tidak memerlukan konversi bytecode kompleks selama pembuatan, tidak seperti yang untuk Hibernate di GraalVM.
Dengan sepenuhnya menghilangkan refleksi dan proksi dinamis dari lapisan akses data, Predator sangat menyederhanakan pembuatan aplikasi yang bekerja dengan data yang berjalan di GraalVM.
Aplikasi sampel Predator JDBC berjalan pada Substrat tanpa masalah dan memungkinkan Anda untuk membuat gambar asli yang jauh lebih kecil (25 MB lebih sedikit!) Dari pada Hibernate perlu bekerja, berkat lapisan runtime yang jauh lebih tipis.
Kami melihat hasil yang sama ketika kami mengimplementasikan kompilasi aturan Validasi Bean untuk Micronaut 1.2. Ukuran gambar asli menurun 10 MB, segera setelah kami menghapus ketergantungan pada Hibernate Validator, dan ukuran JAR sebesar 2 MB.
Keuntungannya di sini jelas: dengan melakukan lebih banyak pekerjaan selama kompilasi dan membuat lebih banyak runtimes yang kompak, Anda mendapatkan gambar asli yang lebih kecil dan file JAR, yang mengarah pada penyebaran layanan microser yang lebih kecil dan lebih mudah ketika digunakan melalui Docker. Masa depan kerangka kerja Java adalah kompiler yang lebih kuat dan runtime yang lebih kecil dan lebih ringan.
Predator dan masa depan
Kami baru mulai bekerja dengan Predator dan sangat senang dengan peluang yang terbuka.
Awalnya, kami mulai dengan dukungan untuk JPA dan SQL, tetapi di masa depan Anda dapat mengharapkan dukungan untuk MongoDB, Neo4J, SQL Reaktif dan database lainnya. Untungnya, pekerjaan ini jauh lebih sederhana karena sebagian besar Predator sebenarnya didasarkan pada kode sumber GORM, dan kita dapat menggunakan kembali logika GORM untuk Neo4J dan GORM untuk MongoDB untuk merilis implementasi ini lebih cepat dari yang Anda harapkan.
Predator adalah puncak dari menggabungkan berbagai blok bangunan di Micronaut Core yang memungkinkan untuk mengimplementasikannya, dari API AoT, yang juga digunakan untuk menghasilkan dokumentasi Swagger, hingga dukungan Bean Introspection yang relatif baru, yang memungkinkan Anda untuk menganalisis objek saat runtime tanpa refleksi.
Micronaut menyediakan blok bangunan untuk hal-hal menakjubkan. Predator adalah satu hal seperti itu, dan kami baru mulai bekerja pada beberapa fitur yang menjanjikan dari Micronaut 1.0.
PEMBARUAN: Setelah pengumuman yang mengejutkan, pembunuh Data Spring diganti namanya menjadi Data Micronaut: https://micronaut-projects.imtqy.com/micronaut-data/1.0.x/guide/