HOW-TO: เปลี่ยน Type Mapping อย่างไรใน Elasticsearch ให้ไม่กระทบ Production
สวัสดีครับผู้อ่านทุกท่าน วันนี้ผมจะมาแชร์ปัญหาหนึ่งที่เคยเจอ จากการใช้ Elasticsearch ครับ โดยปัญหาที่ผมเจอนั้นมี Error หน้าตาแบบนี้ครับ
บางคนอาจจะรู้แล้วว่าสาเหตุของปัญหาคืออะไร แต่บางคนอาจจะยังไม่รู้ เรามาลองดูกันครับว่า ผมทำอะไรลงไปถึงเกิด Error นี้
สมมติว่าก่อนหน้านี้ผมต้องการจะเก็บข้อมูล type user ลงใน index ชื่อ my_index โดย document นั้น มี 2 field คือ field name กับ field foo ผมสามารถทำได้ง่ายๆ โดย
แต่แล้วอยู่มาวันหนึ่ง มีความต้องการบางอย่าง ที่จะเปลี่ยนค่าใน field foo จากที่เคยเก็บค่า 0.5 ไว้ ไปเป็นเก็บค่า boolean true แทน ผมจึง
ซึ่งผลลัพธ์ที่ได้ก็คือ Error ด้านบนสุดครับ
ถึงตรงนี้หลายๆคนคงรู้แล้วว่าสาเหตุเกิดจาก การแก้ไขข้อมูล document ไม่ตรงกับ mapping ของ type user นั่นเอง เพราะตอนแรกที่เรา create document type user id1 นั้น Elasticsearch ได้สร้าง mapping ไว้สำหรับ type นั้นไว้แล้วเป็นดังนี้
อธิบายเพิ่มเติมสำหรับตัว mapping ที่จริงมันคือ schema definition ซึ่งจะใช้ในการ search document นั่นเอง
วิธีการแก้ปัญหา เราสามารถทำอะไรได้บ้างล่ะ หนึ่งคือ เราสามารถ ใช้ field อื่นในการเก็บข้อมูลใหม่ เช่น อาจจะเก็บค่า boolean นี้ไว้ที่ field ชื่อ bar แทนที่จะเป็น foo ซึ่งข้อเสียของมันคือเราก็จะต้องใช้ ชื่อใหม่ไม่ให้ตรงกับชื่อเก่า และถ้าปรับอีกก็ต้องหาชื่อใหม่เรื่อยๆ
แล้วเราสามารถเปลี่ยน mapping type ได้ไหม คำตอบคือเราสามารถทำได้แค่การ re-index เท่านั้น ไม่สามารถเปลี่ยน mapping ได้
ซึ่งการ re-index เป็นการ copy document ทั้งหมดจาก index หนึ่งมายังอีก index หนึ่ง
ก่อนอื่นเลยเราสร้าง index ใหม่มาชื่อ my_index_v1 โดยการ PUT mapping ให้กับมันตั้งแต่เริ่ม โดยหยิบ mapping อันเก่ามาแล้ว แก้type ของ foo เป็น boolean
จากนั้นทำการ re-index เพื่อ ย้ายข้อมูล document ทั้งหมด ไม่ว่า type message, bot หรือ user โดยใช้คำสั่ง
ขั้นตอนนี้อาจจะเจอ warning เนื่องจาก user.foo ที่ copy มาเป็น float แต่เราสามารถ update หลังจากนั้นได้ ปกติ
จากนั้นเราจึง ลบ my_index ทิ้ง, สร้างใหม่ด้วย mapping ชุดใหม่ แล้ว re-index จาก my_index_v1 กลับไปทับ my_index ที่สร้างมาใหม่ เพื่อที่เราจะได้ใช้ ชื่อ my_index เหมือนเดิม
แต่การทำแบบนี้มีข้อเสีย คือ หลังจากนี้ทุกครั้งที่เราจะre-index เราจะต้องมีช่วงเวลาที่ไม่สามารถเข้าถึง my_index ได้เป็นเวลาสั้นๆๆ
ดังนั้นเราจึงขอนำเสนอการใช้ alias เพื่อให้การ re-index ของเราเป็นไปได้อย่างราบลื่นไม่กระทบ production
หลังจากเราสร้าง my_index_v1 ด้วย mapping และ update ข้อมูลแบบที่เราต้องการ เสร็จเรียบร้อบแล้ว ก่อนอื่นให้เราลบ index เก่า (my_index) ทิ้งเพราะเราจะใช้ชื่อของมันเป็น alias
จากนั้นเราจึงสร้าง alias ของ index my_index_v1 ด้วยชื่อ my_index ด้วยคำสั่ง
เป็นอันเสร็จพิธี หลังจากนี้ เราสามารถเรียกใช้ชื่อ my_index แทน my_index_v1 ได้เลยโดยที่เราไม่ต้องกันการเข้าถึง my_index และ application ไม่ต้องรับรู้ถึงการเปลี่ยนแปลงด้วย
แล้วถ้าเราต้องการเปลี่ยน type อีกล่ะ? เราก็สร้าง my_index_v2 ด้วย mapping ใหม่, re-index, update docs. แล้วจึงแก้ไข alias อีกครั้งด้วยคำสั่ง
เพื่อลบ symbolic link ที่เคยชี้ไป my_index_v1 มาชี้ที่ my_index_v2 แทนนั่นเอง
ตรงนี้ผมก็ขอจบการเปลี่ยน mapping ด้วยวิธีการ re-index แบบใช้ alias ไว้แต่เพียงเท่านี้นะครับ อาจจะมีวิธีที่ดีกว่านี้หรือ มีจุดที่สามารถปรับให้ดีขึ้นได้ก็อยากให้ช่วยแชร์ช่วยแนะนำกันได้เลยครับ ขอบคุณครับ