Mengembangkan extension kustom PostgreSQL, khususnya runtime procedural language (PL) seperti engine BASIC atau interpreter skrip tertanam, menuntut stabilitas tinggi. Kerusakan memori atau kesalahan penanganan transaksi pada level ekstensi C dapat merusak backend process Postgres secara keseluruhan. PostgreSQL menyediakan dua test harness bawaan untuk menjamin kualitas ini: pg_regress untuk regression test fungsional deterministik, dan isolationtester untuk pengujian perilaku konkurensi serta integritas transaksi antar-sesi.
Arsitektur Pengujian Native: pg_regress dan Makefile PGXS
Harness pg_regress bekerja dengan menjalankan file skrip SQL (direktori sql/), menangkap seluruh output teks (stdout dan stderr), lalu membandingkannya baris demi baris menggunakan diff terhadap file referensi yang diharapkan (direktori expected/). Jika terjadi perbedaan sekecil apa pun, tes dinyatakan gagal.
Untuk mengintegrasikan pengujian ini pada proyek ekstensi berbasis PostgreSQL Extension Building Infrastructure (PGXS), konfigurasikan file Makefile ekstensi Anda:
EXTENSION = my_runtime
DATA = my_runtime--1.0.sql
MODULES = my_runtime
# Daftarkan nama file pengujian tanpa ekstensi .sql
REGRESS = init_runtime memory_safety error_handling
# Daftarkan pengujian isolasi / konkurensi
ISOLATION = concurrent_execution deadlock_guard
PG_CONFIG = pg_config
PGXS := $(shell $(PG_CONFIG) --pgxs)
include $(PGXS)
Perintah make installcheck akan mengeksekusi suite pengujian ini terhadap kluster database yang sedang berjalan.
Validasi Memory Safety dan Error Handling pada pg_regress
Kesalahan fatal pada extension procedural language berbasis Server Programming Interface (SPI) adalah kebocoran MemoryContext saat mengeksekusi sub-query atau ketika terjadi runtime error. Jika fungsi PL gagal memanggil SPI_finish() atau mengeksekusi pembersihan pada blok PG_TRY() / PG_CATCH(), memori backend akan terus membengkak hingga process terminated.
1. Input Test: sql/error_handling.sql
File ini memverifikasi bahwa runtime menangani syntax error dan runtime failure tanpa meninggalkan state SPI yang rusak:
CREATE EXTENSION my_runtime;
-- Validasi runtime error handling
DO $$
BEGIN
PERFORM my_eval('SYNTAX ERROR 10 GOTO');
EXCEPTION WHEN OTHERS THEN
RAISE NOTICE 'Caught expected syntax error: %', SQLERRM;
END;
$$ LANGUAGE plpgsql;
-- Validasi pembersihan SPI MemoryContext pasca error
SELECT my_eval('LET x = 10; RETURN x * 2;');
-- Deteksi alokasi memori yang tidak dilepas di SPI
SELECT pg_backend_memory_contexts.name, pg_backend_memory_contexts.total_bytes > 0 AS has_mem
FROM pg_backend_memory_contexts
WHERE name = 'SPI Exec'
LIMIT 1;
2. Expected Output: expected/error_handling.out
File ini mendokumentasikan output canonical yang harus dihasilkan oleh test harness:
CREATE EXTENSION my_runtime;
NOTICE: Caught expected syntax error: Parse error at line 1
DO
my_eval
---------
20
(1 row)
name | has_mem
------+---------
(0 rows)
Jika pembersihan konteks memori gagal dilakukan oleh C code runtime, baris SPI Exec akan muncul pada output query, menyebabkan perbedaan diff dan menggagalkan eksekusi make installcheck.
Uji Konkurensi dengan isolationtester
Pengujian regresi standar tidak mampu memvalidasi race condition, lock acquisition, atau deadlock antar worker process. PostgreSQL menyediakan tool isolationtester yang menggunakan file spesifikasi (.spec) untuk mengontrol urutan eksekusi step query antar-sesi secara presisi.
Spesifikasi Konkurensi: specs/concurrent_execution.spec
File spesifikasi berikut memvalidasi bahwa runtime kustom mampu mengisolasi eksekusi query concurrent tanpa menimbulkan crash atau unhandled row lock conflict:
setup
{
CREATE EXTENSION IF NOT EXISTS my_runtime;
CREATE TABLE runtime_state (id INT PRIMARY KEY, val TEXT);
INSERT INTO runtime_state VALUES (1, 'initial');
}
teardown
{
DROP TABLE runtime_state;
}
session s1
step s1_begin { BEGIN; }
step s1_mutate { SELECT my_eval('UPDATE runtime_state SET val = ''mutated_s1'' WHERE id = 1'); }
step s1_commit { COMMIT; }
session s2
step s2_begin { BEGIN; }
step s2_read_block { SELECT my_eval('SELECT val FROM runtime_state WHERE id = 1 FOR UPDATE'); }
step s2_commit { COMMIT; }
# Definisikan permutasi urutan eksekusi per step
permutation s1_begin s2_begin s1_mutate s2_read_block s1_commit s2_commit
Pada permutasi di atas, isolationtester akan memverifikasi bahwa s2_read_block secara otomatis menunggu lock dilepaskan oleh s1_commit tanpa menyebabkan kegagalan engine native.
Debugging dan Praktik Terbaik
- Tangani Output Non-Deterministik: Hindari mencetak timestamp, memory address pointer, atau format error library eksternal yang bervariasi antar-OS ke dalam file
.out. Gunakan explicit column casting dan filtering terstruktur. - Kombinasikan dengan Valgrind: Jalankan suite
pg_regressdi bawah Valgrind untuk mendeteksi memory leak terselubung:make installcheck USE_VALGRIND=1. - Cek File Hasil: Jika pengujian gagal, periksa file
regression.diffsuntuk melihat letak ketidaksesuaian output aktual dan referensi.
Komentar
0 komentar
Masuk ke akun kamu untuk ikut berkomentar.
Belum ada komentar
Jadilah yang pertama ikut berdiskusi!