พื้นฐานการแนวคิด In-Memory Computing และ Real Time Data Processing ด้วย Hazelcast Part 1
ในปัจจุบันเราจะพบว่าเรามีเทคนิคและคำศัพท์ใหม่ๆเกี่ยวกับวงการ IT หลายๆอย่างมากมายทั้งในแง่ของการจัดการหรือจะเชิงของเทคนิคเช่น Framework/ Ideaในการทำงานแบบใหม่ๆมีความร่วมมือกันมากขึ้นอย่าง DevOps หรือจะเทคโนโลยีเฉพาะทางใหม่ๆอย่าง Blockchain ซึ่งปัจจุบันก็ต่อยอดไปถึง Web3 กันได้แล้ว แต่สิ่งนึงที่จะไม่เคยหายไปเลยนั่นก็คือเรื่องของข้อมูลที่เมื่อเกิดขึ้นมาแล้วก็มักจะอยู่กับเราไปตลอด (ถ้าหากเป็นข้อมูลที่จำเป็นต้องเก็บ) ซึ่งจุดที่น่าสนใจก็คือเทคนิคของการแตกย่อย Service หรือจะออกแบบยังไงให้ระบบกระจายกันไปเพื่อได้คุณสมบัติ Scale Out ก็ก็ยังคงเรื่องของความเร็วในการประมวลผลให้ผู้ใช้ปลายทางไม่รู้สึกว่าข้างหลังมีอะไรเปลี่ยนไปอย่างไร หรือมีอะไรช้าไหม สนใจแค่ว่าใช้งานปุ๊ปทุกอย่างเป็นปกติไม่เกิด Delay เกิดกว่าที่ SLA ที่ตกลงไว้ก็พอ ซึ่งเมื่อพูดถึงเรื่องของข้อมูลที่เกิดขึ้น “Data” ของเรานั้นอาจจะแบ่งประเภทเป็น Data at Rest และ Data in Motion
โดย Data at Rest นั้นน่าจะเป็นสิ่งที่เราคุ้นเคยกันมากที่สุดก็คือเรื่องของข้อมูลสุดท้ายก็ถูกจัดเก็บลงไปจริงๆสักที่หนึ่งแล้วเราก็ค่อยนำข้อมูลเหล่านั้นมาใช้ในภายหลังซึ่งก็ดูแล้วเป็นเรื่องปกติและทั่วๆไปดี แต่ด้วยปัจจุบันเพื่อนๆอาจจะเคยได้ยินคำแว้บๆแบบ Big Data กันมาบ้างซึ่งด้วยแนวคิดของ Big Data ที่มี 3V เป็นตัวพื้นฐานอย่าง Volume, Velocity, Varity นึกง่ายๆไม่ต้องจำก็คือ ข้อมูลนั้นเยอะแถมยังเข้ามาอย่างรวดเร็วด้วยรูปแบบของข้อมูลที่หลากหลายอีกต่างหาก ให้เพื่อนๆลองนึกถึง Social media ก็น่าจะพอนึกออกกันใช่ไมหคับว่าวันนึงมีข้อมูลเยอะมากแค่ไหน ดังนั้นแล้วการแบ่งประเภทนอกจาก Data at rest ที่เราอาจจะเห็นว่ามันก็ดูเป้นแค่ข้อมูลนิ่งๆรับมาเก็บไปค่อยๆขยับดูเหมือนอาจจะไม่พอ ก็เลยมีการแบ่งให้เห็นพฤติกรรมของข้อมูลอีกประเภทหนึ่งอย่าง Data in Motion ที่หมายถึงข้อมูลที่มีการเคลื่อนไหวตลอดนั้นเองซึ่งถ้าดูอย่าง Data in Motion ก็จะเห็นว่าพวกเหตุการณ์แบบ Real Time, Steaming, Messaging ที่เกิดขึ้นอย่างรวดเร็ว Latency ต่ำๆก็ดูพอจะเหมือนเข้าท่าในเรื่องของ Data in motion ได้ แต่ความน่าสนใจก็อยู่ที่ตรงคำว่า Processing กับ Analytic ในฝังของ Data in Motion เพราะถ้าเราลองนึกดูว่าข้อมูลที่มาทั้งเยอะหลากหลายต่อเนื่องตลอดเวลาแบบนี้ แล้วการวิเคราะห์ข้อมูลเพื่อนำไปตัดสินใจหรือแยกแยะว่ามีข้อมูลบางอย่างที่มี pattern ที่แปลกไปดูแล้วน่าสนใจต่อการเจาะวิเคราะห์ลงไปว่า ทำไม pattern บางอย่างถึงเปลี่ยนไปนั้นเกิดขึ้นได้อย่างไร ?
ที่มีคำถามนี้เกิดขึ้นก็เพราะว่าด้วยพฤติกรรมการใช้งานของผู้ใช้หรือจะเป็นปัจจัยภายนอกที่ไม่ได้ได้เกิดมนุษย์โดยตรงก็เกิดขึ้นตลอด การวิเคราะห์ตรวจจับข้อมูลแบบเดิมๆที่ไม่ได้ Real Time ก็อาจจะทำให้เราพลาดข้อมูลที่สำคัญบางอย่างไป ซึ่งทีนี้เราจะลองมาดูตัวอย่างกันว่าแล้ว Data in Motion หรือการทำ Real time processing นั้นมีประโยชน์อะไรกับเราบ้าง
ถ้าหากเราลองมาดูถึงข้อแรกกันเลยอย่าง “ทำให้เร็วขึ้น” ผมเชื่อว่าเพื่อนๆพี่ๆทุกคนก็น่าจะต้องการข้อนี้อยู่แล้ว 😆 แต่แล้วในเชิงเทคนิคก็อาจจะมีศัพท์ที่เรียกว่า Caching ซึ่งการทำ Caching นั้นก็คือการที่เรานำข้อมูลที่เราเคยเรียกขึ้นมาแล้วไปฝากเอาไว้กับหน่วยความจำที่เร็วกว่าที่เก็บข้อมูลถาวรอย่าง storage ทั่วๆไปข้อเรา เพราะที่เราจะนำมาเก็บในตอนนี้ก็คือการว่าไว้ใน RAM (Memory) ซึ่งการนำข้อมูลมาฝากไว้ใน Memory ก็ทำให้เวลาที่เราจะเรียกอ่าข้อมูลเดิมอีกเช่นรายการของสินค้าที่มีข้อมูลยาวๆก็ถูกฝากเอาไว้เลยที่ Memory ดังนั้นต่อไปนี้ตราบใดที่ข้อมูลสินค้าตัวนั้นยังไม่เคยอัพเดท ผู้ใช้ระบบของเราก็เรียกตรงผ่าน Cache ที่ฝากไว้ใน Memory ได้เลยทำให้การ Query ข้อมูลนั้นไวกว่าการไปค้นหาใหม่ตั้งแต่ต้นมาก
ซึ่งถ้าเพื่อนๆพี่ลองนึกต่อไปอีกก็อาจจะมีคำถามว่าแล้วถ้าข้อมูลมันอัพเดทตลอดเลยเนี่ยแบบนี้ Cache ก็น่าจะไม่มีประโยชน์หรือเปล่านะ ? เพราะอัพเดทปุ๊ปแปลว่าของที่ฝากไว้ใน Cache มันก็กลายเป็นข้อมูลเก่าไปล่ะสินะ ซึ่งทีนี้ถ้าเราลองนึกต่อไปอีกว่าเอ๊แต่จริงๆแล้วคำว่าข้อมูลเก่าไปแล้ว ? แค่นั้นคือเก่าแล้ว “ไม่สามารถใช้ได้” หรือว่าจริงๆแล้วมันเก่าไปแต่จริงๆเราก็ยังยอมรับได้อยู่ ? ซึ่งถ้าเราเริ่มมีคำถามแบบนี้แล้วการออกแบบ Data Platform ของเราให้เหมาะสมก็อาจจะเริ่มเป็นจุดเริ่มต้นที่ดีครับว่าเราควรจะเลือกใช้เทคนิคแบบไหนที่เหมาะสม เพราะทุกเทคนิคก็จะมีข้อดีข้อเสียที่แตกต่างกันไป หรือจริงๆแล้วอาจจะไม่ได้เรียกว่าข้อดีข้อเสีย แต่จริงๆมันคือพฤติกรรมของมันที่เราต้องยอมรับว่า ถ้าเราใช้การออกแบบนี้พฤติกรรมที่ตามมาก็จะเป็นแบบนี้เช่นเดียวกันเหมือนกับตัวอย่างที่ยกตัวอย่างไปนั่นเองครับ ซึ่งถ้าจะให้ยืนยันว่าวิธีไหนเหมาะสมที่สุดนั้นก็คงต้องบอกว่าขึ้นกับประเภทงานที่เราใช้นั่นเองครับ ซึ่งครั้งนี้เราจะลองมาดูที่เทคนิคแบบ Real-time processing กับ Data Caching กันก่อนครับว่าแล้วถ้าไอเดียของมันจะออกมาเป็นยังไงกันนะ ซึ่งสุดท้ายแล้วถ้าพี่ๆเพื่อนมีไอเดียที่น่าสนใจก็แปะความคิดเห็นไว้ได้เลยนะครับผม 😆
โดยจากไอเดียตอนต้นเมื่อกี้เราก็จะเห็นว่าทำให้เร็วนั้นมีเทคนิคอย่างไรไปแล้วทีนี้เราจะมาดูที่ Usecase การใช้งานจริงอย่างสิ่งที่ตามมาเมื่อเราทำ “ให้มันเร็วขึ้น” ได้สำเร็จแล้วสิ่งที่ตามมาก็จะเป็นเรื่องของ “ช่วยลดต้นทุนหรือสามารถช่วยทำเงินให้มากขึ้นได้” ซึ่งสิ่งนี้ก็มีผลโดยตรงกับเรื่องของ “ความคาดหวังลูกค้า” คือหัวใจสำคัญ
เพราะในปัจจุบันลูกค้าของเรานั้นเข้ามาได้ทุกๆช่องทางเลยทั้ง Physical และ Digital ซึ่งถึงแม้ตอนนี้ Physical ของเราอาจจะลดลงเพราะเรื่องของพ้นกระทบ Covid-19 บ้างแต่ในเรื่องของ Digital คือสิ่งที่ชัดเจนว่าเพิ่มขึ้นอย่างต่อเนื่องแน่นอน และถ้าเราสังเกตว่าเพิ่มขึ้นนี่คือเพิ่มขึ้นแบบไหนกันนะ ? ลองนึกถึงการดูหนังหรือ Video Streaming สมัยก่อนเราอาจจะยอมรับได้กับภาพของหนังแผ่น CD ที่ซื้อมาแล้วภาพไม่ค่อยชัดแต่เราก็ไม่ค่อยรู้สึกอะไร แต่ตอนนี้แค่วีดีโอใน youtube ก็มีความชัดขั้นต่ำไปอยู่ที่ 1080p แล้วจึงจะถือว่าเป็น HD เพราะ Youtube ไม่นัด 720p เป็นความละเอียด HD ไปแล้วด้วยซ้ำ แล้วเราเองก็แทบจะไม่มีใครกดปรับดู Video ที่ความละเอียด 360p อีกแล้วด้วยซ้ำไป มักจะกด 1080p ทันทีหรือกดสูงสุดไปเลยถึง 4K ซึ่งตรงนี้สะท้อนให้เห็นว่าเราทุกคนนั้นต้องการบริการที่มี “คุณภาพเพิ่มมากขึ้นอยู่ตลอด” ซึ่งไม่ใช่แค่คุณภาพเพียงอย่างเดียวแต่ในเรื่องของ “ความรวดเร็วในการตอบสนองก็เป็นสิ่งที่เราต้องการเช่นด้วยกัน” เพราะตอนนี้เองถ้าหน้าเว็บโหลดนานเกินกว่าเรานับในใจได้ 1 ถึง 2 วินาทีเราคงเริ่มรู้สึกแล้วว่ามันช้า ทั้งๆที่สมัยก่อนเราโหลดหน้าเว็บบน Internet Explorer นานๆและภาพก็ไม่ชัดแต่เราก็รู้สึกเป็นเรื่องปกติ จึงเห็นว่ากระแสการพัฒนา Software ทั้งในรูปแบบของ Technical Skill การออกแบบระบบหรือการสร้างวัฒนธรรมองค์กรให้ มีคุณภาพรวดเร็วพร้อมตอบสนองได้อย่างทันท่วงที ก็เกิดขึ้นด้วยไปพร้อมๆกับผู้บริโภคที่เป็นลูกค้า (และตัวเราเองก็เป็นลูกค้าเช่นเดียวกัน) ดังนั้นการออกแบบ Microservices ให้ Scale Out ได้เรื่อยๆหรือการ Streaming Data จำนวนมากนับว่าเกือบจะเป็นความต้องการพื้นฐานไปเลย ยิ่งปัจจุบันมี Web3 ด้วยยิ่งไปไกลเลย ฮือ (ผมเองก็ยังไม่รู้เลยว่ามันไปถึงไหนแล้ว 😭)
ซึ่งด้วยเหตุผลนี้และจากเกริ่นนำไปในตอนข้างต้นเราจึงทดลองนำ Tool อย่าง Hazelcast มาแนะนำกัน ซึ่ง Hazelcast เองนั้นก็มีคุณสมบัติเรื่องของ In-Memory Processing อย่างที่ได้กล่าวไปตอนต้นและสามารถเชื่อมต่อกับระบบที่หลากหลายได้
เห็นว่าต้นทางจะเป็น Kafka, Database ก็สามารถส่งมาหา Hazelcast ได้เพื่อให้นำมาประมวลผลและส่งผลลัพธ์ออกไปแบบ Real-time โดยตัว Hazelcast เองนั้นก็เป็น Opensource ซึ่งสามารถนำไปใช้ได้เลยโดยมี License แบบ Source Available License
โดยเราจะกลับมาเรื่องเทคนิคการประมวผลแบบ In-Memory โดย Hazelcast กันต่อ เพื่อให้เพื่อนๆพี่ๆได้เห็นภาพจากตัวอย่างกันมากขึ้นจากตัวอย่างเรื่องการประมวลผลแบบ In-Memory เทียบกับการดึงข้อมูลทั่วๆไปแบบดั่งเดิม
จากภาพนี้จะเห็นว่าใน Old Way คือเราจะเขียนโปรแกรมเพื่อให้โปรแกรมไปดึงข้อมูลมาจากฐานข้อมูลหมายความว่าโปรแกรมที่เราเป็นคนพัฒนาขึ้นมานั้นก็จะต้องเป็นคนถามแล้วก็นำข้อมูลเหล่านั้นมาประมวลผลที่เรา ซึ่งวิธีแบบนี้ในสถาณะการณ์ของข้อมูลทั่วๆไปไม่ได้เยอะหรือต้องการประมวลผลที่เร็วก็อาจจะไม่มีปัญหาอะไร แต่สำหรับในกรณีที่เรามีข้อมูลจำนวนมากและพบว่าทำยังไงโปรแกรมเรามันก็ติดคอขวดเร็วขึ้นกว่านี้ไม่ได้สักทีให้เราลองนึกถึงท่าการ Scale Out แต่เป็นการ Scale Out แบบช่วยในการประมวลผล Logic ตัวนึงแบบ Parallel ซึ่งคำว่า Parallel นั้นจะให้ความสำคัญที่ไปที่เรื่องของ ถ้าเรามีงาน (Data) ขนาดใหญ่ก้อนนึงมากๆ จะทำยังไงให้สามารถแตกเป็นงานย่อยเพื่อที่ให้คนอื่นช่วยกันประมวลผลงานย่อยที่แตกออกมานั่นเอง ทำให้เราได้ผลลัพธ์ของการประมวลผลที่เร็วขึ้น และด้วยตัว Hazelcast เองก็มีคุณสมบัตินี้ด้วยเช่นเดียวกัน แต่ในภาพด้านขวา Hazelcast Way คือเราจะนำตัวโปรแกรมส่งไปหาแหล่งของ Data เองด้วยตัว Code โปรแกรมของเราที่เล็กกว่าการส่งไปหาแหล่ง Data ก็จะเร็วกว่าการ Poll Data มาที่ทำโปรแกรมเราแบบปกตินั่นเอง ซึ่งแนวคิดนี้มีชื่อเป็นภาษาอังกฤษว่า “Moving Computation is Cheaper than Moving Data” มาจาก Apache Hadoop” ซึ่งถ้าเพื่อนๆพี่ๆเห็นคำนี้แล้วรู้สึกน่าสนใจอยากลองดูเพิ่มเติมก็สามารถกดที่ link ได้เลย
โดยภาพบนนี้จะแสดงให้เห็น Flow เพิ่มเติมว่าการนำ In-Memory มาใช้จะไปวางหรือมันจะมีหน้าตา Architecture อย่างไรบ้างแกน North South คือพูดถึงเรื่องของการขอเข้าถึงข้อมูลจากระบบหนึ่งไปยังอีกระบบหนึ่งส่วน West-East คือเรื่องของ Pipeline การ Streaming ข้อมูล ซึ่ง In-Memory นั้นก็สามารถอยู่ร่วมได้ทั้งกับ Batch/Streaming ETL เดิมๆหรือการ Cache ข้อมูลจำนวนมากไว้เพื่อให้สามารถดึงข้อมูลไปใช้วิเคราะห์ได้อย่างรวดเร็วก็จะเป็นตัวอย่างของ In-Memory Computing
ในความสามารถส่วนต่อมาของ Hazelcast ก็คือเรื่องของ Stream Processing เพื่อมาควบคู่กับ In-Memory Processing แนวคิดของ Streaming Processing Pipeline นั้นก็มีง่ายๆคือมีต้นทางข้อมูลที่เรียกว่า Source และมี Operation ที่ใช้ในการบอกว่าเราจะเปลี่ยนแปลงทำอะไรกับข้อมูลที่ส่งมาและส่งท้ายก็คือ Sink ที่หมายถึงว่าจะส่งผลลัพธ์สุดท้ายไปที่ไหน
ซึ่งถ้าเราลองมาดูรายละเอียดของแนวคิด Stream Processingในภาพนี้จะเห็นว่าตัว Code ของ Hazelcast นั้นถูกเขียนในรูปแบบของ Declarative ในการจัดการ Logic แปลงข้อมูลตามเงื่อนไขต่างๆและจะถูกนำมาสร้างเป็น Directed Acyclic Graph (DAG) ซึ่งจริงๆก็หมายถึงกราฟ Flow ที่มีทิศทางจุดเริ่มต้นและสิ้นสุดของ flow นั้นเองแบบในรูปเลย (ชื่อดูน่าตกใจสินะครับ 😆)
ซึ่งถ้าโปรแกรมของเรานั้นถูกส่งไปรันบนหลายๆ Node ได้และมี Node รองรับพอโปรแกรมก็จะสามารถช่วยนำไปกระจายกันประมวลผลได้แบบภาพนี้แบบในตัวอย่างตอนแรกที่ยกตัวอย่างไปเรื่องของการออกแบบโปรแกรมให้ได้คุณสมบัติ Parallel คือการแตกงานใหญ่ให้กลายเป็นงานย่อยและช่วยกันประมวลผลได้ซึ่งภาพนี้ Map Flow ก็ดูเหมือนจะเป็นภาพที่ดี แต่เพื่อนๆพี่ๆก็อาจจะสงสัยเพิ่มเติมว่าเอ๊แล้วจริงๆข้างในมันเป็นอย่างไรกันแน่นะแบบถ้าลองเป็นสมมติของจริงสักหน่อย
เราก็จะลองมาดูตัวอย่างจริงกันอย่างการนับคำจากประโยค “To be, or not to be” ในรูปแบบของ Pipeline Processing กันดีกว่า โดยเราจะส่งประโยคตัวอย่างเข้าไปและให้โปรแกรมของเรานั้นนับว่ามี “to” และ “be” อย่างล่ะกี่คำกันนะ ? (คำตอบคืออย่างล่ะ 2 คำ) ซึ่ง Pipeline ที่เราแบ่งก็จะมี Datasource ที่ส่งประโยคเข้าไปและมี Operation ด้วยกันสองตัวคือ Step 1 (S1) ใช้ในการ Tokenize คำ และ Step 2 (S2) ในในการรวมผลลัพธ์จำนวนคำ และสุดท้ายก็ส่งผลลัพธ์ออกไปยังปลายทางที่ Sink
จากภาพจะเห็นว่าประโยค “To be, or not to be” นั้นถูกแตกออกมาเป็น Token คำเหมือนตอนเราใช้ Java Tokenizer ทั่วๆไปที่ Step 1 (S1)
และตัว Step 2 (S2) ที่ใช้ในการ Aggregate รวมจำนวนคำตามเงื่อนไขของเราคือ Aggregate คำว่า to และ be ว่ามีกี่คำดั่งภาพ คำไหนไม่ใช่อย่าง not, or ก็จะไม่ถูก Aggregate รวมเข้าไปนั่นเองแต่ตอนนี้เราจะเห็นว่าเราอย่างนับคำได้แค่แบบตามลำดับของ Token คำที่ค่อยๆถูกป้อนเข้ามาแบบตามลำดับ Serial/Sequential
ถ้าเรามีอภิมหาคำจำนวนมหาศาลเอาแบบจำนวนคำจากหนังสือทั้งโลกใน Amazon Book Online หรือนับคำทั้งหมดใน Facebook ไปเลยเราจะนับยังไงดีน้าาา ให้มันเร็วขึ้นกว่าเดิมมม ไม่งั้นสงสัยจะได้รอถึงปีหน้าแน่ๆเลย งั้นเราก็ลองเพิ่มเทคนิคการทำแบบ Parallel ไปเลยให้มี Pipeline S1 และ S2 เข้าไปอีกทำงานเหมือนเดิมเป๊ะๆ แต่ว่าแตกกิ่งออกมา ทีนี้ต่อให้ Data Source จะมีขนาดมหึมามหาศาลขนาดไหนพอเรามองภาพนี้ปุ๊ปเราก็รู้มุกแล้วว่า อ้ออ ที่แท้ Logic เดิมเราอยากคำนวนนับให้ไวๆขึ้นก็แตกกิ่ง Pipeline ไปเลยสักร้อย Pipeline หรือจะกี่ Pipeline ก็ได้ตามเครื่องที่เรา Scale Out Hazelcast ออกไป
ซึ่งเราก็ไม่ต้องกังวลเลยว่าเรื่องของ Logic การ Routing คำภายในจะเป็นอย่างไรเพราะเดี่ยว Hazelcast จัดการให้เราขอแค่เราเขียน Logic ให้ถูกต้องก็พอนั้นเองจึงเห็นว่า S1 ทั้งสอง Pipeline ก็สามารถส่งไปหา S2 ของ Pipeline ไหนก็ได้เช่นกันตามคิวที่ว่างเลยนั่นเอง ด้วยเทคนิคนี้บวกกับ In-Memory ทำให้เราสามารถประมวลผลได้อย่างรวดเร็วมากๆ ซึ่งเทคนิคมีชื่อเรียกแบบทางการว่า Map-Reduce
และสำหรับเรื่องสุดท้ายที่เราอาจจะกังวลคือเรื่องของ Fault ที่อาจจะเกิดขึ้นได้ในระหว่างการทำ Stream Processing เพราะด้วยปริมาณข้อมูลที่มากอะไรที่โอกาสเกิดน้อยๆสุดท้ายก็จะมีโอกาสเกิดเจอขึ้นได้นั่นเองเพราะด้วยจำนวนความน่าจะเป็นที่มากขึ้นดังนั้นทาง Hazelcast จึงมีเรื่องของการจัดการ Transaction Fault Tolerance แบบ Distributed Transaction ในโหมดของ “Exactly Once” ด้วย ซึ่งคำว่า Exactly Once ก็หมายถึงต้องมีการทำ Transaction ให้เกิดขึ้นแน่ๆเป๊ะๆหนึ่งครั้งห้ามขาดและก็ห้ามเกิน ที่มีคำนี้เกิดขึ้นนั่นก็เพราะว่าการจัดการ Transaction ในปริมาณมากๆหรือกับระบบแบบ Distributed System นั้นอาจจะมีโอกาสที่เกิด Fault ขึ้นได้ โดยถ้าเราอยากให้ Hazelcast Stream ของเราสามารถได้คุณสมบัติแบบ Exactly Once จึงต้องมีคุณสมบัติแบบภาพด้านล่างนี้
จากภาพหมายความว่าผู้ส่ง (Source) ต้องมีความสามารถในการส่งข้อมูลซ้ำได้เมื่อข้อมูลหายไป หรือว่าผู้รับ (Sink) นั้นอาจจะได้รับหรือไม่ได้รับแล้วแต่ไม่ได้ตอบกลับมาเพราะเหตุผลอะไรก็ตาม ซึ่งตรงนี้หมายความว่าคนรับ (Sink) ต้องมีคุณสมบัติเป็น Idempotent ด้วย เพราะอย่างตัวอย่างคือถ้าหากข้อมูลที่ส่งมานั้น Failed ล้มเหลวหรือเพราะ Internet ดับไปแว้บนึงหรือ Delay นานเกินตัวคนส่งก็อาจจะคิดว่าข้อมูลนั้นไปไม่ถึง ทั้งๆที่จริงๆอาจจะไปถึงแล้ว ตัว Source คนส่งจึงต้องมีคุณสมบัติ Replayable เพื่อให้มั่นใจว่า Message ไม่ขาดหาย ส่วนคนรับ (Sink) เองก็ต้องมีคุณสมบัติที่ถ้าหากข้อมูลถูกเปลี่ยนแปลงแล้วจะคงเหมือนเดิมไม่อย่างนั้นอาจจะเกิดข้อมูลซ้ำซ้อนมากกว่าครั้งนึงทำให้ผลลัพธ์ผิดพลาดได้นั่นเองจึงใช้คำว่า Upsert เพื่อตัวตรวจสอบยังไงก็ตรามแต่สุดท้ายมันจะเข้าไปที่ Record ID เป็น 24 แน่ๆที่ Record เดียวเท่านั้นจะไม่มีการ double record ซ้ำให้เกินออกมา ไม่อย่างนั้นจาก Exactly Once ก็จะกลายเป็น At-least Once แทนนั่นเองโดยปกติแล้วนิยามการส่งข้อมูลแบบนี้ก็จะมีด้วยกันสามแบบคือ “Atleast Once” คือขอให้มีอย่างน้อยครั้งหนึ่งหมายความว่า Transaction จะไม่หายไปจะมีเกิดขึ้นแน่นอนเพียงแต่มีโอกาสเกิดซ้ำได้มากกว่าหนึ่งครั้ง (Depliucated) ด้วยเช่นกัน ถ้าหากระบบเราไม่สามารถจัดการให้เกิดเหตุการณ์ซ้ำแล้วให้เป็นคุณสมบัติแบบ Idempotency ก็อาจจะทำให้เกิดข้อมูลที่ผิดพลาดเกิดขึ้นได้ดังนั้น Atleast Once จะเหมาะกับระบบที่ต่อให้ข้อมูล double ซ้ำก็ไม่สนใจถือว่าTransaction เกิดซ้ำขึ้นมาไม่ผิด ซึ่งแนวคิเตรงนี้เองไม่ได้เจาะจงแค่การใช้งาน Hazelcast เท่านั้น แต่ถ้าเราลองไปดู Kafka ก็เราจะเห็นว่า Kafka เองก็จะต้องมี Consumer ที่ให้ความสำคัญเน้นเป็น Smart Consumer ที่กล่าวถึงเรื่อง Idempotency เช่นกัน หรืออีกตัวหนึ่งคือ “Atmost Once” ซึ่งหมายถึงส่งแค่ครั้งเดียวเท่านั้นแต่ถ้าปลายทางได้รับหรือไม่ได้รับก็ไม่สนใจดังนั้นอาจจะเกิดเรื่องของข้อมูลหายระหว่างทางได้ และตัวสุดท้ายก็คือ Exactly Once แบบที่เราเห็นไปตอนแรก
แต่กระนั้นเองในโลกความเป็นจริงเราอาจจะไม่สามารถมี Data Source และ Sink ที่สามารถ Replay หรือ Idempotent ได้ทุกตัวทาง Hazelcast เองจึงมีตัวอย่างการทำงานของ Exactly Once เพิ่มเติมด้วยหลักการดั่งภาพนี้ด้วยการใช้ 2 Phase Commit ที่ใช้ XA Protocol
แนวคิดก็คือถ้าหากการจะขอ commit เกิดขึ้นแล้ว Resource ที่กระจายอยู่หลายที่ไม่สามารถตอบว่าทุกคน commit ได้ก็จะถึงว่า Transaction ล้มเหลวและ Rollout Transaction กลับไปนั่นเอง โดยตัว Hazelcast Jet Streaming จะเป็นคนตรวจสอบเองว่า Source ที่ส่งข้อมูลมานั้นโดนนำไปอ่านข้อมูลชุดนั้นแล้วหรือยังและสำหรับขาออกที่จะต้องถูกเขียนไปหา Sink ก็จะเป็นคนตรวจสอบเช่นกันดังนั้นด้วยวิธีที่ใช้ XA บน Hazelcast ก็จะทำให้ Source และ Sink ที่ไม่สามารถ Replay และไม่ได้เป็น Idempotent สามารถเกิดคุณสมบัติ Exactly Once ได้นั้นเอง
แต่ถ้าเราต้องการ Consistency สูงมากๆก็อาจจะส่งผลกระทบให้กับระบบช้าลงได้เหมือนกันเหมือนกับ Kafka ถ้าหากเราใช้ค่า ACK แบบ All เพื่อให้มั่นใจว่าทุกๆ Broker นั้น Replicated ข้อมูลไปหมดแล้วการตอบก็จะช้ากว่าการใช้ ACK แบบ 1 ที่ขอแค่ Master Node ขณะนั้นตอบมาก็พอแล้วนั่นเอง ก็เรียกว่าเป็นพฤติกรรมโดยปกติของการเลือกใช้ที่เราอาจจะต้องดูตามความเหมาะสมนั่นเอง
โดยในส่วนสุดท้ายนี้เราจะลองมาดูที่ Platform ของ Hazelcast กันว่าถ้าเราอยากจะใช้ Hazelcast นั้นจะมี Architecture เป็นอย่างไรบ้าง
ด้วยความที่ Hazelcast นั้นเป็นแนวคิดของ In-memory กับ Streaming Processing แบบ Distributed Computing ยิ่งเราเพิ่ม Server/Node เข้าไปมากเท่าไหร่เราก็จะยิ่งได้ประโยชน์อย่างการ Scale ขนาดของ Volume Data ให้รองรับได้มากขึ้น เพิ่ม Fault Tolerance เพราะ Copy Backup ของ Data นั้นถูกกระจายไปอยู่หลายที่
โดย Instance ของ Hazelcast จริงๆแล้วเราก็สามารถนำมารันบน Physical/ VM Server ได้หลาย Instance ก็เหมือนเรามี Java Application รันหลายๆตัวในหนึ่ง Server แต่แน่นอนว่าก็ไม่ทำให้ประสิทธิภาพของโปรแกรมเราทำงานได้ดีขึ้นแบบจากการที่มันใช้ Resource แบ่งกันอยู่นั่นเอง ดังนั้นแล้วถ้าเป็น Production Mode เราก็จะให้ 1 Server/VM มี Instance ของ Hazelcast แค่หนึ่งตัวนั้นเองแล้วค่อยเชื่อมหลายๆ Server ที่มี Instance ของ Hazelcast เข้าด้วยกันให้กลายเป็น Cluster
โดยถ้าหากเรามี Cluster ของ Server/ VM ที่ประกอบไปด้วย Instance ของ Hazelcast หลายๆตัวแล้วเมื่อไหร่ โดยหลักการแล้ว Hazelcast จะพยายามกระจายข้อมูลให้ balance เท่าๆกันมากที่สุดที่จะเป็นไปได้
ส่วนข้อมูล Copied Backup สำรองไปอย่าง Instance ต่างๆดั่งภาพที่จะมี Copied A อยู่ที่ Instance กล่องข้างล่างซ้ายจากต้นฉบับบที่อยู่ในกล่องข้างบนซ้าย
ถ้าหากเราอยากได้ประสิทธิภาพเพิ่มมากขึ้นเมื่อไหร่สิ่งที่เราทำก็เพียงแค่เพิ่มตัว Instance ของ Hazelcast เข้าไปให้ join ใน Cluster และตัว Hazelcast เองก็ยังจะทำการกระจาย Re-distributed Load ของข้อมูลไปยัง Server ใหม่อีกครั้ง
สำหรับ Benchmark Hazelcast นั้นใช้ NEXmark Benchmark ออกมาเป็นตัวอย่างเพื่อให้เราใช้ในการตัดสินใจและออกแบบขนาดของ Throughput เราอยากจะรองรับโดยได้กราฟออกมาเป็นดั่งนี้
การติดตั้ง Hazelcast นั้นก็จะใช้ Java Runtime ในการรันสำหรับ Development Environment ก็จะมี Spec ที่ต้องใช้ดั่งนี้
แต่ด้วยในยุคปัจจุบันเราจะพบว่า Traffic การ Scaling ของผู้ใช้นั้นเพิ่มขึ้นอย่างต่อเนื่องตลอด Cloud จึงเป็นบทบาทสำคัญในการรองรับความต้องการที่ยืดหยุ่นการมีการ Scale อย่างต่อเนื่อง ซึ่ง Hazelcast ก็เห็นถึงความสำคัญตรงนี้จึงออกแบบให้ตัว Platform นั้นสามารถ Scale ได้แบบ Multi-Cloud ซึ่งคำว่า Cloud ในที่นี้ไม่ได้หมายความว่าเราจะต้องยึดติดกับ Public Cloud Provider เท่านั้น แต่ถ้าองค์กรของเรามี Private Cloud Platform ตัว Hazelcast เองก็ยังสามารถนำไปติดตั้งลงได้อีกด้วยผ่านการมี Platform ที่รองรับ Multi Cloud อย่างเต็มรูปแบบอย่าง Openshift ซึ่งเป็น Kubernetes Distribution ที่ผ่านการ Certified จาก Cloud Native Computing Foundation ทำให้มั่นใจได้ว่าการเลือกใช้ Platform ที่ได้รับความนิยมและผ่านการทดสอบมาแล้วว่า Kubernetes คือ Platform สำหรับ Application ยุคใหม่ในอนาคตก็ทำให้ถือว่า Hazelcast เป็นตัวเลือกที่หยืดหยุ่นในทุกๆ Environment จากภาพ Architecture ด้านล่างจึงเห็นว่า Hazelcast นั้นก็สามารถทำงานบน Openshift ได้ในรูปแบบของ Container ดั่งภาพด้านล่างนี้
อีกทั้งการจัดการบริหาร Cluster Hazelcast เองก็ยังสามารถติดตั้งผ่าน Operator Framework ได้อีกด้วย ซึ่งตัว Operator จะเป็นตัวที่ใช้ในการจัดการ Reconcile ความถูกต้องของ Resource ของ Hazelcast Clsuter ที่ถูกสร้างขึ้นมารวมไปถึงการ Upgrade Version ต่างๆ ด้วยคุณสมบัตินี้ทำให้ Hazelcast จึงเหมาะกับ Environment แบบ Cloud Native ที่ต้องการความหยืดหยุ่น และสามารถ Deploy ไปได้ทุกที่แบบ Multi Cloud นั่นเอง
ดังนั้นเรื่องของ Day2 Operation ในการจัดการ Cluster ที่ง่ายไม่ซับซ้อนผ่าน Operator และ Kubernetes Platform จึงน่าจะเป็นตัวเลือกที่น่าสนใจสำหรับเพื่อนๆพี่ๆที่กำลังทดลองสนใจ Streaming กับ In-Memory Computing Platform
ซึ่งสำหรับครั้งนี้ก็คิดว่าน่าจะเป็นการเกริ่นนำที่ให้เพื่อนๆพี่ๆทุกคนได้เห็นถึงแนวคิดและภาพรวมของ Platform Hazelcast ไว้เจอกันใหม่ใน Part หน้าครับผม
อ้างอิง Hazelcast Exactly Once: https://hazelcast.com/glossary/distributed-transaction/
อ้างอิง Hazelcast XA Transaction: https://docs.hazelcast.com/imdg/4.2/transactions/providing-xa-transactions
อ้างอิง Kafka Acknowledgement: https://strimzi.io/blog/2020/10/15/producer-tuning/
อ้างอิง Hazelcast Operator: https://docs.hazelcast.com/operator/5.2/
อ้างอิง Openshift Certified Kubernetes Platform: https://www.redhat.com/en/technologies/cloud-computing/openshift/container-platform
อ้างอิง Hazelcast Cloud Native Architecture: https://hazelcast.com/partner/ibm/
อ้างอิง Hazelcast Course Introduction: https://training.hazelcast.com/page/courses-a-la-carte
อ้างอิง Data processing at the speed of business with IBM and Hazelcast: https://community.ibm.com/community/user/communities/community-home/librarydocuments/viewdocument?DocumentKey=770a0574-7faa-4c18-a10f-ac7ce936878e
อ้างอิง New Opportunities for Simplified Stream Processing — with Hazelcast Jet 4.0 : https://www.youtube.com/watch?v=ylFWzWDdWkQ&ab_channel=Hazelcast