ためすう
sidekiq で namespace を使えるようにする
2019-06-19やったこと
Rails5 で sidekiq を利用しているのですが、queue に namespace を指定するとエラーが発生しました。
今回はこのエラーを解消を目指します。
確認環境
$ ruby --version
ruby 2.6.3p62 (2019-04-16 revision 67580) [x86_64-darwin17]
$ rails --version
Rails 5.2.3
$ gem list | grep sidekiq
sidekiq (5.2.7)
前提
sidekiq がインストール済みとします。
調査
発生したエラー
config/initializers/sidekiq.rb
Sidekiq.configure_server do |config|
config.redis = { url: 'redis://localhost:6379', namespace: 'sidekiq' }
end
namespace を指定しました。
エラー
2019-06-15T07:14:53.315Z 47524 TID-ov7xxfh88 INFO: Booting Sidekiq 5.2.7 with redis options {:url=>"redis://localhost:6379", :namespace=>"sidekiq", :id=>"Sidekiq-server-PID-47524"}
2019-06-15T07:14:53.426Z 47524 TID-ov7xxfh88 INFO: Running in ruby 2.6.3p62 (2019-04-16 revision 67580) [x86_64-darwin17]
2019-06-15T07:14:53.426Z 47524 TID-ov7xxfh88 INFO: See LICENSE and the LGPL-3.0 for licensing details.
2019-06-15T07:14:53.426Z 47524 TID-ov7xxfh88 INFO: Upgrade to Sidekiq Pro for more features and support: http://sidekiq.org
2019-06-15T07:14:53.429Z 47524 TID-ov7xxfh88 ERROR: Your Redis configuration uses the namespace 'sidekiq' but the redis-namespace gem is not included in the Gemfile.Add the gem to your Gemfile to continue using a namespace. Otherwise, remove the namespace parameter.
どうやら、redis-namespace をインストールする必要があるようです。
redis-namespace をインストール
Gemfile
gem 'redis-namespace'
$ bundle install
設定ファイルの修正
キューを入れるときの設定も追記します。
※ namespace も myapp に変えました。
config/initializers/sidekiq.rb
Sidekiq.configure_server do |config|
config.redis = { url: 'redis://localhost:6379', namespace: 'myapp' }
end
Sidekiq.configure_client do |config|
config.redis = { url: 'redis://localhost:6379', namespace: 'myapp' }
end
rails と redis をそれぞれ再起動したら、OKです。
参考
第3章 アルゴリズムを変更する: Template Method
2019-06-18「Rubyによるデザインパターン」の読書メモです。
時々、個人の見解入りです。
修正前
例えば下記のようなコードがあったとします。
ここから、htmlではなくプレーンテキスト形式でもレポートを表示する要件が加わったとき、どうすれば良いでしょうか。
class Report
def initialize
@title = '月次報告'
@text = ['順調', '最高']
end
# 簡略化
def output_report
puts('<html>')
puts("<title>#{@title}</title>")
@text.each do |line|
puts("<p>#{line}</p>")
end
puts('</html>')
end
end
Report.new.output_report
修正後
class Report
def initialize
@title = '月次報告'
@text = ['順調', '最高']
end
def output_report
output_start
output_head
output_body_start
output_body
output_body_end
output_end
end
private
def output_body
@text.each do |line|
output_line(line)
end
end
def output_start
end
def output_head
output_line(@title)
puts
end
def output_body_start
end
def output_line(line)
raise "Called abstract method: #{__method__}"
end
def output_body_end
end
def output_end
end
end
class HTMLReport < Report
private
def output_start
puts '<html>'
end
def output_head
puts "<head><title>#{@title}</title></head>"
end
def output_body_start
puts('<body>')
end
def output_line(line)
puts("<p>#{line}</p>")
end
def output_body_end
puts('</body>')
end
def output_end
puts '</html>'
end
end
class PlainTextReport < Report
private
def output_line(line)
puts(line)
end
end
p '-- HTMLReport --'
HTMLReport.new.output_report
p '-- PlainTextReport --'
PlainTextReport.new.output_report
これで新しいフォーマットが必要になった場合も、サブクラス追加で出来ますね。
※ 本のサンプルコードに少し手を加えています。
おまけ
ダックタイピング
「もしアヒルのように見えて、アヒルのように鳴くのなら、それはアヒルである」
注意点
すべての起こりうる事態に対処するために、サブクラスに大量のメソッドをオーバーライドさせるようなテンプレートクラスを作成するのは避けましょう。
sidekiq で管理画面を表示する
2019-06-18やったこと
sidekiq で管理画面を表示できるようにします。
確認環境
$ ruby --version
ruby 2.6.3p62 (2019-04-16 revision 67580) [x86_64-darwin17]
$ rails --version
Rails 5.2.3
$ gem list | grep sidekiq
sidekiq (5.2.7)
調査
config/routes.rb
require 'sidekiq/web'
mount Sidekiq::Web, at: '/sidekiq'
http://localhost:3000/sidekiq/
にアクセスすると管理画面が見られます。
参考
序章,第1章 よいプログラムとパターン
2019-06-17「Rubyによるデザインパターン」の読書メモです
時々、個人の見解入りです。
序章
デザインパターンは、ソフトウェアを構築する際に繰り返し発生する一般的な問題を認識し、解決する手助けをするという意味があります。
第1章 よいプログラムとパターン
パターンのためのパターン
変わるものを変わらないものから分離する
理想的なシステムでは、すべての変更は局所的です。すべてのコードをくまなく調べる必要がないようにすべきです。
コードの流れを追うという意味では、そうだと思います。
同じプロダクトだけど、管理しているソースコードが違ったりすると、これは難しくなりそうです。
インターフェースに対してプログラムし、実装に対して行わない
この記事に似たようなことを書いた気がするので置いておきます。
継承より集約
継承
継承は、まさにその性質から、サブクラスをスーパークラスに結びつける傾向があります。スーパークラスの振る舞いを変更すれば、それはサブクラスの振る舞いを変えてしまうことになります。
test1.rb
class Vehicle
def start_engine
p "#{self.class.name}:#{__method__}"
end
def stop_engine
p "#{self.class.name}:#{__method__}"
end
end
class Car < Vehicle
def sunday_drive
start_engine
# ... something
stop_engine
end
end
Car.new.sunday_drive
集約
test2.rb
class Engine
def start
p "#{self.class.name}:#{__method__}"
end
def stop
p "#{self.class.name}:#{__method__}"
end
end
class Car
def initialize
@engine = Engine.new
end
def sunday_drive
@engine.start
# something...
@engine.stop
end
end
Car.new.sunday_drive
Engine クラスを切り出しました。
Engine の差し替えもやりやすくなります。
委譲、委譲、委譲
委譲先のオブジェクトに責任転嫁する際に、追加のメソッド呼び出しが必要になります。
=> Ruby ではそのようなメソッドは書かなくてもOK (詳しくは、第10章、第11章)
必要になるまで作るな
YAGNI 原則
「You Ain’t Gonna Need It (必要になるまで作るな)」
将来のことは誰にも分からないので、いつか必要になる可能性もあると同時に
必要にならない可能性もあります。
本書で扱うGoFの14パターン
- Template Method
- Strategy
- Observer
- Composite
- Iterator
- Command
- Adapter
- Proxy
- Decorator
- Singleton
- Factory Method
- Abstract Factory
- Builder
- Interpreter
さらに
- 内部ドメイン特化言語
- 専門分野に特化した小さな言語を作るとても動的な仕組み
- メタプログラミング
- 必要なクラスやオブジェクトを実行時に動的に作るテクニック
- Convention over Configuration (CoC)
- 構成ファイル (たいていはXML)の憂鬱への治療薬
Ruby で alias を使う
2019-06-17やったこと
Ruby で alias を使ってみます。
確認環境
$ ruby --version
ruby 2.6.3p62 (2019-04-16 revision 67580) [x86_64-darwin17]
調査
app/controllers/tasks_controller.rb
class TasksController < ApplicationController
def new
logger.debug(__method__)
end
def new222
logger.debug(__method__)
end
alias :new :new222
end
ログの確認
Started GET "/tasks/new" for ::1 at 2019-06-15 18:18:07 +0900
(0.3ms) SELECT "schema_migrations"."version" FROM "schema_migrations" ORDER BY "schema_migrations"."version" ASC
↳ /Users/xxxx/.rbenv/versions/2.6.3/lib/ruby/gems/2.6.0/gems/activerecord-5.2.3/lib/active_record/log_subscriber.rb:98
path is /tasks/new
Processing by TasksController#new as HTML
new222
Rendering tasks/new.html.erb within layouts/tasks
Rendered tasks/new.html.erb within layouts/tasks (264.0ms)
Completed 200 OK in 290ms (Views: 282.6ms | ActiveRecord: 0.0ms)
new222 が呼ばれます。
参考
アジャイルソフトウェア開発の奥義 第7章 リファクタリング
2019-06-16「アジャイルソフトウェア開発の奥義」の読書メモです。
時々、個人の見解入りです。
腐敗するソフトウェアの兆候
硬さ
変更しにくいシステム
たった1つの変更で、それと依存関係のあるモジュールを芋づる式に変更しなければならないような場合、設計が「硬い」という。
もろさ
「もろさ」とは、たった1つの変更によって他の多くの部分が壊れてしまうような傾向があるとうことだ。
移植性のなさ
再利用が困難
扱いにくさ
- ソフトウェアの扱いにくさ
- 開発環境の扱いにくさ
不必要な複雑さ
設計している時点で不必要な要素が含まれている設計である。
必要になったタイミングで、最小限の修正をする方が良いと思います。
不必要な繰り返し
同じような構造を繰り返し含み、抽象化してまとめられる部分がまとまっていないソフトウェア
個人的に抽象化してあるとは、ポリモーフィズムを使っていることなのかなと考えています。
不透明さ
読みにくく、わかりにくい。その意図がうまく伝わってこないソフトウェア
ソフトウェアのプロジェクトにおいて最も不確実なものは仕様要求であることを忘れてはならない。仕様要求は常に不安定な状態にある。
設計にパッチを当て新しい要求に対応するような一時しのぎをするのではなく、将来同じような変更があった場合に対応できるように設計を改善するチャンスだととらえる。
Metrics optimization
学習目標
- Describe the role of correct metric optimization method in a competition
- Analyze new metrics
- Create constant baselines
- Recall the most important classification and regression metrics
- Describe what libraries can be used to optimize a particular metric
Motivation
Metrics
- 提出物の評価で使われるもの
- コンペによって異なるのは、何に対して最適化するのかが決まっているからである
- バリデーションデータのメトリクスを改善しても、テストデータのメトリクスが改善しないことがある
Regression metrics review I
Regression
- MSE, RMSE, R-squared
- MAE
- ®MSPE, MAPE
- ®MSLE
平均二乗誤差: MSE (Mean Square Error)
$$ MSE = 1/N * \sum_{i=1}^N (y_i - \hat{y_i})^2 $$
$$ RMSE = \sqrt{MSE} $$
R-squared
$$ R^2 = 1 - \frac{MSE}{\frac{1}{N}\sum_{i=1}^N (y_i - \bar{y_i})^2} $$
MSEが0のとき、R-squared は1
MSEが定数と等しいとき、R-squared は0
MAE: Mean Absolute Error
$$ MAE = \frac{1}{N}\sum_{i=1}^N|y_i - \hat{y_i}| $$
- MSE ほどには外れ値にペナルティを与えない
- 最適な定数は中央値
MAE vs MSE
- 外れ値があるなら、MAE
- ただし、外れ値があるのが正常なら、MSE
- 異常値であるなら、MAE
参考
アジャイルソフトウェア開発の奥義 第5章 リファクタリング
2019-06-15「アジャイルソフトウェア開発の奥義」の読書メモです。
時々、個人の見解入りです。
書き上げるモジュールと保守するモジュールのすべてを必ずリファクタリングすることを強く勧める。リファクタリングに費やす時間は、それをしなかったことで自分自身を含む関係者が将来費やすことになる労力に比べれば微々たるものだからだ。
ソフトウェアモジュールの3つの機能
- 特定の処理を実行する機能
- 変化を許容する機能
- 読み手にそのモジュールの意図を伝える機能
この章で書かれていないが、リファクタリングをするには、自動テストが書かれている必要があると思います。
自動テストされていれば、自動テストが用意されている分については挙動が変わっていないと保証できます。
Rails5 で sidekiq を使う
2019-06-15やったこと
Rails5 で sidekiq を使います。
確認環境
$ ruby --version
ruby 2.6.3p62 (2019-04-16 revision 67580) [x86_64-darwin17]
$ rails --version
Rails 5.2.3
調査
インストールからやっていきます。
redis のインストール
$ brew install redis
$ redis-server --version
Redis server v=5.0.5 sha=00000000:0 malloc=libc bits=64 build=272e5301ea2dcb73
redisサーバーを起動します
$ redis-server /usr/local/etc/redis.conF
sidekiq のインストール
Gemfile
gem 'sidekiq', '~> 5.2', '>= 5.2.7'
$ bundle install
もしかしたら、エラーが出るかもしれないのでこちら見てみてください
Rails5 で bundle install が失敗する件 – ためすう
sidekiq の設定
config/initializers/sidekiq.rb
Sidekiq.configure_server do |config|
config.redis = { url: 'redis://localhost:6379'}
end
config/sidekiq.yml
:pidfile: ./tmp/pids/sidekiq.pid
:logfile: ./log/sidekiq.log
:queues:
- task
$ bundle exec sidekiq -C config/sidekiq.yml
sidekiq の ワーカー作成
app/workers/task_worker.rb
class TaskWorker
include Sidekiq::Worker
sidekiq_options queue: :task
def perform(id)
logger.debug('id 出力')
logger.debug(id)
end
end
※ ログを出力するには、設定する必要があるのですが、また今度。
Queue を登録
app/controllers/tasks_controller.rb
class TasksController < ApplicationController
def new
TaskWorker.perform_async 333
end
end
参考
Rails5 で bundle install が失敗する件
2019-06-15やったこと
Rails5 で sidekiq をインストールしようとしたところ、bundle install が失敗してしまいました。
今回、そのエラーを解消したいと思います。
確認環境
$ ruby --version
ruby 2.6.3p62 (2019-04-16 revision 67580) [x86_64-darwin17]
$ rails --version
Rails 5.2.3
調査
問題の再現
Gemfile
gem 'sidekiq', '~> 5.2', '>= 5.2.7'
bundle install で失敗します
$ bundle install
The dependency tzinfo-data (>= 0) will be unused by any of the platforms Bundler is installing for. Bundler is installing for ruby but the dependency is only for x86-mingw32, x86-mswin32, x64-mingw32, java. To add those platforms to the bundle, run `bundle lock --add-platform x86-mingw32 x86-mswin32 x64-mingw32 java`.
Fetching source index from https://rubygems.org/
Retrying fetcher due to error (2/4): Bundler::HTTPError Could not fetch specs from https://rubygems.org/
Retrying fetcher due to error (3/4): Bundler::HTTPError Could not fetch specs from https://rubygems.org/
Retrying fetcher due to error (4/4): Bundler::HTTPError Could not fetch specs from https://rubygems.org/
Could not fetch specs from https://rubygems.org/
解決までの過程
bundle update してみます
$ gem update --system
ERROR: While executing gem ... (Gem::RemoteFetcher::UnknownHostError)
timed out (https://rubygems.org/specs.4.8.gz)
失敗したので、次は、wget できるかやってみます
$ wget https://api.rubygems.org/specs.4.8.gz
--2019-06-15 11:30:11-- https://api.rubygems.org/specs.4.8.gz
Resolving api.rubygems.org... 2a04:4e42::70, 151.101.0.70, 151.101.192.70, ...
Connecting to api.rubygems.org|2a04:4e42::70|:443... failed: Operation timed out.
Connecting to api.rubygems.org|151.101.0.70|:443... connected.
HTTP request sent, awaiting response... 200 OK
Length: 3886510 (3.7M) [application/octet-stream]
Saving to: 'specs.4.8.gz'
specs.4.8.gz 100%[=====================================================================>] 3.71M 1.91MB/s in 1.9s
2019-06-15 11:31:29 (1.91 MB/s) - 'specs.4.8.gz' saved [3886510/3886510]
wget は出来ました。IPv6 での通信に失敗しているようです。
IPv4 を使うようにします。
該当ドメインの IP アドレスを調べます。
$ host api.rubygems.org
api.rubygems.org is an alias for rubygems.org.
rubygems.org has address 151.101.128.70
rubygems.org has address 151.101.0.70
rubygems.org has address 151.101.64.70
rubygems.org has address 151.101.192.70
rubygems.org has IPv6 address 2a04:4e42::70
rubygems.org mail is handled by 10 mxa.mailgun.org.
rubygems.org mail is handled by 10 mxb.mailgun.org.
hosts を書き換えてみます。
$ sudo vim /etc/hosts
151.101.128.70 api.rubygems.org
151.101.0.70 api.rubygems.org
151.101.64.70 api.rubygems.org
151.101.192.70 api.rubygems.org
$ bundle install
sidekiq がインストールできました。