Monday, October 02, 2006

Prevayler

มองหา Persistent Storage ที่เหมาะๆมานานแล้ว
วันนี้ได้ฤกษ์ลองเล่น ​Prevayler ดู
(จริงๆ แล้ว prevayler มีมานานมากแล้ว
แต่ด้วย nature ของผมที่เมื่อก่อนเป็นพวก emotionally attached to database
พอเห็นคำเปรยที่ว่า Memory Only ก็เดินหนีแล้ว
)

ลองดู Architecture ก่อน
  • Prevayler maintain ข้อมูลใน Memory เท่านั้น
  • ข้อมูลที่ maintain อยู่ในรูป Plain Old Java Object
  • เนื่องจากข้อมูลเก็บอยู่บน Memory เป็นหลัก
    เพื่อให้ persistent สามารถ keep state ได้
    prevayler ใช้วิธีทำ snapshot ลงบน Disk
  • การแก้ไขข้อมูลจะต้องทำผ่าน Transaction
    โดยใช้ pattern ที่เรียกว่า Command pattern
  • เมื่อเราสั่ง execute Command Object(Transaction)
    Prevayler จะทำการ serialize Command Object ของเราลง Journal file ก่อนที่ update ข้อมูลใน memory
    เพื่อทีในกรณีที่ system fail ก่อนที่จะทำ snapshot
    จะได้สามารถทำ recover persistent state กลับมาได้

ฟังดูแล้ว ก็น่าสนใจดี

ลองดูภาพการใช้งานจริง
สมมติ application ตัวอย่างเป็น Bank application
ที่ต้อง maintain account ของ customer

ใน prevayler ไม่มี concept เรื่อง table แบบ Relational DB
ข้อมูลที่จะเก็บลง Prevayler ต้องอยู่ในรูป Object
ดังนั้นเราจึง define Bank Class ขึ้นมา เพื่อใช้ maintain account object ของเรา
public class Bank implements Serializable {

private static final long serialVersionUID = -3472784405094724103L;

public List<Account> accounts = new ArrayList<Account>();

public List<Account> getAccounts() {
return accounts;
}

public void addAccount(Account acct) {
accounts.add(acct);
}

}

ตัว Account Class ก็เขียนง่ายๆดังนี้
public class Account implements Serializable {

private static final long serialVersionUID = 5447177127896043827L;

private String code;
private BigDecimal amount;

public Account(String code, BigDecimal amount) {
this.code = code;
this.amount = amount;
}

... getter/setter here.

}


เนื่องจากกลไกที่ prevayler ใช้ในการทำ snapshot
ก็คือ java serialize ธรรมดา (ตรงนี้สามารถเปลี่ยนวิธีได้ เช่นไปใช้ xml serialize)
ดังนั้น class Bank กับ Account ของเรา ก็เลยต้อง implement Serializable

การเริ่่มต้นใช้งาน prevayler จะเริ่มต้นดังนี้
Prevayler prevayler = PrevaylerFactory.createPrevayler(new Bank(), "bank");     
Bank bank = (Bank) prevayler.prevalentSystem();


ในการที่จะสร้าง Account ขึ้นมา, เราจะไม่ใช้วิธี access Bank Object ตรงๆ
แต่จะทำผ่าน Command Object
ลองดูตัวอย่าง Command Object ที่ใช้ create Account
public class CreateAccount implements Transaction {

private static final long serialVersionUID = 8524963860312931007L;

private Account account;

public CreateAccount(Account account) {
this.account = account;
}

public void executeOn(Object prevalentSystem, Date executionTime) {
Bank bank = (Bank) prevalentSystem;
bank.addAccount(account);
}

}

เมื่อจะใช้งาน ก็สั่งผ่าน prevayler object
prevayler.execute(new CreateAccount(new Account("11", new BigDecimal("200.00"))));


ที่นี้ก็มาถึงคำถามถึงจุดนี้ถ้าเราปิดโปรแกรมเราลงไป แล้วเปิดขึ้นมาใหม่
Bank Object เราจะยังมี Account Object ที่พึ่งสร้างอยู่หรือไม่
คำตอบก็คือ "มี"
เพราะเมื่อเราสั่ง execute Command Object เมื่อไร
prevayler จะทำการสร้าง journal file ขึ้นมา
(1 command ต่อ 1 journal file)
กลไกการเขียน journal ก็ใช้ java serialize ธรรมดา

ที่นี้การเก็บข้อมูลในรูป journal อย่างเดียวคงไม่เหมาะแน่
เพราะมันจะเยอะแยะไปหมด และน่าจะกินเวลา startup time
(prevayler cliam ว่าสามารถ execute journal ใน rate 6000 journal/sec.)
prevayler ก็เลยมีความสามารถในการทำ snapshot ด้วย
โดยการสั่ง
prevayler.takeSnapshot();


หลังจากทดลองเล่น + อ่าน Architecture แล้ว
ก็จัดการ add เข้า Collection Tools ของตัวเอง
โดย usecase ที่จะนำไปใช้ ก็คือ
  • พวก Desktop Application ตัวเล็กๆ
  • กลไก Data บางอย่างที่ขี้เกียจทำ ORM (เริ่มเบื่อแล้ว)
    เช่น User preference ใน Web application
  • เอาไว้เก็บพวก workflow instance ก็ไม่เลว
  • เก็บ Fact ของพวก rule base ก็น่าจะได้


ข้อเตือนใจ
  • Prevayler maintain ทุกอย่างใน memory
    snapshot เป็นแค่ backup
    ดังนั้นต้องแน่ใจว่ามี Ram พอ, กับข้อมูลไม่เยอะจนเกินไป
  • อย่าไปนึกว่าจะมี กลไก Index แบบ Relational database
    การ access object เป็นไปตาม java object ธรรมดา
    ดังนั้นอะไรที่อยากให้ access เร็ว ก็ต้องใช้พวก Map
  • ถ้า Domain เราซับซ้อน
    command object ก็จะมีปริมาณสูง ทำให้ maintain ลำบากขึ้น (อะไร,อยู่ที่ไหน)

Related link from Roti

No comments: