Skip to content

Commit

Permalink
Crontasks (#95)
Browse files Browse the repository at this point in the history
* Adding support Faraday-retry

* Annontate is not loaded by default

* Upgrading to report close_events task status

* Improved db_cleanup task

* Improved clean_active_sessions task

* Adding Whenever gem

* Setting required:false

* Initial schedule file used by whenever

* First scheduled jobs

* Code cleaning and misc fixes to better deploy

* Testing a new Capistrano config

* Making sure whenever write jobs that have proper access to the bundler

* Experimenting with Capistrano and Sidekiq

* Better Poll list display on small screens

* Fixing a typo
  • Loading branch information
spaquet authored Jul 22, 2023
1 parent 621904b commit 12dee11
Show file tree
Hide file tree
Showing 13 changed files with 199 additions and 44 deletions.
13 changes: 7 additions & 6 deletions Capfile
Original file line number Diff line number Diff line change
Expand Up @@ -19,19 +19,20 @@ install_plugin Capistrano::SCM::Git
# https://github.com/capistrano/rails
# https://github.com/capistrano/passenger
#
# require "capistrano/rvm"
require "capistrano/rails"
require "capistrano/rbenv"
# require "capistrano/chruby"
require "capistrano/bundler"
require "capistrano/rails/assets"
require "capistrano/rails/migrations"
# require "capistrano/passenger"
require "whenever/capistrano"

require "capistrano/sidekiq"
install_plugin Capistrano::Sidekiq
install_plugin Capistrano::Sidekiq::Systemd
# require "capistrano/sidekiq"
# install_plugin Capistrano::Sidekiq
# install_plugin Capistrano::Sidekiq::Systemd

require 'capistrano/puma'
install_plugin Capistrano::Puma # Default puma tasks
# install_plugin Capistrano::Puma::Systemd

set :rbenv_type, :user
set :rbenv_ruby, "3.2.2"
Expand Down
21 changes: 14 additions & 7 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,12 @@ gem 'pg_search', '~> 2.3.6'
# Stripe (payment, subscription processing) [https://github.com/stripe/stripe-ruby]
gem 'stripe', '~> 8.6.0'

# To enable retry in Faraday v2.0+
gem 'faraday-retry', '~> 2.2.0'

# Whenever gem to mamage crontab & tasks [https://github.com/javan/whenever]
gem 'whenever', '~> 1.0.0', require: false

group :development, :test do
# See https://guides.rubyonrails.org/debugging_rails_applications.html#debugging-with-the-debug-gem
gem 'debug', platforms: %i[mri mingw x64_mingw]
Expand Down Expand Up @@ -180,8 +186,8 @@ group :development do
# Vulnerability scanner
gem 'brakeman', '~> 6.0.1'

# Add Model annotations
gem 'annotate', '~>3.2.0'
# Add Model annotations [https://github.com/ctran/annotate_models]
gem 'annotate', '~>3.2.0', require: false

# Add Bullet to monitor and help fix N+1 DB queries
gem 'bullet'
Expand All @@ -191,11 +197,12 @@ group :development do
# facilitate the deployment to Digital #
# Ocean.
# [https://gorails.com/deploy/ubuntu/22.04]
gem 'capistrano', '~> 3.17', require: false
gem 'capistrano-rails', '~> 1.6.3', require: false
gem 'capistrano-rbenv', '~> 2.2.0', require: false
gem 'capistrano-bundler', require: false
gem 'capistrano-sidekiq', '~> 3.0.0.alpha.2', require: false
gem 'capistrano', '~> 3.17', require: false
gem 'capistrano-rails', '~> 1.6.3', require: false
gem 'capistrano-rbenv', '~> 2.2.0', require: false
gem 'capistrano-bundler', require: false
gem 'capistrano-sidekiq', '~> 3.0.0.alpha.2', require: false
gem 'capistrano3-puma', '~> 6.0.0.beta.1', require: false
end

group :test do
Expand Down
12 changes: 12 additions & 0 deletions Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,10 @@ GEM
capistrano (>= 3.9.0)
capistrano-bundler
sidekiq (>= 6.0.6)
capistrano3-puma (6.0.0.beta.1)
capistrano (~> 3.7)
capistrano-bundler
puma (>= 5.1, < 7.0)
capybara (3.39.2)
addressable
matrix
Expand All @@ -158,6 +162,7 @@ GEM
actionpack (>= 3.1)
caxlsx (>= 3.0)
chartkick (5.0.2)
chronic (0.10.2)
concurrent-ruby (1.2.2)
connection_pool (2.4.1)
countries (5.5.0)
Expand All @@ -182,6 +187,8 @@ GEM
faraday-multipart (1.0.4)
multipart-post (~> 2)
faraday-net_http (3.0.2)
faraday-retry (2.2.0)
faraday (~> 2.0)
ffi (1.15.5)
ffi-compiler (1.0.1)
ffi (>= 1.0.0)
Expand Down Expand Up @@ -457,6 +464,8 @@ GEM
websocket-driver (0.7.5)
websocket-extensions (>= 0.1.0)
websocket-extensions (0.1.5)
whenever (1.0.0)
chronic (>= 0.6.3)
xpath (3.2.0)
nokogiri (~> 1.8)
xsv (1.2.1)
Expand All @@ -482,6 +491,7 @@ DEPENDENCIES
capistrano-rails (~> 1.6.3)
capistrano-rbenv (~> 2.2.0)
capistrano-sidekiq (~> 3.0.0.alpha.2)
capistrano3-puma (~> 6.0.0.beta.1)
capybara
caxlsx
caxlsx_rails
Expand All @@ -491,6 +501,7 @@ DEPENDENCIES
debug
down (~> 5.0)
faker!
faraday-retry (~> 2.2.0)
groupdate
hiredis (~> 0.6.3)
honeybadger (~> 5.0)
Expand Down Expand Up @@ -532,6 +543,7 @@ DEPENDENCIES
view_component
web-console
webdrivers
whenever (~> 1.0.0)
xsv

RUBY VERSION
Expand Down
6 changes: 3 additions & 3 deletions Puma-Service.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,9 @@ sudo systemctl daemon-reload
sudo systemctl enable puma.service
sudo systemctl start puma.service

sudo systemctl start puma-thepew51
sudo systemctl stop puma-thepew51
sudo systemctl status puma-thepew51
sudo systemctl start puma
sudo systemctl stop puma
sudo systemctl status puma
ps -eo pid,comm,euser,supgrp | grep nginx

When using SSL add the following to ExecStart to enable Puma over HTTPS
Expand Down
4 changes: 2 additions & 2 deletions app/views/polls/_poll.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@
<th scope="row" class="px-6 py-4 font-medium text-gray-900 dark:text-white w-2/5 max-w-xs break-words">
<%= poll.title %>
</th>
<td class="px-6 py-4">
<td class="px-6 py-4 hidden md:table-cell">
<%= poll.user.profile.nickname %>
</td>
<td class="px-6 py-4">
<td class="px-6 py-4 hidden md:table-cell">
<%= poll.created_at.strftime('%b %d, %y') %>
</td>
<td class="px-6 py-4">
Expand Down
6 changes: 3 additions & 3 deletions app/views/polls/index.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,13 @@
<th scope="col" class="px-6 py-3 w-2/5 max-w-xs">
Poll name
</th>
<th scope="col" class="px-6 py-3">
<th scope="col" class="px-6 py-3 hidden md:table-cell">
Created by
</th>
<th scope="col" class="px-6 py-3">
<th scope="col" class="px-6 py-3 hidden md:table-cell">
Created on
</th>
<th scope="col" class="px-6 py-3">
<th scope="col" class="px-6 py-3 ">
Participants
</th>
<th scope="col" class="px-6 py-3">
Expand Down
24 changes: 10 additions & 14 deletions config/deploy.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,17 +11,12 @@
# changing the branch... as master no longer exists on GitHub
set :branch, proc { `git rev-parse --abbrev-ref HEAD`.chomp }

set :rails_env, "production"
set :linked_dirs, fetch(:linked_dirs, []).push('log', 'tmp/pids', 'tmp/cache', 'tmp/sockets', 'vendor/bundle', 'public/system')

# Default deploy_to directory is /var/www/my_app_name
# set :deploy_to, "/var/www/my_app_name"
set :deploy_to, "/home/deploy/#{fetch :application}"

# Default value for :format is :airbrussh.
# set :format, :airbrussh

# You can configure the Airbrussh format using :format_options.
# These are the defaults.
# set :format_options, command_output: true, log_file: "log/capistrano.log", color: :auto, truncate: :auto

# Default value for :pty is false
# set :pty, true

Expand All @@ -42,15 +37,16 @@
# set :keep_releases, 5
set :keep_releases, 2

set :use_sudo, true

# Uncomment the following to require manually verifying the host key before first deploy.
# set :ssh_options, verify_host_key: :secure

# Puma configuration
# set :use_sudo, true
# set :linked_files, %w{config/master.key config/database.yml}
set :rails_env, "production"
set :linked_dirs, fetch(:linked_dirs, []).push('log', 'tmp/pids', 'tmp/cache', 'tmp/sockets', 'vendor/bundle', 'public/system')
# set :linked_files, %w{config/database.yml config/master.key}
# Puma
# Commented out as we control puma manually for the moment.
# set :puma_service_unit_name, "puma.service"
# set :puma_user, fetch(:user)
# set :puma_role, :web

# Sidekiq
# set :sidekiq_service_unit_name, "sidekiq"
Expand Down
2 changes: 1 addition & 1 deletion config/deploy/production.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
# server "example.com", user: "deploy", roles: %w{app web}, other_property: :other_value
# server "db.example.com", user: "deploy", roles: %w{db}

server 'demo.thepew.io', user: 'deploy', roles: %w{app web}
server 'demo.thepew.io', user: 'deploy', roles: %w{app db web}

# role-based syntax
# ==================
Expand Down
35 changes: 35 additions & 0 deletions config/schedule.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# Use this file to easily define all of your cron jobs.
#
# It's helpful, but not entirely necessary to understand cron before proceeding.
# http://en.wikipedia.org/wiki/Cron

# Example:
#
# set :output, "/path/to/my/cron_log.log"
#
# every 2.hours do
# command "/usr/bin/some_great_command"
# runner "MyModel.some_method"
# rake "some:great:rake:task"
# end
#
# every 4.days do
# runner "AnotherModel.prune_old_records"
# end

# Learn more: http://github.com/javan/whenever

# Fix an issue when using rbenv (at least)
env :PATH, ENV['PATH']

set :output, "log/cron.log"

# Clear Active Sessions from sessions that are tooooo long
every 6.hours do
rake 'clean_active_sessions:clean[false]'
end

# Close Events
every 1.day do
rake 'close_events:close_events[false]'
end
20 changes: 20 additions & 0 deletions lib/capistrano/tasks/sidekiq.rake
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
namespace :sidekiq do
after 'deploy:starting', 'sidekiq:stop'
after 'deploy:finished', 'sidekiq:start'

task :stop do
on roles(:app) do
within current_path do
# execute(:sudo, 'systemctl kill -s TSTP sidekiq')
execute(:sudo, 'systemctl stop sidekiq')
end
end
end

task :start do
on roles(:app) do |host|
execute(:sudo, 'systemctl start sidekiq')
info "Host #{host} (#{host.roles.to_a.join(', ')}):\t#{capture(:uptime)}"
end
end
end
27 changes: 26 additions & 1 deletion lib/tasks/clean_active_sessions.rake
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,40 @@ namespace :clean_active_sessions do
# - rake "clean_active_sessions:clean[false]"
desc 'Remove session that are older than 20 days'
task :clean, [:dry_run] => :environment do |_t, args|
start_at= Time.now.utc
failed_list = []
session_count = 0
dry_run = true unless args[:dry_run] == 'false'

Rails.logger.error "[clean_active_sessions:clean] Starting at #{start_at}."
puts("[#{Time.now.utc}] Running clean session :: INI#{' (dry_run activated)' if dry_run}")

ActiveSession.where('active_sessions.created_at <= ?', 2.days.ago).find_each do |session|
puts("Deleting Session: #{session.id}#{' (dry_run activated)' if dry_run}")
Rails.logger.error "[clean_active_sessions:clean] Deleting Session: #{session.id}#{' (dry_run activated)' if dry_run}."

session.destroy! unless dry_run

session_count += 1

rescue StandardError => e
failed_list.push({ session_id: session.id, reason: session.inspect })

Rails.logger.error "[clean_active_sessions:clean] session_id: #{session.id} failed to update user name."
Rails.logger.error "[clean_active_sessions:clean] session_id: #{session.id} failed reason: #{e.inspect}"
end

if failed_list.count > 0
Rails.logger.info "[clean_active_sessions:clean] Failed list: #{failed_list}"
p "[#{Time.now.utc}] [clean_active_sessions:clean] Failed list: #{failed_list}"
end

# End the task
# Compute task duration
end_at= Time.now.utc
duration = ((end_at - start_at) / 60.seconds).to_i

# Display closing messages and report to Rails logger for centralized logs
Rails.logger.error "[clean_active_sessions:clean] Ending at #{end_at}. Sessions terminated: #{session_count} in #{duration}"
puts("[#{Time.now.utc}] Running clean session :: END#{' (dry_run activated)' if dry_run}")
end
end
48 changes: 42 additions & 6 deletions lib/tasks/close_events.rake
Original file line number Diff line number Diff line change
Expand Up @@ -8,23 +8,59 @@ namespace :close_events do
# - rake "close_events:close_events[false]"
desc 'Close events'
task :close_events, [:dry_run] => :environment do |_t, args|
start_at= Time.now.utc
dry_run = true unless args[:dry_run] == 'false'
failed_list = []
event_closed_count = 0

Rails.logger.error "[close_events] Starting at #{start_at}."
puts("[#{Time.now.utc}] Running close_events :: INI#{' (dry_run activated)' if dry_run}")

# List all the opened events and can be closed, meaning the always_on flag is set to false
# Only event with a status of `opened` are affected.
# Current rule is to close an event 24h after its end_date.
@events = Event.where("always_on = false AND end_date < ?", 1.days.ago).opened

@events.each do |event|
# Closing the event
event.closed!
puts("[#{Time.now.utc}] Events to be closed: #{@events.count}")

# If there is no event to be closed we simply exit the task.
if @events.count > 0 then
@events.each do |event|
# Closing the event
event.closed!

# Removing the PIN
event.pin = nil

# Updating the event
event.update!

# Update the counter
event_closed_count += 1

# Rescue used to log errors and report to Rails looger
rescue StandardError => e
failed_list.push({ event_id: event.id, reason: event.inspect })

Rails.logger.error "[close_events] event_id: #{event.id} failed to update user name."
Rails.logger.error "[close_events] event_id: #{event.id} failed reason: #{e.inspect}"
end

# Removing the PIN
event.pin = nil
if failed_list.count > 0
Rails.logger.info "[close_events] Failed list: #{failed_list}"
p "[#{Time.now.utc}] [close_events] Failed list: #{failed_list}"
end

puts("[#{Time.now.utc}] #{event_closed_count} events have been closed")
end


# End the task
# Compute task duration
end_at= Time.now.utc
duration = ((end_at - start_at) / 60.seconds).to_i
# Display closing messages and report to Rails logger for centralized logs
Rails.logger.error "[close_events] Ending at #{end_at}. Closed #{event_closed_count} event(s) in #{duration}"
puts("[#{Time.now.utc}] Running close_events :: duration: #{duration} :: END#{' (dry_run activated)' if dry_run}")
puts("[#{Time.now.utc}] Running close_events :: END#{' (dry_run activated)' if dry_run}")
end

Expand Down
Loading

0 comments on commit 12dee11

Please sign in to comment.