Wednesday, May 6, 2009

scalable PHP APPS

Untuk membuat aplikasi php yang lebih bagus performance nya, selain dari arsitektur aplikasi dan database nya sendiri, juga perlu kita pikirkan rancangan arsitektur server nya.

Berikut rancangan arsitektur server aplikasi PHP yang saya sukai

Dari arsitektur di atas .. setidaknya ada beberapa hal , yang akan saya bahas di artikel selanjutnya :
  1. Load balancing dengan nginx
  2. Apa itu cgi dan fastcgi ? dan mengapa php fastcgi ?
  3. Mysql master slave replication
  4. Mysql proxy

Tuesday, May 5, 2009

PREVENT YOUR PHP APPS, FROM SQL INJECTION

MENURUT SAYA , CARA PALING MUDAH DAN SEDERHANA untuk menghindari sql injection di aplikasi php kita, adalah dengan menggunakan fungsi prepare statement yang di sediakan oleh pdo.

Bagi anda yang sudah menggunakan php5, bisa menggunakan pdo lib sebagai layer untuk koneksi aplikasi anda ke database.

referensi menarik dari PDO bisa anda baca di http://www.phpro.org/tutorials/Introduction-to-PHP-PDO.html

contoh prepare statement

/*** prepare the SQL statement ***/
$stmt = $dbh->prepare("SELECT username FROM tabel_user WHERE password = :pwd");

/*** bind the paramaters ***/
$stmt->bindParam(':pwd', $password, PDO::PARAM_STR, 5);

/*** execute the prepared statement ***/
$stmt->execute();

/*** fetch the results ***/
$result = $stmt->fetchAll();


kita telah binding :pwd dengan inputan $password di mana masukan yang di bolehkan ada string dengan panjang 5

cukup sederhana...

CARA MENGHILANGKAN NOTICE di php 5

php 5, sekarang itu lebih stritch, kalo variabel tidak di assign di awal, kemudian kita langsung lakukan operasi echo atau print, maka akan muncul notice. Sebenarnya untuk development mode, munculnya notice ini bagus, untuk memberitau ke kita, bahwa variabel kita belum di assign

misal jika kita langsung lakukan operasi, echo $nama
padahal $nama itu kosong dan belum di define maka pasti muncul notice ...

cara penanggulangannya ya deklarasikan dulu
$nama = NULL;
/* proses php lainnya di sini */
echo $nama;

pasti gak muncul notice ..., kalo udah kebanyakan kasus seperti ini dan pusing , jadi pengen notice nya dihilangkan yang artinya mau di abaikan saja , ya mau gak mau ubah aja di php.ini nya ... di bagian error reporting nya di set menjadi :

error_reporting = E_ALL & ~E_NOTICE

artinya semua error tetep di tampilkan kecuali notice opsi ~ itu artinya negasi, atau tidak di tampilkan. walaupun cara ini sebenarnya tidak recommended untuk development mode. untuk option error_reporting ini ada beberapa di antarnya
  1. E_ALL, maka akan menampilkan semua error dan warning dari script php kita
  2. E_ERROR, akan menampilkan hanya fatal error dari script kita
  3. E_WARNING, akan menampilkan warning dari script kita
  4. E_NOTICE, menampilkan notice dari script kita
kalo untuk tahap development saya, biasanya saya menggunakan

error_reporting = E_ALL & E_NOTICE

Sunday, May 3, 2009

ORM, DOCTRINE PADA SYMFONY FRAMEWORK

NOTE : tulisan ini dengan asumsi pembaca sudah mengerti symfony framework

ketika awal membuat program elfa di kantor kami
RIZKY menyarankan dengan gigih dan berapi api untuk tidak menggunakan ORM, atau wrapper istilahnya mysql er ...

tapi aku tetep kekeuh menggunakan ORM dan doctrine yang terpilih menjadi ORM. alasanku sebenarnya simple saja, kalo mau menggunakan SP ada beberapa masalah berikut :
  1. SDM untuk masalah sp sp an ini belum cukup banyak di kantor kamu, dan untuk elfa ini SDM yang terlibat mencar mencar, artinya gak cuman megang elday tok, kecuali tika yang saat ini idle terus gw pinjam, menjadi query er juga tapi tentu saja dengan doctrine
  2. Berbeda dengan risma, om rq bisa terus memonitor sp sp nya dia, melakukan testing, hingga menemukan beberapa bug punya nya ucil. Nah untuk elfa ini aku khawatir gak bisa di monitor sampai sedetail itu
  3. Ada kondisi SDM harus bertukar tukar. miftah dan sulis masih harus di sicerdas, jika suatu saat kondisi terburuk katakan lah aku yang harus menggantikan, aku lebih suka mendebug function di model layer php daripada harus mendebug SP. karena tools profiling di doctrine ini cukup mendukung untuk melakukan debuging. Dan aku yakin SDM yang available sekarang lebih suka mendebugnya di level PHP :)
dan setelah membaca blog dari mysql performance blog, ada yang menyarankan menggunakan "wrapper PDO", karena tools untuk profiling nya cukup banyak, mulai dari log query , execution time, terus penanganan DIRTY QUERY, dan inputan untuk menghindari mysql injection...., doctrine adalah ORM yang menggunakan lib pdo

Dengan kata lain DOCTRINE akan mengeliminir kesalahan kesalahan programer untuk melakuan DIRTY QUERY ... dan memudahkan debuging dengan ada nya profiling toolsnya ... dan bukan hanya itu kita juga bisa melakukan unit testing untuk masing masing function nya. Ini ... yang akan aku bahas di tulisan ini juga

Bagiku ada cons dan pros nya menggunakan ORM atau TIDAK, business layer di DBMS atau di PHP. tapi untuk memudahkan monitoring kita harus memilih salah satu, jangan pakek dua dua nya, manage nya bakal ribet ... kalo makek ORM dan business layernya di PHP, ya gunakan lah itu terus sebisa mungkin di project itu jangan di campur dengan menulis logic di DBMS nya
kalo udah nulis logic di DBMS nya, ya tulislah logic nya di DBMS nya , jangan di campur nulis logic di PHP nya ...

untuk program elfa ini, miftah dan tika benar benar aku paksa menuliskan logic nya pada business layer nya yang sudah disediakan doctrine. Ini habit coding yang harus di galakkan ...

JADI kita punya 2 miniatur project di sini, elfa dengan PHP business logic, dan RISMA dengan DBMS business logic.

comment ku selama ini untuk make ORM ... gak sempurna , masih banyak kekurangan di sana sini .. dan karena ORM ini diharapkan mampu kompatibel dengan berbagai DBMS harga yang harus di bayar adalah kita gak bisa menggunakan function specific dari DBMS ... , tapi Doctrine versi terakhir sudah bisa , dengan jalan kita ngasih flag dulu, kalo kita mau makek MYSQL oleh karena itu ijinkan untuk menggunakan function specific dari MYSQL ...gitu... akhirnya bisa di panggil function nya itu..

Untuk doctrine ...
ada 3 macam data yang bisa kita buat di fixtures.yml
  1. initiate data, ini adalah data inisialisasi yang harus di isikan agar sistem berjalan
  2. test data, ketika kita ingin melakukan unit testing pada function logic kita, tentu saja kita butuh tabel kita itu kosong untuk kemudian di isi data testing kemudian di lakukan unit testing , pasti ada data yang ke insert, ke update, ke delete. pada tiap automated testing data ini akan selalu di truncate dan di isi lagi dengan data testing untuk di testing
  3. user data, ini adalah data real dari user ketika data sudah masuk production.
Oleh karena itu framework apapun baik itu symfony, ruby on rails, java ... khan pada dasarnya adalah best practise pengalaman para programmer2 yang membuat framework itu. Dan mereka selalu menyediakan 3 environment database :

1. production
2. test
3. development


ISOLATION LEVEL di MYSQL

Dalam menyambut pembuatan program program MLM yang bernama **** di kantor saya yang bakal menjadi tandingan situs aplikasi binary nya DBS ,
saya sempat berdiksusi ama rekan kerja dan juga Master MySQL kita bung rizky prihanto, yang kebetulan hari ini lagi jadi pembicara bagi komunitas mysql. ada beberapa hal menarik

Dulu bung rizky pernah bilang kepada daku mengenai mekanisme transaksi di mysql untuk progam **** yang kita sedang develop ini. Kalau tidak salah, dulu kami sempat di bahas mengenai mekanisme locking table, di mana default dari INNODB adalah kalo tabel itu di select maka akan secara otomatis tabel tersebut locking.

Coba baca manual mysql dikit dikit, ternyata bener ketika di select dulu, maka tabel itu akan ke lock tapi ... bagi temen temen yang belum tau,
patut di ketahui bahwa mekanisme itu terjadi dalam ruang lingkup :
  1. statement transaction
  2. tabel yang ke lock itu tergantung pada tipe isolation levelnya, pada kasus isolation level repeatble read, read commited dan unread commited, operasi select tidak akan melock table, tapi operasi update otomatis akan melocking tabel , nah pada isolation level serializable, operasi select akan melakukan locking table.
oke muncul kata kata aneh ya.

1. START TRANSACTION
2. COMMIT
3. SAVEPOINT
4. ROLLBACK TO SAVEPOINT
5. ROLLBACK
6. INSERT DELAYED
7. SNAPSHOT
8. SERIALIZABLE
9. TRANSACTION ISOLATION LEVEL

nah email ini ... akan membahas beberapa di antaranya yaitu START TRANSACTION, COMMIT, ROLLBACK dan TRANSACTION ISOLATION LEVEL.

TRANSACTION ISOLATION LEVEL

seperti yang dulu pernah di bahas sama master DBMS kita di stt telkom bapak dhinta darmontoro, dan bagi kelas malam yang sekelas sama aku, pasti gak asing sama istilahh ini, karena dulu ada kerja lab nya ... ya salah satunya mendemo kan mekanisme isolation level, tapi pada waktu itu menggunakan ORACLE, bukan MYSQL. Bagaimana dengan MYSQL, gak jauh berbeda ternyata

ISOLATION LEVEL TERBAGI MENJADI

1. READ COMMITED,
2. UNREAD COMMITED,
3. REPEATABLE READ
4. SERIALIZABLE

Nah kita coba satu satu aja ya ...

kita mulai dari default nya INNODB dulu

REPEATABLE READ


kita liat dulu dengan menuliskan sintaks berikut ini

SELECT @@tx_isolation;
+-----------------+
| @@tx_isolation |
+-----------------+
| REPEATABLE-READ |
+-----------------+
1 row in set (0.00 sec)

ya sudah REPEATABLE READ

aku coba buka 2 console di kubuntu ku agar bisa menjalankan 2 transaksi yang berbeda.

REPEATABLE READ KASUS 1

Transaksi 1 :

trx1 mysql> begin;
Query OK, 0 rows affected (0.00 sec)

trx1 mysql> select * from mysaldo;
+-------+
| saldo |
+-------+
| 100 |
+-------+
1 row in set (0.00 sec)

trx1 mysql> update mysaldo set saldo = saldo + 100;
Query OK, 1 row affected (0.00 sec)
Rows matched: 1 Changed: 1 Warnings: 0

trx1 mysql> select * from mysaldo;
+-------+
| saldo |
+-------+
| 200 |
+-------+
1 row in set (0.00 sec)

Transaksi 2 :

trx2 mysql> begin;
Query OK, 0 rows affected (0.00 sec)

trx2 mysql> select * from mysaldo;
+-------+
| saldo |
+-------+
| 100 |
+-------+
1 row in set (0.00 sec)

kita kembali dulu ke Transaksi 1

kemudian kita lakukan commit di Transaksi 1

trx1 mysql> commit;
Query OK, 0 rows affected (0.00 sec)

lalu kita kembali ke Transaksi 2 dan lakukan select lagi

trx2 mysql> select * from mysaldo;
+-------+
| saldo |
+-------+
| 100 |
+-------+
1 row in set (0.00 sec)

masih 100, karena apa karena di transaksi 2 kita belum melakukan commit
nah inilah yang di sebut REPATABLE READ
coba lakukan commit ...

trx2 mysql> commit;
Query OK, 0 rows affected (0.00 sec)

trx2 mysql> select * from mysaldo;
+-------+
| saldo |
+-------+
| 200 |
+-------+
1 row in set (0.00 sec)

REPEATABLE READ KASUS 2

Transaksi 1 :

trx1 mysql> begin;
Query OK, 0 rows affected (0.00 sec)


trx1 mysql> select * from mysaldo;
+-------+
| saldo |
+-------+
| 100 |
+-------+
1 row in set (0.00 sec)

trx1 mysql> update mysaldo set saldo = saldo - 50;
Query OK, 1 row affected (0.00 sec)
Rows matched: 1 Changed: 1 Warnings: 0

trx1 mysql> select * from mysaldo;
+-------+
| saldo |
+-------+
| 50 |
+-------+
1 row in set (0.00 sec)

NAH SKARANG KITA LIHAT TRANSAKSI 2 YA ...

trx2 mysql> begin;
Query OK, 0 rows affected (0.00 sec )

trx2 mysql> select * from mysaldo;
+-------+
| saldo |
+-------+
| 100 |
+-------+
1 row in set (0.00 sec)

trx2 mysql> update mysaldo set saldo = saldo + 200;

DIA AKAN MENUNGGU TRANSAKSI 1 MELAKUKAN COMMIT DULU, JADI KONDISINYA TIDAK LANGSUNG MUNCUL PESAN
mysql> Query OK, 1 row affected (44.27 sec)

NAH SETELAH aku masuk lagi ke Transaksi 1 dan melakukan

trx1 mysql> commit;
Query OK, 0 rows affected (0.01 sec)

MAKA di TRANSAKSI 2
baru muncul

trx2 Query OK, 1 row affected (44.27 sec)
Rows matched: 1 Changed: 1 Warnings: 0

trx2 mysql> select * from mysaldo;
+-------+
| saldo |
+-------+
| 250 |
+-------+
1 row in set (0.00 sec)

JADI TERNYATA REPEATABLE READ , data yang di baca selalu konsisten , tidak mungkin menghasilkan result/nilai yang berbeda, inti dari repeatble read ini adalah result select selalu konsisten, bukan begitu ? seperti yang terlihat di atas, coba kita bandingkan dengan metode isolation level yang lainnya:

READ UNCOMMITED

kita ubah dulu isolation level dari mysql menjadi read uncommited

mysql> SET GLOBAL TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
Query OK, 0 rows affected (0.00 sec)

mysql> SELECT @@GLOBAL.tx_isolation, @@tx_isolation;
+-----------------------+-----------------+
| @@GLOBAL.tx_isolation | @@tx_isolation |
+-----------------------+-----------------+
| READ-UNCOMMITTED | REPEATABLE-READ |
+-----------------------+-----------------+
1 row in set (0.00 sec)

yoii sudah sudah ...

READ UNCOMMITED KASUS 1

Transaksi 1 :

trx1 mysql> begin;
Query OK, 0 rows affected (0.00 sec)

trx1 mysql> select * from mysaldo;
+-------+
| saldo |
+-------+
| 100 |
+-------+
1 row in set (0.00 sec)

trx1 mysql> update mysaldo set saldo = saldo + 100;
Query OK, 1 row affected (0.00 sec)
Rows matched: 1 Changed: 1 Warnings: 0

trx1 mysql> select * from mysaldo;
+-------+
| saldo |
+-------+
| 200 |
+-------+
1 row in set (0.00 sec)

berhenti sampai sini, transaksi 1 tidak kita commit dulu ... langsung ke transaksi 2

trx2 mysql> select * from mysaldo;
+-------+
| saldo |
+-------+
| 200 |
+-------+
1 row in set (0.00 sec)

sudah bisa di baca di trx2 walaupun belum di commit lho di trx1

kalo aku rollback trx1 , di trx2 result dari select di mysaldo kembali menjadi 100

beda khan dengan isolation level repeateble read di atas, oke lanjut ke kasus 2

READ UNCOMMITED KASUS 2

oke dari read uncommited kasus 1, pikiranku yang agak menggelitik adalah ... misalnya transaksi ku yang pertama menambah jadi 100, jadi saldo ku sekarang adalah 200.
dan aku sebagai user sudah bisa langsung lihat ... bahwa duitku sekarang adalah 200, hasil dari penambahan di transaksi 1, walaupun transaksi 1 belum di commit.

Nah aku tergoda membelanjakan duitku itu 200 untuk beli pulsa misalnya. Ketika aku mau beli pulsa seharaga 200, tiba tiba trx1 yang penambahan 100 itu rollback bukannya commit ..... lalu gimana dong ????

kita coba aja ya ....

Transaksi 1 :

trx1 mysql> begin;
Query OK, 0 rows affected (0.00 sec)

trx1 mysql> select * from mysaldo;
+-------+
| saldo |
+-------+
| 100 |
+-------+
1 row in set (0.00 sec)

trx1 mysql> update mysaldo set saldo = saldo + 100;
Query OK, 1 row affected (0.00 sec)
Rows matched: 1 Changed: 1 Warnings: 0

stop gak di commit dulu pindah ke transaksi 2

Transaksi 2 :

mysql> begin;
Query OK, 0 rows affected (0.00 sec)

mysql> select * from mysaldo;
+-------+
| saldo |
+-------+
| 200 |
+-------+
1 row in set (0.00 sec)

mysql> update mysaldo set saldo = saldo - 200;

UPDATE INI TERNYATA TIDAK BISA ... DIA STATUS NYA WAITING TRANSAKSI 1 COMMIT DULU ATAU ROLLBACK,
KALO TRANSAKSI 1 COMMIT artinya PROSES BENER GAK ADA YANG SALAH, SALDO KU SEKARANG JADI 0 ...
TAPI KALO TERNYATA TRANSAKSI 1 ROLLBACK , apakah yang akan terjadi ???


di transaksi 1 kita melakukan rollback :

trx1 mysql> rollback;
Query OK, 0 rows affected (0.01 sec)

INILAH YANG TERJADI DI TRANSAKSI 2 :

trx2 mysql> select * from mysaldo;
+-------+
| saldo |
+-------+
| -100 |
+-------+
1 row in set (0.00 sec)

Hasilnya -100
untuk itulah isolation level read uncommited ini sering di kenal dengan sebutan DIRTY READ



READ COMMITED

mysql> SET GLOBAL TRANSACTION ISOLATION LEVEL READ COMMITTED;
Query OK, 0 rows affected (0.00 sec)

mysql> SELECT @@GLOBAL.tx_isolation, @@tx_isolation;
+-----------------------+------------------+
| @@GLOBAL.tx_isolation | @@tx_isolation |
+-----------------------+------------------+
| READ-COMMITTED | READ-UNCOMMITTED |
+-----------------------+------------------+
1 row in set (0.00 sec)


READ COMMITED KASUS 1

Transaksi 1

trx1 mysql> select * from mysaldo;
+-------+
| saldo |
+-------+
| 100 |
+-------+
1 row in set (0.00 sec)

trx1 mysql> update mysaldo set saldo = saldo + 100;
Query OK, 1 row affected (0.00 sec)
Rows matched: 1 Changed: 1 Warnings: 0

STOP JANGAN DI COMMIT DULU .. lanjutkan ke transaksi 2 dulu

Transaksi 2

mysql> begin;
Query OK, 0 rows affected (0.00 sec)

mysql> select * from mysaldo;
+-------+
| saldo |
+-------+
| 100 |
+-------+
1 row in set (0.00 sec)

Kita lihat pada kasus 1 ini tidak ada beda dengan isolation level repeatable read ... hasil transaksi 1 ketika belum di commit tidak bisa di baca di transaksi 2
berbeda dengan read uncomited yang sudah langsung bisa di baca di transaksi 2

lanjut ...

pada transakasi 1 kita melakukan commit

trx1 mysql> commit;
Query OK, 0 rows affected (0.02 sec)

Kita lihat apa yang terjadi di transaksi 2

mysql> select * from mysaldo;
+-------+
| saldo |
+-------+
| 200 |
+-------+
1 row in set (0.00 sec)

Kita lihat di sini ... level isolation level ini berbeda dengan repeatable read, jika repeatable read kasus seperti ini, di transaksi 2 belum akan kebaca menjadi bernilai 200. tapi masih 100 karena transkasi 2 nya harus di commit dulu juga baru bisa kebaca 200

untuk kasus kedua dari model dari read commited ini saya rasa udah terbayang. Yang jelas jika saya di transaksi 1 dapat tambahan 100 dan belum commit
maka saya tidak akan bisa membaca saldo saya 200, dan ketika transaksi pertama sudah commit baru terbaca 200, artinya ketika di transaksi kedua 200 ini mau saya habis belanjakan sudah tidak di jumpai masalah pada isolation level read uncommited

lanjut pada isolation level terakhir ....


SERIALIZABLE

set isolation level jadi serializeable ...

nah serializeable ini satu langkah lebih dari pada isolation level repeatable read. karena dengan serializable ini query select sudah di anggap transaksi dan akan melakukan locking tabel. jadi jika pada transaksi 1 kita hanya melakukan select, kemudian di interrupt transaksi kedua dengan melakukan update.
maka update di transkasi kedua ini tidak akan jalan sebelum transkasi 1 commit dulu. Di mana hal ini masih memungkinkan jida di lakukan dengan isolation level repeatable read

Transaksi 1

trx1 mysql> begin;
Query OK, 0 rows affected (0.00 sec)

trx1 mysql> select * from mysaldo;
+-------+
| saldo |
+-------+
| 100 |
+-------+
1 row in set (0.00 sec)

STOP di interrupt transaksi kedua

Transaksi 2

trx2 mysql> begin;

Query OK, 0 rows affected (0.00 sec)

trx2 mysql> select * from mysaldo;
+-------+
| saldo |
+-------+
| 100 |
+-------+
1 row in set (0.00 sec)

mysql> update mysaldo set saldo = saldo + 100;

Nah update ini tidak bisa , karena tabel mysaldo masih ke lock gara gara operasi select pada transaksi 1. Kita lihat dengan demikian upaya modifikasi tabel pada transaksi 2 tidak akan bisa menunggu transaksi 1 commit dulu.


aku pribadi masih tetep megang repeatble read, serializeable lebih bagus, tapi lebih banyak menggunakan resource dan punya issue performance ...

tapi seperti yang ada dalam forum mysql , kalo urusan nya sudah data penting, WHO CARES ABOUT PERFORMANCE ????

tanggapan, pertanyaan ... share, dan koreksi atas tulisan ini ....

monggo kita bahas ....