Friday, March 16, 2007

เกินมาจากไหน

เมื่อ 3 วันก่อน Database ของระบบที่ให้บริการอยู่เกิดล่มลง
จากการวิเคราะห์ log files ต่างๆ
มีข้อสันนิษฐานว่า จำนวน sessions ที่ client connect เข้ากับ database
มันเกิดค่าที่ตั้งไว้ (ไม่ใช่สาเหตุตรงๆที่ทำให้ล่ม แต่มันทำให้ resources ต่างๆมันไม่พอ)

จากการนับจำนวน user ที่เป็น client ต่อตรง ณ ขณะนั้น
มีจำนวน 262 จอ
นำจำนวน session มาลบออก ก็จะได้เป็นจำนวน connection
ที่ web application ต่อ = 485 - 262 = 218 connection

มาจากไหนวะ ตั้ง 218, connection leak เหรอ
จากการคำนวณแล้ว connection ทั้งหมดที่ web app พึงใช้น่าจะประมาณ 52 connection

ปัญหานี้เคยคิดไว้เหมือนกัน
เลยทำหน้าจอสอบถามใน web app ให้ admin สามารถดูได้ว่า
connection pool size ที่ใช้อยู่ปัจจุบันมีค่าเท่าไร
เปิดเข้าไปดูใน app
ปรากฎว่ามี active อยู่แค่ 5
แล้วมันจะกลายเป็น 218 ได้ไงวะเนี่ย

ผ่านไป 3 วัน
เข้าไปอ่านทบทวน file ~tomcat/conf/context.xml ที่ config Datasource ไว้
<!-- The contents of this file will be loaded for each web application -->
<Context>

<Resource auth="Container"
description="DB
Connection"
driverClass="ca.ingres.jdbc.IngresDriver"
maxPoolSize="21"
minPoolSize="5"
acquireIncrement="5"
name="jdbc/ssoDS"

...
</context>


อ่านบรรทัดแรกปุ๊บ สะดุ้งโหย๋ง
ชิบหาย web application เรามีตั้ง 10 กว่า app
มิน่าเล่า
ค้นหาเอกสาร tomcat ดู
ถ้าอยาก config datasource ให้ share กันระหว่าง context ได้
ต้อง config ไว้ใต้ elememt GlobalNamingResources ใน file server.xml

จากการสอบถาม เจ้าตัวผู้เป็นคน config
เขาบอกว่า เดิมก็พยายาม config ไว้ใน GlobalNamingResources เหมือนกัน
แต่ทำแล้วไม่ work สักที ก็เลยย้ายไปลองใส่ไว้ใน context.xml
ซึ่งใส่ปุ๊บก็ work เลย.

เข้าข่าย programming by coincident อีกแล้ว

Related link from Roti

Thursday, March 15, 2007

Terracotta #2

ต่อจากตอนที่แล้ว

Roots
คือ objects ที่เราต้องการให้ทุก node ที่เข้ามา join cluster
มองเห็นเป็น object เดียวกันหมด

สิ่งที่เกิดขึ้นเมื่อเรา define root object ก็คือ
ตอนที่ class loader กำลังเริ่ม load class
จะมีการ add Advice (AOP) แบบนี้เข้าไป
Object around() : get(@Root * *.*) {
String name = thisJoinPointStaticPart.getSignature().toLongString();
return ClusterManager.getOrCreateRoot(name);
}

ซึ่งพฤติกรรมของมันก็คือ เมื่อมีการพยายามที่จะ get ค่า จาก field ที่เป็น root
around advice ก็จะ intercept การ access นั้น
แล้ว delegate ต่อไปยัง ClusterManager แทน
ซึ่ง ClusterManager ก็จะทำการ connect ไปยัง Terracotta Server
เพื่อขอค่า instance ของ root object นั้นมา

ที่ฝั่ง server เมื่อได้รับ request ก็จะทำการตรวจสอบว่า
ใน repository ของตัวเองมี root object ตามชื่อนั้นหรือไม่
ถ้าไม่มี ก็จะทำการสร้างขึ้นมาใหม่ให้ แต่ถ้ามีก็จะทำการส่ง Object นั้นกลับมา

จะเห็นได้ว่า Terracotta Server จะเป็น Node หลักที่มี Data เก็บไว้ในเครื่องตัวเอง
ส่วน Virtual machine อื่นๆ ที่ join เข้าไป ก็จะมี replicate set ของ Data นั้นๆ

object ที่จะกำหนดให้เป็น Root จะเป็น Object ใดๆ ก็ได้
ไม่จำกัดว่าต้องเป็น Serializable
กลไกที่ Terracotta ใช้ replicate changed จะมีลักษณะเป็น fine grained
นั่นคือ replicate เฉพาะส่วนที่เปลี่ยนแปลง ไม่ได้ทำหมดทั้งก้อน

จากตัวอย่างเมื่อวาน ถ้าเราลองเปิด admin UI ของ Terracotta ดู
จะเห็นว่าเรามี root object ที่ชื่อ bid.Bidding.bid



Virtual Heap
เมื่อมีการ define Root Object ก็ต้องมีการ replicate object นั้น ไปให้ทุก Virtual machine ที่ต้องการใช้
วิธีการที่ Terracota ใช้ก็คือการ มีการกำหนดพื้นที่สำหรับเก็บข้อมูลบนแต่ละ Virtaul machine ขึ้นมา
ซึ่งเรียกว่า Virtual Heap
การเก็บข้อมูลใน Virtual Heap จะใช้หลักเดียวกับ cache ก็ืคือ
พื้นที่เก็บมีจำกัด ข้อมูลไหนที่ไม่ค่อยได้ใช้ จะต้องถูก flush ออก เพื่อเปิดทางให้กับข้อมูลที่ถูกใช้บ่อยๆแทน

เวลาที่ Node ของเรา เริ่ม join เข้า cluster
Terracota จะยังไม่ copy ข้อมูลจาก server มาทันที
แต่จะรอให้มีการ access object นั้นก่อน จึงจะ copy ข้อมูลมาใส่ไว้ใน Virtual Heap ให้
(ทำในลักษณะ fine grained นั่นคือ เฉพาะ field ที่เกิดการ access จริงๆ)

Related link from Roti

Wednesday, March 14, 2007

Terracotta #1

ได้ฤกษ์ทดลอง Terracotta แล้ว หลังจากที่ดองเรื่องไว้นานแสนนาน
สำหรับคนที่ไม่รู้จัก Terracotta
Terracotta คือ clustering Techonology อันหนึ่ง
ที่ใช้หลักการ AOP เข้าไปเปลี่ยนแปลง java byte code ของเรา
ทำให้ POJO Object ของเราประพฤติตนในลักษณะ cluster ได้

AOP library ที่ Terracotta เลือกใช้ก็คือ AspectWerkz
ส่วน byte code manipulation library เขาใช้ ASM

สิบปากว่าไม่เท่าตาเห็น
ทดสอบเขียน application ง่ายๆ ในเรื่องการประมูล

เริ่มด้วย class Bid แทน item ที่กำลังถูกประมูลอยู่
ภายในมี information แค่ว่า ใครประมูลล่าสุด ด้วยราคาเท่าไร
package bid;

import java.math.BigDecimal;

public class Bid {
private String name = "";
private BigDecimal currentPrice = BigDecimal.ZERO;

public BigDecimal getCurrentPrice() {
return currentPrice;
}
public void setCurrentPrice(BigDecimal currentPrice) {
this.currentPrice = currentPrice;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}


จากนั้นก็ทดลองเขียน class Bidding ที่เป็น console application ที่ใช้ในการประมูล
โดยวิธีใช้งานมันจะหน้าตาประมาณนี้
Command (b-bid, n-new, s-show, q-quit) : n
id ? 1
Command (b-bid, n-new, s-show, q-quit) : s
id : 1, -> name = , currentPrice = 0
Command (b-bid, n-new, s-show, q-quit) : b
id ? 1
name :pok
price :200
Command (b-bid, n-new, s-show, q-quit) : s
id : 1, -> name = pok, currentPrice = 200
Command (b-bid, n-new, s-show, q-quit) : q

ตั้งโครง class Bidding ก่อน โดยในการประมูลครั้งหนึ่งๆ เรามี item ที่จะประมูลได้หลายอันพร้อมๆกัน
ดังนั้นเราจึงใช้ HashMap ในการเก็บ instance Bid ที่กำลังประมูลอยู่
public class Bidding { 

ConsoleReader console;
HashMap<String,Bid> bids = new HashMap<String,Bid>();

public static void main(String[] args) throws IOException {
new Bidding().run();
}
}

method run ก็คือ method ที่ใช้ loop เพื่อรับ input จากหน้าจอ console ของ user
    public void run() throws IOException {
console = new ConsoleReader();

while (true) {
String tmp = console.readLine("Command (b-bid, n-new, s-show, q-quit) : ");

switch (tmp.charAt(0)) {
case ('q'):
return;
case ('n'):
newBid();
break;
case ('s'):
show();
break;
case ('b'):
bid();
break;
}
}
}

method newBid คือ method ที่ใช้สร้าง bid item อันใหม่
    public void newBid() throws IOException {
String id = console.readLine("id ? ");
Bid bid = new Bid();
bids.put(id, bid);
}

method show ทำหน้าที่ print bid item ทั้งหมด
    public void show() throws IOException {
for (String key : bids.keySet()) {
Bid bid = bids.get(key);
console.printString("id : " + key + ", -> ");
console.printString("name = " + bid.getName() + ", currentPrice = " + bid.getCurrentPrice());
console.printNewline();
}
}

method bid ก็คือการเสนอราคาประมูล
    public void bid() throws IOException {
String id = console.readLine("id ? ");
Bid bid = bids.get(id);
String bidder = console.readLine("name :");
String price = console.readLine("price :");
BigDecimal p = new BigDecimal(price);
if (p.compareTo(bid.getCurrentPrice()) > 0) {
bid.setCurrentPrice(p);
bid.setName(bidder);
}
}


จะเห็นว่าเราเขียนโดยไม่มีแนวคิดเรื่อง cluster หรือ share thread อยู่ในนี้เลย
ทีนี้ก็มาถึงคำถามว่า ถ้าเราต้องการจะให้มันเป็น cluster ขึ้นมาหล่ะ
เราจะทำอย่างไร
เริ่มด้วยลักษณะ cluster ที่เราต้องการก่อน
เราจะ start program bidding หลายๆตัว บน virtual machine คนละตัวกัน
แต่เราต้องการให้ bidding มองเห็น instance variable bids (HashMap) ของทุกตัวเป็นตัวเดียวกัน
จะได้แย่งกันประมูลได้

เริ่มแรกสุด terracotta มี architecture เป็นแบบ hub and spoke
ไม่ได้เป็น peer-to-peer ดังนั้นเราต้อง start terracotta server ขึ้นมาก่อน
โดยใช้คำสั่ง start-tc-server.sh

จากนั้นเราก็ต้องกำหนด configuration เพื่อให้ terracotta รู้ว่า
ควรจะตัดต่อ code เราตรงไหนบ้าง
โดยสิ่งแรกที่ต้องกำหนดก็คือ root object
root object คือ object ที่เราอยากให้ ทุกๆ virtual machine ที่ join เข้าวง cluster มองเห็นเป็นตัวเดียวกัน
ในกรณีของเรา root object ก็คือ

HashMap<String,Bid> bids = new HashMap<String,Bid>();

ซึ่งใน config ของ terracotta เราจะกำหนดโดยใช้ xml ดังนี้
<roots>
<root>
<field-name>bid.Bidding.bids</field-name>
</root>
</roots>


เมื่อกำหนด root object แล้ว
เราก็ต้องบอกให้ terracotta จัดการ modify access method ทุกๆอันของเรา
ที่มีการเรียกใช้ root object
โดยเราสามารถเลือก lock ว่าจะเป็น read lock, write lock, และ concurrent

เริ่มด้วย method show ก่อน
อันนี้เลือกได้ว่าจะเป็น read หรือ concurrent หรือไม่กำหนดเลยก็ได้
ถ้าเป็น read ถ้ามันเจอใครกำลัง write อยู่ มันจะ wait รอ
ส่วน concurrent เป็นแบบที่ไม่สนว่าใครกำลังทำอะไร จะเขียนหรืออ่านก็ได้
ลองกำหนดเป็นแบบ read จะได้ไม่เจอ case แปลกๆเช่น ชื่อเป็นชื่อของคนคนหนึ่งและราคาเป็นของอีกคนๆหนึ่ง
  <named-lock>
<lock-name>show</lock-name>
<method-expression>* bid.Bidding.show(..)</method-expression>
<lock-level>read</lock-level>
</named-lock>

สังเกตุว่ามันมี type เป็น named-lock
ซึ่งมีความหมายว่า method ที่เราร้อย AOP นี้ไม่ได้เขียนแบบคำนึงถึง multi-thread มาก่อน (ไม่ได้ใช้ keyword พวก synchronized)
โดยเราต้องกำหนด name ของ lock ให้มันด้วย

จากนั้นก็ว่าด้วย method newBid โดย
เรากำหนดให้เป็น write lock
  <named-lock>
<lock-name>newBid</lock-name>
<method-expression>* bid.Bidding.newBid(..)</method-expression>
<lock-level>write</lock-level>
</named-lock>


method สุดท้ายก็คือ bid
method นี้พิเศษหน่อยตรงมันจะมี process การอ่าน console ก่อน จากนั้นจึงค่อย เขียนลงไป
เราเลยลองใช้ synchronized ครอบลงไป เพื่อไม่ให้เจอ user ใส่ข้อมูลค้างไว้
แล้วลุกไปกินกาแฟ ทำให้ระบบเราหยุดเพราะเจอ write lock เปิดทิ้งไว้
แก้ code ดังนี้
        synchronized(bids) {
if (p.compareTo(bid.getCurrentPrice()) > 0) {
bid.setCurrentPrice(p);
bid.setName(bidder);
}
}

จากนั้นก็กำหนด configuration ของ lock
แต่คราวนี้จะสังเกตว่าเราเปลี่ยนไปใช้ autolock type แทน
  <autolock>
<method-expression>* bid.Bidding.bid(..)</method-expression>
<lock-level>write</lock-level>
</autolock>


เสร็จ แค่นี้เราก็ทดลอง run มันจาก หลายๆ virtual machine เข้าไปประมูล
บน bids ชุดเดียวกันได้แล้ว
เวลา run ก็ใช้คำสั่งประมาณนี้
โดย tc-config.xml ก็คือ configuration file ของเรา
~/dev/terracotta-2.2/dso/bin/dso-java.sh -cp bin:jline-0.9.91.jar -Dtc.config=tc-config.xml bid.Bidding 


ง่ายจนน่าตกใจ

Related link from Roti

Tuesday, March 13, 2007

Propaganda

รอบกายเรานี้มีแต่ propaganda
ดังนั้นเราควรจะเข้าใจกฎเกณฑ์พื้นฐานของมันหน่อย

Theorists of propaganda have identified five basic rules:

1. The rule of simplification: reducing all data to a simple confrontation between “Good and Bad”, “Friend and Foe”.
ผมเคยถกประเด็น technique กับ it consultant ที่มีอาชีพทนายมาก่อน
เจอ technique นี้เข้าไป แพ้หลุดลุ่ยเลย
ไอ้เราก็มัวอักๆอักๆ เพราะสำหรับเราแล้ว
ทุกๆอย่างมันมีทั้งข้อดีและข้อเสีย

2. The rule of disfiguration: discrediting the opposition by crude smears and parodies.
ป้ายสีอีกฝ่ายเสีย

3. The rule of transfusion: manipulating the consensus values of the target audience for one’s own ends.
เกาะกระแส
หาประโยชน์จากเสียงส่วนใหญ่

4. The rule of unanimity: presenting one’s viewpoint as if it were the unanimous opinion of all right-thinking people: drawing the doubting individual into agreement by the appeal of star-performers, by social pressure, and by “psychological contagion”.
(unanimity แปลว่า การยอมรับอย่างเป็นเอกฉันท์)
โชคร้ายที่เราไม่มีกลไกที่จะรู้ได้ว่า เสียงส่วนใหญ่เป็นอย่างไร
นักการเมือง ก็มักอ้างว่าความคิดเห็นตัวเองตรงเสียงส่วนใหญ่
ส่วนใครที่ค้าน ก็มักจะถูกจัดการว่า คุณต้องยอมรับประโยชน์ส่วนรวม, หรือใช้ชั้นเชิงการแสดงชักจูง

5. The rule of orchestration: endlessly repeating the same messages in different variations and combinations.
ตัวอย่างที่เห็นชัด ก็คือ โครงการที่เกี่ยวกับสถาบันกษัตริย์ (ไม่ได้พูดประเด็นถูกผิดนะ)

The Five rules of propaganda

Related link from Roti

Monday, March 12, 2007

ว่ายน้ำในทะเล

อาทิตย์ที่ผ่านมา ผมพาแฟนและลูกชายไปเที่ยวหาดเจ้าหลาว, จันทบุรีมา
ไม่นึกว่าชายหาดเมืองจันฯ ก็สวยไม่แพ้ที่อื่นเหมือนกัน
แต่ที่ชอบก็คือน้ำทะเลช่วงนี้ ที่เรียบสนิท ปราศจากคลื่น

กิจกรรมหนึ่งที่ผมขาดไม่ได้เวลาไปเที่ยวทะเล
ก็คือการว่ายน้ำในทะเล ที่ภาษาอังกฤษเขาใช้คำว่า open water swimming
ประเด็นที่ผมหลงไหลในการว่ายน้ำทะเล มีอยู่ดังนี้
1. ผมชอบบรรยากาศ สีสรร ของใต้น้ำ สีกับแสงมันสวยดี (จริงๆแล้วมันมีแค่ฟ้ากับเขียว แต่มันมีหลายเฉดสี)
สัมผัสของน้ำก็ทำให้รู้สึกดี
2. ความหนาแน่นของน้ำทะเล ทำให้เรารู้สึกตัวเบา ว่ายเหนื่อยน้อยกว่าในน้ำจึดเยอะ
3. ความรู้สึกเปิด อิสระ, ไม่มีลู่ว่าย ไม่มีเส้นสีดำบนพื้นสระที่กำหนดแนวว่าย
ไม่มีผนังสระที่ต้องกลับตัว, ไม่มี boundery ที่บอกว่า เฮ้ยว่ายครบ 100 metre แล้ว
มีแต่ว่ายเท่าไรก็ไปไม่ถึงไหน
4. ภาวะสมาธิที่เกิดจากความเงียบ,การทำซ้ำ และการ focus ที่การเคลื่อนไหว



อุปสรรคของการว่ายน้ำในทะเล
เท่าที่เจอมาก็มี
1. ความกลัว อันนี้เป็นผลมาจากหนังที่เกี่ยวฉลามทั้งหลาย



พอเข้าเขตที่ลึก วิตกจริตจะมาก่อนเลย
ยิ่งที่ลึกๆและน้ำใสๆ ถ้าความลึกเกิน 80 ฟุต สีของน้ำจะเป็นสีน้ำเงินเข้มเลย
ที่เคยกลัวที่สุดก็คือ ครั้งที่ไปดำน้ำแถวตรัง
โดยเรือมุ่งหน้าออกจากภูเก็ต และระหว่างทางมีการจอดเรือแวะพักทานข้าวที่ข้างเกาะแห่งหนึ่ง
ผมก็เลยได้ทีกระโดดน้ำว่ายเข้าเกาะ
แต่เนื่องจากเกาะมีลักษณะเป็นภูเขาใต้น้ำที่โผล่ยอดขึ้นมา ไม่มีหาดทราย ไม่มีทางลาดใต้น้ำ ที่ใล่จากตื้นไปลึก
ว่ายไปได้สักพัก จินตนาการก็เริ่มทำงาน
ปลาฉลามตัวใหญ่มันจะอยู่แถวนี้หรือเปล่าวะ
พยายามสงบจิตสงบใจว่ายต่อจนถึงเกาะ (ซึ่งก็น่ากลัวไปอีกแบบ เพราะเป็นหน้าผาทั้งบนบกและใต้น้ำ)
แต่อาการก็ไม่หาย ขาว่ายกลับเรือนี่เป็นเอามาก
สุดท้ายก็เลยต้องเปลี่ยนไปว่ายท่ากบ
เพื่อลดเวลาที่ต้องมองจ้องใต้น้ำ ให้น้อยที่สุด
(ไม่มองก็ไม่ได้ เพราะจินตนาการเรื่องฉลาม มันบังคับให้เราต้องเฝ้าระวังดูใต้น้ำ)

ความกลัวฉลามนี้จะไม่เกิดถ้ามีเพื่อนว่ายไปด้วย
อย่างที่เคยว่ายที่เกาะโลซิน ปัตตานี ซึ่งน้ำลึก > 80 ฟุต
ครั้งนั้นมีเพื่อนว่ายไปด้วย ว่ายอยู่ชั่วโมงกว่า ไม่รู้สึกกลัวเลย

2. แมงกระพรุน และแตนทะเล
ชื่อหลังนี้ไม่รู้ชื่อจริงๆของมัน รู้แต่ว่า มันมองไม่เห็น แต่โดนแล้วทั้งเจ็บและแสบเป็นบ้าเลย
แถมยังมีเยอะด้วย ว่ายทีไรก็โดนทุกที
ยิ่งถ้าโดนที่ปาก ก็จะเจ็บเป็นพิเศษเลย
ทะเลแถวอันดามัน นี่เป็นภาคบังคับเลย ไม่เจอก็แปลก
ส่วนแมงกระพรุนนั้นโชคดีว่ายังไม่เคยเจอแมงกระพรุนไฟ
เคยเจอแต่แมงกระพรุนธรรมดา
ซึ่งเวลาเจอก็มักจะเกิดอาการสะดุ้งตกใจ
เพราะมือหรือขามักไปโดนก่อนที่ตาจะเห็น

ที่ประทับใจที่สุด ก็เป็นที่เกาะช้าง
เพราะเคยเจอแมงกระพรุนตัวเล็กๆขนาดประมาณ 1 นิ้ว
ตัวใสมากถ้าอยุ่ในน้ำทะเลนี่จะมองไม่เห็น
เคยว่ายแหวกเข้าไปในฝูงใหญ่ของมัน
ได้อารมณ์ชวนสะดุ้งมากเลย มองไม่เห็นแต่สัมผัสได้

3. กระแสน้ำ
อันนี้มักจะเจอตามเกาะ, พวกชายฝั่งธรรมดายังไม่เคยเจอ
ที่แรงสุดที่เคยเจอก็ที่เกาะแสมสาร สัตหีบ
ผมลงจากหาดทรายบนเกาะไปว่ายน้ำเล่น
ว่ายออกไปไม่กี่เมตร ก็เจอกระแสน้ำแล้ว
พัดทีตัวปลิวเลย
เวลาจะกระแสน้ำแรงๆ ท่าว่ายที่เหมาะที่สุดก็คือท่ากบ

4. อึ
ไม่ได้สะกดผิด, อันนี้เกิดขึ้นเวลาที่เราว่ายจากฝั่งไปขึ้นเรือ
ซึ่งบันใดเรือมันมักจะอยู่ท้ายเรือข้างๆห้องน้ำ
อันนี้ต้องเล็งและกะจังหวะให้ดี
ไม่งั้นจะเกิดภาวะสยองของการว่ายฝ่าเหล่าของเสียสีเหลืองได้

Related link from Roti