2 poin oleh GN⁺ 2024-11-04 | 1 komentar | Bagikan ke WhatsApp
  • Kombinasi PostgreSQL+Redis yang umum pada layanan web memang praktis, tetapi sebagian dari antrean pekerjaan latar belakang, distributed lock, dan Pub/Sub juga dapat ditangani hanya dengan PostgreSQL
  • FOR UPDATE SKIP LOCKED di PostgreSQL 9.5 melewati baris yang terkunci tanpa menunggu, sehingga dapat dimanfaatkan untuk implementasi antrean yang mencegah beberapa worker mengambil pekerjaan yang sama
  • Distributed lock di tingkat aplikasi dapat dibuat dengan advisory locks PostgreSQL, dengan menggunakan ulang engine lock internal untuk tujuan yang didefinisikan aplikasi
  • LISTEN/NOTIFY di PostgreSQL 9 mendukung langganan kanal string arbitrer dan pengiriman notifikasi, sehingga dapat dipakai sebagai lapisan Pub/Sub; Rails ActionCable juga mendukung penggunaan PostgreSQL secara bawaan
  • Redis tetap unggul untuk caching TTL dan manipulasi data sementara, tetapi sebagian sistem dapat mengurangi ketergantungan pada Redis untuk menurunkan biaya operasional dan kompleksitas pengembangan

Peran Redis yang Dapat Diserap oleh PostgreSQL

  • Layanan web pada umumnya memakai PostgreSQL sebagai penyimpanan data, dan Redis untuk koordinasi antrean pekerjaan latar belakang atau operasi atomik terbatas
  • Redis sendiri berguna, tetapi sebagian peran Redis dalam kombinasi ini dapat digantikan hanya dengan fitur PostgreSQL

Antrean pekerjaan latar belakang

  • Redis sering dipakai dalam layanan web untuk koordinasi antrean pekerjaan yang meneruskan pekerjaan ke pool worker latar belakang
    • Mencatat pekerjaan latar belakang yang harus dijalankan beserta data inputnya
    • Harus menjamin hanya satu dari banyak worker yang mengambil pekerjaan tersebut
    • Redis cocok untuk kebutuhan ini karena kaya akan operasi atomik pada struktur data
  • Mulai PostgreSQL 9.5, opsi SKIP LOCKED pada pernyataan SELECT ... FOR ... dapat digunakan
    • Jika opsi ini ditentukan, PostgreSQL akan mengabaikan baris yang harus menunggu pelepasan lock
    • Dengan FOR UPDATE SKIP LOCKED, lock tingkat baris diperoleh secara implisit atas baris yang dikembalikan
    • Berkat SKIP LOCKED, tidak ada kemungkinan terblokir oleh lock dari transaksi lain
    • Jika ada pekerjaan lain yang perlu diproses, pekerjaan itu akan dikembalikan
    • Meskipun beberapa worker menjalankan perintah yang sama, mereka tidak akan menerima baris yang sama karena lock tingkat baris
  • Alur dasarnya adalah memilih satu pekerjaan berstatus pending di dalam transaksi, mengubah pekerjaan itu menjadi running, lalu mengembalikannya
    • BEGIN
    • SELECT ... FOR UPDATE SKIP LOCKED
    • UPDATE jobs SET status = 'running' ... RETURNING jobs.*
    • COMMIT
  • Hal yang perlu diperhatikan adalah, ketika jumlah worker dan pekerjaan sama-sama besar, biaya untuk memindai antrean sambil mencoba memperoleh lock dapat meningkat
    • Pada sebagian besar aplikasi yang pernah ditangani, jumlah worker latar belakang kurang dari 12, sehingga kemungkinan biaya ini tidak besar

Lock aplikasi

  • Ada kasus ketika hanya satu instance yang boleh berjalan untuk pengguna tertentu di seluruh proses server, misalnya rutinitas yang melakukan sinkronisasi dengan layanan pihak ketiga
  • Distributed lock seperti ini adalah kasus penggunaan umum lain untuk Redis
  • PostgreSQL dapat mencapai tujuan yang sama dengan advisory locks
    • Advisory locks memungkinkan engine lock yang digunakan PostgreSQL secara internal dimanfaatkan untuk tujuan yang didefinisikan aplikasi

Pub/Sub

  • Redis juga sering dipakai saat mendorong event ke klien aktif
    • Memberi tahu pengguna bahwa pesan baru dapat dibaca
    • Melakukan streaming ke klien segera setelah data siap
    • Umumnya WebSocket menjadi lapisan pengiriman event, sedangkan Redis berperan sebagai engine Pub/Sub
  • Sejak PostgreSQL 9, fitur Pub/Sub disediakan melalui pernyataan LISTEN dan NOTIFY
    • Klien PostgreSQL dapat berlangganan kanal pesan tertentu yang berupa string arbitrer dengan LISTEN
    • Jika klien lain mengirim NOTIFY ke kanal tersebut, semua klien yang sedang berlangganan akan menerima notifikasi
    • Secara opsional, pesan kecil dapat dilampirkan bersama notifikasi
  • Jika Anda menggunakan Rails dan ActionCable, penggunaan PostgreSQL didukung secara bawaan

Area yang Masih Layak Mempertahankan Redis

  • Redis menangani area yang berbeda dari PostgreSQL, dan kuat pada pekerjaan yang bukan target PostgreSQL
    • Caching data dengan TTL
    • Penyimpanan dan manipulasi data sementara
  • PostgreSQL memiliki cakupan fitur yang luas untuk dianggap sekadar database SQL sederhana atau komponen buram di balik ORM
  • Sebagian pekerjaan yang diserahkan ke Redis mungkin juga cocok untuk PostgreSQL
  • Menghilangkan Redis dapat menjadi opsi untuk mengurangi biaya operasional dan kompleksitas pengembangan yang muncul saat bergantung pada banyak layanan data

1 komentar

 
GN⁺ 2024-11-04
Komentar Hacker News
  • Banyak orang terlalu ngotot dengan arsitektur yang terlalu terdistribusi, jadi sering tidak melihat keunggulan nyata Redis. Jika dijalankan di mesin yang sama dengan aplikasi, responsnya bisa jauh di bawah 1 milidetik, dan itu memungkinkan aplikasi melakukan hal-hal yang sulit dilakukan dengan Postgres
    Memang benar Postgres itu hebat, tetapi ia tidak berjalan di memori pada mesin yang sama dengan aplikasi. Kalau yang dibutuhkan hanya hal seperti antrean, mungkin penyimpanan key-value in-memory tidak diperlukan. Inti dari penyimpanan key-value in-memory adalah menangani pekerjaan yang membutuhkan karakteristik performa RAM, dan karakteristik itu tidak bisa didapat jika harus melewati koneksi jaringan

    • Jika hanya ada satu proses lokal yang akan memakai Redis di dalam mesin sebagai cache in-memory, lebih baik langsung pakai struktur data dari bahasa pemrograman yang digunakan
    • Penasaran seberapa berbeda overhead Postgres dan Redis saat dijalankan secara lokal. Juga tidak paham kenapa Postgres dianggap tidak berjalan secara lokal
      Tidak ada sihir khusus pada Postgres; ia hanyalah program yang berjalan di proses lain seperti Redis. Pada koneksi lokal, ia memakai jalur cepat untuk mengurangi latensi, dan juga bisa memakai mekanisme transfer data massal yang lebih cepat. Saya sudah beberapa kali memakainya seperti itu
    • Django punya cache bawaan yang mendukung Redis, dan juga opsi cache in-memory, tetapi ditandai “bukan untuk produksi”. Alasannya, kalau ada beberapa instance Django, masing-masing cache in-memory akan berbeda
      Namun untuk hal seperti alat kerja internal, satu instance tunggal bisa dibiarkan hidup lama, dan cache in-memory ini membuatnya sangat cepat. django-cachalot adalah library yang otomatis menangani invalidasi cache setiap kali ada penulisan ke tabel. Pendekatannya memang agak kasar, tetapi memberi peningkatan performa hampir tanpa usaha, dan aplikasi internal yang jarang diperbarui pada praktiknya hampir berjalan sepenuhnya di RAM lalu hanya kembali ke kueri basis data biasa saat terjadi cache miss
      https://github.com/noripyt/django-cachalot
    • Yang lebih cepat daripada Redis dalam konfigurasi ini adalah hash map. Tidak ada alasan Postgres tidak bisa berjalan di server yang sama dengan aplikasi, dan ini sebenarnya konfigurasi yang cukup umum
    • Overengineering dan distribusi prematur memang masalah nyata, tetapi Redis adalah singkatan dari “Remote Dictionary Server”. Tujuan aslinya jelas bukan untuk dijalankan secara lokal. Tentu saja, menjalankannya secara lokal tetap bisa jadi pilihan desain yang sah, misalnya ketika dictionary bawaan bahasa tidak mendukung range query
  • Diskusi di sini banyak berisi argumen defensif dari sudut pandang Redis, tetapi tentu ada area tertentu di mana Redis memang lebih baik
    Namun menurut saya itu bukan inti artikelnya. Kalimat kuncinya bisa diringkas sebagai: “PostgreSQL memiliki jauh lebih banyak kemampuan daripada yang Anda perkirakan jika Anda mendekatinya hanya sebagai basis data SQL sederhana atau sesuatu yang tersembunyi di balik ORM.” Jika Anda hanya memakai basis data dari balik ORM, besar kemungkinan Anda melewatkan banyak fitur di basis data mana pun. Jika Anda perlu menambahkan layanan lain seperti Redis, mungkin lebih baik memakai basis data yang sudah Anda siapkan daripada menambah dependensi baru

  • Memahami apa yang bisa dilakukan Postgres itu bagus. Ini basis data yang kuat
    Argumen sebaliknya adalah hambatan untuk memakai Redis sangat rendah, dan imbalannya berupa performa tinggi, dukungan library yang kaya, serta bisa mengurangi beban basis data utama. Misalnya, Anda bisa membuat cache respons API di Postgres. TTL bisa ditangani dengan pekerjaan cron yang menyapu nilai cache lama. Atau ya tinggal pakai Redis
    Advisory lock itu keren dan berguna, tetapi jika Anda memakai sesuatu seperti PgBouncer, bisa muncul masalah antara session advisory lock dan transaction interleaving. Sistem terpisah memang punya kekurangan seperti panggilan jaringan, ketersediaan, dan pengetahuan domain, tetapi kompromi sebesar Redis tergolong cukup kecil

    • Keuntungannya adalah ada satu hal lebih sedikit yang harus dikelola di produksi. Mulai dengan PostgreSQL, lalu tambahkan sistem khusus saat benar-benar dibutuhkan karena performa, skala, atau biaya
  • Ini artikel yang cukup lama, tetapi sekarang polanya sudah sangat umum. Untuk 90% proyek yang hanya butuh job queue untuk pengiriman email atau pembuatan laporan, mereka tidak memproses jutaan pesan per detik, jadi pendekatan yang menyederhanakan stack layak dipertimbangkan
    Saya sering memakai pola seperti ini untuk menghindari masalah yang saya alami di Celery, lalu memisahkannya menjadi framework tersendiri: https://github.com/TkTech/chancy masukan sangat diterima
    Ada banyak alat seperti ini, dan beberapa di antaranya bahkan layanan komersial, jadi jelas ada permintaannya
    https://worker.graphile.org/ (Node.js)
    https://riverqueue.com/ (Go)
    https://github.com/acaloiaro/neoq (Go)
    https://github.com/contribsys/faktory (Go)
    https://github.com/sorentwo/oban (Elixir)
    https://github.com/procrastinate-org/procrastinate (Python)

    • Saya sedang mencari job queue yang sederhana. Huey juga lumayan, tetapi saya sudah memakai Postgres, dan pekerjaan yang dimasukkan ke antrean hanya sekitar sekali per jam, jadi yang berbasis Redis selalu terasa berlebihan
    • Di ekosistem Node juga ada PG-Boss: https://github.com/timgit/pg-boss
  • PGQueuer memakai FOR UPDATE SKIP LOCKED dan LISTEN/NOTIFY dari PostgreSQL untuk menyediakan job queue, lock, dan notifikasi real-time
    Jika Anda sudah memakai PostgreSQL, ini adalah alternatif minimalis yang tidak memerlukan Redis
    https://github.com/janbjorge/PGQueuer
    Sekadar catatan, ini buatan saya

  • Saya suka Postgres, tetapi ada beberapa keterbatasan
    Jika Anda butuh penyimpanan key-value, Anda perlu mempertimbangkan apakah memahami autovacuum, mengetahui batas connection pool, dan apakah yang diinginkan adalah throughput atau keamanan. Jika butuh queue, lihat apakah pemrosesannya berurutan, ada rate limit, fan-out, atau pemisahan per topik. Jika butuh publish/subscribe, pertimbangkan apakah penerimaan duplikat menjadi masalah, apakah kehilangan pesan menjadi masalah, dan apakah replay diperlukan. Jika butuh locking, Anda perlu mengetahui batas connection pool dan statement_timeout. Sebagian besar masalah di atas memang bisa diatasi, tetapi tidak sesederhana itu

    • Perlu juga dilihat apakah implementasinya tetap tidak runtuh ketika transaksi yang berjalan lama menghalangi penghapusan tuple oleh vacuum
  • Hambatan besar dalam publish/subscribe Postgres adalah ukuran maksimum pesan hanya 8000 byte
    Solusi sementara yang disarankan adalah menaruh data ke tabel lalu hanya mengirim ID-nya, tetapi jika tidak ingin data itu tersimpan selamanya, Anda harus melakukan garbage collection terhadap data tersebut, dan tiap pesan menambah pekerjaan. Tentu ini bisa cocok dalam beberapa kasus, tetapi untuk banyak kasus penggunaan Redis, batasan ini membuatnya sulit dianggap setara

  • Mari lihat apakah pgsql bisa menangani 15.000 koneksi klien

    • Perbedaan Postgres dan MySQL di sini benar-benar mengejutkan
      Seseorang dari Planetscale mengatakan di podcast bahwa instance MySQL milik GitHub masing-masing menangani lebih dari 50 ribu koneksi. Sebaliknya, di Postgres, jika membutuhkan lebih dari 100 koneksi, biasanya Anda sudah memerlukan PgBouncer
    • Anda bisa memakai connection pooling. Ini bisa digunakan hanya dengan beberapa klik, seperti AWS RDS Proxy
  • Queue, lock, dan publish/subscribe memang bisa dilakukan. Tetapi penggunaan Redis yang paling penting, yaitu caching, tidak ada di sini
    Update di Postgres terkenal lebih mahal daripada insert, menghasilkan sampah, dan memerlukan vacuum. Karena jaminan durabilitas yang sebenarnya tidak terlalu penting untuk caching, penulisan juga menjadi cukup lambat. Kedaluwarsa otomatis sangat praktis dan membantu mengurangi kesalahan

    • Sebagian besar fitur yang tidak diperlukan bisa dimatikan. Misalnya, Anda bisa membuat tabel sebagai unlogged: https://www.postgresql.org/docs/current/sql-createtable.html...
      Synchronous commit, autovacuum, dan lainnya juga bisa dimatikan. Tentu Redis masih akan lebih cepat, tetapi perbedaannya mungkin tidak cukup penting untuk diperhatikan oleh perusahaan pada umumnya
  • Jika inti tulisannya mau disampaikan lebih jelas, mulailah dengan Postgres lalu pindah ke Redis saat benar-benar diperlukan
    Sebaiknya jumlah komponen yang bergerak dijaga sesedikit mungkin

    • Redis sendiri tidak terlalu sulit untuk di-deploy. Daripada merencanakan migrasi yang bisa menimbulkan efek samping tak terduga di kemudian hari, memakai Redis sejak awal juga merupakan pilihan yang masuk akal