Post นี้จะว่าด้วย solution สำหรับ java application ก่อน
ครั้งต่อไปถึงจะว่าด้วย caching บน RoR (ruby on rails)
Tool ที่ผมเลือกใช้สำหรับ solution นี้ ก็คือ
OSCacheOSCache มาพร้อมกับวิธีการใช้ 3 แบบใหญ่ๆคือ
- ใช้ API ตรงๆ
อันนี้เหมาะกับ implement caching ในฝั่ง business logic
- ใช้ JSP Taglib
อันนี้เหมาะสำหรับ fine-grain cache
เพราะเราสามารถกำหนดบริเวณที่เราต้องการ cache ได้ตามใจชอบ
(เลือกเฉพาะบางส่วนใน jsp ได้)
- ใช้ CacheFilter
อันนี้เหมาะสำหรับ cache ระดับ Page
(เพราะว่าสะดวกในการ config ดี)
ขั้นแรกในการใช้ OSCache ก็คือ
การกำหนด Configuration ที่เราต้องการ
- เลือกกำหนดขนาดของ cache ที่เราต้องการก่อน
ค่า default จะอยู่ที่ 1000 objects
(สามารถกำหนดเป็น unlimit ก็ได้)
- ประเด็นที่ตามมาก็คือ algorithm ในการควบคุมขนาดของ cache
OSCache มีให้เลือก 2 แบบ
- LRU (Least-Recently-Used)
- FIFO
ประเด็นที่น่าสนใจอันหนึ่งของ LRU
ก็คือ OSCache implement LRU โดยใช้
java.util.LinkedHashMap
ซึ่งถ้าเราตามไปดู javadoc ของ LinkedHashMap จะเห็นคำอธิบายดังนี้
A special constructor is provided to create a linked hash map whose order of iteration is the order in which its entries were last accessed, from least-recently accessed to most-recently (access-order). This kind of map is well-suited to building LRU caches.
และถ้าตามไปดู source code ของ OSCache ในส่วน
LRU ก็จะพบว่า วิธีการที่มัน implement LRU จะทำเป็น 3 step
ก็คือ
- ทดลองดูว่าใน classpath มี LinkedHashMap หรือไม่
ถ้ามีก็ใช้ตัวนี้
- ถ้าไม่มี ก็จะมองหา org.apache.commons.collections.SequencedHashMap
ของ jakarta-commons collection แทน
- ถ้าไม่พบ ก็จะลงเอยด้วยการใช้ LinkedList
ซึ่งอันนี้อาจจะมีผลเสียต่อ performance แทนในกรณี load สูงๆ
- เลือกว่าจะทำ Persistent ของ cache ด้วยหรือไม่
โดย default แรกสุด จะเก็บ cache ใน Memory เท่านั้น
แต่เราสามารถเลือก persistent ลง Disk ได้ด้วย
โดย option ที่น่าสนใจก็คือ
cache.persistence.overflow.only
ถ้าเรา set ค่านี้เป็น True เวลาที่ Memory Cache เราเต็ม
entry ที่ถูก remove ออก จะย้ายมาลง Disk แทน
- กำหนด
cache.blocking
default คือ false นั่นคือ กรณีที่ entry ของเราหมดอายุ
แล้วมี thread 2 thread เข้ามาพร้อมๆกัน thread แรก
จะเป็นผู้ได้รับเลือกให้เป็นคนที่ update ค่านั้น ส่วน thread ที่ 2
จะได้ค่า ณ ขณะนั้น (stale entry) กลับไปแทน
(ซึ่งก็ถือว่าใช้ได้นะ เพราะถือว่า entry มีอายุเกินกว่าที่เรากำหนดไปไม่กี่ millisec)
แต่ถ้ากำหนดเป็น true ก็จะมีผลว่า thread ที่ 2
จะถูก block เพื่อรอให้ thread 1 ทำการ update ให้เสร็จก่อน
Note: เท่าที่ลองใล่ debug ดู พบว่า
กรณีที่เป็น new Entry ใหม่ๆเลย ไม่ว่าเราจะเลือก block เป็น true หรือ
เป็น false ก็ตาม
OSCache จะเลือกใช้วิธี blocking สำหรับพวก new Entry เสมอ
ตัวอย่าง source code ให้ลองดูที่
document ของ OScache โดยตรง
เพราะเขาเขียนไว้ดีแล้ว
feature ที่น่าสนใจอื่นๆก็คือ
- การกำหนด cron expression
ที่ทำให้เรากำหนด schedule ของการ expire ได้
เช่นกำหนดให้ cache expire ทุกเช้าวันจันทร์
- การกำหนด group ของ cache
อันนี้เหมาะสำหรับกำหนดกลุ่ม cache ที่มี content depend ถึงกัน
เวลาเรามีการ update content จะได้สั่ง flush cache ทีเดียวทั้ง group ได้เลย
- ใน jsp taglib หรือ cacheFilter สามารถกำหนด
scope ของ cache ได้
เช่นถ้าเป็น session, cache ก็จะถูกแยกเป็นของใครของมันเลย
ไม่ share กัน
แต่ถ้าเป็น application ก็จะมี cache แค่ instance เดียว
ให้ทุก session share ใช้กัน
เพิ่มเติมประเด็นที่ต้อง design ดีๆ เวลาใช้ cache
ก็คือ กรณีที่ table ใน database เรามีการเปลี่ยนแปลง
แล้วเราต้องการ flush cache (delete cache) ที่เกี่ยวข้องทิ้งทั้งหมด
เราจะทำได้อย่างไร
ทางออกก็อาจจะเช่น design ให้ key ที่มี content เกี่ยวข้องกัน มี prefix เหมือนๆกัน
(เวลาสั่ง flush สามารถสั่ง flush พวกที่มี prefix ตามที่กำหนดได้)
หรือเลือกใช้ group feature เข้ามาช่วย