3 คะแนน โดย GN⁺ 2026-01-30 | 1 ความคิดเห็น | แชร์ทาง WhatsApp
  • Oban.py คือ เวอร์ชันที่พอร์ต Oban ซึ่งเป็นเฟรมเวิร์กประมวลผลงานของ Elixir มาสู่ Python บนพื้นฐานของ PostgreSQL โดยสามารถแทรกและประมวลผลงานได้ด้วยฐานข้อมูลเพียงอย่างเดียว
  • งานถูกสร้างและโรลแบ็กภายในทรานแซกชันของฐานข้อมูล และรองรับฟีเจอร์หลากหลาย เช่น การจัดการคิว การเก็บผลลัพธ์ และการตั้งเวลาแบบ cron
  • เวอร์ชันโอเพนซอร์สมีข้อจำกัด เช่น การรัน asyncio แบบเธรดเดียว และ การแทรก/ยืนยันผลแบบทีละรายการ แต่เวอร์ชัน Pro ให้ การประมวลผลแบบขนาน เวิร์กโฟลว์ และ smart concurrency
  • การทำงานภายในประกอบด้วย 5 ขั้นตอนคือ Insert → Notify → Fetch → Execute → Ack และใช้ FOR UPDATE SKIP LOCKED ของ PostgreSQL เพื่อป้องกันการชนกันด้าน concurrency
  • การเลือกผู้นำ การกู้คืนงานกำพร้า และการ retry แบบ backoff ก็ทำบนฐานข้อมูลเช่นกัน จึงทำให้ การประมวลผลแบบกระจายที่เสถียรโดยไม่ต้องมี external broker เป็นไปได้

ภาพรวมของ Oban.py

  • Oban.py คือ เฟรมเวิร์กประมวลผลงานแบบอิงฐานข้อมูล ที่พอร์ต Oban ของ Elixir มาสู่ Python
    • แทรกและประมวลผลงานภายในทรานแซกชันของฐานข้อมูล และหากล้มเหลวทรานแซกชันทั้งหมดจะถูกโรลแบ็ก
    • มีฟีเจอร์ควบคุมหลากหลาย เช่น การจำกัดคิว การเก็บงานที่เสร็จแล้ว การคงผลลัพธ์ และการตั้งเวลาแบบ cron
  • มีให้เลือก 2 เวอร์ชัน
    • โอเพนซอร์ส (OSS) : รัน asyncio แบบเธรดเดียว, แทรก/ยืนยันผลทีละรายการ, กู้คืนแบบเรียบง่าย
    • เวอร์ชัน Pro: การประมวลผลแบบขนานบน process pool, รองรับ workflow, relay, unique jobs และ smart concurrency
  • OSS เหมาะกับโปรเจ็กต์ส่วนตัวหรือการประเมินทดลองใช้ ส่วนสภาพแวดล้อมขนาดใหญ่แนะนำเวอร์ชัน Pro

เส้นทางการประมวลผลงาน

  • หลังแทรกงาน จะถูกบันทึกลงตาราง oban_jobs ด้วย state='available' และส่งการแจ้งเตือนไปยังแต่ละโหนดผ่าน NOTIFY ของ PostgreSQL
  • Stager ของแต่ละโหนดจะตรวจจับคิวดังกล่าวแล้วปลุก Producer ขึ้นมา จากนั้น Producer จะดึงงานไปประมวลผล
  • ตอนเลือกงาน ใช้ FOR UPDATE SKIP LOCKED ของ SQL เพื่อให้ ประมวลผลแบบขนานได้โดยไม่รันงานซ้ำ
    • แถวที่ถูกล็อกอยู่แล้วจะถูกข้าม ทำให้โปรดิวเซอร์อื่นดึงงานอื่นไปได้ทันที
  • งานจะถูก dispatch เป็น async task และเมื่อเสร็จสิ้นจะจัดการ acknowledgement ผ่าน callback
  • เวอร์ชัน Pro ใช้ dispatcher แบบ process pool แทน asyncio เพื่อรองรับการรันแบบขนานหลายคอร์

โปรเซสเบื้องหลัง

  • การเลือกผู้นำ (Leader Election)
    • ใช้ INSERT ... ON CONFLICT ของ PostgreSQL และ lease แบบอิง TTL เพื่อกำหนดผู้นำ
    • โดยไม่ต้องมี consensus protocol แยกต่างหาก ผู้นำเพียงตัวเดียวจะรับหน้าที่ ล้างและกู้คืนงาน
  • Lifeline (กู้คืนงานกำพร้า)
    • หากงานที่กำลังรันอยู่นานเกินเวลาที่กำหนด (rescue_after, ค่าเริ่มต้น 5 นาที) จะถูก กู้คืนกลับเป็นสถานะ available
    • เวอร์ชัน Pro ตรวจสอบการมีชีวิตอยู่ของ producer แต่ OSS ตัดสินจากเวลาเพียงอย่างเดียว
  • Pruner (ล้างงาน)
    • ลบงานที่เสร็จสิ้น ยกเลิก หรือถูกทิ้ง ซึ่งมีอายุมากกว่า max_age (ค่าเริ่มต้น 1 วัน)
    • จำกัดช่วงการลบด้วย LIMIT เพื่อป้องกันภาระโหลดบนฐานข้อมูล

การ retry และ backoff

  • หากงานเกิด exception ขึ้น Executor จะเป็นผู้ตัดสินว่าจะ retry หรือไม่
    • หากยังไม่ถึงจำนวนครั้งสูงสุด (max_attempts) ก็จะ retry และหากเกินจะถูกทิ้ง
  • backoff เริ่มต้นเป็นแบบ เพิ่มขึ้นเชิงเลขชี้กำลัง พร้อม jitter
    • ลดปัญหาโหลดพุ่งจากการ retry พร้อมกันจำนวนมาก (Thundering Herd) เมื่อเกิดความล้มเหลวจำนวนมาก
    • ตัวอย่าง: ครั้งที่ 1 รอประมาณ 17 วินาที, ครั้งที่ 5 รอประมาณ 47 วินาที, ครั้งที่ 10 รอประมาณ 17 นาที
  • คลาส worker สามารถใช้เมธอด backoff() เพื่อสร้าง ลอจิก backoff แบบกำหนดเอง ได้

คุณสมบัติเด่นและการประเมิน

  • PostgreSQL ทำหน้าที่หลัก
    • ผ่าน FOR UPDATE SKIP LOCKED, LISTEN/NOTIFY, ON CONFLICT เพื่อจัดการทั้ง การควบคุม concurrency การส่งสัญญาณ และการเลือกผู้นำ
    • สร้างชั้นการประสานงานด้วย ฐานข้อมูลเดียวโดยไม่ต้องใช้ Redis หรือ external broker
  • ไม่ขนานเต็มรูปแบบแต่รองรับ concurrency
    • ด้วย asyncio จึงเหมาะกับงานแบบ I/O-bound ส่วนงานแบบ CPU-bound แนะนำเวอร์ชัน Pro
  • โครงสร้างโค้ดชัดเจน
    • ด้วยการตั้งชื่อที่สม่ำเสมอและการแยกความรับผิดชอบที่ชัด ทำให้เป็น โค้ดเบสที่อ่านง่าย
  • การแบ่งบทบาทของ OSS และ Pro ชัดเจน
    • OSS สำหรับการทดลองและงานขนาดเล็ก ส่วน Pro สำหรับสภาพแวดล้อมขนาดใหญ่และต้องการประสิทธิภาพสูง
  • สรุป: เป็น Python port ที่สะอาดและมีโครงสร้างดี ซึ่งสร้าง job queue ที่สมบูรณ์ได้ด้วย PostgreSQL เพียงอย่างเดียว เหมาะกับผู้ใช้ Elixir หรือผู้พัฒนาที่ต้องการระบบงานแบบไม่มีโครงสร้างพื้นฐานภายนอก

1 ความคิดเห็น

 
GN⁺ 2026-01-30
ความคิดเห็นจาก Hacker News
  • ฉันคือคนที่สร้าง Sidekiq ขอแสดงความยินดีกับสิ่งที่ Shannon และ Parker เพิ่งเปิดตัว
    เมื่อก่อนฉันก็เคยลังเลเรื่องเดียวกัน — จะโฟกัสที่ Ruby ต่อไป หรือจะขยาย Sidekiq ไปยังภาษาอื่นดี สุดท้ายก็พบว่าไม่มีทางเป็นผู้เชี่ยวชาญได้ทุกภาษา ก็เลยสร้าง Faktory แทน มันเป็นสถาปัตยกรรมที่ให้เซิร์ฟเวอร์กลางจัดการวงจรชีวิตของคิว ส่วนไคลเอนต์ของแต่ละภาษาก็เรียบง่าย เช่นมีไคลเอนต์อย่าง faktory-rs ข้อเสียคือมันไม่ได้โฟกัสที่คอมมูนิตี้ของภาษาใดภาษาหนึ่ง ทำให้ยากที่จะมีตัวอย่างที่เหมาะกับภาษานั้น
    แนวทางที่โฟกัสคอมมูนิตี้เดียวอาจให้ผลลัพธ์ที่ดีกว่าก็ได้ เดี๋ยวเวลาก็จะบอกเอง

    • ขอบคุณนะ Mike! คุณคือ แรงบันดาลใจ ของจริง Parker กับผมมีจุดแข็งคนละแบบ และเราเชื่อในพลังเสริมที่เกิดจากการทำงานร่วมกันระหว่าง Python กับ Elixir
    • Faktory เป็นแรงบันดาลใจสำคัญให้กับระบบคิวงานแบบไม่ผูกกับภาษา Ocypod ที่ผมสร้างขึ้น ขอบคุณที่ปล่อยเป็นโอเพนซอร์ส
    • ถ้าจะพูดให้แม่นกว่านี้ ไม่ควรบอกว่าทั้งคู่มีพื้นฐานมาจาก Resque มากกว่าหรือ?
    • อาจไม่ได้ตั้งใจ แต่คอมเมนต์ของคุณดูเหมือนพยายามโปรโมตโปรเจกต์ตัวเอง ซึ่งที่นี่คนมักไม่ค่อยชอบแบบนั้น
    • คำว่า “based on” ดูจะพูดเกินไปหน่อย Sidekiq เรียบง่ายกว่ามากเมื่อเทียบกับ workflow, cron, partitioning, งานที่มี dependency, การจัดการความล้มเหลว ฯลฯ ที่ Oban รองรับ
  • แก่นสำคัญของ Oban คือมันสามารถใส่งานและประมวลผลงานได้ด้วยฐานข้อมูลเพียงอย่างเดียว คุณสามารถเพิ่มงานส่งอีเมลไว้ในทรานแซ็กชันการสร้างผู้ใช้ แล้วถ้าล้มเหลวก็ rollback ทั้งหมดได้
    หลายคนบอกว่าไม่ควรใช้ relational DB เป็น job queue แต่พวกเขามองข้ามความสำคัญของทรานแซ็กชัน บทความของ Brandur Leach ชื่อ Job Drain ก็อธิบายแนวคิดนี้ไว้ดีมาก

    • เห็นด้วยอย่างมาก ผมเคยเห็น event หายไปเพราะ Dual Write Problem อยู่หลายปี สุดท้ายพอกลับมาใช้แนวทางบน SQL ปัญหาก็หายภายในวันเดียว
      แต่ตอนนี้ไม่มีใครจำความลำบากนั้นได้แล้ว “รูปแบบ transactional outbox” เป็นสิ่งจำเป็น และผมชอบวิธีที่ได้ การรับประกันแบบ ACID เหมือนกับข้อมูลของผมเอง
      ต่อให้ไม่รู้ภายในของ DB มากนัก แค่ลงทุนเรียนเรื่อง isolation level กับลำดับการ commit สักหนึ่งสัปดาห์ ก็อาจช่วยประหยัดเวลาการ debug ระบบกระจายได้เป็นปี
    • แบบนี้เรียกว่า Transactional Outbox Pattern นั่นแหละ
    • ผมก็คิดว่าฟีเจอร์นี้เจ๋งมากเหมือนกัน แต่ของผมใช้ pg_timetable อยู่
    • เรากำลังสร้าง AI app builder และใช้ Elixir, Phoenix และแน่นอนว่าใช้ OBAN ด้วย
      ในยุคที่มี AI process ยาว ๆ เยอะมาก ความ ทนทาน แบบนี้เป็นสิ่งจำเป็น ใน ecosystem ภาษาอื่นฟีเจอร์แบบนี้ต้องจ่ายเงินเพิ่ม แต่ใน Oban มีมาให้เลย
    • ไม่เคยนึกถึงทรานแซ็กชันในมุมนี้มาก่อน ประทับใจจริง ๆ
  • ทีม Oban ขึ้นชื่อใน ecosystem ของ Elixir เรื่อง วิศวกรรมที่ประณีต แต่การล็อก process pool ไว้ในเวอร์ชันโปรทำให้สับสน
    ตัวอย่างเช่น แพ็กเกจ $135/เดือนมีการรันหลายโปรเซส, workflow, global limits, unique jobs, bulk operations, encrypted sources และซัพพอร์ตเฉพาะทาง
    โปรเจกต์ของผม Chancy ฟรีทั้งหมด และสามารถผสม asyncio, process, thread และ sub-interpreter ได้อย่างอิสระ
    ผมเลยคิดว่าการย้ายฟีเจอร์เหล่านี้ไปไว้ใน OSS แล้วเก็บเงินจาก enterprise support น่าจะดีกว่า เพราะ ecosystem ของ Python มีคู่แข่งมากกว่ามาก

    • เราอาจย้ายบางฟีเจอร์กลับไปเป็น OSS ตามความสนใจและการใช้งาน ซึ่งเราเคยทำแบบนั้นใน Elixir มาแล้ว
      โมเดลที่ขายแค่ซัพพอร์ตอย่างเดียวดูจะไม่ค่อยเวิร์ก แต่ใน Python อาจต่างออกไป
      ecosystem ของ Python นี่มี ทุกอย่างเยอะมาก จริง ๆ
    • เผื่อเป็นข้อมูล Django 6.0 เพิ่ม Django Tasks API เข้ามาแล้ว ทำให้สลับ backend ได้ แต่ตอนนี้ยังไม่มี backend สำหรับ production จริง ๆ
      ถ้าเพิ่มการรองรับ Django Tasks ให้ Chancy หรือทำแพ็กเกจ django-chancy ขึ้นมาเอง น่าจะถูกนำไปใช้ได้เร็ว
    • ขอบคุณที่แชร์ Chancy ดูน่าสนใจดี นอกจากเรื่อง API ที่ยังเปลี่ยนได้ ตอนนี้ใช้ใน production แบบ เสถียร ได้หรือยัง?
  • Oban เวอร์ชัน OSS รองรับแค่การรัน asyncio แบบ single-threaded ทำให้งานที่เป็น CPU-bound ไปบล็อก event loop
    เพราะงั้นผมเลยรู้สึกว่ามันยังไม่น่าลอง อินเทอร์เฟซของ Celery อาจไม่ดีนัก แต่คุ้นเคยและขยายได้ไม่จำกัดทั้งแนวตั้งและแนวนอน
    แต่พอรู้ว่าสามารถรัน worker node ได้หลายตัว ก็เริ่มเปลี่ยนความคิดนิดหน่อย

  • การแบ่งฟีเจอร์ OSS/Pro ก็พอเข้าใจได้ แต่พอเห็นว่า “เวอร์ชัน Pro ใช้ heartbeat ที่ฉลาดกว่าเพื่อติดตามว่า producer ยังมีชีวิตอยู่” ก็รู้สึกเสียดาย
    การที่ ฟีเจอร์ด้านความน่าเชื่อถือ ต้องเสียเงิน ทำให้โปรเจกต์ OSS ตัดสินใจนำไปใช้ยาก

    • ผมก็รู้สึกเหมือนกัน Oban เจ๋งนะ แต่การที่ “เวอร์ชันที่ดีกว่าของฟีเจอร์เดียวกัน” อยู่ในแบบเสียเงินมันน่าเสียดาย
      เวอร์ชันพื้นฐานควรต้องดีที่สุด แล้วค่อยเก็บเงินกับฟีเจอร์เสริม ตอนนี้เส้นแบ่งมันดูอยู่ใน ตำแหน่งที่แปลก ๆ
    • คิวที่ใช้ Redis หรือ RabbitMQ อาจทำงานหายได้หลังจากการปิดตัวแบบไม่คาดคิด
      ประโยคที่ยกมานั้นไม่ค่อยแม่นเท่าไร — การติดตามว่า producer ยังอยู่เหมือนกัน ความต่างมีแค่ วิธีการกู้งานกำพร้า เท่านั้น
  • อยากให้ workflow ด้าน BI/ML/DS ใน Python ย้ายมาอยู่บน Elixir
    ผมคิดว่า Elixir ที่มีความเป็น functional, fault-tolerant และ concurrent สูง เป็นฐานที่ธรรมชาติกว่าสำหรับงานแบบนี้มาก

  • บริษัทเราก็ใช้ Celery อยู่ แต่มันไม่ได้ดีเท่าไร Temporal ก็ดูหนักเกินไป ส่วน Oban ดูเบาและน่าสนใจ
    อยากรู้ว่าคนที่เคยใช้ทั้งสองอย่างเปรียบเทียบกันอย่างไร

    • เครื่องมือทั้งสองตัวมี ปรัชญาต่างกันโดยสิ้นเชิง
      Temporal เหมาะกับองค์กรที่ต้องการการรับประกันของ workflow และยอมรับความซับซ้อนได้ เช่น ธนาคาร
      ส่วน Oban เป็นคิวแบบอิงฐานข้อมูล ซึ่งคุณต้องเสริมความน่าเชื่อถือเอง
      ผมคิดว่าถ้ามีทั้งคู่ร่วมอยู่ในระบบเดียวกันก็ดี
    • เราเพิ่งย้ายจาก Celery ไป Prefect และพอใจมาก มันเหมาะมากกับงานระดับหลายพันรายการ
      ตอนนี้เราใช้ทั้ง ProcessWorker แบบเรียบง่ายและ worker บน ECS ร่วมกัน
    • ผมเพิ่งกลับมาทำ Python เว็บอีกครั้งหลังห่างไปนาน แล้วก็ตกใจที่เห็น คำบ่น เรื่อง Celery เยอะมาก
      เลยสงสัยว่าเดี๋ยวนี้ Celery เสถียรน้อยลงหรือจัดการยากขึ้นหรือเปล่า
  • เป็นโปรเจกต์ที่น่าสนใจ แต่ก็สังเกตได้ว่าฟีเจอร์หลักบางส่วนมีเฉพาะใน Pro
    โปรเจกต์ที่มาก่อนและทำ durable workflow บน Postgres แบบ OSS ได้แก่ DBOS และ Absurd
    เป็นเรื่องดีที่เห็นแนวทางแบบยึดฐานข้อมูลเป็นศูนย์กลางเพิ่มขึ้น

    • ใน Elixir ก็เคยมีโปรเจกต์ OSS คล้ายกันมาก่อน และการทำ durable workflow ของ Oban ก็เกิดก่อน DBOS อยู่หลายปี
      โมเดลโอเพนซอร์สเต็มตัวและอยู่ได้ด้วยการขายซัพพอร์ตอย่างเดียวเป็น โมเดลในฝัน หวังว่าสักวันจะทำได้
  • สงสัยว่า Postgres จะ แรงพอ สำหรับการประมวลผลงานระดับหลายร้อยล้านรายการไหม ก่อนหน้านี้ผมย้ายไป Redis + Sidekiq แล้วเห็นประสิทธิภาพดีขึ้นมาก

    • อยากรู้ว่าในช่วงเวลาเท่าไร ผมใช้ Rails/Solid Queue + Postgres ประมวลผลงานวันละ 20 ล้านงานบน VM ราคา $45 และยังเหลือสบาย ๆ
  • เวอร์ชัน OSS บอกว่าถ้ามีงานยาว ๆ มันอาจถูกกู้คืนผิดพลาดได้แม้ producer จะยังทำงานอยู่
    งั้นแปลว่าควรใช้กับงานสั้น ๆ เท่านั้นหรือ?

    • ไม่มีข้อจำกัดเรื่องความยาวของงาน ความต่างมีแค่ ความเร็วในการกู้คืนหลังการชนล้ม เท่านั้น
      จังหวะการกู้คืนจะต่างกันเฉพาะในกรณีที่ไม่สามารถรอให้ปิดตัวอย่างปกติได้