AI Debug Database Trên Databricks Với PostgreSQL: Hướng Dẫn Chi Tiết

Bài này có bản video ngắn:TikTok

1. Tổng quan

Khi ứng dụng kết nối tới PostgreSQL trên Databricks (qua Lakebase hay Lakehouse Federation) gặp lỗi, việc đọc stack trace, log HikariCP và EXPLAIN ANALYZE thủ công tốn rất nhiều thời gian. Databricks tích hợp AI Assistant có thể đọc log, truy vấn SQL và cấu hình connection pool để gợi ý nguyên nhân và cách sửa trực tiếp.

Bài viết này giải thích cơ chế AI debug database hoạt động ra sao, sau đó đi qua ba tình huống lỗi PostgreSQL thường gặp (pool cạn kiệt, N+1 query, thiếu quyền) kèm code Spring Boot để bạn tái hiện và tự kiểm chứng gợi ý mà AI đưa ra. Đọc xong, bạn sẽ biết cách chuẩn bị log và truy vấn sao cho AI (hoặc chính bạn) chẩn đoán nhanh và chính xác hơn.

2. Databricks Postgres (Lakebase) là gì

Lakebase là PostgreSQL managed chạy ngay trong Databricks workspace, dùng cho các workload OLTP (transactional) bên cạnh Delta Lake cho OLAP. Ứng dụng backend kết nối vào Lakebase giống như kết nối một PostgreSQL thông thường — qua JDBC, với host/port/database do Databricks cấp.

2.1. Kết nối từ Spring Boot

<dependency>
    <groupId>org.postgresql</groupId>
    <artifactId>postgresql</artifactId>
    <version>42.7.4</version>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
spring.datasource.url=jdbc:postgresql://<workspace>.lakebase.databricks.com:5432/orders_db
spring.datasource.username=app_user
spring.datasource.password=${DB_PASSWORD}
spring.datasource.hikari.maximum-pool-size=10
spring.datasource.hikari.connection-timeout=3000

Đây là cấu hình tối thiểu: maximum-pool-size và connection-timeout là hai thông số sẽ xuất hiện liên tục trong các case debug ở phần 4, vì AI thường bắt đầu phân tích từ đây.

3. AI Debug Database trong Databricks hoạt động thế nào

3.1. Nguồn dữ liệu AI đọc

Databricks Assistant không “đoán” ngẫu nhiên — nó đọc ba nguồn:

  • Stack trace / exception log từ job hoặc notebook.
  • Query plan (EXPLAIN ANALYZE) khi bạn hỏi về truy vấn chậm.
  • Schema và metadata (bảng, index, constraint) để kiểm tra tính hợp lệ của SQL.

Nói cách khác, AI chỉ tốt khi log của bạn đủ chi tiết. Một exception chỉ in "Connection is not available" sẽ cho gợi ý mơ hồ hơn nhiều so với log có kèm pool size hiện tại và số connection đang active.

3.2. Luồng debug điển hình

1. Bạn paste stack trace hoặc câu SQL lỗi vào Databricks Assistant.
2. AI xác định loại lỗi: connection pool, syntax, permission, lock, timeout...
3. AI đối chiếu với schema/metadata hiện có trong workspace.
4. AI đề xuất fix: đổi config, sửa SQL, thêm index, chạy GRANT...
5. Bạn áp dụng fix, chạy lại, AI xác nhận log sạch.

Phần tiếp theo mô phỏng đúng luồng này với ba case cụ thể.

4. Thực hành: Tái hiện lỗi và đọc gợi ý debug

4.1. Chuẩn bị dữ liệu

CREATE TABLE customers (
    id BIGSERIAL PRIMARY KEY,
    name VARCHAR(100) NOT NULL
);

CREATE TABLE orders (
    id BIGSERIAL PRIMARY KEY,
    customer_id BIGINT REFERENCES customers(id),
    total NUMERIC(12,2) NOT NULL,
    created_at TIMESTAMP DEFAULT now()
);

Và entity tương ứng:

@Entity
@Table(name = "orders")
public class Order {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "customer_id")
    private Customer customer;

    private BigDecimal total;
}

4.2. Case 1 — Connection pool cạn kiệt

Service sau mắc lỗi connection leak vì mở EntityManager thủ công mà không đóng:

@Service
public class OrderReportService {

    @PersistenceContext
    private EntityManager entityManager;

    public List<Order> findAllOrdersUnsafe() {
        EntityManager em = entityManager.getEntityManagerFactory()
                .createEntityManager(); // không bao giờ close()
        return em.createQuery("SELECT o FROM Order o", Order.class)
                .getResultList();
    }
}

Gọi API này liên tục sẽ sinh ra log:

java.sql.SQLTransientConnectionException: HikariPool-1 - Connection is not available,
request timed out after 3000ms.

Khi đưa stack trace này cho AI cùng với file application.properties, gợi ý trả về thường là:

- Pool size (10) không phải nguyên nhân chính; vấn đề là connection không được trả lại pool.
- Rà soát các nơi gọi createEntityManager()/getConnection() thủ công.
- Đảm bảo dùng try-with-resources hoặc để Spring quản lý transaction.

Fix đúng là bỏ hẳn việc tự tạo EntityManager, dùng repository chuẩn:

public interface OrderRepository extends JpaRepository<Order, Long> {
    List<Order> findAll();
}
@Service
public class OrderReportService {

    private final OrderRepository orderRepository;

    public OrderReportService(OrderRepository orderRepository) {
        this.orderRepository = orderRepository;
    }

    public List<Order> findAllOrdersSafe() {
        return orderRepository.findAll(); // Spring tự quản lý connection
    }
}

Lưu ý: Tăng maximum-pool-size khi gặp lỗi này là sai hướng — nó chỉ trì hoãn sự cố thay vì sửa connection leak thật.

4.3. Case 2 — N+1 query làm chậm hệ thống

public List<OrderSummary> summarize(List<Order> orders) {
    return orders.stream()
            .map(o -> new OrderSummary(o.getId(), o.getCustomer().getName())) // lazy load
            .toList();
}

Vì customer là FetchType.LAZY, mỗi phần tử sinh ra một query riêng tới customers. Chạy EXPLAIN ANALYZE trên log Databricks sẽ thấy hàng trăm lần:

SELECT * FROM customers WHERE id = $1;

AI khi đọc query plan này thường chỉ ra:

- 200 queries lặp lại cùng pattern SELECT ... WHERE id = $1 trong một request.
- Nghi vấn N+1 do lazy loading không có JOIN FETCH.
- Gợi ý: dùng JOIN FETCH hoặc @EntityGraph để gộp thành 1 query.

Fix bằng JPQL JOIN FETCH:

@Query("SELECT o FROM Order o JOIN FETCH o.customer")
List<Order> findAllWithCustomer();

Kết quả: chỉ còn một câu SELECT ... JOIN customers ... thay vì N+1 lần truy vấn riêng lẻ.

4.4. Case 3 — Thiếu quyền (permission denied)

SELECT * FROM orders WHERE total > 1000;
ERROR: permission denied for table orders

AI đối chiếu metadata role trong Databricks và trả về gợi ý GRANT phù hợp:

GRANT SELECT ON orders TO app_user;

Lưu ý: Luôn kiểm tra gợi ý GRANT trước khi chạy trong production — AI không biết policy bảo mật nội bộ của bạn, chỉ biết cú pháp cần thiết để query chạy được. Đừng cấp quyền rộng hơn mức tối thiểu cần dùng (nguyên tắc least privilege).

5. Viết test xác nhận fix

Sau khi sửa N+1 ở case 4.3, cần test để đảm bảo không regress:

@DataJpaTest
class OrderRepositoryTest {

    @Autowired
    private OrderRepository orderRepository;

    @Autowired
    private TestEntityManager entityManager;

    @Test
    void shouldFetchOrdersWithCustomerInOneQuery() {
        Customer customer = new Customer();
        customer.setName("Nón Lá Dev");
        entityManager.persist(customer);

        Order order = new Order();
        order.setCustomer(customer);
        order.setTotal(new BigDecimal("150.00"));
        entityManager.persist(order);
        entityManager.flush();

        List<Order> orders = orderRepository.findAllWithCustomer();

        assertThat(orders)
                .hasSize(1)
                .first()
                .extracting(o -> o.getCustomer().getName())
                .isEqualTo("Nón Lá Dev");
    }
}

Test này không trực tiếp “đếm số query”, nhưng kết hợp với Hibernate statistics (spring.jpa.properties.hibernate.generate_statistics=true) bạn có thể log số lượng query thực thi và assert nó bằng 1 — đây cũng chính là loại log mà AI dùng để xác nhận fix có hiệu quả hay không.

6. Giám sát để AI debug hiệu quả hơn

Muốn AI (hoặc đồng nghiệp đọc log sau này) chẩn đoán nhanh, hãy log có cấu trúc thay vì chỉ e.printStackTrace():

@RestControllerAdvice
public class DatabaseExceptionHandler {

    private static final Logger log = LoggerFactory.getLogger(DatabaseExceptionHandler.class);

    @ExceptionHandler(DataAccessException.class)
    public ResponseEntity<String> handle(DataAccessException ex) {
        log.error("db_error_type={}, sql_state={}, message={}",
                ex.getClass().getSimpleName(),
                extractSqlState(ex),
                ex.getMostSpecificCause().getMessage());
        return ResponseEntity.status(503).body("Database error, retry later");
    }

    private String extractSqlState(DataAccessException ex) {
        if (ex.getCause() instanceof SQLException sqlEx) {
            return sqlEx.getSQLState();
        }
        return "unknown";
    }
}

Log dạng key-value (db_error_type=, sql_state=) dễ parse hơn nhiều so với stack trace thuần văn, và là input lý tưởng khi bạn paste vào Databricks Assistant để hỏi “lỗi này do đâu”.

7. Khi nào KHÔNG nên dùng AI debug

  • Lỗi liên quan dữ liệu nhạy cảm: không paste log chứa dữ liệu khách hàng thật vào AI assistant, kể cả nội bộ — hãy mask trước.
  • Vấn đề performance cấp hạ tầng (disk I/O, network latency giữa region): AI đọc được query plan nhưng không thấy được tầng hạ tầng vật lý, dễ đưa gợi ý sai hướng (ví dụ bảo “thêm index” trong khi nguyên nhân là network).
  • Lỗi transient một lần: nếu exception chỉ xảy ra một lần do mạng chập chờn, debug bằng AI tốn thời gian hơn là chỉ retry và theo dõi tiếp.
  • Thay thế code review: gợi ý GRANT hay đổi pool size của AI cần người có quyền hạ tầng xác nhận lại, không nên tự động apply vào production.

8. Kết luận

AI debug database trên Databricks mạnh ở việc đọc nhanh stack trace, query plan và metadata để khoanh vùng nguyên nhân — connection leak, N+1 query, thiếu quyền là ba case rất phổ biến mà bài viết đã tái hiện bằng Spring Boot và PostgreSQL. Giá trị thật sự nằm ở việc bạn log có cấu trúc và viết test xác nhận fix, vì AI chỉ tốt bằng chất lượng dữ liệu bạn cung cấp. Luôn kiểm chứng gợi ý — đặc biệt là GRANT quyền hay thay đổi hạ tầng — trước khi áp dụng vào hệ thống thật.

databrickspostgresqlaidebuggingspring-boot