Menjalankan binary pre-compiled di GNU Guix kerap memicu galat No such file or directory. Masalah ini bukan disebabkan oleh file biner yang hilang, melainkan ketiadaan dynamic linker standar Filesystem Hierarchy Standard (FHS) seperti /lib64/ld-linux-x86-64.so.2 pada root sistem GNU Guix. Artikel ini membahas cara memaketkan binary database profiler pihak ketiga di Guix, mengatasi dependency dynamic linking dengan patchelf, dan memanfaatkannya untuk mendiagnosis slow query akibat sequential scan pada PostgreSQL.
Penyebab Binary Pre-compiled Gagal di GNU Guix
Distribusi Linux standar menempatkan dynamic linker di /lib/ld-linux.so.* atau /lib64/ld-linux-x86-64.so.2. Header ELF pada binary yang dikompilasi secara dinamis menyertakan path absolut ke interpreter ini pada section .interp.
GNU Guix mengisolasi setiap pustaka dan executable ke dalam store immutabel di /gnu/store/<hash>-<name>-<version>/. Karena direktori /lib64 ditiadakan demi integritas dependensi reproduktif, kernel Linux langsung menolak mengeksekusi binary begitu gagal menemukan path interpreter ELF yang tercatat.
Selain interpreter, binary komersial atau pihak ketiga sering membutuhkan pustaka C standar (glibc) dan database client library (misalnya libpq.so) yang tidak berada pada path pencarian standar (DT_RUNPATH).
Definisi Paket Guix dengan Patchelf
Solusi reproduktif di GNU Guix adalah mendefinisikan manifest paket yang menggunakan utility patchelf. Skrip modifikasi tahap instalasi ini mengganti path PT_INTERP langsung ke glibc di dalam store serta menyuntikkan RUNPATH untuk runtime dependencies seperti libpq.
Berikut adalah manifest mandiri db-profiler.scm untuk memaketkan binary profiler:
(use-modules (guix packages)
(guix download)
(guix build-system copy)
(gnu packages base)
(gnu packages elf)
(gnu packages databases))
(package
(name "db-profiler")
(version "2.4.0")
(source (origin
(method url-fetch)
(uri "https://internal-tools.corp.local/bin/db-profiler-2.4.0-x86_64.tar.gz")
(sha256
(base32 "0v8hryv2fdfqw6x085g7x81k2m13jh6jcf936f4s0f0zslx37w6w"))))
(build-system copy-build-system)
(arguments
(list
#:install-plan '(( "db-profiler" "bin/" ))
#:phases
#~(modify-phases %standard-phases
(add-after 'install 'patch-binary
(lambda* (#:key inputs outputs #:allow-other-keys)
(let* ((out (assoc-ref outputs "out"))
(bin (string-append out "/bin/db-profiler"))
(ld-so (string-append (assoc-ref inputs "glibc")
"/lib/ld-linux-x86-64.so.2"))
(rpath (string-append (assoc-ref inputs "postgresql") "/lib:"
(assoc-ref inputs "glibc") "/lib")))
(invoke "patchelf" "--set-interpreter" ld-so bin)
(invoke "patchelf" "--set-rpath" rpath bin)))))))
(inputs
(list patchelf glibc (list postgresql "lib")))
(synopsis "Standalone DB Profiler")
(description "Profiler database biner yang dimodifikasi agar kompatibel dengan lingkungan store GNU Guix.")
(home-page "https://internal-tools.corp.local")
(license #f))
Uji lingkungan terisolasi ini tanpa mencemari sistem host melalui Guix Shell:
guix shell -f db-profiler.scm -- db-profiler --version
Catatan: Bila binary membutuhkan runtime dependency lain (seperti OpenSSL), tambahkan package yang bersangkutan ke daftar
inputsdan masukkan direktorilib-nya ke dalam stringrpath.
Mendeteksi Bottleneck Slow Query
Jalankan profiler untuk memonitor instance PostgreSQL lokal atau staging:
guix shell -f db-profiler.scm -- db-profiler --dsn="postgres://app:secret@localhost:5432/production_replica" --threshold-ms=250
Profiler menangkap kueri berikut yang dieksekusi terus-menerus dan memakan waktu sekitar 1.2 detik per transaksi:
SELECT id, tenant_id, amount, created_at
FROM transactions
WHERE tenant_id = 104
AND status = 'PENDING'
AND created_at >= NOW() - INTERVAL '14 days'
ORDER BY created_at DESC;
Tabel transactions berisi 12 juta baris, di mana 98% baris berstatus SETTLED atau CANCELLED, dan hanya sebagian kecil berstatus PENDING.
Analisis Query Plan via EXPLAIN (ANALYZE, BUFFERS)
Jalankan analisis mendalam di psql untuk membaca beban I/O memori dan alur eksekusi:
EXPLAIN (ANALYZE, BUFFERS)
SELECT id, tenant_id, amount, created_at
FROM transactions
WHERE tenant_id = 104
AND status = 'PENDING'
AND created_at >= NOW() - INTERVAL '14 days'
ORDER BY created_at DESC;
Hasil rencana eksekusi:
Gather Merge (cost=185420.10..185445.22 rows=215 width=32) (actual time=1180.412..1185.120 rows=188 loops=1)
Workers Planned: 2
Workers Launched: 2
Buffers: shared hit=4210 read=145890
-> Sort (cost=184419.88..184420.15 rows=90 width=32) (actual time=1176.102..1176.120 rows=63 loops=3)
Sort Key: created_at DESC
Sort Method: quicksort Memory: 30kB
Buffers: shared hit=4210 read=145890
-> Parallel Seq Scan on transactions (cost=0.00..184416.92 rows=90 width=32) (actual time=82.110..1175.810 rows=63 loops=3)
Filter: ((status = 'PENDING'::text) AND (tenant_id = 104) AND (created_at >= (now() - '14 days'::interval)))
Rows Removed by Filter: 3999937
Buffers: shared hit=4210 read=145890
Planning Time: 0.182 ms
Execution Time: 1185.240 ms
Interpretasi Metrik
- Parallel Seq Scan: Mesin database memindai seluruh blok tabel secara paralel karena tidak tersedia indeks yang mencakup predikat gabungan tersebut.
- Buffers:
shared hit=4210 read=145890menunjukkan database harus membaca 145.890 blok disk (setara ~1.14 GB data, asumsi blok 8KB). Beban I/O ini menjadi akar latency query. - Rows Removed by Filter: Database membuang hampir 4 juta baris per worker untuk menemukan hanya 63 baris data yang relevan.
- Sort Step: Terjadi operasi sorting terpisah di memori karena data dari pemindaian sekuensial tidak terurut sesuai
ORDER BY created_at DESC.
Optimasi Menggunakan Composite Partial Index
Melihat karakteristik data di mana status PENDING hanya mencakup sebagian kecil data aktif, membuat B-tree index standar pada seluruh tabel memboroskan ruang penyimpanan dan biaya write/update.
Solusi paling presisi adalah membuat Composite Partial Index:
CREATE INDEX idx_transactions_pending_tenant_created
ON transactions (tenant_id, created_at DESC)
WHERE status = 'PENDING';
Mengapa Pendekatan Ini Berhasil?
- Partial (
WHERE status = 'PENDING'): Hanya mengindeks baris yang relevan. Baris dengan statusSETTLEDatauCANCELLEDdiabaikan, menjaga footprint indeks tetap ringkas (hanya beberapa megabyte alih-alih ratusan megabyte). - Composite (
tenant_id, created_at DESC): Mendukung filter kesetaraantenant_id = 104, sekaligus menyediakan data yang sudah terurut secara fisik menurutcreated_at DESC. Database tidak perlu lagi melakukan in-memory sort terpisah.
Verifikasi Hasil Optimasi
Jalankan kembali profiling eksekusi dengan perintah EXPLAIN:
EXPLAIN (ANALYZE, BUFFERS)
SELECT id, tenant_id, amount, created_at
FROM transactions
WHERE tenant_id = 104
AND status = 'PENDING'
AND created_at >= NOW() - INTERVAL '14 days'
ORDER BY created_at DESC;
Rencana eksekusi baru:
Index Scan using idx_transactions_pending_tenant_created on transactions (cost=0.41..82.35 rows=190 width=32) (actual time=0.045..0.198 rows=188 loops=1)
Index Cond: ((tenant_id = 104) AND (created_at >= (now() - '14 days'::interval)))
Buffers: shared hit=12 read=0
Planning Time: 0.145 ms
Execution Time: 0.228 ms
Komparasi Kinerja
- Execution Time: Turun drastis dari 1185.24 ms menjadi 0.23 ms.
- I/O Read: Turun dari 145.890 page read ke 0 page read (100% buffer cache hit sebanyak 12 page).
- Strategi Scan: Berubah dari
Parallel Seq Scanyang memicu overhead CPU dan I/O masif menjadiIndex Scanbertarget langsung.
Mengemas profiler binary langsung lewat GNU Guix menjamin integritas runtime tools tanpa mengorbankan kontrol dependensi, sementara pemahaman mendalam terhadap query plan dan indexing menghasilkan mitigasi performa yang tepat sasaran pada layer basis data.
Komentar
0 komentar
Masuk ke akun kamu untuk ikut berkomentar.
Belum ada komentar
Jadilah yang pertama ikut berdiskusi!