Drop rubyrep object secara manual di PostgreSQL

  Selama saya melakukan test terhadap rubyrep, ada kejadian dimana saya harus menghapus object object milik rubyrep secara manual. Menggunakan perintah rubyrep uninstall gagal, karena tidak dapat berkomunikasi dengan salah satu servernya. Alhasil dengan terpaksa harus melakukan pembersihan secara manual.

Semua object object milik rubyrep, secara default memiliki nama depan yang diawali dengan awalan “rr_“. Object yang terinstal adalah trigger dan table.

Berikut langkah langkah melakukan drop rubyrep secara manual :

  • Login ke database postgresql dimana tabel tabel rubyrep terinstall
  • Dapatkan daftar trigger trigger yang dipergunakan rubyrep
SELECT 'DROP FUNCTION ' || ns.nspname || '.' || proname || '('
       || oidvectortypes(proargtypes) || ') CASCADE;'
FROM pg_proc 
INNER JOIN pg_namespace ns ON (pg_proc.pronamespace = ns.oid)
WHERE ns.nspname = 'public'  order by proname;
  • Copy Paste perintah drop function yang dimulai dengan awalan “rr_”  tersebut untuk menjalankan perintah drop tersebut.
  • Dapatkan daftar tabel tabel yang di pergunakan rubyrep
select 'DROP TABLE '||tablename||' CASCADE; ' 
from pg_tables 
where tablename ilike 'rr_%';
  • Copy Paste perintah drop tabel yang dimulai dengan awalan “rr_” tersebut untuk menjalankan perintah drop tersebut.
  • Done.
Nah, dengan cara ini maka semua tabel dan trigger milik rubyrep sudah hilang dari sistem. Silahkan mencoba.

Dari RubyRep ke Bucardo

  Setelah beberapa saat rubyrep dipergunakan di server produksi kita, akhirnya saya putuskan untuk menghentikan pemakaian rubyrep dan beralih ke bucardo.

Rubyrep adalah produk replikasi untuk postgresql yang cukup baik. Dibuat oleh Arndt Lehmann. Instalasinya cukup mudah dengan setting yang flexible. Rubyrep juga dapat yang saya pergunakan adalah yang versi JRuby. Karena kemudahan instalasinya itulah maka saya mencoba rubyrep terlebih dahulu.

Bucardo adalah produk replikasi untuk postgresql juga. Awalnya dipergunakan di Backcounty.com pada tahun 2002 pada postgresql versi 7.2. Bucardo dirilis untuk umum tahun 2007 dan hingga saat ini versi stabil yang terakhir adalah versi 4. Untuk versi 5 masih beta dan sudah di kembangkan selama 3 tahun. Versi 5 inilah yang saya pergunakan untuk menggantikan rubyrep.

Alasan mengapa berhenti memakai rubyrep adalah murni karena rubyrep tidak cocok dengan arsitektur applikasi kita. Berikut alasan alasannya :

  • Tabel dengan banyak kolom, applikasi kita memiliki beberapa tabel yang memiliki banyak kolom, lebih dari 25 kolom, dan tabel tabel ini harus dapat direplikasikan ke server lain yang berada di luar kota.
  • Kecepatan replikasi, untuk tabel dengan banyak kolom, kecepatan replikasi antar kota dengan rubyrep hanya max 20 record, sementara dengan bucardo kecepatannya antara 200-250 record.

Rubyrep cukup bagus, hanya saja untuk arsitektur applikasi kita, rubyrep masih belum dapat bekerja optimal dibandingkan dengan bucardo. Jadi setelah membandingkan hasil test antara bucardo dan rubyrep, maka saya semakin mantab untuk beralih ke bucardo. Apabila anda memiliki pengalaman menggunakan bucardo, mohon infonya.

Yang harus di hindari pada saat memakai replikasi berbasis trigger dengan rubyrep

  Hallo semua, rubyrep lagi. Saya akan sharing sedikit mengenai bagaimana applikasi yang kita buat berinteraksi dengan rubyrep. Setelah sekian lama menggunakan rubyrep dan menemui beberapa kendala, crash, macet, proses yang lambat, proses yang cepat sekali, akhirnya saya dapat mengetahui beberapa karakteristik dari applikasi kita yang mempengaruhi kinerja dari rubyrep.

Didalam applikasi yang kita buat, terdapat beberapa teknik pemrograman yang membuat replikasi berbasis trigger menjadi berat kerjanya, yakni :

  1. Delete-Insert dibandingkan Update.
    • Proses ini terjadi pada saat kita update suatu Dokument, dimana dokumen ini di disimpan dalam 1 tabel master, dengan 2 atau lebih tabel detail.
    • Teknik pemrograman yang kita pakai pada saat update dokumen adalah update tabel master, delete semua tabel detail, dan insert ulang semua tabel detail.
    • Pada saat kita masih menggunakan 1 Database, teknik (delete-insert) ini adalah pilihan yang sangat tepat dibandingkan dengan teknik (update).
    • Tetapi saat ini, dengan replikasi berbasis trigger teknik tersebut sudah tidak cocok lagi, mengapa ? karena dengan melakukan proses delete dan insert maka record yang di replikasi akan 2x lebih banyak di bandingkan dengan update. Ini yang kita alami, sehingga dengan melihat lalu lintas data yang di replikasi dapat dikatakan teknik ini memakan sumber daya energi yang lebih banyak di bandingkan teknik update.
    • Solusinya adalah menghindari teknik delete-insert dan beralih ke teknik update.
  2. Hindari Variable Karakter sebagai primary key
    • Rubyrep tetap akan mengenali primary key tersebut,  tetapi karena tipe datanya adalah variable karakter maka spasi, karakter yang unik unik pun akan tersimpan.
    • Sehingga pada saat proses replikasi, karakter unik tersebut dapat mempengaruhi Rubyrep.
    • Dalam kasus kita, kita sering menemukan spasi diakhir data kita dan ini mengakibatkan Rubyrep gagal mengupdate ke server yang lain, sehingga prosesnya terhenti.
    • Solusinya adalah merubah applikasi agar spasi tersebut dapat hilang atau hindari tipe data ini sebagai primary key.
  3. Perhatikan field Auto Increment sebagai primary key
    • Field jenis ini umumnya memakai tipe data Auto Increment, Serial, Sequence, Auto numeric, dan sejenisnya.
    • Perhatikan bagaimana replikasi berbasis trigger menangani field jenis ini, karena anda melibatkan lebih dari 1 server, sehingga field auto increment harus dapat menghasilkan data yang berbeda untuk setiap servernya.
    • Jika anda memakai sequence anda bisa menambahkan suatu kode di depat nomor sequence yang di generate.
    • Kalau saya pribadi berusaha menghindari field jenis ini agar tidak ribet, lebih enak membuat data secara manual sebagai primary keynya.
  4. Besar byte per record dalam satu tabel
    • Semakin besar byte per record dalam satu tabel maka semakin berat juga kinerja replikasi dalam proses membandingkan datanya.
    • Penggunaan memori akan meningkat dan kecepatan replikasi akan menurun adalah efek samping yang timbul jika kita mengabaikan hal ini.
    • Menurut pengalaman saya 25 kolom dalam bentuk varchar(256) cukup berat untuk di proses.
    • Jika memang kita harus mereplikasi tabel jenis ini, kita harus mengatur jumlah record yang harus di baca oleh rubyrep.

Rubyrep saya Crash !

  Pagi ini seperti biasanya saya login ke database Postgresql untuk melihat kondisi database, sekaligus check apakah masih ada record record yang belum ter-replikasi melalui rubyrep. Ternyata ada 350 ribu record yang belum terreplikasi. Saya check rubyrepnya juga masih berjalan normal melalui perintah top dan ps. Saya tunggu beberapa saat dan menghitung kembali berapa jumlah record yang ada di tabel rr_pending_changes. Ternyata jumlahnya terus meningkat, berarti tidak ada data yang terkirim ke server master yang lainnya.

Applikasi rubyrepnya jalan, indikasi di top dan ps menunjukkan kalau java nya masih aktif bekerja. Saya menggunakan rubyrep versi JRuby. Postgresql di mesin source juga masih aktif bekerja. Akhirnya saya simpulkan rubyrepnya yang tidak bekerja, walaupun top dan ps menunjukkan kalau applikasinya masih aktif. Akhirnya saya kill semua proses rubyrep dan saya aktifkan rubyrep kembali dengan parameter log saya aktifkan.

Kemudian setelah rubyrep aktif kembali, saya cek lagi berapa jumlah record yang pending sekarang, sekitar 370 ribu record. Saya lihat log di left dan di right. Di log untuk database source (left log), ruby rep memproses perintah seperti di bawah ini berulang ulang hingga berbaris baris ke bawah.

select "change_table", "change_key", "change_new_key", "change_type", "change_time", "id"
from "rr_sby_pending_changes"
where ("id") > (5100081) order by "id" limit 1

Sedangkan di dalam log database target (right log), saya menemukan ada 1 perintah insert yang error, yang menunjukkan kegagalan insert data karena data sudah ada, duplicate value violate constraint _pk. Error ini menunjukkan bahwa ada 1 record yang gagal di duplikasi ke mesin target karena di mesin target data tersebut telah ada.

Untuk mengatasi hal ini, saya menghapus record data di tabel rr_pending_changes yang gagal tersebut. Kemudian saya restart kembali rubyrep dan memperhatikan error log yang kemungkinan muncul. Setelah beberapa saat muncul juga problem sejenis seperti diatas, tetapi dengan perintah sql serperti berikut di dalam log database source (left log) :

select "change_table", "change_key", "change_new_key", "change_type", "change_time", "id"
from "rr_sby_pending_changes"
where ("id") > (5945381) order by "id" limit 1000

Perintah ini muncul berulang ulang juga di dalam log, dan di console yang menjalankan rubyrep muncul error JAVA, GC overhead limit dan Java Heap Space. Kedua error ini menunjukkan bahwa applikasi rubyrep memakan semua resource JAVA hingga habis tidak bersisa. Saya baca di group milist rubyrep menunjukkan bahwasannya saya masih memakai default setup Jruby untuk 500mb, dan disarankan untuk menaikkan heap sizenya menjadi 1GB dengan perintah -J-Xmx1024m.

Dikarenakan saya masih ragu ragu, saya ulangi proses rubyrep saya. Setelah sekian lama tetap saja error tersebut muncul kembali dan akhirnya rubyrep saya crash, stop bekerja, hilang dari top dan ps.

Akhirnya saya ikuti saran dari milist rubyrep, saya ubah beberapa bagian dari file konfigurasi dan script rubyrep, sehingga script eksekusi rubyrep saya sekarang akan seperti ini :

 #!/bin/bash

 script_dir="`dirname \"$0\"`"

 jruby_path="$script_dir"/jruby/bin/jruby
 rubyrep_path="$script_dir"/bin/rubyrep

 $jruby_path --server -J-Xmx1024m $rubyrep_path $*

selain itu saya juga merubah file konfigurasi rubyrep dengan menambahkan 2 parameter berikut ini :

config.options[:row_buffer_size] = 100
config.options[:commit_frequency] = 500

Parameter yang pertama akan memaksa rubyrep untuk mengambil 100 record untuk setiap table untuk di prosess, defaultnya adalah 1000 record. Dengan hanya mengalokasikan 100 record diharapkan proses rubyrep tetap berjalan dan memory Jruby tidak cepat habis.

Parameter yang kedua akan memaksa rubyrep untuk malakukan commit untuk setiap 500 transaksi yang di proses, default 1000. Dengan parameter ini process commit transaksi akan lebih cepat, sehingga bufer memory dapat dipergunakan untuk memproses record yang lainnya.

Kemudian saya ulangi prosess replikasi rubyrep. Posisi record yang terpending saat ini adalah 500ribu record. Setelah bererapa lama prosess rubyrep berjalan akhirnya saya melihat log di database sumber (left log) menunjukkan adanya perintah insert, delete dan update (DML), yang artinya replikasinya sudah berjalan kembali.

Jika saya tetap mengacu pada tulisan yang terdahulu, maka untuk 500 000 record dengan kecepatan replikasi 20 record/detik, maka dibutuhkan waktu sekitar 6.5 jam. Lama juga ya. Bagaimana dengan anda ?