- Dalam pengujian Jepsen, ditemukan kasus bahwa klaster Amazon RDS for PostgreSQL Multi-AZ tidak mempertahankan Snapshot Isolation, tingkat isolasi terkuat yang berlaku di seluruh node
- Penyebab utamanya adalah urutan visibilitas transaksi di primary ditentukan oleh lock di dalam memori, sementara secondary mengikuti urutan WAL, sehingga kedua urutan tersebut bisa tidak selaras
- Bahkan tanpa injeksi fault atau failover, pada kondisi menggunakan storage
gp3dan instancedb.m6id.large, G-nonadjacent cycle muncul setiap beberapa menit pada sekitar 150 write TPS / 1600 read-only TPS - Anomali ini termasuk Long Fork, muncul di semua versi yang diuji dari PostgreSQL 13.15 hingga 17.4 yang didukung AWS, dan Short Fork/Write Skew tidak teramati
- Untuk transaksi yang penting bagi keselamatan, penggunaan read-only secondary dapat membuat urutan eksekusi terlihat berbeda, sehingga perlu mempertimbangkan penggunaan writer endpoint saja atau menyertakan minimal 1 write
Pembaruan penyebab Long Fork
- Sergey Melnik dari AWS serta matashii dan Ants Aasma, peserta komentar HN, mengidentifikasi penyebab Long Fork pada klaster PostgreSQL
- Primary PostgreSQL menentukan urutan agar transaksi terlihat melalui lock di dalam memori
- Secondary membuat transaksi terlihat sesuai urutan di dalam Write-Ahead Log(WAL)
- Jika urutan lock dan urutan WAL berbeda, primary dan secondary dapat melihat urutan transaksi yang tampak secara berbeda
- Perilaku ini pernah dibahas dalam tulisan mailing list PostgreSQL tahun 2013, dan Melnik menulis artikel di blog AWS yang menjelaskan transaction visibility pada klaster PostgreSQL dan read replica
- Jepsen menyarankan AWS dan PostgreSQL mendokumentasikan isu ini bersamaan dengan pekerjaan perbaikannya
Tingkat isolasi dan arsitektur RDS for PostgreSQL
- PostgreSQL adalah database SQL open-source serbaguna, dan melalui MVCC menyediakan tiga tingkat isolasi transaksi
Read UncommitteddanRead Committedsama-sama berperilaku sebagai Read CommittedRepeatable Readsebenarnya bukan Repeatable Read, melainkan menyediakan Snapshot IsolationSerializablemenyediakan Serializability
- Amazon RDS for PostgreSQL adalah layanan AWS yang menyediakan klaster PostgreSQL terkelola
- Mengotomatiskan provisioning, manajemen storage, replikasi, backup, upgrade, dan lainnya
- Multi-AZ deployments mendistribusikan node database ke beberapa Availability Zone untuk mengurangi kemungkinan fault yang berkorelasi
- RDS menggunakan replikasi sinkron agar respons diberikan setelah durabilitas transaksi dipastikan di primary dan setidaknya 1 instance secondary
- Pengguna diberi dua URL yang berbicara dengan PostgreSQL wire protocol
- primary endpoint: untuk transaksi read-write
- reader endpoint: untuk transaksi read-only
- Primary endpoint mendukung semua tingkat isolasi PostgreSQL, tetapi secondary tidak mendukung Serializable
- Tingkat isolasi terkuat yang dapat digunakan di seluruh node adalah Snapshot Isolation, yang oleh PostgreSQL disebut
Repeatable Read
Desain pengujian
- Jepsen menyesuaikan library pengujian untuk PostgreSQL agar sesuai dengan Amazon RDS for PostgreSQL, dan menggunakan program wrapper kecil
- Pada setiap putaran pengujian, klaster RDS diprovisioning melalui API CreateDBCluster milik AWS
- Storage:
gp3 - Instance:
db.m6id.large
- Storage:
- Satu node EC2 untuk menjalankan pengujian dijalankan, lalu main endpoint dan read-only endpoint klaster RDS disediakan
- Tidak ada injeksi fault dan failover juga tidak dipicu
- Workload utama terdiri dari transaksi yang menangani daftar bilangan bulat unik
- Setiap daftar disimpan dalam satu row, dan di-encode sebagai field
TEXTberisi nilai yang dipisahkan koma - Transaksi membaca daftar berdasarkan primary key atau menambahkan bilangan bulat unik ke daftar dengan
CONCAT
- Setiap daftar disimpan dalam satu row, dan di-encode sebagai field
- Melalui workload ini, Elle checker dapat menyimpulkan dependensi aliran data antartransaksi dan menemukan cycle pada graf untuk memverifikasi berbagai tingkat isolasi
Pengamatan G-nonadjacent cycle
- Bahkan pada kondisi normal dan concurrency tingkat menengah, Amazon RDS for PostgreSQL 17.4 menunjukkan G-nonadjacent cycle setiap beberapa menit
- Satu eksekusi pengujian 2 menit menjalankan sekitar 150 write TPS dan 1600 read-only TPS, serta memuat cycle yang terdiri dari 4 transaksi
- Contoh cycle terdiri dari empat transaksi
T1,T2,T3,T4T1menambahkan9ke row 89 sehingga membuat daftar[4 9], danT2mengamatinyaT3menambahkan11ke row 90 sehingga membuat daftar[11]T4menambahkan3ke row 90 dan membaca daftar hasil[11, 3], sehingga menimpa version dariT3T2mengamati append dariT1pada row 89, tetapi tidak melihat append dariT3pada row 90- Sebaliknya,
T4mengamati append dariT3pada row 90, tetapi melewatkan append dariT1pada row 89
- Karena cycle ini mencakup read-write dependency yang tidak saling bersebelahan, ini adalah G-nonadjacent cycle yang melanggar Snapshot Isolation
- Pada
Repeatable ReadPostgreSQL standar, perilaku seperti ini seharusnya tidak terjadi, dan Jepsen tidak mengamatinya pada PostgreSQL standar
Mengapa bertentangan dengan Snapshot Isolation
- Dalam Snapshot Isolation, semua transaksi harus terlihat seolah-olah berjalan di atas snapshot database pada timestamp awal
s - Efek transaksi kemudian terlihat oleh transaksi lain pada commit timestamp
cberikutnya - Jika hasil pengamatan dalam contoh cycle ditulis sebagai relasi timestamp, relasi tersebut saling bertentangan
- Karena
T2membaca append dariT1, awalT2harus setelah commitT1:c1 < s2 - Karena
T2tidak mengamati append dariT3, makas2 < c3 - Karena
T4menimpa dan mengamatiT3, makac3 < s4 - Karena
T4tidak mengamati append dariT1, makas4 < c1
- Karena
- Semua relasi ini tidak dapat berlaku sekaligus, sehingga bertentangan dengan model timestamp Snapshot Isolation
Long Fork dan hasil per versi
- Cycle tersebut juga merupakan contoh Long Fork
- Transaksi pertama dan kedua membentuk satu fork status logis
- Transaksi ketiga dan keempat membentuk fork kedua
- Kedua fork memperbarui row yang berbeda tetapi tidak mengamati efek satu sama lain
- Short Fork, yakni Write Skew, tidak teramati
- Hasil ini mengindikasikan kemungkinan bahwa Amazon RDS for PostgreSQL menyediakan Parallel Snapshot Isolation, yang sedikit lebih lemah daripada Snapshot Isolation
- Anomali G-nonadjacent muncul dalam berbagai bentuk, mulai dari kasus yang hanya terhubung oleh write-read edge hingga kasus yang mencakup lebih dari 4 transaksi
- Anomali jenis yang sama terjadi pada semua versi yang diuji, dari PostgreSQL 13.15, versi tertua yang didukung AWS, hingga versi terbaru 17.4
Hal yang perlu diperiksa pengguna
- Karena Long Fork dan G-nonadjacent cycle lain ada, klaster Amazon RDS for PostgreSQL Multi-AZ tidak menjamin Snapshot Isolation
- Dalam hal ini, klaster RDS for PostgreSQL Multi-AZ menyediakan semantik keselamatan yang lebih lemah daripada PostgreSQL single-node, yang pada pengujian Jepsen sebelumnya tampak menyediakan Strong Snapshot Isolation
- Pengguna dapat meninjau apakah struktur transaksinya rentan terhadap Long Fork, atau memverifikasi lewat eksperimen apakah invariant yang dimaksud tetap terjaga
- Transaksi read dapat melihat hasil yang berbeda dari transaksi lain terkait urutan eksekusi transaksi
- Anomali tampaknya terkait dengan query ke read-only secondary, sehingga Snapshot Isolation kemungkinan dapat dipulihkan dengan cara berikut
-
Gunakan hanya writer endpoint
- Sertakan minimal 1 write pada semua transaksi yang penting bagi keselamatan
- Verifikasi Jepsen adalah pendekatan eksperimental; dapat membuktikan keberadaan bug, tetapi tidak dapat membuktikan ketiadaannya
- Laporan ini bukan hasil investigasi mendetail atas perilaku RDS for PostgreSQL, melainkan produk dari eksplorasi awal
-
1 komentar
Pendapat Hacker News
Saya berharap tulisan di dunia perangkat lunak lebih sering seperti ini: “Amazon RDS for PostgreSQL adalah layanan Amazon Web Services (AWS) yang menyediakan instans terkelola untuk basis data PostgreSQL. Kami menunjukkan bahwa klaster multi-AZ Amazon RDS for PostgreSQL melanggar snapshot isolation, model konsistensi terkuat yang didukung di semua endpoint…”
Langsung, tepat ke inti, tanpa hiasan, sehingga mirip dengan cara berbagi hasil penelitian di bidang STEM lain. Dulu saya suka tulisan blog jenaka yang menjelaskan sesuatu dengan meme, tetapi sekarang saya merindukan tulisan yang plain dan sederhana
Kalau saya menulis tulisan teknis yang sangat mendalam, hampir tidak ada like atau komentar, sampai seorang Staff Engineer berkata, “sebaiknya target audiensnya dibuat lebih sempit.” Sebaliknya, ketika menguji Kubecost awal dan menulis bahwa rekomendasinya hanya menghemat sedikit biaya serta bisa menimbulkan masalah performa container, meski itu tulisan yang cukup teknis tentang CPU throttling dan cgroups, orang-orang sangat menyukainya begitu saya memasukkan meme
Belakangan saya membuat library eksternal Python kecil dengan C, mengaksesnya lewat ctypes, lalu menulis tulisan yang lebih kering tentang perbandingan alokasi stack/heap; ketika saya memasukkan meme, hasilnya mirip. Saya tidak suka tren seperti ini, tetapi kalau ingin menjangkau pembaca yang lebih luas, saya juga tidak tahu cara menghindarinya. Jensen tidak menargetkan pembaca seperti itu, dan tulisan yang ketat serta murni memang layak diapresiasi
Tidak ada di judul dan tidak terlalu jelas di tulisannya, tetapi masalah ini terbatas pada klaster multi-AZ, fitur RDS yang relatif baru. Ini berbeda dari instans multi-AZ yang sudah akrab bagi banyak orang
Instans multi-AZ adalah fitur lama: DB utama direplikasi secara sinkron ke DB sekunder di availability zone lain, dan jika yang utama gagal, RDS melakukan failover ke sekunder
Klaster multi-AZ memiliki dua sekunder, dan transaksi direplikasi secara sinkron ke setidaknya salah satunya. Saat salah satu sekunder gagal atau performanya menurun, ini lebih tangguh daripada instans multi-AZ, dan juga memungkinkan akses read-only ke sekunder
Namun klaster multi-AZ kemungkinan memiliki lebih banyak sihir tambahan di dalamnya yang bukan fitur dasar PostgreSQL, dan mungkin karena itulah ia gagal dalam pengujian Jepsen
Namun PostgreSQL masih memiliki kekurangan yang memungkinkan masalah mirip pola ini. Transaksi yang belum direplikasi dan kliennya menghilang di tengah commit akan langsung terlihat. Dalam contoh, jika T1 terjadi pada leader yang terpisah lalu koneksi terputus saat commit, T2 juga terjadi pada node yang terpisah, dan T3/T4 kemudian terjadi pada leader baru, hasil yang sama bisa terlihat. Tetapi ini tidak terlalu cocok dengan penjelasan bahwa pengujian ini tidak melakukan fault injection
Sunting: Saya tidak melihat tulisan yang menjelaskan bahwa pola ini disebabkan oleh ketidaksesuaian urutan commit antara replika dan node utama. Agak memalukan, karena saya pernah mempresentasikan cara memperbaiki masalah ini
Investigasi yang bagus. Belakangan ini banyak pengembang perangkat lunak yang bahkan tidak begitu memahami transaksi itu sendiri, apalagi berbagai model transaksi. Saya bahkan pernah melihat developer CRUD yang disebut “senior developer” sama sekali tidak memahami transaksi database.
Dalam praktiknya, ketika skala trafik sudah ada dan perangkat lunak menyelesaikan masalah yang tidak sepele, transaksi dan model transaksi sangat penting untuk performa dan kode yang bebas bug.
Misalnya, dalam sebuah proyek besar, setelah banyak analisis, kami mengubah default SQL Server dari Read Committed ke Read Committed Snapshot Isolation, dan kontensi lock berkurang drastis sehingga pengguna sangat puas. Para software engineer di proyek itu banyak memakai transaksi, tetapi sebelum diajari dasar-dasarnya, mereka sama sekali tidak memahami model transaksi atau lock.
Karena saya terutama bekerja di bidang ritel, saya sering melihat sistem yang penuh error mirip race condition, padahal tingkat isolasi seperti ini bisa sangat membantu, jadi makin disayangkan.
Namun kasus seperti ini terutama saya lihat pada engineer startup, sementara developer Oracle/MSSQL tipikal di perusahaan besar setidaknya punya dasar yang benar, jadi saya menilai mereka cukup tinggi.
Beberapa kali dalam karier saya melihat pendekatan ini menghasilkan akibat yang benar-benar buruk.
Namun hal yang perlu diperhatikan saat transisi ini adalah semua kode yang bergantung pada pembacaan yang memblokir akan rusak. Misalnya kode seperti
select with existsharus ditulis ulang dengan lock eksplisit atau cara lain.Di perusahaan lama, ketika kami mengubah perintah
pg_dumpdalam skrip backup agar mulai memakai worker paralel (flag-j), saat restore kami kadang melihat error yang mengisyaratkan inkonsistensi, seperti error duplikasi key dan error constraint foreign key.Saat itu saya mencoba melaporkannya ke AWS dan mailing list PostgreSQL, tetapi karena sulit direproduksi, tidak ada kemajuan, dan akhirnya kami menyerah lalu kembali ke dump single-thread. Saya penasaran apakah fenomena yang saya lihat waktu itu terkait dengan masalah ini.
Setelah membaca tulisan ini, dampak sebenarnya tampaknya adalah data lama bisa dikembalikan ketika pembacaan cepat terjadi segera setelah penulisan pada baris yang sama. Transaksi tulis ditandai selesai, tetapi sebelum seluruh lapisan terdistribusi pada instance RDS multi-AZ benar-benar diperbarui, jika baris yang sama langsung dibaca, baris itu mungkin belum ada atau kolomnya belum sepenuhnya diperbarui sehingga nilai sebelumnya bisa muncul.
Dengan cara kerja snapshot PostgreSQL, ini tampaknya bukan berarti hanya sebagian byte dari tipe kolom multibyte diperbarui lalu nilai yang tidak masuk akal terbaca.
Pada akhirnya ini terlihat seperti race condition yang akan konvergen seiring waktu. Atau saya penasaran apakah ada yang membacanya sebagai berarti transaksi-transaksi belakangan dalam “long fork” bisa saja tidak pernah selesai selamanya bahkan dalam kondisi normal.
Kalimat “Pekerjaan ini dilakukan Jepsen secara independen tanpa kompensasi” adalah sesuatu yang tidak ingin dilihat pihak berkepentingan RDBMS bahkan pada hari baik sekalipun. Sepertinya ada beberapa email bernada khawatir yang lalu-lalang secara internal. Seperti biasa, hormat untuk aphyr.
Belum sepenuhnya jelas apakah ini bukan masalah pada klaster PostgreSQL upstream multi-instance. Saya penasaran apakah benar jika dipahami bahwa AWS melakukan sesuatu pada konfigurasi klaster, atau menambahkan patch yang memicu perilaku ini
Replikasi PostgreSQL umumnya punya berbagai cara dan hasilnya juga berbeda. Misalnya ada laporan Patroni dari Bin Wang: https://www.binwang.me/2024-12-02-PostgreSQL-High-Availabili...
Yang ditemukan di sini juga adalah bahwa PostgreSQL saat ini tidak menyediakan perilaku snapshot yang konsisten antara node primer dan replika. Kemungkinan transaksi baca-saja T2 dijalankan di node sekunder, sementara transaksi perubahan T1/T3/T4 dijalankan di node primer
Sebagai latar belakang, snapshot pada node PostgreSQL sekunder bergantung pada urutan persistensi transaksi, yaitu posisi record commit di WAL, saat menentukan transaksi mana yang terlihat. Sebaliknya, urutan visibilitas pada node primer ditentukan oleh saat backend yang meng-acknowledge transaksi tersebut pertama kali menerima pemberitahuan bahwa transaksi sudah sepenuhnya di-commit, lalu saat setelahnya ia menandai commit
Di masing-masing node primer dan sekunder, urutan commit di antara backend yang terhubung konsisten, tetapi urutan commit antara primer dan sekunder bisa sedikit berbeda. Ada pekerjaan yang sedang berjalan untuk memperbaikinya, tetapi masih dalam tahap yang sangat aktif
AWS tampaknya mem-patch PostgreSQL agar mereplikasi ke dua instans, lalu menganggap cukup jika salah satu dari keduanya mengonfirmasi perubahan. Kapan konfirmasi ini terjadi bukan informasi publik
Secara pribadi, saya rasa untuk PostgreSQL, replikasi tingkat filesystem seperti drbd lebih baik. Instans AWS Multi-AZ gaya lama mungkin menggunakan cara ini. Namun throughput menurun dan tidak bisa membaca dari instans sekunder
Khususnya bagian ini: https://youtu.be/fLqJXTOhUg4?t=434
Judul yang dikirim menanyakan inti persoalannya. RDS for PostgreSQL 17.4 tidak mengimplementasikan isolasi snapshot dengan benar
Perdebatan bisa cukup sengit: apakah judul terlalu keras terhadap sistem, terlalu bersahabat, apakah memuat temuan paling berarti dari belasan masalah yang ditemukan, apakah adil menurut standar Jepsen yang berusaha menjadi perantara jujur untuk hasil keselamatan database, atau bagaimana orang akan menafsirkannya 10 tahun kemudian saat mereka masih terus menautkannya tetapi itu tidak lagi berlaku untuk versi terbaru
Setelah beberapa upaya yang membuat frustrasi, kami menghindari masalah ini dengan kebijakan memberi semua judul laporan dalam bentuk “Jepsen: ”. Jika HN menginginkan teks tautan yang lebih deskriptif atau lebih berwarna, tentu mereka bisa memilihnya sendiri
Meski begitu, ini ditulis oleh Kyle Kingsbury, semacam Chuck Norris-nya jaminan transaksi, jadi AWS harus menjawab atau memberi klarifikasi. Begitu pula meski tampaknya ini hanya berlaku pada klaster multi-AZ, salah satu dari dua opsi RDS untuk PostgreSQL. Deployment Multi-AZ bisa memiliki satu atau dua instans DB standby, dan di sini yang dibahas adalah konfigurasi dengan dua instans DB standby
Dokumentasi AWS tidak memuat janji seperti itu. Manual RDS setebal 5.494 halaman pun hampir hanya menyebut isolation atau serializable di dokumentasi parameter masing-masing engine
Juga tidak ada tentang konsistensi baca global pada klaster Multi-AZ. Memang disebut replikasi semisinkron, sehingga writer menunggu konfirmasi record log dari satu standby, tetapi dua reader bisa berada di atas snapshot yang berbeda
[1] - "New Amazon RDS for MySQL & PostgreSQL Multi-AZ Deployment Option: Improved Write Performance & Faster Failover" - https://aws.amazon.com/blogs/aws/amazon-rds-multi-az-db-clus...
[2] - "Amazon RDS Multi-AZ with two readable standbys: Under the hood" - https://aws.amazon.com/blogs/database/amazon-rds-multi-az-wi...
Jika pengembang mengasumsikan isolasi snapshot, tetapi Amazon RDS for PostgreSQL sebenarnya hanya menyediakan isolasi snapshot paralel, saya penasaran bug keamanan atau bug tingkat aplikasi apa yang bisa muncul, terutama pada konfigurasi multi-AZ yang menggunakan endpoint replika baca
git push. Memulai transaksi, membaca status saat ini lalu memeriksa apakah sesuai dengan status yang diharapkan, menulis status baru, lalu melakukan commit bersama hash status baru. Dalam situasi yang tidak menguntungkan, bisa muncul hash commit yang tidak cocok dengan status valid mana punFakta bahwa hal seperti ini sulit ditalar itu sendiri membuat masalahnya sulit dihindari. Jadi solusi paling mudah mungkin kira-kira: jika penulisan bergantung pada pembacaan bersyarat, “mungkin isolasi snapshot bisa dipulihkan dengan hanya menggunakan endpoint writer”
Namun cukup mengejutkan bahwa pendekatan “hanya menggunakan endpoint writer” belum diuji, terutama dalam situasi kehilangan ketersediaan
User1 menulis komentar, lalu User2 menulis komentar, kemudian User1 dalam transaksi terpisah memeriksa bahwa hanya ada 1 komentar dan menerima badge. User2 juga dalam transaksi terpisah melakukan pemeriksaan yang sama, hanya melihat 1 komentarnya sendiri, dan bisa menerima badge
Dalam isolasi snapshot, ini tidak mungkin terjadi. Setidaknya salah satu dari transaksi terpisah itu harus melihat 2 komentar
Makalah asli tentang snapshot paralel juga layak dibaca: https://scispace.com/pdf/transactional-storage-for-geo-repli...
Saat melihat kalimat “fenomena ini terjadi di semua versi yang diuji, dari 13.15 hingga 17.4”, saya sempat khawatir apakah menaikkan versi mayor adalah pilihan yang salah, tetapi tampaknya bukan begitu. Ini bukan regresi, melainkan lebih dekat ke permintaan fitur atau bug lama