เมื่อผมเจอ Bug ใน Spring JPA และเรื่องเล่าปนน้ำตา
เรื่องนี้ไม่ใช่เรื่อง Drama อะไรนะครับ แต่มันค่อนข้างตะเตือนไตผมนิดหน่อย เรียกได้ว่าขำทั้งน้ำตาเลยทีเดียว
เรื่องมันมีอยู่ว่า Wongnai ใช้ Java + Spring เป็นแกนหลักในการพัฒนา Product อยู่แล้ว เมื่อ Wongnai เริ่มทำ Microservices ผมก็เลยเสนอว่าจะใช้ Spring Boot มาใช้พัฒนา Service ในส่วนที่ผมได้รับมอบหมายมา
ด้วยเหตุที่ Service ตัวนี้ทำหน้าที่เป็น Web API จึงเกิด Requirement ว่า API ต้องสามารถ Filter ผ่าน Query String ได้ ยกตัวอย่าง API Restaurant สามารถ Filter ตามประเภทร้านได้ เช่น /api/restaurants?categoryId=5
ทีนี้ Spring Boot ก็ใช้ Spring Data JPA เป็น Object Relation Mapper (ORM) เพื่อแปลงข้อมูลแต่ละ Row ใน Relational Database ให้เป็น Java Object และด้วยความโชคดีของผู้ใช้ Spring ที่ Spring Data JPA รองรับ Querydsl ซึ่งเป็น Library สำหรับสร้าง SQL Query ที่สามารถเช็คความถูกต้องของ SQL Query ได้ตั้งแต่เรา Compile Java Code เลยทีเดียว
ประเด็นมันมีอยู่ว่า Spring Data JPA และ Querydsl สามารถใช้งานร่วมกับ Spring MVC ได้ทันที ทำให้เราสามารถสร้าง Web API ที่สามารถ Query Database ได้โดยตรงตามที่ผมต้องการ แต่ว่ามันดัน Query Field ที่เป็นชนิด List ไม่ได้ครับ เพราะเมื่อเราพยายาม Query จะเกิด NullPointerException
สาเหตุมันเกิดจาก QuerydslPredicateBuilder.reifyPath พยายามจะสร้าง SQL Query จาก URL query string แต่ field ที่เราจะ query มันเป็นชนิด List ทำให้ Querydsl generate query class ที่มี method ต่างจากปกติ ส่งผลให้ QuerydslPredicateBuilder พยายาม access method ที่ไม่มีอยู่จริงและเกิด NullPointerException ในที่สุด
เมื่อผมค่อนข้างมั่นใจว่าเป็น Bug แน่ๆ จึงเริ่มไปถามคำถามจาก StackOverflow ก่อนเผื่อว่ามีใครจะมี Workaround เบื้องต้น และก็โชคดีที่เจอคุณ Oliver Gierke (น่าจะเป็นผู้ที่ดูแล Project Spring Data) มาตอบคำถาม และคุณ Oliver Gierke ก็ยืนยันว่ามันเป็น Bug จริงๆ
คุณ Oliver Gierke ก็สร้าง Ticket บน Spring JIRA ขึ้นมา ผมก็ตามไปดู Ticket นั้นและก็ตั้งใจว่าจะมา Fix bug ด้วยตัวผมเอง!
ผ่านไปหลายวัน ผมก็ทุ่มสุดตัวเพื่อ Fix bug ตัวนี้ด้วยตัวเอง ตั้งแต่ Debug หาจุดที่เกิดปัญหาใน QuerydslPredicateBuilder ทำความเข้าใจ Code และก็เริ่มเขียน Test เพื่อ Reproduce ปัญหา จากนั้นก็เขียน code เพื่อ fix bug จนผมทำได้สำเร็จ!
ทีนี้ก็เริ่มกระบวนการส่ง Patch เข้าไปครับ ผมก็ไปอ่าน Contribution Guide ก็เจอว่าต้องไปสมัครบนหน้าเว็บของ Spring ก่อนว่ายอมรับข้อตกลง และใส่ @author Tanapol Nearunchorn ลงใน Javadoc ของ class ที่ผมได้แก้ไป (ผมคิดว่าคงเท่ห์มากถ้ามีเชื่อเราบน Code ของ Project ระดับโลก) และก็สร้าง Pull Request ส่งเข้าไปใน Github เท่านี้ก็เสร็จเรียบร้อย
ผมว่าทุกคนที่อ่านมาถึงตอนนี้คงจะรู้แล้วว่ามันเกิดอะไรขึ้น ถ้าเปรียบเป็นหนัง เวลานี้ก็คือจุดที่หักมุมของเรื่องครับ
หลังจากที่ได้ Submit Pull Request บน Github ไปแล้ว ก็เกิด Code conflict ผมก็ตามดูจนเจอว่า คุณ Oliver Gierke ได้ fix bug ตัวเดียวกับผมไปเรียบร้อย ผมนี่ทั้งขำและเศร้าพร้อมๆ กันเลยทีเดียว เกือบจะมีชื่อผมติดอยู่บน Source code ของ Spring แล้วแท้ๆ
อย่างไรก็ตาม ผมก็ไปตามอ่าน Commit log ของคุณ Oliver Gierke และก็ได้เรียนรู้อะไรดีๆ จาก commit นั้นด้วยครับ เมื่อนำมาเทียบกันแล้วก็พบว่าคุณ Oliver Gierke แก้ไขได้ใกล้เคียงกับที่ผมเขียนมากเลย แต่ว่า code ของคุณ Oliver Gierke นั้น refactor ได้ดีกว่าของผม ซึ่งเค้าได้ตัด code ส่วนที่ไม่เกี่ยวข้องออกไปไว้อีกที่หนึ่ง มันทำให้ code ดูดีขึ้นกว่าที่ผม submit ไปครับ
เรื่องราวก็จบลงเพียงเท่านี้ บทความนี้ไม่มีรูปให้ได้ชมกัน อย่างไรก็ดี ผมก็มีสิ่งที่อยากจะฝากให้กับทุกคนว่า การ Contribute Open-source นั้นไม่ยากอย่างที่คิดครับ เช่น Bug ที่ผมเจอ ใช้เวลาแค่ไม่กี่ชั่วโมงก็สามารถแก้ไขได้ (ผมคิดว่าตัวผมอาจจะใช้เวลามากกว่าหลายๆ คนด้วยครับ เพราะว่าผมไม่คุ้นกับ code ของ Spring Data JPA จึงต้องใช้เวลาทำความเข้าใจ) และผมก็เชื่อว่า Developer ชาวไทยหลายๆ คนเก่งพอที่จะช่วย Contribute ใหักับ Open-source หรือแม้กระทั่งสร้าง Open-source ของคนไทยเองครับ
ขอให้พัฒนาตัวเองกันต่อไปครับ อย่าหยุดเรียนรู้ อย่าหยุดเขียน Code ผมพยายามพัฒนาตัวเองตลอดเวลา อ่านหนังสือ อ่านข่าว ลองเล่นเทคโนโลยีทั้งใหม่และเก่า
สุดท้าย อยากเจอเรื่องราวสนุกๆ อยากมาพูดคุยกัน มาร่วมงานกับ Wongnai ได้ครับ