Rak server database di ruang server dengan kabel jaringan dan lampu indikator

Panduan Konfigurasi Connection Pool di Java

Konfigurasi connection pool di Java menentukan seberapa cepat aplikasi merespons permintaan database dan seberapa jauh ia bisa bertahan saat beban naik. Sebagian besar masalah “koneksi habis” atau query yang menggantung berakar pada ukuran pool dan timeout yang dipilih asal-asalan, bukan pada query-nya. Artikel ini membandingkan tiga library populer lalu menunjukkan langkah konfigurasinya untuk aplikasi intranet berbasis JDBC maupun Spring.

Kenapa connection pool perlu diukur, bukan disalin

Membuka koneksi JDBC baru berarti handshake TCP, autentikasi, dan alokasi memori di sisi server database. Proses itu makan puluhan milidetik. Connection pool menyimpan koneksi yang sudah terbuka agar permintaan berikutnya tidak mengulang biaya tersebut.

Pool punya batas. Setiap koneksi aktif memakai satu thread di database dan sebagian memori. Kalau kamu memberi 100 koneksi ke lima aplikasi berbeda, database harus melayani 500 koneksi bersamaan. Banyak database intranet yang dijalankan di server tunggal akan tersendat jauh sebelum angka itu tercapai.

Ukuran pool yang tepat bergantung pada jumlah core database, lama tiap query, dan pola akses aplikasi. Rumus praktisnya: jumlah koneksi aktif yang dibutuhkan sekitar (jumlah core CPU database × 2) + jumlah disk spindle. Untuk database intranet dengan 4 core, angka itu berkisar 10 sampai 12 koneksi per aplikasi. Sisanya adalah ruang tunggu, bukan ruang kerja.

Membandingkan HikariCP, DBCP2, dan C3P0

Ketiganya menyelesaikan masalah yang sama dengan pendekatan berbeda. Pilihanmu memengaruhi jumlah kode konfigurasi dan perilaku saat database sempat tidak tersedia.

HikariCP

HikariCP adalah default Spring Boot sejak versi 2.0 dan paling sering direkomendasikan karena overhead-nya kecil. Pool ini memakai struktur data tanpa lock pada jalur pengambilan koneksi, sehingga waktu tunggu antar-thread lebih pendek. Konfigurasinya sedikit: ukuran maksimum, minimum idle, dan timeout.

HikariCP tidak menyediakan statement cache bawaan sekuat beberapa pesaingnya, jadi kamu bergantung pada cache di driver JDBC. Pool ini juga tidak punya mekanisme retry otomatis saat database mati total; koneksi baru akan dicoba saat ada permintaan.

Apache Commons DBCP2

DBCP2 lebih tua dan lebih gemuk, tetapi banyak dipakai di aplikasi enterprise yang sudah berjalan lama. Fiturnya lengkap, termasuk validasi koneksi dengan query uji, eviction berbasis waktu, dan PreparedStatement pooling. Dokumentasinya rapi.

Harganya adalah performa. Pada beban tinggi, DBCP2 bisa lebih lambat dibanding HikariCP karena sinkronisasi internalnya. Untuk aplikasi intranet dengan puluhan pengguna aktif, selisih itu biasanya tidak terasa. Untuk layanan yang melayani ratusan permintaan per detik, selisihnya bisa berarti.

C3P0

C3P0 muncul di era Hibernate 3 dan masih ditemukan di proyek warisan. Ia menawarkan fitur pemulihan otomatis dan connection tester yang agresif, tetapi kode basisnya sudah jarang diperbarui. Pilih C3P0 hanya kalau kamu memang memelihara aplikasi lama dan migrasi ke HikariCP berisiko terlalu besar.

Langkah 1: Menyiapkan dependensi

Untuk proyek Maven baru, tambahkan HikariCP dan driver JDBC yang sesuai. Versi driver mengikuti database yang kamu pakai.

<dependency>
  <groupId>com.zaxxer</groupId>
  <artifactId>HikariCP</artifactId>
  <version>5.1.0</version>
</dependency>
<dependency>
  <groupId>org.postgresql</groupId>
  <artifactId>postgresql</artifactId>
  <version>42.7.3</version>
</dependency>

Kalau kamu memakai Spring Boot, dependensi spring-boot-starter-jdbc sudah membawa HikariCP secara transitif. Kamu tidak perlu mendeklarasikannya sendiri.

Langkah 2: Menulis konfigurasi pool

Berikut konfigurasi HikariCP lewat kode Java biasa, tanpa framework.

HikariConfig config = new HikariConfig();
config.setJdbcUrl("jdbc:postgresql://db-intranet:5432/absensi");
config.setUsername("app_absensi");
config.setPassword(System.getenv("DB_PASSWORD"));
config.setMaximumPoolSize(12);
config.setMinimumIdle(4);
config.setConnectionTimeout(3000);
config.setIdleTimeout(600000);
config.setMaxLifetime(1800000);
config.setPoolName("absensi-pool");

HikariDataSource ds = new HikariDataSource(config);

Beberapa nilai di atas perlu penjelasan. connectionTimeout 3000 ms berarti aplikasi gagal cepat saat pool penuh, bukan menggantung menunggu tanpa batas. maxLifetime 30 menit lebih pendek dari timeout koneksi di banyak database, sehingga koneksi didaur ulang sebelum server memutusnya sepihak.

Untuk Spring Boot, nilai yang sama ditulis di application.properties.

spring.datasource.hikari.maximum-pool-size=12
spring.datasource.hikari.minimum-idle=4
spring.datasource.hikari.connection-timeout=3000
spring.datasource.hikari.max-lifetime=1800000

Kalau aplikasimu memakai JPA, konfigurasi ini tetap berlaku karena Hibernate memakai DataSource yang sama. Untuk memilih antara memakai JPA atau query JDBC langsung, pertimbangkan jumlah tabel dan kompleksitas relasi di aplikasi intranetmu.

Langkah 3: Memantau pool yang sedang berjalan

Konfigurasi tanpa pemantauan hanya tebakan. HikariCP mengekspos metrik lewat JMX dan Micrometer. Yang perlu kamu perhatikan ada tiga: jumlah koneksi aktif, jumlah thread yang menunggu, dan waktu tunggu rata-rata.

  • Koneksi aktif menyentuh batas maksimum terus-menerus. Artinya pool terlalu kecil atau ada query yang terlalu lama ditahan.
  • Thread menunggu lebih dari 0 dalam waktu lama. Aplikasi sedang mengantre koneksi; periksa query yang lambat sebelum menaikkan ukuran pool.
  • Waktu tunggu rata-rata di bawah 1 ms. Pool masih longgar. Jangan buru-buru menaikkan ukuran.

Kalau aplikasi berjalan di beberapa server aplikasi dan saling terhubung lewat jaringan intranet, kualitas jaringan juga memengaruhi latensi koneksi. Mengukur packet loss dan latensi dengan cara cek ping dengan CMD membantu memisahkan masalah jaringan dari masalah pool.

Langkah 4: Menangani kegagalan database

Database yang restart akan memutus semua koneksi di pool. HikariCP menandai koneksi itu mati dan membuat yang baru saat ada permintaan, tetapi aplikasi tetap perlu menangani pengecualian yang muncul di sela-sela itu.

Terapkan timeout di tingkat query juga, bukan hanya di pool. Di JDBC kamu bisa memakai Statement.setQueryTimeout, sedangkan di Spring Data JPA ada anotasi @Transactional(timeout = 5). Tanpa batas waktu di level ini, satu query yang menggantung akan menahan satu koneksi dari pool sampai selesai.

Untuk layanan internal yang saling memanggil, pola retry dan circuit breaker perlu dipikirkan bersama desain pool, seperti dibahas di pola arsitektur API Java untuk layanan internal. Koneksi database yang habis sering kali merupakan efek lanjutan dari satu layanan yang lambat.

Langkah 5: Menentukan ukuran pool yang masuk akal

Mulai dari angka kecil, lalu naikkan berdasarkan data pemantauan. Urutan yang saya sarankan:

  1. Hitung batas atas dari kapasitas database: jumlah core × 2, ditambah ruang untuk proses administratif.
  2. Tetapkan ukuran pool per aplikasi di bawah angka itu, dibagi rata kalau ada beberapa aplikasi.
  3. Jalankan beban uji dengan pola akses yang mirip produksi, bukan sekadar smoke test.
  4. Pantau koneksi aktif dan waktu tunggu selama minimal satu siklus pemakaian, misalnya satu minggu kerja untuk aplikasi intranet.
  5. Naikkan 2 koneksi setiap kali waktu tunggu konsisten di atas ambang yang kamu tetapkan, lalu ukur ulang.

Jangan mengubah ukuran pool dan query secara bersamaan. Kalau keduanya diubah, kamu tidak akan tahu mana yang memperbaiki masalah.

Kesalahan yang sering terjadi

Menaruh password database langsung di kode sumber adalah kesalahan paling umum. Gunakan variabel lingkungan atau secret manager yang dibaca saat start. Kesalahan kedua: menetapkan minimumIdle sama dengan maximumPoolSize. Itu membuat pool membuka semua koneksi sejak awal dan menahannya meski aplikasi sedang sepi. Ketiga: lupa menutup ResultSet dan Statement, sehingga koneksi kembali ke pool dalam keadaan masih terpakai.

Untuk aplikasi yang juga mengekspos API internal, pastikan pengaturan pool selaras dengan konfigurasi servlet atau framework REST pilihanmu. Perbandingan mendalam soal itu ada di panduan memilih antara Servlet, Spring MVC, dan JAX-RS.

Ringkasan

HikariCP cocok menjadi pilihan default untuk aplikasi Java baru karena konfigurasinya ringkas dan overhead-nya kecil. DBCP2 masuk akal untuk sistem enterprise yang sudah berjalan, sedangkan C3P0 sebaiknya hanya dipertahankan di proyek warisan. Apa pun pilihannya, ukuran pool harus dihitung dari kapasitas database dan diverifikasi lewat pemantauan, bukan disalin dari contoh di internet.

Pertanyaan yang Sering Diajukan

Berapa ukuran connection pool yang ideal untuk aplikasi intranet?

Tidak ada angka tunggal. Mulai dari sekitar dua kali jumlah core CPU database, lalu sesuaikan berdasarkan jumlah koneksi aktif dan waktu tunggu yang terukur. Untuk database dengan 4 core dan satu aplikasi utama, 10 sampai 12 koneksi biasanya cukup.

Apakah perlu menaikkan ukuran pool saat aplikasi terasa lambat?

Periksa dulu query yang berjalan lama dan waktu tunggu di pool. Kalau koneksi aktif jarang menyentuh batas maksimum, masalahnya bukan di ukuran pool. Menaikkan angka hanya menambah beban ke database tanpa memperbaiki latensi.

Bagaimana cara aman menyimpan kredensial database di aplikasi Java?

Simpan di variabel lingkungan atau layanan pengelola rahasia, lalu baca saat aplikasi start. Hindari menaruhnya di file properti yang ikut masuk repositori. Kalau aplikasi berjalan di beberapa server, pastikan mekanisme distribusi rahasia itu konsisten dan bisa diaudit.