Pipeline evaluasi Large Language Model (LLM)—seperti pengujian konsistensi respons Claude (Anthropic) pada variasi bahasa dan token limit—menghasilkan jutaan baris log metrik per hari. Tabel evaluasi yang memuat metrik scoring teks (misal: helpfulness, harmlessness, rasio latensi, dan token count) kerap mengalami degradasi performa agregasi secara drastis seiring bertambahnya volume tabel.
Bottleneck terjadi akibat pembacaan sequential scan pada disk berukuran puluhan gigabyte saat query agregasi analitik dijalankan. Masalah ini dapat diselesaikan di PostgreSQL dengan kombinasi declarative range partitioning bulanan dan composite index B-Tree yang dirancang sesuai selektivitas predikat query.
Skema Database Metrik Evaluasi LLM
Tabel llm_evaluasi_logs menampung evaluasi keluaran model berdasarkan model ID, kode bahasa (ISO 639-1), token usage, dan ragam skor observabilitas.
-- Tabel partisi utama
CREATE TABLE llm_evaluasi_logs (
id BIGSERIAL,
model_id VARCHAR(50) NOT NULL,
language_code VARCHAR(10) NOT NULL,
prompt_tokens INT NOT NULL,
completion_tokens INT NOT NULL,
alignment_score NUMERIC(4,3) NOT NULL,
hallucination_score NUMERIC(4,3) NOT NULL,
created_at TIMESTAMPTZ NOT NULL,
PRIMARY KEY (id, created_at)
) PARTITION BY RANGE (created_at);Catatan: Kolom created_at wajib disertakan ke dalam Primary Key karena PostgreSQL mewajibkan partition key menjadi bagian dari unique constraints/primary key pada tabel berpartisi.Implementasi Declarative Range Partitioning
Pemisahan data berdasarkan rentang waktu memastikan PostgreSQL engine memanfaatkan fitur partition pruning. Query yang memfilter rentang tanggal tertentu hanya akan memindai berkas partisi fisik yang relevan, melewati sisa tabel lainnya.
-- Pembuatan partisi bulanan
CREATE TABLE llm_evaluasi_logs_y2024m01 PARTITION OF llm_evaluasi_logs
FOR VALUES FROM ('2024-01-01 00:00:00+00') TO ('2024-02-01 00:00:00+00');
CREATE TABLE llm_evaluasi_logs_y2024m02 PARTITION OF llm_evaluasi_logs
FOR VALUES FROM ('2024-02-01 00:00:00+00') TO ('2024-03-01 00:00:00+00');
CREATE TABLE llm_evaluasi_logs_y2024m03 PARTITION OF llm_evaluasi_logs
FOR VALUES FROM ('2024-03-01 00:00:00+00') TO ('2024-04-01 00:00:00+00');Penyusunan Composite Index B-Tree
Agregasi berkala sering kali menggunakan filter spesifik: model (contoh: claude-3-5-sonnet), bahasa input (contoh: id), dan rentang waktu evaluasi. Urutan kolom pada B-Tree composite index harus mengikuti prinsip selektivitas: kesetaraan (equality) didahulukan sebelum rentang (range).
-- Menempatkan kolom kesetaraan di depan dan kolom rentang di akhir
CREATE INDEX idx_eval_model_lang_created
ON llm_evaluasi_logs (model_id, language_code, created_at);Penempatan model_id dan language_code di urutan terdepan memungkinkan index jump langsung ke subset node data yang tepat, kemudian memindai created_at secara sequential di daun (leaf nodes) B-Tree tanpa perlu filter ulang di memori.
Komparasi Query dan Analisis EXPLAIN ANALYZE
Skenario analitik: Menghitung rata-rata skor keselarasan (alignment) dan skor halusinasi untuk model Claude pada permintaan berbahasa Indonesia di bulan Januari 2024.
1. Query Sebelum Optimasi (Tanpa Index yang Tepat / Tanpa Pruning)
Pada tabel datar monolitik tanpa partisi dan hanya mengandalkan index tunggal pada created_at:
EXPLAIN ANALYZE
SELECT
AVG(alignment_score) AS avg_alignment,
AVG(hallucination_score) AS avg_hallucination
FROM llm_evaluasi_logs_monolithic
WHERE model_id = 'claude-3-5-sonnet'
AND language_code = 'id'
AND created_at >= '2024-01-15 00:00:00+00'
AND created_at < '2024-01-20 00:00:00+00';Output eksekusi engine:
Gather (cost=1000.00..45230.12 rows=1420 width=64) (actual time=18.421..324.512 rows=1 loops=1)
Workers Planned: 2
Workers Launched: 2
-> Parallel Seq Scan on llm_evaluasi_logs_monolithic (cost=0.00..44088.12 rows=592 width=16) (actual time=14.112..315.890 rows=480 loops=3)
Filter: ((created_at >= '2024-01-15 00:00:00+00') AND (created_at < '2024-01-20 00:00:00+00') AND ((model_id)::text = 'claude-3-5-sonnet'::text) AND ((language_code)::text = 'id'::text))
Rows Removed by Filter: 1205300
Execution Time: 326.114 ms2. Query Teroptimasi (Partition Pruning + Composite Index)
Query yang sama dijalankan pada tabel berpartisi dengan composite index:
EXPLAIN ANALYZE
SELECT
AVG(alignment_score) AS avg_alignment,
AVG(hallucination_score) AS avg_hallucination
FROM llm_evaluasi_logs
WHERE model_id = 'claude-3-5-sonnet'
AND language_code = 'id'
AND created_at >= '2024-01-15 00:00:00+00'
AND created_at < '2024-01-20 00:00:00+00';Output eksekusi engine:
Aggregate (cost=420.15..420.16 rows=1 width=64) (actual time=2.145..2.146 rows=1 loops=1)
-> Index Scan using llm_evaluasi_logs_y2024m01_model_id_language_code_created_at_idx on llm_evaluasi_logs_y2024m01 (cost=0.42..412.90 rows=1450 width=16) (actual time=0.048..1.280 rows=1440 loops=1)
Index Cond: (((model_id)::text = 'claude-3-5-sonnet'::text) AND ((language_code)::text = 'id'::text) AND (created_at >= '2024-01-15 00:00:00+00') AND (created_at < '2024-01-20 00:00:00+00'))
Execution Time: 2.210 msKinerja meningkat lebih dari 100 kali lipat. Partisi Februari dan Maret sama sekali tidak diakses (partition pruning), dan Index Scan langsung mengekstrak tuple yang valid tanpa membuang siklus I/O disk untuk memfilter baris non-target.
Trade-offs dan Pertimbangan Operasional
- Index Overhead: Composite index mempercepat pembacaan data agregasi, namun menambah latency transaksi saat logging massal (high-throughput ingest). Gunakan pooling koneksi atau worker buffering (seperti Redis batch stream) sebelum commit ke database utama.
- Automasi Partisi: PostgreSQL tidak membuat tabel partisi rentang secara otomatis untuk bulan baru. Gunakan ekstensi seperti
pg_partmanatau cron job terjadwal untuk menginisialisasi partisi sebelum bulan evaluasi berjalan guna menghindari error penulisan skema.
Komentar
0 komentar
Masuk ke akun kamu untuk ikut berkomentar.
Belum ada komentar
Jadilah yang pertama ikut berdiskusi!