Thứ ba, 06/01/2026 Ngọc Hân
Mytel MagicWheel – HikariPool Connection Timeout do Open-EntityManager-In-View (WebService)
1. Mô tả lỗi
Trong một số thời điểm tải cao, hệ thống ghi nhận lỗi:
HikariPool - Connection is not available, request timed out after 30000ms
(total=100, active=100, idle=0, waiting=…) Biểu hiện:
-
Một số request bị timeout
-
Giao dịch không được xử lý hoặc xử lý thất bại
-
Log xuất hiện lỗi
JDBCConnectionException/HikariPool timeout -
Ảnh hưởng trực tiếp đến trải nghiệm người dùng
Lỗi thường xuất hiện ở các luồng xử lý gọi sang hệ thống MPS và chờ phản hồi dài (người dùng xác nhận USSD).
2. Phạm vi ảnh hưởng
-
Các API có luồng xử lý:
-
Truy vấn database qua JPA Repository
-
Sau đó gọi external system (MPS) với thời gian chờ dài (20s ~ 50s)
-
-
Khi có nhiều request đồng thời:
-
Connection pool bị sử dụng hết
-
Các request khác không lấy được JDBC connection
-
Ảnh hưởng:
-
TPS của hệ thống giảm
-
Một phần request thất bại trong thời gian ngắn
-
Có thể gây gián đoạn dịch vụ khi tải cao
3. Nguyên nhân
3.1. Nguyên nhân gốc
Nguyên nhân không phải do database chậm hay pool size nhỏ, mà do:
JDBC connection bị giữ quá lâu trong vòng đời HTTP request
3.2. Luồng xử lý TRƯỚC khi fix
(spring.jpa.open-in-view = true – mặc định Spring Boot)
Mô tả
-
Spring Boot mặc định bật Open-EntityManager-In-View (OEIV)
-
EntityManager được mở ngay khi request bắt đầu
-
JDBC connection bị giữ xuyên suốt HTTP request
-
Bao gồm cả thời gian chờ external MPS
Sơ đồ luồng xử lý:
Hệ quả:
-
Mỗi request giữ 1 JDBC connection trong 20–50s
-
N request đồng thời → N connection bị chiếm
-
Khi pool đạt giới hạn:
-
Request mới không lấy được connection → timeout
-
👉 Đây là tình trạng thiếu connection do bị giữ quá lâu, không phải leak vĩnh viễn.
3.3. Luồng xử lý SAU khi fix
(spring.jpa.open-in-view = false)
Mô tả
-
EntityManager chỉ tồn tại trong phạm vi repository / transaction
-
JDBC connection được giải phóng ngay sau DB operation
-
External MPS call không giữ connection
Sơ đồ luồng xử lý:
Kết quả:
-
JDBC connection chỉ bị giữ trong thời gian rất ngắn (ms)
-
External call dài không ảnh hưởng connection pool
-
Hệ thống ổn định hơn khi tải cao
3.4. Lưu ý quan trọng về hành vi mặc định của Spring Boot
-
Tất cả các phiên bản Spring Boot (1.x → 3.x) đều mặc định bật:
spring.jpa.open-in-view = true -
Spring Boot:
-
Không log warning
-
Không cảnh báo cấu hình
-
Không tự động disable
-
Điều này dẫn đến:
-
Ứng dụng chạy bình thường khi tải thấp
-
Lỗi chỉ bộc lộ khi:
-
Tải cao
-
External call có latency lớn
-
-
Rất dễ bị bỏ qua nếu không hiểu rõ vòng đời EntityManager và JDBC connection
👉 Với REST API / microservice, OEIV là anti-pattern và cần được tắt chủ động.
4. Phương án xử lý
4.1. Tắt Open-EntityManager-In-View
spring:
jpa:
open-in-view: false Tác dụng:
-
EntityManager chỉ tồn tại trong phạm vi repository / transaction
-
JDBC connection được giải phóng ngay sau khi kết thúc thao tác DB
-
Không còn giữ connection trong thời gian chờ external MPS
4.2. Bật giám sát connection leak (phòng ngừa)
spring:
datasource:
hikari:
leak-detection-threshold: 20000 -
Log cảnh báo nếu 1 connection bị giữ > 20s
-
Không ảnh hưởng hiệu năng
-
Giúp phát hiện sớm nếu lỗi tương tự tái diễn trong tương lai
4.3. Nguyên tắc coding bắt buộc
-
Không gọi external API bên trong transaction
-
Không để transaction bao quanh logic chờ external response
-
Chỉ thao tác DB trong scope ngắn nhất có thể
-
Tách rõ:
-
DB operation
-
External API call
-
5. Bài học rút ra
-
JDBC connection là tài nguyên rất hạn chế, cần được giải phóng càng sớm càng tốt
-
Open-EntityManager-In-View là anti-pattern đối với REST API và microservice
-
External call có latency cao không được giữ transaction / connection DB
-
Các lỗi kiến trúc kiểu này:
-
Không lộ ở môi trường tải thấp
-
Chỉ xuất hiện khi TPS cao + latency lớn
-
-
Luôn bật cơ chế giám sát để:
-
Phát hiện sớm
-
Tránh lỗi tái diễn trong tương lai
-