Dùng Sidekiq vào tính năng xếp hạng 1 dãy các dữ liệu
Dạo gần đây mình có khởi động lại project cá nhân để luyện tập tay nghề(Hàng dùng view mặc định của Ruby, không dùng framework JS tử tế cho frontend và dùng Bootstrap 5.3 làm giao diện thôi. Cũng tối cổ so với trend TailwindCSS với chỗ React và VueJS). Ý tưởng thì là 1 website chứa lyrics, tua nhạc và có metronome chạy theo đúng tempo bài hát. Cái mà mình lần trước có deploy với AWS AppRunner rồi nhưng chưa đưa video được.
Vấn đề
Trong quá trình làm thì mình có nảy ra thêm việc tích hợp 1 số tính năng bảng xếp hạng âm nhạc, thông tin nghệ sĩ,.... Nhưng với quá trình xếp hạng ấy cũng nảy ra vấn đề làm sao để đánh giá nghệ sĩ từ bài hát và album của họ?
Và một trong những giải pháp ở đây là đánh giá dựa trên lượt view bài hát và lượt view album. Các nghệ sĩ sẽ được đánh giá dựa theo tổng lượt view của tất cả các bài hát và tất cả các album.(Đây là giải pháp của mình. Thực tế có thể sẽ có khác)
Vì vậy trước tiên mình có db như này và đã triển khai ổn thoả:
OK, đã có lượt view của album và lượt view của bài hát. 2 dữ liệu này sẽ được refresh mỗi khi có 1 người dùng vào trang info của 1 album hay 1 bài hát nào đó.
Tuy nhiên, nếu cứ mỗi lượt view được add vào như vậy, dẫn đến albums_points
và songs_points
sẽ thay đổi realtime, dẫn đến mọi người khác nhau có thể sẽ nhìn bản xếp hạng ở những vị trí khác nhau(rất phù hợp với tính năng trending nhưng không phù hợp lắm với xếp hạng). Mình muốn bảng xếp hạng theo lượt view được cố định trong 1 tuần rồi có bảng xếp hạng mới.
Và để giải quyết thì mình quyết định chạy scheduled job để cập nhật hàng tuần. Đó là lý do động vào Sidekiq
Code
Gemfile
Trước tiên, ở Gemfile
mình thêm sidekiq
như sau
# Gemfile
# ...
# Use Sidekiq
gem 'sidekiq'
gem 'sidekiq-scheduler'
Sau đó chạy bundle update
docker-compose
redis:
image: redis
ports:
- "6379:6379"
app:
build: .
command: bash -c "rm -f tmp/pids/server.pid && bundle exec rails assets:precompile && bundle exec rails s -p 3000 -b '0.0.0.0'"
volumes:
- .:/rails-lyrics-site
- bundle:/usr/local/bundle
- tmp-data:/rails-lyrics-site/tmp
- public-data:/rails-lyrics-site/public
ports:
- "3000:3000"
depends_on:
- db
- redis
sidekiq:
build: .
command: bundle exec sidekiq
volumes:
- .:/rails-lyrics-site
depends_on:
- db
- redis
sidekiq
muốn hoạt động được phải phụ thuộc vào redis
, và cũng cần được tách ra như 1 component riêng chạy song song song với phía container web nên ta thêm định nghĩa container:
redis:
image: redis
ports:
- "6379:6379"
và
sidekiq:
build: .
command: bundle exec sidekiq
volumes:
- .:/rails-lyrics-site
depends_on:
- db
- redis
Cuối cùng thêm container phụ thuộc ở app
.
Setup phía config
Ta cần setup:
- Ở
config/application.rb
:
config.active_job.queue_adapter = :sidekiq
- Tạo mới
config/initializers/sidekiq.rb
:
redis_config= { url: 'redis://redis:6379/0' }
Sidekiq.configure_server do |config|
config.redis = redis_config
end
Sidekiq.configure_client do |config|
config.redis = redis_config
end
- Thêm Sidekiq view ở
config/routes.rb
# frozen_string_literal: true
require 'sidekiq/web'
require 'sidekiq-scheduler/web'
Rails.application.routes.draw do
# ....
mount Sidekiq::Web, at: '/sidekiq'
match '*unmatched', to: 'application#not_found', via: :all
# For details on the DSL available within this file, see https://guides.rubyonrails.org/routing.html
end
- Tạo mới
config/sidekiq.yml
:
:scheduler:
:schedule:
update_artist_pop_points:
cron: '0 30 6 * * 1'
class: ExampleJob
Bước cuối là start server lên kiểm tra localhost:3000/sidekiq
. Khi hiện màn hình giống như này tức là các bạn thành công.
Tạo 1 job và chạy thử
Để tạo 1 Job trong Rails, chúng ta có command rails g job
. Ví dụ, để tạo 1 Job có trên là Blo.....omJob, ta chạy rails g job bloom
Ở tình huống này mình sẽ chạy:
rails g job UpdateArtistPopPointsJob
Ở app/jobs/update_artist_pop_points_job.rb
, mình thêm code:
# frozen_string_literal: true
class UpdateArtistPopPointsJob < ApplicationJob
queue_as :default
def perform(*_args)
# Do something later
Artist.find_each do |artist|
artist.songs_points = 0
artist.albums_points = 0
artist.albums.each do |album|
artist.albums_points += album.views_count
end
artist.songs.each do |song|
artist.songs_points += song.views_count
end
artist.save!
end
end
end
Chỉnh lại config/sidekiq.yml
:
class: UpdateArtistPopPointsJob
Các bạn có thể thấy là giờ Job của mình đã được lên ở trong Sidekiq và kích hoạt trong 6 ngày nữa. Mình có thể lựa chọn kích hoạt luôn.
Bonus: Authentication cho sidekiq
Rõ ràng trang kia thiên hướng về phía admin. Thế nên để chỉ admin xem được trang này, mình sẽ chỉnh ở routes:
authenticate :user do
mount Sidekiq::Web, at: '/sidekiq'
end
Kết
Bài mình viết tới đây là hết
All rights reserved