สวัสดีครับ กลับมาพบกันอีกครั้ง วันนี้ผมจะขอมาเฉลยโจทย์ข้อสอบ Data Engineer สำหรับเข้าทำงานที่บริษัท Credit OK จากที่ได้ทราบไปกันว่าเมื่อประมาณต้นปีเราได้เปิดรับ Data Engineer กันไป ก็มีผู้สนใจสมัครเข้ามากันประมาณหนึ่ง และแน่นอนเหมือนเดิมทุกๆ ครั้ง ผมก็จะตั้งโจทย์มาคัดเลือกผู้สมัคร แต่ครั้งนี้พิเศษ ผมจะเอามาเฉลยแนวทางให้ทุกท่านได้ศึกษากัน ซึ่งน่าจะมีประโยชน์สำหรับผู้ที่กำลังสนใจงานทางด้าน Data Engineer กันอยู่ ขอเน้นว่าเฉลยเป็นแนวนะครับ ไม่ใช่เฉลยเป็น Code ตัวอย่าง ดังนั้นถ้าใครไม่เคยสัมผัส GCP มาก่อนเลยอาจจะรู้สึกว่าผมพูดภาษาต่างดาวอยู่หรือเปล่า แต่ชาว GCP เขาเข้าใจกันแหละ 😋
ก่อนอื่นต้องขอออกตัวก่อนเลยว่า โจทย์ของเราไม่ได้ว้าว ไมไ่ด้พิศดารท่ายาก ไม่ได้ซับซ้อนมากมายแต่อย่างใด เราไม่ใช่บริษัทใหญ่ ที่มีผู้สมัครเป็นพันเป็นหมื่นคนยื่น Resume เข้ามาขอร่วมงานด้วย เราไม่ได้มีเทพเรียงรายมาให้ชี้นิ้วเลือกสรรได้ขนาดนั้น จึงตั้งโจทย์ออกมาในลักษณะที่กำลังจะได้เห็น แม้จะง่าย แต่ก็ต้องยากพอที่จะคัดกรองเอาคนที่มึนๆ งงๆ เดินหลงทางมาสมัคร หรือมีความทรหดไม่พอออกไปจากขั้นตอน เพื่อลดเวลาการตรวจข้อสอบของผมเอง การสมัครครั้งนี้ ผู้สมัครทุกคนผมลงไปพูดคุยเอง เหมือนคอยสอนอยู่ห่างๆ ระหว่างกระบวนการ (ทำให้สุดท้ายตอนต้องปฏิเสธรู้สึกลำบากใจมากเป็นพิเศษ เหอะๆ)
เหตุที่เป็นแบบนี้เพราะว่า ในตลาดของเราไม่มี Data Engineer ตัวเทพมากนัก ยิ่งเฉพาะเจาะจงต้องใช้เครื่องมือบน Google Cloud Platform (GCP) ด้วย ยิ่งเหลือไม่ถึงหยิบมือ พวกเหล่าปวงเทพเขาก็มีทางเลือกมากอยู่แล้ว กลุ่มนั้นก็ไม่ต้องไปห่วงเขา แต่คนที่ผมสนใจ และทีมมองหาอยู่ คือคนที่ได้ค้นพบตัวเองมาแล้วว่า สนใจทางด้านนี้จริงๆ มีความรู้พื้นฐานมาบ้าง ศึกษาหาความรู้ด้วยตัวเองมาแล้วเป็นอย่างดี และมีศักยภาพในการพัฒนาความรู้ต่อยอดได้อย่างยิ่งยวด เราจะพาเขาเข้ามาเพื่อให้ได้ทำงาน ให้ได้เจอโจทย์ จะได้เติบโตไปพร้อมกัน ดังนั้นความจริงวิธีการพิชิตการเข้าชิงตำแหน่งในบริษัทของเราจึงไม่ใช่คนที่เก่งแต่ Technical เพียงอย่างเดียว แต่เป็นคุณสมบัติด้านอื่นๆ ที่ทีมของเรามองหาประกอบอยู่ด้วยอย่างสมดุล โดยเฉพาะทักษะทางด้านการสามารถเรียนรู้และประยุกต์ใช้ความรู้ได้ศึกษามาได้เป็นอย่างดี
ความรู้ฝั่ง Technical สอนกันง่าย แต่การมี Attitude ในการทำงานที่ดีนั้นสอนกันยาก มันเกิดจากการสั่งสมมาตลอดชีวิต
ด้วยเหตุนี้ โจทย์ที่ดูแสนที่ง่ายนี้จึงถูกสอดแทรกไปด้วยกับดักเป็นระยะ เพื่อวัดวิธีการคิด วิธีการแก้ปัญหาแบบออกนอกตำราของผู้สมัครที่ผมคาดหวังจะได้เห็น
บ่นมามากละ ไปดูโจทย์กันเลยดีกว่า ผมก็ไม่ได้อะไรเป็นทางการมากอะนะ เขียนมันลง Google Docs เนี่ยแหละ Credit OK Data Engineer Challenge 1
โดยสรุปแล้วจุดประสงค์ของโจทย์ก็คือ ให้ทำ Web Scraping (ซึ่งทุกวันนี้ฮิตกันเหลือเกิน เห็นสอนออนไลน์กันให้พรึบ ไม่รู้จะมาฮิตสะสมข้อมูลอะไรนักช่วงนี้) เพื่อวัดทักษะและขอดูลายมือการเขียน Python จากนั้นก็เอามา Feed ข้อมูลเข้า Data Pipeline ของ GCP เพื่อวัดทักษะการใช้เครื่องมือ และปิดท้ายด้วยการทำ Data Studio เพื่อวัด Common Sense ในการนำข้อมูลมาใช้
อนึ่งแนวทางที่ผมมาเฉลยให้อ่านกันนี้เป็นเพียงหนึ่งวิธีการในการพิชิตโจทย์ ความจริงยังมีอีกหลายวิธี ผู้สมัครทำสำเร็จด้วยวิธีการอื่นๆ มาเยอะแยะก็ใช้ได้เหมือนกัน ตราบใดที่อธิบายได้ว่า ทำไมวิธีการที่ตัวเองเลือกมาเหมาะสมมากกว่าอีกวิธีที่ตนไม่ได้เลือก
แนวทางการโจทย์
1. Web Scraping ลายมือ Python ของคุณเป็นอย่างไร
เรื่อง Web Scraping นี่ผมคงจะไม่พูดมาก เว้นไปแต่ขอขอบคุณเจ้าของ Data Source ด้วยนะครับ (ถือวิสาสะไปใช้ แต่ผมก็คิดว่ามีอีกหลายคนที่ทำก็อยู่ 555)
ส่วน Lib ที่ผู้สมัครส่วนใหญ่ใช้กันก็เป็น Beautiful Soup ยอดฮิตนั้นเอง คิดว่ามันคงจะง่ายมั้งเลยเลือกกัน
จะอะไรก็แล้วแต่ ผมไม่ได้สนใจว่าผู้สมัครทำอย่างไรถึงได้ Data มา แต่ผมสนใจว่า Code ที่ผู้สมัครเขียนมาทำงานได้ถูกต้อง และมีลายมือการเขียน Python ที่ดี
ลายมือการเขียน Python ที่ดีเป็นอย่างไร ลองศึกษาจาก Django Coding Style และ PEP 8 กันดู เขียนให้ได้แบบนี้ ทุก enter ทุก space ต้องเป๊ะ นั่นคือสิ่งที่ผมมองหา
ต่อมาคือเรื่อง Common Sense เรื่องการตั้งชื่อตัวแปรต่างๆ เนื่องจากเว็บมันมีหัวตารางเป็นภาษาไทย (หรืออาจจะภาษาอังกฤษถ้าผู้สมัครหาเจอ) ก็ต้องทำการแปลงหัวตารางเหล่านั้นออกมาเป็นชื่อตัวแปรที่ดี พร้อมสำหรับการประมวลผล ไม่ใช่ทำมาส่งๆ นอกจากนั้นข้อมูลข้างในก็ควรจะดีด้วยเช่นกัน ชนิดตัวแปร มีการทำความสะดวก มีการแปลงให้ถูก Type หรือไม่ (Python ความจริงมี Type นะ มันแค่ Dynamic แต่ก็ต้องแปลง)
แล้ว JSON Data Structure สุดท้าย ควรเป็น Array หรือควรเป็น Object ที่มี Key เป็น Date เพราะเหตุใดจึงควรทำเช่นนั้น แล้วถ้าทำเป็น Object แล้ว ยังควรมี Date อยู่ในแต่ละ Record อีกหรือไม่
นอกจากนั้นก็ยังมีเรื่องของการช่างสังเกตว่า ความจริงเราไม่มีความจำเป็นต้องไปเก็บหลายๆ หน้า เพราะว่า query parameters บน URL สามารถปรับเปลี่ยนได้ ทำให้ได้ข้อมูลย้อนหลังทั้งหมดมาใน Request เดียว
สุดท้ายคือการเอาขึ้นไปวางบน Google Cloud Function (GCF) ทำเป็นหรือไม่ แล้ว Config Parameters ต่างๆ อย่างไร เพราะเหตุใด
2. ETL บน GCP ทักษะการใช้งานเครื่องมือของคุณแม่นขนาดไหน
หลังจากได้ Cloud Function สำหรับการดึงข้อมูลหุ้นมาเป็น JSON ที่พร้อมสำหรับการย่อยแล้ว ก็มาถึงขั้นตอนการออกแบบ ETL ซึ่งเราน่าจะมีประมาณนี้ในเบื้องต้น
เบื้องต้นคือเรามี GCF (5) แล้ว ก็คือ GCF ที่เอาไว้ Scrape ข้อมูล ต่อมาผมจะขอเล่าเป็นลำดับให้ฟังว่ามันควรจะมีอะไรบ้าง เพราะอะไร แล้วมีอะไรที่ต้องระวัง
ต่อจาก GCF (5) แล้ว เราก็ลงมาที่ GCF (4) คือ ตัวสำหรับโหลด Data เข้า Big Query สำหรับ GCF ตัวนี้ความจริงทำสิ่งที่เรียบง่ายมากๆ เช่นกัน นั่นก็คือ การไป Request (5) ซึ่งจะได้ข้อมูลราคาหุ้นออกมาเป็น JSON ที่สวยงาม ถ้าตั้งชื่อ Column ให้สวยๆ อยู่แล้ว (และยังไม่ได้เอา date ออกจากไปแต่ละ record) ก็สามารถรันแปลง Object เป็น Array แล้วจับโยนเข้า BigQuery (6) ได้เลย ซึ่งการจะยิงไปที่ BigQuery (6) ในกรณีนี้ วิธีการที่ง่ายที่สุดก็คงจะเป็นใช้ Streaming Insert ลง Table ไปนี่แหละ
แต่ความยากลำบากมันอยู่ตรงที่ว่า GCF (5) นั้น ผมไม่ได้ให้สิทธิ์ในการทำ Unauthenticated request กับผู้สมัครไปด้วย 💩 นั่นหมายความว่า GCF (5) จะไม่สามารถ Request เข้าถึงได้ตรงๆ จะต้องใช้ Service Account เพื่อไปขอ Token จาก Google ก่อน เรื่องนี้เป็นประเด็นเรื่องความปลอดภัยที่เราไม่ควรเปิด GCF โท่งๆ ให้ใครก็ได้เข้ามา ซึ่งผมอนุโลมให้ว่า ถ้าเปิดไม่ได้ก็มาบอก เดี๋ยวจะเปิดให้ก็ได้ แต่จะให้ดีคือ ทำ Auth ให้ถูกต้อง ตามนี้ https://cloud.google.com/functions/docs/securing/authenticating
มาถึงจุดนี้แล้ว คือถ้าเรา Request GCF (4) โดยใส่ Symbol เข้าไป มันก็จะไปเรียก GCF (5) เพื่อไปดูดข้อมูลหุ้นมา แล้วทำการประมวลผล จับลง Big Query (6) ได้เรียบร้อย คำถามต่อมาก็คือ แล้วเราจะดึงหุ้นให้ครบทุกตัวในตลาดได้อย่างไร?
คำตอบก็คือ ก็ไปเปิดหาลิสของ Symbol ของหุ้นทั้งหมดในตลาดซิ หาไม่ได้ยากเลย จากนั้นก็ทำการยิง GCF (4) รัวๆ ไป ก็จะได้ข้อมูลครบตามที่ต้องการ
ผู้สมัครส่วนใหญ่ก็จะใช้วิธีการเอาลิสหุ้นมา Hard code ลง GCF แล้วก็ให้มันยิง GCF (4) ซึ่งผลลัพธ์ก็คือ มันก็จะ timeout ก็เพราะว่า ลิสหุ้นนั้นมาหลายตัว แต่งาน Request ดันทำเป็น Sequential มันก็เลยทำไม่ทัน
ทางหนึ่งที่แก้ปัญหาได้ก็คือ เขียนให้ GCF มันทำงานเป็น Parallel หรือ Async ก็ได้ แต่ปัญหาของวิธีก็คือ มันจะกลายไปเป็นการ Flood เว็บข้อมูลต้นทาง ซึ่งนอกจากไปขโมยข้อมูลเขามาใช้แล้ว ยังไปทำร้ายเขาอีก (อย่าทำแบบนี้กับใคร เข้าใจไหม) ดังนั้นวิธีการนี้จึงไม่สมควร เราจึงใช้สิ่งตัวทำ Background Task เข้ามาช่วย นั่นก็คือ Google Cloud Tasks นั่นเอง
วิธีการนั้นก็ตรงไปตรงมา จากที่จะโหลดงานเข้า GCF (4) โดยตรง ก็เปลี่ยนมาโหลดงานเข้า Cloud Tasks แทน ซึ่งจะใช้ Cloud Tasks ได้ ก็ต้องมีการไปสร้าง Queue อะไรกันก่อน ซึ่งระบบ Queue ของ Cloud Tasks นั้น เราสามารถกำหนดได้ว่า จะให้ทำพร้อมกันกี่ Concurrency เพื่อป้องกันไม่ให้โหลดฝั่ง Data Source มากเกินไป นอกจากนั้นก็ยังมีระบบจัดการ Retry Request ที่มีปัญหาด้วย มันจะออกมาเป็นกระดานให้เราดูเลยว่า งานไหนมีปัญหาบ้างรันไม่ผ่านบ้าง สามารถมา Retry ได้ อันนี้ก็จะช่วยให้เรามั่นใจว่าข้อมูลหุ้นทุกตัวเข้าถึงปลายทางแน่นอน
ส่วนเรื่องการ Insert Task ที่ GCF (2) นั้นก็เขียนกันไปแบบตรงไปตรงมา คือไปสั่งให้ Cloud Tasks มันไป Request GCF (4) ให้เรา อ่อ แล้วก็อย่าลืมเรื่อง GCF (4) มันต้องใช้ Service Account ในการ Invoke ด้วย ซึ่งตัว Cloud Tasks เองก็สามารถจัดการตรงนี้ให้เราได้ด้วย
มาถึงจุดนี้แล้ว เมื่อเรารัน GCF (2) เราก็เราก็จะได้ลิสของหุ้นทุกตัวในตลาด Feed เข้าไปอยู่ใน Queue ของ Cloud Tasks และค่อยๆ ถูกรันไปทีละอัน สองอัน อย่างสวยงาม
ขั้นต่อมาคือ ถ้าจะรันอัพเดทข้อมูลหุ้นทั้งตลาดทุกวันจะทำอย่างไร วิธีการก็คือ ใช้ Cloud Scheduler เข้ามาช่วยนั่นเอง
Cloud Scheduler มันก็คือ Crontab ดีๆ นี่เอง เราสามารถสั่งให้มัน Trigger Pub/Sub Topic เพื่อไป Trigger GCF (2) ได้โดยตรง
มาถึงจุดนี้แล้ว เราเพียงกด Run บน Cloud SQL 1 ที ระบบก็จะทำการ Feed ข้อมูลหุ้นทั้งหมดลงใน BigQuery ของเราโดยอัตโนมัติ ต่อมาเรามาคุยฝั่ง Data Analytics กันบ้าง
ตอนนี้เรามี BigQuery (6) ซึ่งเก็บข้อมูลหุ้นทั้งหมดอยู่ แต่ปัญหาอย่างหนึ่งที่จะพบเลยก็คือข้อมูลมันซ้ำกัน อย่างเช่น ถ้าเรารัน GCF ที่ Symbol ตัวเดียวกัน 2 ครั้ง สิ่งที่เกิดขึ้นก็คือ เราจะได้ราคาหุ้นตัวเดิม ที่วันเดียวกัน ซ้อนกัน 2 Records ซึ่งสามารถ Distinct ออกไปได้ตรงๆ
ดังนั้น ก่อนที่จะเอาข้อมูลไปแสดงผลบน Data Studio เราจึงควรทำความสะอาดข้อมูลเสียก่อน เนื่องจากในเคสนี้ข้อมูลไม่ได้ใหญ่โตอะไรมาก ดังนั้นเราสามารถสร้าง BigQuery View ขึ้นมาเพื่อทำการ Distinct Data ได้เลย ก็เขียน SQL เพื่อสร้าง View ไป แล้วเมื่อเราเปิด BigQuery View (7) ขึ้นมา ก็จะพบว่าข้อมูลนั้นสวยเนียนพร้อมสำหรับการใช้งาน
ข้อดีของเราใช้ BigQuery View ที่เหนือกว่าการทำ Pipeline ให้มันโหลดข้าม Table มาก็คือ ข้อมูลจะสดใหม่มาก ข้อมูลที่มาอยู่ใน BigQuery Table (6) จะสามารถถูกเอาขึ้นไปแสดงบนทันทีเมื่อกด Refresh บน Data Studio
แต่ข้อเสียของมันก็คือ พวกข้อมูล Dup ทำให้การ Query ของเราเปลืองทรัพยากรมากขึ้น ทุกๆ ครั้งที่เรารัน มันจะเกิดข้อมูลข้อมูลขึ้นมา 60 records (ข้อมูลย้อนหลังได้มากสุด 60 วัน) ของหุ้นแต่ละตัว ในขณะที่เมื่อขึ้นวันใหม่มีเพียง 1 record เท่านั้นที่เป็นของใหม่ ส่วน 59 records ที่เหลือเป็นข้อมูลเดิมที่มีอยู่แล้ว ถึงแม้ Big Query จะเอาอยู่ แต่เราก็ควรทำความสะอาดหน่อย ไม่เช่นนั้นเราจะเก็บข้อมูลเกินความจำเป็นถึง 60 เท่าตัว
ดังนั้น ทุกๆ คืน อาจจะควรรัน BigQuery Scheduler สักครั้ง เพื่อทำการ Dedup ข้อมูลซ้ำออกจาก BigQuery Table (6) ซึ่งก็สามารถทำได้โดยใช้ BigQuery Scheduler ให้มันรันหลังจากที่ Cloud Scheduler (1) มันรันไปแล้วสักพักหน่อย (เว้นระยะเวลาให้มากกว่าที่ Cloud Tasks มันรันเสร็จหมดสักหน่อย) ถ้าทำได้เช่นนี้แล้ว ใน BigQuery Table (6) ของเราก็จะเก็บข้อมูลพอดิบพอดีสำหรับการเอาไปใช้แสดงผล
3. Data Studio คุณมี Common Sense ในการเอาข้อมูลมาใช้งานหรือเปล่า
สุดท้ายคือเรื่อง Data Studio อันนี้ก็ตรงไปตรงมา ถ้าสามารถทำให้ BigQuery View (7) สวยงามได้แล้ว การเอามาใช้งานก็ย่อมง่ายแท้
ซึ่งสิ่งที่ผมคาดหวังที่จะเห็นก็คือ ตารางที่แสดงราคาหุ้นย้อนหลังกลับไปเท่าที่จะทำได้นั่นเอง ซึ่งอันนี้ก็ Basic เนอะ ก็จะต้องมีเลือกวันที่ from และ to แล้วก็มีลิสของหุ้นที่จะให้แสดงบน Graph ซึ่งกราฟที่เหมาะก็คือ Line Chart นั่นเอง (ขอโทษทีไม่มีรูปให้ดูแน่น่าจะพอเห็นภาพกันเนอะ)
แต่ปัญหาคืองี้ เนื่องจากตลาดหุ้นปิดวันเสาร์ อาทิตย์ ถ้าเราเอากราฟมาแสดงตรงๆ เลย เราจะเห็นกราฟมันตกไปที่ 0 ทุกๆ วันที่ตลาดหุ้นปิด กราฟมันก็จะเป็นฟันขึ้นๆ ลงๆ แบบนี้ทุกสัปดาห์ ซึ่งมันไม่ Make sense
เพื่อให้สามารถเปรียบเทียบข้อมูลได้อย่างดี เราจึงควรจะ Exclude ข้อมูลวันที่ตลาดหุ้นปิดออกไป ทีนี้กราฟก็จะกลายเป็นเส้นเชื่อมต่อกันจนสุดท้ายตั้งแต่วันแรกจนถึงวันสุดท้ายอย่างสวยงาม
เท่านี้ก็เรียบร้อยแล้ว Data Pipeline ของเราตั้งแต่การดึงข้อมูลมาเก็บไปจนถึงการเอามาแสดงผลอย่างสวยงามบน Data Studio ข้างล่างนี้คือ Pipeline ทั้ง Flow ครับ
ส่งท้าย
สำหรับการเฉลยแนวทางการทำข้อสอบ Data Engineer ที่ทาง Credit OK เพิ่งเปิดรับไปก็เป็นประมาณเท่าที่เห็นครับ ก็คือถ้าสามารถทำได้ก็ถือว่า Skill GCP ทางฝั่ง Technical ผ่านละ น่าจะพร้อมสำหรับการมาเจอโจทย์จริง ที่เต็มไปด้วยข้อมูลที่เน่าเฟะ และมีขนาดที่ใหญ่โตกว่านี้มาก
สำหรับท่านที่อ่านมาถึงตรงนี้แล้ว ไม่เคยอ่านเรื่อง Data Engineer กันมาก่อนแล้วสนใจ ผมเคยเขียนเอาไว้และขอแนะนำให้ลองเข้าไปอ่านกันดูที่นี่ได้เลยครับ Data Engineer ทักษะที่ต้องมี โจทย์ เครื่องมือ และความท้าทายที่ต้องเจอ
ส่วนตำแหน่ง Data Engineer ของเราตอนนี้ก็ได้ถูก Fill In เรียบร้อยแล้ว แต่สำหรับท่านที่อ่านๆ ดูแล้วเอ๊ะ อยากมาร่วมงานด้วยจัง ก็สามารถเข้าไปเช็คกระดานประกาศหางานได้ที่เว็บ Blognone Jobs ได้เลยครับ ถ้ามีประกาศหางานเราก็จะไปโพสเอาไว้ที่นั่นน่ะแหละ (หรืออีกช่องทางก็มา Follow FB ของผมเอาไว้ได้เช่นกัน)
ส่วนบทความนี้ ผมก็ขอจบเอาไว้แต่เพียงเท่านี้ ท่านใดมีคำถาม หรือเห็นว่าตรงไหนผิดไป หรือมีแนวทางการทำ Data Pipeline ที่เจ๋งกว่านี้ ก็สามารถมาพูดคุยกันได้ที่ช่อง Comment ข้างล่างเลยนะครับ ส่วนวันนี้ผมก็ขอจากไปแต่เพียงเท่านี้ ขอขอบคุณและสวัสดีครับ