16 คะแนน โดย GN⁺ 2026-01-15 | 2 ความคิดเห็น | แชร์ทาง WhatsApp
  • Rails 8 ได้ ถอดการพึ่งพา Redis ออกจากสแตกพื้นฐาน และเปลี่ยนไปประมวลผลงานทั้งหมดบน ฐานข้อมูลเชิงสัมพันธ์ (RDB) ผ่าน SolidQueue·SolidCache·SolidCable
  • แม้ Redis จะรวดเร็วและเสถียร แต่ก็ก่อให้เกิด ความซับซ้อนในการปฏิบัติการ เช่น การตั้งค่า ความปลอดภัย การจัดการคลัสเตอร์ และการสำรองข้อมูล
  • SolidQueue ใช้ความสามารถ FOR UPDATE SKIP LOCKED ของ PostgreSQL เพื่อทำ การประมวลผลงานแบบขนานโดยไม่เกิดการแข่งขันกัน
  • มีฟีเจอร์แบบเสียเงินของ Redis+Sidekiq อย่าง งานตามรอบ การควบคุมการทำงานพร้อมกัน และแดชบอร์ดมอนิเตอร์ริง (Mission Control) ให้ใช้ ฟรี
  • แอป Rails ส่วนใหญ่ใช้ SolidQueue ก็เพียงพอ และมีเพียงบางกรณีที่ต้องการ การประมวลผลความเร็วสูงมากหรือแบบเรียลไทม์ เท่านั้นที่ยังควรใช้ Redis

ต้นทุนแฝงของ Redis

  • นอกเหนือจากค่าโฮสติ้งแบบพื้นฐานแล้ว Redis ยังมีภาระในการดูแลต่อเนื่อง เช่น การติดตั้ง การบำรุงรักษา การตั้งค่าความปลอดภัย และการจัดการ HA cluster
    • จำเป็นต้องมี การเชื่อมต่อเครือข่ายและการตั้งค่าไฟร์วอลล์ระหว่าง Rails กับ Redis, การยืนยันตัวตนของไคลเอนต์, และ การออร์เคสเตรตโปรเซส Sidekiq
  • เมื่อเกิดปัญหา ต้อง ดีบักทั้ง Redis และ RDBMS พร้อมกัน และยังต้องมี กลยุทธ์สำรองข้อมูลแบบสองชุด
  • ในทางกลับกัน สแตก Rails ที่ไม่มี Redis จะต้องดูแลเพียง PostgreSQL ตัวเดียว ทำให้เรียบง่ายกว่า

หลักการทำงานของ SolidQueue

  • ใช้ความสามารถ FOR UPDATE SKIP LOCKED ของ PostgreSQL เพื่อให้เวิร์กเกอร์หลายตัวหยิบงานได้พร้อมกันโดยไม่เกิด lock contention
  • โครงสร้างตารางหลัก
    1. solid_queue_jobs: เก็บเมทาดาทาของงาน
    2. solid_queue_scheduled_executions: รอคิวงานที่ถูกตั้งเวลาไว้
    3. solid_queue_ready_executions: คิวงานที่พร้อมรัน
  • โปรเซส worker·dispatcher·scheduler·supervisor จะโพลตารางคนละชุดเป็นระยะและทำงานร่วมกัน
  • ด้วยการออกแบบ MVCC และ autovacuum ของ PostgreSQL จึงรองรับงานแทรกและลบจำนวนมากได้อย่างเสถียร

การตั้งเวลางานซ้ำ

  • SolidQueue รองรับ งานซ้ำแบบ cron มาให้ในตัว และกำหนดค่าได้ผ่านไฟล์ config/recurring.yml
  • ตัว scheduler จะนำงานที่ถึงเวลารันเข้าไปในคิว และ ตั้งเวลารอบถัดไปให้อัตโนมัติ
  • ใช้ไลบรารี Fugit สำหรับพาร์สตารางเวลาแบบภาษาธรรมชาติ และใช้ Concurrent::ScheduledTask เพื่อสร้างเธรด
  • ยืมแนวทางการตั้งเวลาแบบ deterministic ของ GoodJob มาใช้ ทำให้ ตารางเวลายังคงเดิมแม้โปรเซสจะรีสตาร์ต

ฟีเจอร์ควบคุมการทำงานพร้อมกัน

  • SolidQueue ใช้ แพตเทิร์น POSIX semaphore เพื่อรองรับ การจำกัดจำนวนการทำงานพร้อมกันในระดับงาน
    • ตัวอย่าง: เมื่อตั้งค่า limits_concurrency to: 1, key: ->(user) { user.id } จะอนุญาตให้รันงานได้เพียง 1 งานต่อผู้ใช้
  • สามารถกำหนดเวลาหมดอายุของ semaphore (duration) เพื่อ ป้องกันงานชนกันและ deadlock
  • ตารางที่เกี่ยวข้อง
    • solid_queue_semaphores: ติดตามข้อจำกัดการทำงานพร้อมกัน
    • solid_queue_blocked_executions: เก็บงานที่กำลังรอ

การมอนิเตอร์ด้วย Mission Control

  • Mission Control Jobs คือแดชบอร์ดโอเพนซอร์สฟรีสำหรับ Rails 8 ที่สามารถ mount ได้ง่ายที่พาธ /jobs
  • ฟีเจอร์หลัก
    • สถานะคิวแบบเรียลไทม์ การติดตามงานที่ล้มเหลว การสั่ง retry/ทิ้งงาน
    • การแสดงไทม์ไลน์ของงานที่ตั้งเวลาและงานซ้ำ
    • กราฟ throughput และเมตริกแยกตามคิว
  • รองรับการสืบค้นด้วย SQL ทำให้ วิเคราะห์จากฐานข้อมูลได้โดยตรงโดยไม่ต้องใช้เครื่องมือเพิ่ม

การย้ายจาก Sidekiq ไปสู่ SolidQueue

  • ขั้นที่ 1: ตั้งค่า config.active_job.queue_adapter = :solid_queue
  • ขั้นที่ 2: รัน bundle add solid_queue จากนั้นรัน rails solid_queue:install และ db:migrate
  • ขั้นที่ 3: แปลงตาราง cron ใน sidekiq.yml เป็น recurring.yml
  • ขั้นที่ 4: เพิ่ม jobs: bundle exec rake solid_queue:start ลงใน Procfile
  • ขั้นที่ 5: ลบ gem ที่เกี่ยวข้องกับ Redis·Sidekiq
  • โค้ด ActiveJob เดิมสามารถทำงานต่อได้โดยไม่ต้องแก้ไข

กรณีที่ยังจำเป็นต้องใช้ Redis

  • มีงานต่อเนื่องระดับหลายพันรายการต่อวินาที
  • ระบบเรียลไทม์ที่ต้องการ latency ต่ำกว่า 1ms
  • ต้องการ โครงสร้าง pub/sub ที่ซับซ้อน หรือ rate limiting และการนับค่าแบบละเอียด
  • ตัวอย่างเช่น Shopify มีการประมวลผล 833 คำขอต่อวินาที และใช้เวิร์กเกอร์โปรเซส 1,172 ตัว พร้อมโครงสร้างพื้นฐาน Redis

คู่มือการติดตั้งใช้งานจริง

  • เมื่อสร้างแอปใหม่บน Rails 8 จะมีการตั้งค่า SolidQueue·SolidCache·SolidCable ให้อัตโนมัติ
  • แนะนำให้ตั้งค่า การเชื่อมต่อฐานข้อมูลสำหรับ queue แยกต่างหาก ใน config/database.yml
  • เพิ่มการยืนยันตัวตนให้ Mission Control และ mount route /jobs
  • เพิ่ม jobs: bundle exec rake solid_queue:start ลงใน Procfile.dev แล้วรัน bin/dev เพื่อสตาร์ตทั้งระบบ
  • หลังสร้างงานทดสอบแล้ว สามารถตรวจสอบสถานะได้ใน Mission Control

ปัญหาที่พบบ่อยและวิธีแก้

  • สามารถใช้ ฐานข้อมูลเดียว ได้เช่นกัน แต่ความยืดหยุ่นในการปฏิบัติการจะลดลง
  • ใน Mission Control บน production จำเป็นต้องเพิ่มการยืนยันตัวตนเสมอ
  • ค่าเริ่มต้นของ polling interval คือ 1 วินาทีสำหรับงานที่ตั้งเวลาไว้ และ 0.2 วินาทีสำหรับงานทันที ซึ่งเหมาะกับแอปส่วนใหญ่
  • หากใช้ ActionCable/Turbo Streams ต้องตั้งค่า SolidCable ให้เชื่อมต่อฐานข้อมูลแยกต่างหาก

การขยายระบบและประสิทธิภาพ

  • SolidQueue สามารถขยายระบบได้เพียงพอสำหรับแอป Rails ส่วนใหญ่
  • บนพื้นฐาน PostgreSQL สามารถประมวลผลงานได้ 200~300 งานต่อวินาที และ 37signals ประมวลผล 20 ล้านงานต่อวัน ได้โดยไม่ใช้ Redis
  • ตารางเปรียบเทียบ
    รายการ Redis + Sidekiq SolidQueue
    ความซับซ้อนในการตั้งค่า ต้องใช้บริการแยก ใช้ DB ที่มีอยู่
    ภาษาสำหรับคิวรี คำสั่ง Redis SQL
    การมอนิเตอร์ ต้องมีแดชบอร์ดแยก Mission Control
    สถานการณ์เมื่อเกิดปัญหา มากกว่า 6 แบบ 2 แบบ
    Throughput หลายพันรายการ/วินาที 200–300 รายการ/วินาที
    เหมาะกับใคร 99.9% ของแอป 95% ของแอป

บทสรุป

  • Redis และ Sidekiq เป็นเทคโนโลยีที่ยอดเยี่ยม แต่สำหรับ แอป Rails ส่วนใหญ่แล้วก่อให้เกิดความซับซ้อนและต้นทุนที่มากเกินไป
  • SolidQueue ทำให้เกิด ความเรียบง่ายในการปฏิบัติการ การลดต้นทุน และการดูแลรักษาที่มีประสิทธิภาพ ด้วยแนวทางฐานข้อมูลเดียว
  • ในยุคของ Rails 8 จึงแนะนำให้ เปลี่ยนมาใช้ SolidQueue เป็นตัวเลือกพื้นฐาน

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

 
kaydash 2026-01-17

Redis ก็ดีนะ

 
GN⁺ 2026-01-15
ความเห็นจาก Hacker News
  • คิดว่า ผู้เขียนโอเพนซอร์สทุกคนมีสิทธิ์ควบคุมขอบเขตของโปรเจ็กต์ตัวเอง
    แต่ทีมของเรากลับเสียใจกับการเปลี่ยนจาก good_job ไปเป็น SolidQueue
    Basecamp เน้น MySQL เป็นหลัก จึงไม่รับ คิวรีที่เฉพาะกับเอนจิน RDBMS ถ้าไปดู GitHub issue จะเห็นว่าโฟกัสอยู่แค่ประสิทธิภาพของ MySQL
    อีกทั้งยังไม่มี การรองรับงานแบบแบตช์ (PR ที่เกี่ยวข้อง)

    • ฟังดูเหมือนเป็นส่วนผสมที่แย่มาก เราเองก็ใช้ MySQL แต่ขาดคิวรีเฉพาะเอนจินไม่ได้
      ใน JOIN ที่ซับซ้อน MySQL มักวาง query plan ผิด ฉันเลยใช้ STRAIGHT_JOIN เพื่อบังคับลำดับ เป็นการเตรียมไว้สำหรับอนาคต
    • ถ้าผูกกับ MySQL ลึกขนาดนั้น การใช้ ฟีเจอร์เฉพาะของ MySQL ก็สมเหตุสมผลไม่ใช่เหรอ? เหมือนมีอะไรขาดไป
    • อยากฟังให้ชัดกว่านี้ว่าทำไมถึงแนะนำ GoodJob มากกว่า SolidQueue
      ฉันกำลังเทียบสองตัวนี้เป็นตัวเลือกสำหรับย้ายมาจาก resque อยู่ GoodJob ใช้ ฟีเจอร์เฉพาะของ pg เลยเข้ากับ pgbouncer transaction mode ไม่ได้
      มันต้องคง session ไว้ต่อเนื่องเลยค่อนข้างน่ารำคาญ แต่ประโยชน์ด้านประสิทธิภาพก็ไม่ได้มีนัยสำคัญมากสำหรับสเกลส่วนใหญ่
      ถึงอย่างนั้น โมเดลการพัฒนาและความอ่านง่ายของโค้ด ของ GoodJob ก็ทำให้เชื่อมั่นได้มากกว่ามาก
    • เห็นด้วย good_job เป็นแนวทางที่แทบจะสมบูรณ์แบบสำหรับ คิวที่สร้างบน Postgres
    • ยังไม่ค่อยรู้จัก SolidQueue มากนัก แต่ได้ลองใช้ good_job แล้วรู้สึกดีมาก ใช้งานได้ดีจริง ๆ
  • ถ้าทำให้สภาพแวดล้อมโปรดักชันเรียบง่ายขึ้นได้ ก็เป็นเรื่องดีเสมอ
    สำหรับ Rails ฉันคิดว่าสถานการณ์ที่เหมาะที่สุดคือ โครงสร้างที่สลับไป Redis ได้ง่าย
    อยากให้เริ่มต้นด้วย SolidQueue แล้วถ้าไปชนเพดานด้านการขยายระบบก็ย้ายไป Redis ได้
    แอป Rails ส่วนใหญ่ไม่ได้มีทราฟฟิกสูงมาก การต้องดูแลสองระบบกลับซับซ้อนกว่า

    • Rails มี Active Job ซึ่งเป็น API แบบนามธรรม ทำให้สลับไป Redis ได้ค่อนข้างง่าย
      แน่นอนว่าบางแอปก็พึ่งพาการทำงานเฉพาะของคิวบางตัว แต่โดยทั่วไปเปลี่ยนแค่การตั้งค่าก็พอ
    • สงสัยว่า โหมด Redis AOF บันทึกทุกการเปลี่ยนแปลงแบบ WAL หรือเปล่า
      มันใช้ snapshot ควบคู่กันเพื่อไม่ให้ log โตเกินไปไหม และอยากรู้ว่ามันใช้ได้ใน โหมดกระจาย ด้วยหรือไม่
    • ถ้าพึ่งพา transaction มากเกินไป การย้ายระบบจะยากขึ้น
      โดยเฉพาะเวลาที่การสร้างงานเกิดขึ้นพร้อมกับการเปลี่ยนแปลง DB อื่น ๆ การเสียการรับประกันแบบนั้นเป็นปัญหา
    • Rails ไม่ได้แยกตัวประมวลผลงานเบื้องหลังออกจาก DB โปรดักชัน ทำให้สับสน
      Redis ได้เปรียบในจุดนี้เพราะเป็น state store ที่เบาและแยกอิสระ
      ดูเหมือน SolidQueue จะไม่ได้ทำให้การแยกแบบนี้ชัดเจน (riverqueue.com)
  • ฉันลองทดลองใช้ SolidQueue ใน side project
    สรุปคือ ถ้า Sidekiq ไม่ได้มีปัญหา ก็ไม่มีเหตุผลต้องเปลี่ยน
    ค่อยพิจารณาเฉพาะตอนที่อยากเลิกใช้โครงสร้างพื้นฐาน Redis
    ถ้าเป็นโปรเจ็กต์ใหม่ GoodJob ดูสุกงอมกว่าและชุมชนก็ดีกว่า
    ฉันไม่ชอบตรงที่ UI ของ SolidQueue เรียบง่ายเกินไป และไม่มีการปรับดัชนีให้เหมาะสม พอข้อมูลเยอะหน้าก็ค้าง
    อีกเรื่องที่ต้องคิดคือการใช้ RDBMS จะเพิ่ม ต้นทุนการจัดการ connection pool

  • สำหรับคนที่กังวลเรื่องการขยายระบบ ถ้าไปดู benchmark ของ Oban ใน Elixir
    มันประมวลผลงานได้หนึ่งล้านงานต่อนาทีบนโหนดเดียว ซึ่งแอปส่วนใหญ่มีปริมาณงานน้อยกว่านั้นมาก

    • บริษัทเราก็ใช้ Oban และ Oban แนะนำให้ใช้ Redis เป็น notifier หรือไม่ก็ใช้การ polling (เอกสาร scaling)
    • แต่ benchmark นั้นก็ห่างไกลจากแอปจริงมาก
      มันเป็นโครงสร้างที่ใส่งานทีเดียว 5000 งานแบบแบตช์ ดังนั้น TPS จริง ๆ แค่ราว 200
      ถ้าใส่งานทีละงานโดยไม่มีแบตช์ ภาระของ SQL transaction จะสูงกว่านี้มาก
  • เราเก็บ งานไว้ใน DB มาตั้งแต่ก่อนมี SolidQueue
    ข้อดีคือสามารถทำ snapshot สถานะโปรดักชันมาที่สภาพแวดล้อมพัฒนาได้ตรง ๆ
    แต่ rate limiter ยังไว้บน Redis เพื่อไม่ให้ DB รับภาระหนักเกินไป

  • ข้อจำกัดของคิวที่อิง DB คือ payload ขนาดใหญ่
    ถ้าใส่ JSON ก้อนใหญ่ลงคิวจะไม่มีประสิทธิภาพเพราะมี overhead ฝั่งเขียน DB
    Redis (Sidekiq) เร็วกว่าเยอะในกรณีแบบนี้
    SolidQueue+SQLite ก็โอเคถ้าใช้แค่ ส่งผ่าน primary key
    แต่ถ้ามี worker หลายตัว polling DB เดียวกัน ก็จะกลายเป็นคอขวดอย่างรวดเร็ว

    • ในทางปฏิบัติมักส่งพารามิเตอร์งานเป็นแค่ ID หนึ่งหรือสองตัว เท่านั้น มากกว่านั้นจะไม่มีประสิทธิภาพ
    • Redis เองก็ไม่เหมาะกับ payload ใหญ่เพราะมีข้อจำกัดด้านหน่วยความจำ
      ฉันคิดว่าควรเก็บข้อมูลใหญ่ไว้ใน สตอเรจภายนอกอย่าง S3 แล้วส่งแค่ตัวอ้างอิงจะดีกว่า
    • ยังไงส่วนอื่นของระบบก็มักต้องมีสตอเรจอยู่แล้ว ดังนั้นการใช้ ที่เก็บชั่วคราว อย่าง S3 หรือ garage จึงสมจริงกว่า
      ไม่ทราบว่ามีข้อมูลที่สรุปผล benchmark ไว้หรือเปล่า
    • ในเอกสารของ Sidekiq ก็แนะนำให้ส่ง แค่ตัวระบุ
    • การเก็บ payload ใหญ่ใน Redis เป็น แนวปฏิบัติที่ไม่ดี เพราะหน่วยความจำมีจำกัด
  • SolidQueue พูดถึง SKIP LOCKED แต่การคง transaction ไว้สำหรับงานที่ใช้เวลา 15 นาทีเป็นเรื่องเสี่ยง
    transaction ที่เปิดค้างนานจะทำลายประสิทธิภาพ DB และเปราะบางต่อการหลุดของเครือข่าย
    โครงสร้างแบบนี้อาจนำไปสู่ anti-pattern ได้ ทีหลังถึงได้เห็นว่าเหมือนจะใช้วิธี lease

  • เห็นด้วยกับแนวคิด Postgres for everything
    ฉันคิดว่าการ รวมทุกอย่างไว้ที่ PostgreSQL ตัวเดียว เป็นเรื่องดี

    • ฉันก็ชอบแนวคิด all-in กับ PG เหมือนกัน แต่ก็มักได้ยินคำว่า “ถ้ามีแต่ค้อน ทุกอย่างก็จะดูเหมือนตะปู”
      ไม่รู้จะโต้แย้งอุปมานี้ยังไงดี
    • ทุกวันนี้ สตอเรจ NVMe เร็วมากจนรู้สึกว่าข้อดีของ Redis ลดลงไปเยอะ
      เลยสงสัยว่ายังมีเหตุผลอะไรที่ต้องใช้ Redis ทั้งที่เพิ่มความซับซ้อน
  • “ธุรกิจที่ latency ต่ำกว่า 1ms สำคัญ” นี่หมายถึงทำ HFT ด้วย Rails เหรอ?

    • แน่นอนว่าไม่ใช่ ถ้าดู กรณีศึกษาของ SimpleThread บริษัทนั้นก็ห่างไกลจาก HFT มาก
    • เอนจินการเทรดคงไม่รันบน Rails แต่ UI สำหรับมอนิเตอร์การเทรด อาจทำด้วย Rails ก็ได้
  • Postgres จะครองโลก

    • ฉันก็เห็นด้วย ถ้าวันหนึ่งมี ส่วนขยาย pg_kernel ออกมา อาจถึงขั้นลบ Linux ทิ้งได้เลย
    • แต่ก็เป็นไปได้ว่าอีกไม่กี่ปีเราจะตระหนักว่า “Postgres for everything” ก็เป็นแค่ กระแสเกินจริง แบบเดียวกับ “MongoDB for everything”
    • ถึงอย่างนั้น MySQL ก็ยังดูแลง่ายและ ประสิทธิภาพก็ดีพอสมควร (ฉันใช้ Postgres ก็ยังรู้สึกแบบนั้น)
    • ตอนนี้ฉันใช้ PGQM กับ PG_CRON ซึ่งสะอาดตากว่าชุด MySQL + Redis + AWS เมื่อก่อนมาก
    • สุดท้ายสิ่งสำคัญคือ ชุดความสามารถ และ RDBMS จะครองโลก