Data Pipeline ที่ดีควรต้องมีคุณสมบัติ Idempotent

คุณสมบัติ idemponent เนี่ย เป็นคุณสมบัติทางคณิตศาสตร์ที่จะให้ผลลัพธ์เป็นค่าเดิมเสมอ ไม่ว่าจะมีการกระทำหรือดำเนินการไปกี่ครั้งก็ตาม

สมมุติว่าเรากำลังเขียนฟังก์ชั่นหนึ่งใน Python อยู่

>>> fruits = ["Apple", "Orange", "Grape"]
>>>
>>>
>>> def add(fruit):
...     fruits.append(fruit)
...
>>>

หลังจากนั้น เราเอาฟังก์ชั่น add มาใช้งานต่อ ก็จะได้ผลลัพธ์ตามนี้

>>> add("Pineapple")
>>> print(fruits)
['Apple', 'Orange', 'Grape', 'Pineapple']

ซึ่งดูแล้วก็ไม่แปลกอะไรเนอะ แต่จะแปลกทันทีเมื่อเราใช้ฟังก์ชั่น add ซ้ำเข้าไปอีก

>>> add("Pineapple")
>>> add("Pineapple")
>>> add("Pineapple")
>>> add("Pineapple")
>>> print(fruits)
['Apple', 'Orange', 'Grape', 'Pineapple', 'Pineapple', 'Pineapple', 'Pineapple', 'Pineapple']

จะเห็นว่าได้มี Pineapple เพิ่มขึ้นมาอีก 4 ค่า แบบนี้เราบอกได้เลยว่าฟังก์ชั่นนี้ “ไม่ idemponent” ครับ และเราไม่ควรสร้าง data pipeline ที่มีฟังก์ชั่นแบบนี้

Data pipeline ที่ดี เราควรที่จะสามารถรันมันซ้ำกี่รอบก็ได้ ผลที่ได้ควรจะได้เหมือนเดิม จะส่งผลให้เราสามารถที่จะทดสอบการทำงานของ data pipeline เราได้ง่าย ดูแลบำรุงรักษาได้ง่ายอีกด้วย ลองนึกสภาพว่าถ้าเรารัน data pipeline เรื่อยๆ มี input ที่เหมือนเดิม แต่ได้ผลไม่เหมือนกันสักรอบ แบบนี้ต้องเรียกว่าวัดดวงกันล่ะ ต้องมีสักรอบที่ทำงานถูกต้อง อะไรประมาณนี้ ก็คงไม่ดีแน่ๆ :sweat_smile:

การทำให้ data pipeline ของเรามีคุณสมบัติ idemponent ก็ยังมีข้อดีอีกคือ ข้อมูลเราจะไม่ซ้ำซ้อน มีข้อมูลที่สดใหม่ (ไม่มีพวกข้อมูลที่เป็น stale หรือข้อมูลที่ค้างอยู่ในระบบที่เราไม่ได้ใช้งานแล้ว) แล้วก็จริงๆ ยังช่วยประหยัดเนื้อที่ในการเก็บข้อมูลอีกด้วย เพราะเราไม่ได้เก็บข้อมูลซ้ำเข้ามา

ทีนี้จากโค้ดด้านบนเราปรับให้มีคุณสมบัติ idemponent ได้อย่างไร?

ทำแบบนี้เลย แทนที่เราจะใช้ append เราเปลี่ยนมาใช้ + แทน

>>> fruits = ['Apple', 'Orange', 'Grape']
>>> def add(fruit):
...     return fruits + [fruit]
...
>>> new_fruits = add("Pineapple")
>>> new_fruits = add("Pineapple")
>>> new_fruits = add("Pineapple")
>>> new_fruits = add("Pineapple")
>>> new_fruits = add("Pineapple")
>>> new_fruits = add("Pineapple")
>>> new_fruits = add("Pineapple")
>>> print(new_fruits)
['Apple', 'Orange', 'Grape', 'Pineapple']

เห็นได้ชัดเลยว่า ไม่ว่าเราจะเรียก add กี่ครั้ง ผลลัพธ์ที่ได้เราจะได้เหมือนเดิม (โดยการเอาตัวแปร new_fruits ไปใช้งานต่อ)

ตามนั้นครับผม เมื่อไหร่ก็ตามที่มีโอกาสได้สร้าง data pipeline ก็อย่าลืมทำให้มีคุณสมบัติ idemponent กันด้วยนะ :wink:

3 Likes

เจอมาสดๆ ร้อนๆ
น้องในทีมส่งโค้ดที่ต้องรันตาม schedule มาให้เอาเข้า task orchestrator

น้องก็เล่าไป ว่า โค้ดทำงานยังไง ใช้อะไรบ้าง
เอะใจตอนนึง ที่น้องบอกว่า มันจะไปเช็ค xxx จากไฟล์ที่บันทึกเวลาล่าสุดที่ส่งผ่านไว้
ไอ้เราก็งง ว่า เอ้า ก็ต้นทางมันก็มี timestamp ให้อยู่แล้วหนิ ใช้ไม่ได้รึ

สรุปกลายเป็นว่า น้องแกพยายามหาทางดัก send failure แล้วข้อมูลไม่ส่ง โดยการที่ไปเขียนดักไว้ ว่า ถ้ารอบเช้าส่งไม่ผ่าน ให้เอาข้อมูลรอบเช้าไปส่งในรอบเย็นด้วย

สุดท้าย เลยบอกน้องว่า ไปเซ็ต input มาให้เป็น timestamp จะได้กดรันกี่รอบก็ได้ข้อมูลเหมือนเดิม แล้วถ้ามันส่งไม่ผ่านก็กดรันใหม่ซะ
แลนดิ้งอย่างสวยๆ :tea:

1 Like