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 ?

Instalasi Rubyrep, master to master replikasi untuk database PostgreSQL

  Rubyrep yang saya pergunakan adalah versi rubyrep dengan JRuby. Sampai saat ini kecepatan replikasi yang dapat saya peroleh adalah 20 record per detik. Hasil ini dapat berbeda untuk jenis applikasi yang anda gunakan.

Konfigurasi yang saya miliki :

  • memakai 3 server yang terinstall Ubuntu 10.10.
  • 2 Server memiliki Database PostgreSQL 9.1.
  • 1 Server memiliki Java 1.6 untuk RubyRep berjalan.

Berikut langkah langkah yang dilakukan untuk instalasi rubyrep :

  • Download Rubyrep versi JRuby disini.
  • ekstrak file hasil download tersebut.
  • Kemudian buat file konfigurasinya.
$> cd rubyrep
$> ./rubyrep generate myruby.conf
  • Edit file konfigurasi sesuai dengan database anda, berikut adalah konfigurasi milik saya.
 RR::Initializer::run do |config|
  config.left = {
    :adapter  => 'postgresql', # or 'mysql'
    :database => 'db1',
    :username => 'postgres',
    :password => 'postgres',
    :host     => '192.168.0.1',
    :port     => '5432',
#    :logger   => '/opt/rubyrep/db1_sql.log'
  }

  config.right = {
    :adapter  => 'postgresql',
    :database => 'db2',
    :username => 'postgres',
    :password => 'postgres',
    :host     => '192.168.0.2',
    :port     => '5432',
#    :logger   => '/opt/rubyrep/db2_sql.log'
  }

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

  ## General Options :
  config.options[:database_connection_timeout] = 360
  config.options[:rep_prefix] = 'rr_xx'

  ## Syncronization Options :
  config.options[:table_ordering] = :true
  config.options[:sync_conflict_handling] = :later_wins

  ## Replication Options :
  config.options[:replication_conflict_handling] = :later_wins

  ## Table Options :
  ## Exclude tables options :
  config.exclude_tables 'my_big_table'
  config.exclude_tables 'my_big_reporting_table'
  config.exclude_tables 'my_datawarehouse_result_table'
  ## Include tables options :
  config.include_tables /./ # regexp matching all tables in the database
  config.include_tables 'my_image_table',
    :row_buffer_size => 200
  config.include_tables 'my_material_tracking',
    :row_buffer_size => 200
end
    • Setelah itu kita lakukan scan untuk cek database structure
$> cd rubyrep
$> ./rubyrep scan myruby.conf
    • Setelah itu kita lakukan sync untuk melakukan sinkronisasi data
$> cd rubyrep
$> ./rubyrep sync myruby.conf
    • Setelah itu baru kita lakukan replikasi
$> cd rubyrep
$> ./rubyrep replicate myruby.conf
  • Done.
Replikasi akan berjalan dengan cara melakukan log atas semua perintah DML, insert, update, delete.
Untuk perintah DDL , create, truncate, drop, tidak akan direplikasi.
Silahkan mencoba.

Sekilas hasil menggunakan Rubyrep untuk master 2 master Replication.

  Hi all, beberapa bulan terakhir ini saya mencari solusi untuk melakukan replikasi database master 2 master untuk databasePostgresql. Master 2 Master itu artinya 2 buah database saling bersinkronisasi, saling mengupdate satu sama lain, secara real time. Tentu saja Real Time disini didalam konteks kurun waktu yang dapat diterima. Slony-I,  PgCluster, PGPool, bucardo dan rubyrep adalah tools replication yang saya amati.

Postgresql belum memiliki kemampuan untuk replikasi master 2 master. Oleh karena itu beberapa vendor dan pengguna database ini membuat tools sendiri agar dapat melakukan replikasi. Saya tidak akan membahasa satu per satu, tetapi setelah mempertimbangkan berbagai hal akhirnya pilihan saya jatuh ke pada bucardo dan rubyrep, yang mendukung master 2 master replication.

Diantara bucardo dan rubyrep, bucardo adalah tools replikasi yang instalasinya lebih rumit daripada rubyrepRubyrep sangat mudah di install dan di setup. Saya menggunakan rubyrep versi JRuby, versi java, tinggal di download, extract, dan di eksekusi. Untuk versi ruby enterprise belum pernah menggunakan. Bucardo dan rubyrep ini sama sama  berbasis database trigger .
Pada dasarnya, rubyrep hanya dapat digunakan untuk 2 database, kiri (left) dan kanan (right). Sisi kiri dapat dikatakan sebagai database asal dan sisi kanan sebagai database target atau tujuan dalam konteks master slave. Karena replikasinya master 2 master, maka kita dapat melakukan perubahan di dua database, baik di kiri atau di kanan, dan kedua duanya dapat melakukan sinkronisasi.
Rubyrep juga mendukung conflict resolution, sehingga apabila muncul permasalahan yang timbul saat terjadinya replikasi, kita dapat mengatur bagaimana rubyrep harus berinteraksi terhadap permasalahan tersebut.
Rubyrep versi JRuby, diklaim oleh penciptanya, Mr.Arndt Lehmann dapat melakukan replikasi hingga lebih dari 200 record per detik. Sementara hasil yang saya dapatkan adalah rata rata 20 record per detik. Pada saat replikasi tabel yang besar dengan jumlah kolom yang banyak, maka kecepatan replikasi dapat turun hingga 9 record per detik. Replikasi ini terjadi di 2 komputer server dalam switch yang sama. Saya sedang berusaha agar replikasinya dapat di percepat lagi.
Apabila ada rekan rekan yang memiliki pengalaman menggunakan rubyrep, ditunggu sharing nya.

Postgresql 9 Stream Replication II

  Pada bagian I, kita telah mempelajari bagaimana melakukan setup Stream Replication, Master dan Slave. Slave Server ini akan bekerja dalam modus read-only. Kita tidak dapat membuat tabel baru, update data atau hapus data.  Seandainya Master Server rusak karena suatu hal, maka Slave Server ini bisa kita ubah menjadi Master Server.

Berikut adalah langkah langkah mengubah Slave Server menjadi Master server, pada mesin Ubuntu Linux.

Master Server :

  1. Asumsi Master Server rusak karena suatu hal.

Slave Server :

  1. Stop database postgresql

    $ sudo /etc/init.d/postgresql stop
  2. Ubah file konfigurasi postgresql.conf

    $ vim /etc/postgresql/9.0/main/postgresql.conf
  3. ubah parameter yang ada menjadi seperti ini dan save filenya :

    listen_address = '*'
    hot_standby = off
  4. kemudian hapus file recovery.conf di direktory data_directory. Default data_directory postgresql 9 di ubuntu adalah di direktori /var/lib/postgresql/9.0/main. :

    $ sudo rm /var/lib/postgresql/9.0/main/recovery.conf
  5. Kemudian stop database postgresql

    $ sudo /etc/init.d/postgresql start
  6. Nah sekarang database Slave Server anda sudah berubah menjadi database Master Server, anda dapat melakukan testing dengan menjalankan perintah insert, delete, update sql. Selamat Mencoba.
  7. Thank you.

Postgresql 9 Stream Replication

  Stream Replication adalah feature terbaru dari Postgresql 9. Stream Replication termasuk dalam kategory Standby Database, yang artinya database replikasi ini berada dalam mode standby, read-only, tidak dipergunakan untuk transaksi.

Stream Replication di postgresql melakukan sinkronisasi data background prosess yang sebut walsender dan walreceiver. Background process inilah yang bertanggung jawab mengirimkan perubahan data melalui network port.
Perubahan yang terjadi di master server akan dengan cepat diimplementasikan di slave server.

Jadi, transaksi database tetap dilakukan di server master, seperti insert, delete, update dan seterusnya. Server
slave dipergunakan untuk transaksi read-only, seperti query, export data dan seterusnya. Apabila terjadi suatu hal terhadap server master, misalnya terjadi kerusakan sistem, maka server slave bisa disetting untuk menggantikan master server.

Persiapan :

  1. Siapkan 2 server linux ubuntu. Saya menggunakan IP address 192.168.0.1 untuk master dan 192.168.0.2 untuk slave servernya. Stream Replication hanya bisa berjalan apabila versi postgresql sama dan menggunakan operating system dengan versi yang sama pula.
  2. Matikan postgresql yang sedang berjalan, baik di master maupun di slave, agar tidak ada user yang melakukan transaksi ke database :

    $ sudo /etc/init.d/postgresql stop
  3. pastikan versi linux anda berjalan pada arsitektur yang sama, 32 bit atau 64 bit :

    $ sudo uname -m

    jika yang keluar adalah x86_64 berarti 64 bit, jika yang keluar adalah i386 / i686 berarti 32 bit.

Master Server :

  1. buat direktori archive di dalam data_directory untuk menampung file wal. Default data_directory postgresql 9 di ubuntu adalah di direktori /var/lib/postgresql/9.0/main.

    $ sudo mkdir /var/lib/postgresl/9.0/main/archive
    $ sudo chown postgres:postgres /var/lib/postgresl/9.0/main/archive
     
  2. buka file postgresql.conf yang menyimpan informasi parameter inisialisasi :

    $ sudo nano /etc/postgresql/9.0/main/postgresql.conf
     
  3. ubah parameter yang ada menjadi seperti ini dan save filenya :
                     listen_address = '*'
                     wal_level = hot_standby
                     checkpoint_segment = 16                 
                     archive_mode = on
                     archive_command = 'cp -i %p /var/lib/postgresql/9.0/main/archive/%f <dev/null'
                     max_wal_senders = 3
                     wal_keep_segments = 32
    
                
  4. buka file pg_hba.conf, yang menyimpan konfigurasi koneksi ke database :

    $ sudo nano /etc/postgresql/9.0/main/pg_hba.conf
     
  5. tambahkan baris konfigurasi seperti ini, untuk memastikan agar slave dapat terkoneksi ke master server, dan save filenya :

    host replication all 192.168.0.2/32 trust 

Slave Server :

  1. buka file postgresql.conf yang menyimpan informasi parameter inisialisasi :

    $ sudo nano /etc/postgresql/9.0/main/postgresql.conf
     
  2. ubah parameter yang ada menjadi seperti ini dan save filenya :

    listen_address = '*'
    hot_standby = on
     
  3. buat file recovery.conf di direktory data_directory. Default data_directory postgresql 9 di ubuntu adalah di direktori /var/lib/postgresql/9.0/main. :

    $ sudo nano /var/lib/postgresql/9.0/main/recovery.conf
     
  4. tambahkan parameter berikut ini dan save filenya:

    standby_mode = 'on'
    primary_conninfo = 'host=192.168.0.1 port=5432 user=postgres password=postgres'
    restore_command = 'cp /path/for/backups/%f %p'
     

Sinkronisasi :

  1. Setelah master dan slave server selesai di konfigurasi, maka langkah berikutnya adalah melakukan sinkronisasi data. Lakukan step step berikut ini dengan cepat.
  2. Start database postgresql di MASTER SERVER :

    $ sudo /etc/init.d/postgresql start
     
  3. Login ke postgresql di MASTER SERVER :

    $ sudo psql -U postgres
     
  4. Jalankan perintah backup di MASTER SERVER :

    postgres# select pg_start_backup('clone', true);
     
  5. copy file database di MASTER SERVER ke SLAVE SERVER :

    postgres# \! rsync -av --exclude pg_xlog --exclude postgresql.conf --exclude postgresql.pid /var/lib/postgresql/9.0/main/* 192.168.0.2:/var/lib/postgresql/9.0/main/
     
  6. stop backup database di MASTER SERVER :

    postgres# select pg_stop_backup();
     
  7. copy direktory pg_xlog di MASTER SERVER ke SLAVE SERVER:

    postgres# \! rsync -av /var/lib/postgresql/9.0/main/pg_xlog 192.168.0.2:/var/lib/postgresql/9.0/main/
     
  8. Start database postgresql di SLAVE SERVER :

    $ sudo /etc/init.d/postgresql start
     
  9. Nah sekarang anda dapat melakukan testing melalui master server, buatlah tabel kosong dan lakukan semua perintah insert, delete, update ke dalam tabel tersebut, dan insert kan beberapa record kedalamnya, dan lihat hasilnya di slave server.

Thank you.