Monitoring backup bisa hijau sempurna selama setahun — file lahir tiap malam pukul 02:00, ukurannya sehat, retensi berjalan — dan pada hari bencana yang sebenarnya, restore tetap saja gagal: extension belum terpasang di server pengganti, role aplikasi tidak ikut terbawa, atau dump ternyata rusak di tengah file. Selisih antara “punya backup” dan “bisa memulihkan” hanya bisa ditutup satu cara: menjalankan pemulihan sungguhan, di lingkungan terpisah, secara berkala, dan mencatat hasilnya. Latihan semacam ini disebut restore drill, dan prinsip kerjanya diwariskan dari dunia pemadam kebakaran: prosedur yang belum pernah dilatih akan gagal persis saat dibutuhkan. Chapter penutup section ini mengerjakan drill dua jenis pemulihan secara utuh — logis dengan pg_dump/pg_restore, fisik dengan pg_basebackup — lengkap dengan verifikasi checksum, pengukuran waktu, dan pola kegagalan yang paling sering terjadi.
Mengapa Drill Wajib #
Backup menguji jalur menulis; drill menguji jalur membaca. Tiga kelompok kegagalan hanya muncul di jalur kedua. Pertama, kegagalan format dan dependensi: pg_restore membutuhkan server tujuan yang sudah memuat extension dan versi mayor yang sama dengan dump. Kedua, kegagalan cakupan: pg_dump menyimpan satu database — role, tablespace, dan definisi extension hanya ikut lewat pg_dumpall; tanpa drill, kekosongan ini baru terlihat saat aplikasi menolak login di server hasil pulih. Ketiga, kegagalan senyap: media penyimpanan korup diam-diam, sehingga satu blok di tengah arsip sudah tidak terbaca. Ketiganya tidak menampilkan gejala apa pun di monitoring backup — hanya drill yang membukanya.
Kerangka Drill Rutin #
Drill yang bernilai punya lima sifat sekaligus:
| Sifat | Artinya |
|---|---|
| Berkala | terjadwal — kuartalan adalah titik awal yang lazim, bulanan untuk sistem kritis |
| Lingkungan segar | di mesin/container/data directory baru, bukan menimpa cluster produksi |
| Berwaktu | durasi diukur dan dibandingkan dengan RTO yang dijanjikan ke bisnis |
| Terverifikasi | kesamaan data dibuktikan dengan checksum, bukan dilihat sepintas |
| Tercatat | log drill diarsipkan sebagai bukti audit — kapan, oleh siapa, hasil apa |
Tanpa sifat kedua, drill berubah menjadi bencana tersendiri; sisanya tanpa sifat kelima hanya menghabiskan CPU tanpa menghasilkan bukti.
Drill 1: Restore Logis End-to-End #
Drill logis mengikuti hidup satu database dari lahir sampai mati: data dibuat, di-checksum, di-backup, dijatuhkan, dipulihkan, lalu di-checksum ulang. Kesamaan dua checksum adalah bukti matematis bahwa pemulihan tidak kehilangan satu byte pun:
#!/usr/bin/env bash
# Drill restore logis end-to-end: backup, jatuhkan, pulihkan, bandingkan.
set -euo pipefail
DB_LATIHAN=probe_drill_bk7
BERKAS=/tmp/pgtutor-drill/logis.dump
PENGECEKAN="SELECT md5(string_agg(id::text || ':' || item || ':' || jumlah::text, ',' ORDER BY id)) FROM penjualan;"
rm -rf /tmp/pgtutor-drill
mkdir -p /tmp/pgtutor-drill
# 1. Siapkan "database korban" dengan data tetap.
psql -X -q -d postgres -c "DROP DATABASE IF EXISTS $DB_LATIHAN;" \
-c "CREATE DATABASE $DB_LATIHAN;"
psql -X -q -d "$DB_LATIHAN" \
-c "CREATE TABLE penjualan (id int PRIMARY KEY, item text, jumlah int);" \
-c "INSERT INTO penjualan VALUES (1,'Kopi Torabika',12),(2,'Teh Melati',8),(3,'Gula Pasir',25);"
# 2. Checksum kondisi sebelum "bencana".
SEBELUM=$(psql -X -t -A -d "$DB_LATIHAN" -c "$PENGECEKAN")
echo "checksum sebelum : $SEBELUM"
# 3. Backup yang ingin kita uji (job harian versi chapter sebelumnya).
pg_dump -Fc -f "$BERKAS" "$DB_LATIHAN"
# 4. Bencana: database hilang total.
psql -X -q -d postgres -c "DROP DATABASE $DB_LATIHAN;" \
-c "CREATE DATABASE $DB_LATIHAN;"
# 5. Pemulihan dari backup.
pg_restore -d "$DB_LATIHAN" "$BERKAS"
# 6. Checksum kondisi sesudah pemulihan.
SESUDAH=$(psql -X -t -A -d "$DB_LATIHAN" -c "$PENGECEKAN")
echo "checksum sesudah: $SESUDAH"
# 7. Putusan drill.
if [ "$SEBELUM" = "$SESUDAH" ]; then
echo "HASIL DRILL: DATA IDENTIK"
else
echo "HASIL DRILL: DATA BERBEDA" >&2
exit 1
fi
# 8. Bersih-bersih.
psql -X -q -d postgres -c "DROP DATABASE $DB_LATIHAN;"
rm -rf /tmp/pgtutor-drillOutput yang Dihasilkan:
checksum sebelum : 86d94fdb45f4e5f7cf4a2b203c0db584
checksum sesudah: 86d94fdb45f4e5f7cf4a2b203c0db584
HASIL DRILL: DATA IDENTIKKita bedah bagian-bagian yang menentukan:
- Baris
PENGECEKAN=...: kunci verifikasi.md5(string_agg(... ORDER BY id))merangkai seluruh baris tabel dalam urutan tetap lalu meringkasnya menjadi satu sidik jari 32 karakter — satu perubahan data mana pun mengubah hasilnya. Di produksi, jalankan pola yang sama untuk tabel-tabel kritis Anda. - Baris
SEBELUM=$(psql -X -t -A ...): opsi-t -Amenghasilkan nilai polos tanpa bingkai tabel, sehingga sidik jari bisa disimpan di variabel dan dibandingkan dengan[ "$SEBELUM" = "$SESUDAH" ]. - Baris
pg_dump -Fclalupg_restore -d: pasangan backup-pulih yang diuji adalah persis job harian dari chapter Backup Scheduling — drill harus menguji alat yang benar-benar dipakai, bukan alat versi khusus latihan. - Baris
exit 1pada cabang gagal: drill yang menemukan perbedaan data harus gagal keras, agar tercatat sebagai insiden — bukan berlalu sebagai catatan kaki.
Drill 2: Restore Fisik sebagai Instance Baru #
Drill fisik menguji jalur pemulihan tingkat cluster: base backup -Xs dari chapter Base Backup
dinyalakan sebagai server kedua di port 54330, lengkap dengan pengecekan bahwa WAL yang menyertainya benar-benar bisa direplay:
#!/usr/bin/env bash
# Drill restore fisik: base backup dinyalakan sebagai instance terpisah.
set -euo pipefail
DB_LATIHAN=probe_drill_bk7
TUJUAN=/tmp/pgtutor-drill/fisik
PORT_BARU=54330
# pg_ctl hanya ada di direktori biner server (bukan paket klien) di lab ini.
PGBIN=/home/z/my-project/tools/dl/pgdeb/usr/lib/postgresql/17/bin
PENGECEKAN="SELECT md5(string_agg(id::text || ':' || item || ':' || jumlah::text, ',' ORDER BY id)) FROM penjualan;"
bersihkan() {
"$PGBIN/pg_ctl" -D "$TUJUAN" stop >/dev/null 2>&1 || true
psql -X -q -d postgres -c "DROP DATABASE IF EXISTS $DB_LATIHAN;" >/dev/null 2>&1 || true
rm -rf /tmp/pgtutor-drill
}
trap bersihkan EXIT
rm -rf /tmp/pgtutor-drill
mkdir -p /tmp/pgtutor-drill
# Port drill harus benar-benar bebas.
if pg_isready -q -h /tmp -p "$PORT_BARU"; then
echo "port $PORT_BARU sedang dipakai, drill dibatalkan" >&2
exit 1
fi
# 1. Siapkan "database korban" di server utama.
psql -X -q -d postgres -c "DROP DATABASE IF EXISTS $DB_LATIHAN;" \
-c "CREATE DATABASE $DB_LATIHAN;"
psql -X -q -d "$DB_LATIHAN" \
-c "CREATE TABLE penjualan (id int PRIMARY KEY, item text, jumlah int);" \
-c "INSERT INTO penjualan VALUES (1,'Kopi Torabika',12),(2,'Teh Melati',8),(3,'Gula Pasir',25);"
SEBELUM=$(psql -X -t -A -d "$DB_LATIHAN" -c "$PENGECEKAN")
echo "checksum di server utama : $SEBELUM"
# 2. Base backup mandiri (-Xs): seluruh cluster termasuk database korban.
pg_basebackup -D "$TUJUAN" -Fp -Xs --checkpoint=fast --no-password
# 3. Nyalakan salinan sebagai instance baru di port 54330.
# Keluaran pg_ctl penuh titik-titik tunggu, dibuang supaya deterministik.
"$PGBIN/pg_ctl" -D "$TUJUAN" -l /tmp/pgtutor-drill/fisik.log \
-o "-p $PORT_BARU -k /tmp -c listen_addresses=''" start >/dev/null 2>&1
for _ in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20; do
if pg_isready -q -h /tmp -p "$PORT_BARU"; then break; fi
sleep 1
done
pg_isready -h /tmp -p "$PORT_BARU"
# 4. Verifikasi data: koneksi ke instance baru, database yang sama.
SESUDAH=$(psql -X -t -A -h /tmp -p "$PORT_BARU" -d "$DB_LATIHAN" -c "$PENGECEKAN")
echo "checksum di instance restore : $SESUDAH"
# 5. Verifikasi status: replay WAL selesai, server jadi primary baru.
psql -X -t -A -h /tmp -p "$PORT_BARU" -d postgres -c "SELECT pg_is_in_recovery();"
psql -X -t -A -h /tmp -p "$PORT_BARU" -d postgres -c "SELECT timeline_id FROM pg_control_checkpoint();"
if [ "$SEBELUM" = "$SESUDAH" ]; then
echo "HASIL DRILL: DATA IDENTIK"
else
echo "HASIL DRILL: DATA BERBEDA" >&2
exit 1
fi
# 6. Matikan instance drill; trap bersihkan() menuntaskan sisanya.
"$PGBIN/pg_ctl" -D "$TUJUAN" stop >/dev/null 2>&1Output yang Dihasilkan:
checksum di server utama : 86d94fdb45f4e5f7cf4a2b203c0db584
/tmp:54330 - accepting connections
checksum di instance restore : 86d94fdb45f4e5f7cf4a2b203c0db584
f
1
HASIL DRILL: DATA IDENTIKBeberapa keputusan teknis di skrip itu layak dicermati:
- Baris
trap bersihkan EXIT: sejak dini, skrip memasang pembersih otomatis — instance drill dimatikan dan artefak dihapus baik saat drill sukses maupun saatset -emenghentikan skrip di tengah jalan. Tanpa trap, satu drill yang gagal bisa meninggalkan server kedua menyala diam-diam. - Baris
pg_isready -qsebagai penjaga port: bila 54330 sudah dijawab server lain, drill membatalkan dirinya — salah sasaran verifikasi lebih buruk daripada batal berlatih. - Bagian
for _ in 1 2 ... 20: start instance tidak seketika; loop menunggu hingga 20 detik sebelum verifikasi dilanjutkan, danpg_isreadytanpa-qkemudian mencetak status koneksi sebagai baris output drill. - Dua query status terakhir:
pg_is_in_recovery()menjawabf— replay WAL hasil-Xssudah tuntas dan instance bekerja sebagai primary — sementaratimeline_idmasih1karena pemulihan ini tidak melaluirecovery.signal(bedakan dari percabangan timeline di chapter Point-in-Time Recovery ).
Di balik layar, log instance drill memuat cerita pemulihan yang utuh — potongan nyata dari log latihan kami:
2026-10-14 16:36:06.057 UTC [14437] LOG: starting backup recovery with redo LSN 2/9E000028, checkpoint LSN 2/9E000080, on timeline ID 1
2026-10-14 16:36:06.058 UTC [14437] LOG: redo starts at 2/9E000028
2026-10-14 16:36:06.058 UTC [14437] LOG: completed backup recovery with redo LSN 2/9E000028 and end LSN 2/9E000120
2026-10-14 16:36:06.058 UTC [14437] LOG: consistent recovery state reached at 2/9E000120Empat baris itu adalah WAL di tempat kerjanya: server membaca backup_label, mulai replay dari redo LSN, menyelesaikannya di LSN akhir backup, lalu menyatakan keadaan konsisten — persis mekanisme yang dibahas di chapter Write-Ahead Logging
. Stempel waktu, PID, dan alamat LSN Anda pasti berbeda; urutan kejadiannyalah yang harus selalu sama.
Mengukur Waktu: Melawan RTO #
RTO yang dijanjikan ke bisnis (“database kembali online dalam 2 jam”) hanya bermakna bila pernah diukur. Skrip berikut memasang time pada komponen-komponen terberat drill. Angka waktunya bervariasi antar mesin dan antar eksekusi — di lab kami, restore logis basis data tiga baris memang seceks kilat, sedangkan base backup ikut menyalin seluruh cluster:
#!/usr/bin/env bash
# Mengukur durasi komponen drill -- angka waktu bervariasi antar mesin
# dan antar eksekusi; yang stabil hanya orde besarannya.
set -euo pipefail
DB_LATIHAN=probe_drill_waktu
BERKAS=/tmp/pgtutor-drill/waktu.dump
PGBIN=/home/z/my-project/tools/dl/pgdeb/usr/lib/postgresql/17/bin
bersihkan() {
"$PGBIN/pg_ctl" -D /tmp/pgtutor-drill/fisik-waktu stop >/dev/null 2>&1 || true
psql -X -q -d postgres -c "DROP DATABASE IF EXISTS $DB_LATIHAN;" >/dev/null 2>&1 || true
rm -rf /tmp/pgtutor-drill
}
trap bersihkan EXIT
rm -rf /tmp/pgtutor-drill
mkdir -p /tmp/pgtutor-drill
# Siapkan database korban (sama dengan drill logis).
psql -X -q -d postgres -c "DROP DATABASE IF EXISTS $DB_LATIHAN;" \
-c "CREATE DATABASE $DB_LATIHAN;"
psql -X -q -d "$DB_LATIHAN" \
-c "CREATE TABLE penjualan (id int PRIMARY KEY, item text, jumlah int);" \
-c "INSERT INTO penjualan VALUES (1,'Kopi Torabika',12),(2,'Teh Melati',8),(3,'Gula Pasir',25);"
pg_dump -Fc -f "$BERKAS" "$DB_LATIHAN"
echo "== restore logis (drop + create + pg_restore):"
psql -X -q -d postgres -c "DROP DATABASE $DB_LATIHAN;" -c "CREATE DATABASE $DB_LATIHAN;"
time pg_restore -d "$DB_LATIHAN" "$BERKAS"
echo "== base backup + start instance baru:"
time pg_basebackup -D /tmp/pgtutor-drill/fisik-waktu -Fp -Xs --checkpoint=fast --no-password
time "$PGBIN/pg_ctl" -D /tmp/pgtutor-drill/fisik-waktu \
-l /tmp/pgtutor-drill/waktu.log \
-o "-p 54330 -k /tmp -c listen_addresses=''" start >/dev/null 2>&1
"$PGBIN/pg_ctl" -D /tmp/pgtutor-drill/fisik-waktu stop >/dev/null 2>&1Output yang Dihasilkan:
NOTICE: database "probe_drill_waktu" does not exist, skipping
== restore logis (drop + create + pg_restore):
real 0m0.010s
user 0m0.000s
sys 0m0.003s
== base backup + start instance baru:
real 0m0.612s
user 0m0.011s
sys 0m0.575s
real 0m0.108s
user 0m0.004s
sys 0m0.004sTiga angka real di lab kami — 0,010 detik untuk pg_restore, 0,612 detik untuk pg_basebackup, 0,108 detik untuk start instance — adalah hasil dari data berukuran mainan; database produksi ber-gigabyte akan menggesernya ke menit atau jam. Nilai latihan ini pada pola: ukur tiap komponen, jumlahkan, lalu bandingkan dengan RTO — dan simpan trennya per drill, karena durasi yang merambat naik antar kuartal adalah sinyal database Anda tumbuh lebih cepat daripada kapasitas pemulihan Anda.
Daftar Periksa Verifikasi #
Data identik hanyalah satu dari sekian jawaban yang harus dicatat sebelum drill dinyatakan lulus:
| Pemeriksaan | Cara | Bukti |
|---|---|---|
| Jumlah baris tabel kritis | SELECT count(*) sebelum vs sesudah | angka sama |
| Sidik jari data | md5(string_agg(...)) sebelum vs sesudah | hash sama |
| Role login aplikasi | \du atau pg_roles di server pulih | daftar lengkap |
| Extension terpasang | \dx atau pg_available_extensions | versi sama |
| Koneksi aplikasi nyata | string koneksi aplikasi diarahkan ke instance drill | login sukses |
| Durasi total | komponen drill dijumlahkan | masih di bawah RTO |
Dua baris tengah sering jadi kejutan pertama: pg_dump tidak memuat role, dan pg_restore gagal begitu menemui definisi extension yang belum terpasang di server tujuan. Temuan semacam itu di drill adalah kemenangan; temuan yang sama saat bencana adalah bencana kedua.
Mendokumentasikan Runbook #
Setiap drill yang lulus harus mengendap menjadi dokumen yang bisa dijalankan orang lain — runbook. Bentuk minimalnya: daftar langkah bernomor dari “server mati” sampai “aplikasi tersambung kembali”, nama file dan lokasi backup yang dipakai, perintah verifikasi beserta nilai checksum yang diharapkan, keputusan-keputusan yang butuh manusia (kapan boleh promote, siapa yang berhak), dan catatan durasi tiap drill. Simpan runbook bersama kode operasional lain di repositori — ia ikut di-review, dan setiap perubahan topologi (server baru, port baru, lokasi arsip baru) otomatis memicu review runbook. Runbook yang tidak pernah dipakai drill adalah fiksi; runbook yang tiap kuartal dijalankan ulang adalah asuransi yang benar-benar dibayar.
Pola Kegagalan yang Sering Muncul #
Tiga kisah klasik menutup section ini karena semuanya pernah menjatuhkan tim yang merasa sudah punya backup. Pertama, backup kosong: job berjalan “sukses” selama berbulan-bulan, tetapi variabel nama database di skrip cron salah ketik, sehingga pg_dump mencadangkan database kosong — pola alarm -size 0 di chapter Backup Scheduling
lahir dari kisah ini. Kedua, restore berhenti di extension: dump memuat CREATE EXTENSION, sementara paket binary extension tidak terpasang di server pengganti; solusinya adalah memasang paket yang sama sebelum restore, dan drill-lah satu-satunya cara tahu daftar paket itu. Ketiga, role tidak ikut: pg_restore menyelesaikan data dengan sempurna, aplikasi tetap menolak login karena pg_dump memang tidak menyimpan role global — kunci yang hilang ada di pg_dumpall --globals-only, yang semestinya ikut dijadwalkan. Semua pola ini punya satu obat yang sama: drill rutin, terdokumentasi, dan berwaktu.
Kemampuan memulihkan satu server adalah fondasi; tetapi server yang sama juga bisa jadi titik kegagalan tunggal bagi seluruh aplikasi. Section berikutnya menyusun lapisan berikutnya di atas backup: Replication dan High Availability — menyalin data secara berkelanjutan ke server kedua agar bencana di satu mesin tidak berarti bencana bagi ketersediaan layanan.
Referensi #
Berkas Sumber
Berkas pendukung halaman ini dapat diunduh dan dijalankan langsung sesuai penjelasan di atas.
drill-fisik.sh
Unduh berkas#!/usr/bin/env bash
# Drill restore fisik: base backup dinyalakan sebagai instance terpisah.
set -euo pipefail
DB_LATIHAN=probe_drill_bk7
TUJUAN=/tmp/pgtutor-drill/fisik
PORT_BARU=54330
# pg_ctl hanya ada di direktori biner server (bukan paket klien) di lab ini.
PGBIN=/home/z/my-project/tools/dl/pgdeb/usr/lib/postgresql/17/bin
PENGECEKAN="SELECT md5(string_agg(id::text || ':' || item || ':' || jumlah::text, ',' ORDER BY id)) FROM penjualan;"
bersihkan() {
"$PGBIN/pg_ctl" -D "$TUJUAN" stop >/dev/null 2>&1 || true
psql -X -q -d postgres -c "DROP DATABASE IF EXISTS $DB_LATIHAN;" >/dev/null 2>&1 || true
rm -rf /tmp/pgtutor-drill
}
trap bersihkan EXIT
rm -rf /tmp/pgtutor-drill
mkdir -p /tmp/pgtutor-drill
# Port drill harus benar-benar bebas.
if pg_isready -q -h /tmp -p "$PORT_BARU"; then
echo "port $PORT_BARU sedang dipakai, drill dibatalkan" >&2
exit 1
fi
# 1. Siapkan "database korban" di server utama.
psql -X -q -d postgres -c "DROP DATABASE IF EXISTS $DB_LATIHAN;" \
-c "CREATE DATABASE $DB_LATIHAN;"
psql -X -q -d "$DB_LATIHAN" \
-c "CREATE TABLE penjualan (id int PRIMARY KEY, item text, jumlah int);" \
-c "INSERT INTO penjualan VALUES (1,'Kopi Torabika',12),(2,'Teh Melati',8),(3,'Gula Pasir',25);"
SEBELUM=$(psql -X -t -A -d "$DB_LATIHAN" -c "$PENGECEKAN")
echo "checksum di server utama : $SEBELUM"
# 2. Base backup mandiri (-Xs): seluruh cluster termasuk database korban.
pg_basebackup -D "$TUJUAN" -Fp -Xs --checkpoint=fast --no-password
# 3. Nyalakan salinan sebagai instance baru di port 54330.
# Keluaran pg_ctl penuh titik-titik tunggu, dibuang supaya deterministik.
"$PGBIN/pg_ctl" -D "$TUJUAN" -l /tmp/pgtutor-drill/fisik.log \
-o "-p $PORT_BARU -k /tmp -c listen_addresses=''" start >/dev/null 2>&1
for _ in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20; do
if pg_isready -q -h /tmp -p "$PORT_BARU"; then break; fi
sleep 1
done
pg_isready -h /tmp -p "$PORT_BARU"
# 4. Verifikasi data: koneksi ke instance baru, database yang sama.
SESUDAH=$(psql -X -t -A -h /tmp -p "$PORT_BARU" -d "$DB_LATIHAN" -c "$PENGECEKAN")
echo "checksum di instance restore : $SESUDAH"
# 5. Verifikasi status: replay WAL selesai, server jadi primary baru.
psql -X -t -A -h /tmp -p "$PORT_BARU" -d postgres -c "SELECT pg_is_in_recovery();"
psql -X -t -A -h /tmp -p "$PORT_BARU" -d postgres -c "SELECT timeline_id FROM pg_control_checkpoint();"
if [ "$SEBELUM" = "$SESUDAH" ]; then
echo "HASIL DRILL: DATA IDENTIK"
else
echo "HASIL DRILL: DATA BERBEDA" >&2
exit 1
fi
# 6. Matikan instance drill; trap bersihkan() menuntaskan sisanya.
"$PGBIN/pg_ctl" -D "$TUJUAN" stop >/dev/null 2>&1
drill-logis.sh
Unduh berkas#!/usr/bin/env bash
# Drill restore logis end-to-end: backup, jatuhkan, pulihkan, bandingkan.
set -euo pipefail
DB_LATIHAN=probe_drill_bk7
BERKAS=/tmp/pgtutor-drill/logis.dump
PENGECEKAN="SELECT md5(string_agg(id::text || ':' || item || ':' || jumlah::text, ',' ORDER BY id)) FROM penjualan;"
rm -rf /tmp/pgtutor-drill
mkdir -p /tmp/pgtutor-drill
# 1. Siapkan "database korban" dengan data tetap.
psql -X -q -d postgres -c "DROP DATABASE IF EXISTS $DB_LATIHAN;" \
-c "CREATE DATABASE $DB_LATIHAN;"
psql -X -q -d "$DB_LATIHAN" \
-c "CREATE TABLE penjualan (id int PRIMARY KEY, item text, jumlah int);" \
-c "INSERT INTO penjualan VALUES (1,'Kopi Torabika',12),(2,'Teh Melati',8),(3,'Gula Pasir',25);"
# 2. Checksum kondisi sebelum "bencana".
SEBELUM=$(psql -X -t -A -d "$DB_LATIHAN" -c "$PENGECEKAN")
echo "checksum sebelum : $SEBELUM"
# 3. Backup yang ingin kita uji (job harian versi chapter sebelumnya).
pg_dump -Fc -f "$BERKAS" "$DB_LATIHAN"
# 4. Bencana: database hilang total.
psql -X -q -d postgres -c "DROP DATABASE $DB_LATIHAN;" \
-c "CREATE DATABASE $DB_LATIHAN;"
# 5. Pemulihan dari backup.
pg_restore -d "$DB_LATIHAN" "$BERKAS"
# 6. Checksum kondisi sesudah pemulihan.
SESUDAH=$(psql -X -t -A -d "$DB_LATIHAN" -c "$PENGECEKAN")
echo "checksum sesudah: $SESUDAH"
# 7. Putusan drill.
if [ "$SEBELUM" = "$SESUDAH" ]; then
echo "HASIL DRILL: DATA IDENTIK"
else
echo "HASIL DRILL: DATA BERBEDA" >&2
exit 1
fi
# 8. Bersih-bersih.
psql -X -q -d postgres -c "DROP DATABASE $DB_LATIHAN;"
rm -rf /tmp/pgtutor-drill
waktu-drill.sh
Unduh berkas#!/usr/bin/env bash
# Mengukur durasi komponen drill -- angka waktu bervariasi antar mesin
# dan antar eksekusi; yang stabil hanya orde besarannya.
set -euo pipefail
DB_LATIHAN=probe_drill_waktu
BERKAS=/tmp/pgtutor-drill/waktu.dump
PGBIN=/home/z/my-project/tools/dl/pgdeb/usr/lib/postgresql/17/bin
bersihkan() {
"$PGBIN/pg_ctl" -D /tmp/pgtutor-drill/fisik-waktu stop >/dev/null 2>&1 || true
psql -X -q -d postgres -c "DROP DATABASE IF EXISTS $DB_LATIHAN;" >/dev/null 2>&1 || true
rm -rf /tmp/pgtutor-drill
}
trap bersihkan EXIT
rm -rf /tmp/pgtutor-drill
mkdir -p /tmp/pgtutor-drill
# Siapkan database korban (sama dengan drill logis).
psql -X -q -d postgres -c "DROP DATABASE IF EXISTS $DB_LATIHAN;" \
-c "CREATE DATABASE $DB_LATIHAN;"
psql -X -q -d "$DB_LATIHAN" \
-c "CREATE TABLE penjualan (id int PRIMARY KEY, item text, jumlah int);" \
-c "INSERT INTO penjualan VALUES (1,'Kopi Torabika',12),(2,'Teh Melati',8),(3,'Gula Pasir',25);"
pg_dump -Fc -f "$BERKAS" "$DB_LATIHAN"
echo "== restore logis (drop + create + pg_restore):"
psql -X -q -d postgres -c "DROP DATABASE $DB_LATIHAN;" -c "CREATE DATABASE $DB_LATIHAN;"
time pg_restore -d "$DB_LATIHAN" "$BERKAS"
echo "== base backup + start instance baru:"
time pg_basebackup -D /tmp/pgtutor-drill/fisik-waktu -Fp -Xs --checkpoint=fast --no-password
time "$PGBIN/pg_ctl" -D /tmp/pgtutor-drill/fisik-waktu \
-l /tmp/pgtutor-drill/waktu.log \
-o "-p 54330 -k /tmp -c listen_addresses=''" start >/dev/null 2>&1
"$PGBIN/pg_ctl" -D /tmp/pgtutor-drill/fisik-waktu stop >/dev/null 2>&1