เคยอยากนำเข้าข้อมูลจำนวนมากจาก CSV? หรือแก้ไขตัวอักษรที่เข้ารหัสผิดในรีวิวลูกค้า? หรือเปลี่ยนรูปแบบการเก็บข้อมูลใน Redis และต้องย้ายข้อมูลทั้งหมด?
ที่ Avvo เราเรียกว่า "ad-hoc tasks" คืองานที่อาจรันเพียงครั้งเดียว วิธีที่ดีที่สุดในการจัดการ ad-hoc task ใน Rails คืออะไร?
เขียน Database Migration
Migration ใช้ได้ดีถ้าต้องเปลี่ยนโครงสร้างข้อมูลในฐานข้อมูล มันติดตามว่ารันไปแล้ว ส่งต่อการเปลี่ยนแปลงไปยัง environment อื่น - นี่คือสิ่งที่ migration ถูกสร้างขึ้นมาเพื่อทำ
ถ้าคุณเปลี่ยนข้อมูลพร้อมกัน Migration อาจ ใช้ได้ดี แต่มีข้อควรระวัง
การเรียก Permissions.create(...) ใน migration อาจทำให้เสียหาย ถ้า model เปลี่ยนไป migration อาจพัง เพราะ model อาจไม่พร้อมเมื่อ migration รัน หรือ model เปลี่ยนระหว่างเขียนและรัน มีวิธีหลีกเลี่ยงแต่อาจล้มเหลวแบบแปลกๆ
Migrations น้อยประโยชน์ถ้างานไม่เกี่ยวกับ ActiveRecord
ไม่ใช่ deal-breaker แต่ผมมักไม่นำเข้า/เปลี่ยนข้อมูลมากใน migrations มีตัวเลือกดีกว่า
เขียน Rake Task
คุณมีงาน ต้องการรันครั้งเดียว และต้องการทดสอบบนเครื่องและรันใน production
Rake tasks เหมาะสมมาก Rails สร้าง rake tasks ให้:
$ be rails g task locations import create lib/tasks/locations.rake
สร้างไฟล์สำหรับวางโค้ด:
namespace :locations do desc "TODO" task import: :environment do end end
ภายในบล็อก task ใช้ models และโค้ด Rails ทั้งหมดได้ นำเข้าและเปลี่ยนข้อมูง่าย เพราะเขียนโค้ดเหมือนอยู่ที่ Rails console
รันด้วย rake locations:import บน Heroku ใช้ heroku run rake locations:import บน Capistrano ใช้ capistrano-rake gem อาจมีตัวเลือกดีกว่านี้
เขียน Scheduled Job ด้วย sidekiq-scheduler
ถ้าแอปใหญ่พอ คุณ น่าจะ ใช้ Sidekiq, Resque หรือคล้ายๆ กันอยู่แล้ว
Background job processors หลายตัวสามารถกำหนดเวลารันภายหลังได้ ใน Sidekiq มี sidekiq-scheduler gem และมีเทคนิค:
ถ้ามี job ที่ไม่ได้ schedule เองอัตโนมัติ แต่ให้คุณ schedule ด้วยมือเมื่อต้องการ? เหมาะสำหรับ one-off jobs ที่อาจรันซ้ำ หรืออยากรันผ่าน UI
ใน sidekiq-scheduler สามารถ schedule job ไกลในอนาคต และตั้งค่า disabled:
:schedule: location_importer: class: LocationImporterWorker at: '3001/01/01' enabled: false
เวลาเข้า sidekiq-web จะเห็นปุ่ม enqueue job ด้วยมือ:

วิธีนี้รัน job ได้เวลาที่พร้อม ทั้ง development และ production ถ้าต้องรันซ้ำ กดปุ่มได้เลย
ไม่เหมาะถ้าจob อันตราย (กดผิดง่าย) หรือ job ใช้เวลานาน (Sidekiq ทำงานดีถ้า job เร็ว จะยึด worker ไม่ restart ได้จนจบ) แต่ถ้า job เร็วและรันซ้ำได้ปลอดภัย ใช้ได้ดี ถ้าเป็น cleanup task อาจตัดสินใจรันเป็นประจำ
ถ้าต้องการ flexibility ในการตั้ง params ผู้อ่าน Dmitry แนะนำ sidekiq-enqueuer ด้วย sidekiq-enqueuer สามารถ schedule jobs และตั้ง params ผ่าน Sidekiq web interface ได้
SSH เข้า Production วางโค้ดใน Rails Console
แค่เล่นครับ
ควรเลือกอันไหน?
ผมใช้ทุกวิธี แต่ปกติเลือก rake task ก่อน ใช้งานได้ ยากที่จะรันผิด และลบง่ายเมื่อเสร็จ แต่ไม่ได้เลือกทุกครั้ง
อาจเลือก migration ถ้า:
- งานแก้ข้อมูลด้วย SQL ร่วมกับการเปลี่ยน schema
- งานข้อมูลง่าย เช่น เปลี่ยนค่าในคอลัมน์ หรือเพิ่มเรคอร์ดนิดหน่อย
- อยากติดตามง่ายว่า รันไปแล้ว และไม่ให้รันซ้ำ
อาจเลือก Sidekiq job ถ้า:
- คิดว่าอาจรันซ้ำภายหลัง
- คนอื่นต้องรัน (แค่กดปุ่ม)
- data import/cleanup สั้นๆ น่าจะรันเป็นประจำ แม้ตอนแรกคิดว่าไม่
คุณล่ะ? มีตัวเลือกอื่น หรือเลือกต่างกัน? คอมเมนต์บอกมาเลย!