ラベル ruby の投稿を表示しています。 すべての投稿を表示
ラベル ruby の投稿を表示しています。 すべての投稿を表示

2016年2月3日水曜日

異なるクラスのスレッド間で同期させる

久しぶりの投稿

当初はあるクラスのスレッド間で1秒ごとにシーケンシャル処理を行いたいというものだった。

# a.rb
class A
  @m = Mutex.new

  def get
    self.class.instance_variable_get(:@m).synchronize do
      sleep 1
      puts "#{Time.now} => #{self.class.name}"
    end
  end

end

# do.rb
require_relative './a'
require_relative './b' # Aとはクラス名のみ異なる。

ps = [A, B]
ts = []

5.times do
  ps.each do |prog|
    t = Thread.new do
      prog.new.get
    end

    ts << t
  end
end

ts.each { |t| t.join }

do.rbの実行結果は次のとおり。

$ ruby do.rb 
2016-02-03 18:10:30 +0900 => A
2016-02-03 18:10:30 +0900 => B
2016-02-03 18:10:31 +0900 => B
2016-02-03 18:10:31 +0900 => A
2016-02-03 18:10:32 +0900 => B
2016-02-03 18:10:32 +0900 => A
2016-02-03 18:10:33 +0900 => A
2016-02-03 18:10:33 +0900 => B
2016-02-03 18:10:34 +0900 => B
2016-02-03 18:10:34 +0900 => A

  • Aのスレッド間では1秒ごとにメソッドが実行されている。
  • Bのスレッド間では1秒ごとにメソッドが実行されている。
ただ、この全部のスレッドで1秒ごとに処理を実行したいという変更が入ったため変更。
今回はモジュール変数で対応した。


# synchronizable.rb
module Synchronizable
  @@m = Mutex.new

  def m
    @@m
  end
end

# a2.rb

require_relative './synchronizable'

class A2
  include Synchronizable

  def get
    m.synchronize do
      sleep 1
      puts "#{Time.now} => #{self.class.name}"
    end
  end
end

# do2.rb
require_relative './a2'
require_relative './b2' # A2とはクラス名のみ異なる

ps = [A2, B2]
ts = []

5.times do
  ps.each do |prog|
    t = Thread.new do
      prog.new.get
    end

    ts << t
  end
end

ts.each { |t| t.join }

do2.rbの実行結果

$ ruby do2.rb 
2016-02-03 18:24:44 +0900 => B2
2016-02-03 18:24:45 +0900 => B2
2016-02-03 18:24:46 +0900 => A2
2016-02-03 18:24:47 +0900 => A2
2016-02-03 18:24:48 +0900 => A2
2016-02-03 18:24:49 +0900 => B2
2016-02-03 18:24:50 +0900 => A2
2016-02-03 18:24:51 +0900 => B2
2016-02-03 18:24:52 +0900 => A2
2016-02-03 18:24:53 +0900 => B2

すべてのスレッドで1秒ごとに処理が実行されている。

2014年12月8日月曜日

EC2インスタンスのAMIバックアップを取るスクリプト

Rubyで一瞬。

留意事項

  • 認証情報は別途用意する (e.g. ~/.aws/config)。
  • create_imageはAMI作成リクエストを送り直ぐに戻るという想像の下に書いてる。

2014年1月19日日曜日

Ancestry

親子構造を格納するモデルを用意しようとしてたら、AncestryというGemを見つけた

準備

適当にrailsプロジェクトを作成し、Gemfileにgem 'ancestry'を記述しbundle install


scaffoldを作る

$ rails  g scaffold tenant name:string


Tenantモデルを編集

# app/models/tenant.rb
class Tenant < ActiveRecord::Base
  attr_accessible :name, :parent_id
  attr_accessible :name # 2014/1/21 parent_idは不要でした
  has_ancestry
end


フォーム編集用ビューを編集

# app/views/tenants/_form.html.erb
<div class="field">
  <%= f.label :parent %><br />
  <%= f.select :parent_id, options_from_collection_for_select(Tenant.all, :id, :name) %>
</div>


Tenantモデルへancestryカラムを追加してマイグレーション

# rails g migration add_ancestry_to_tenants ancestry:string
class AddAncestryToTenants < ActiveRecord::Migration
  def change
    add_column :tenants, :ancestry, :string, after: :id
    add_index :tenants, :ancestry
  end
end


indexビューを編集(抜粋)

# app/views/tenants/index.html.erb
    <td><%= tenant.parent ? tenant.parent.name : nil %></td>
    <td><%= tenant.ancestors.map &:name %></td>
    <td><%= tenant.children.map &:name %></td>
    <td><%= tenant.descendants.map &:name %></td>

この状態でモデルの作成を繰り返すと、indexの表示はたとえば次のようになる

この時、tenantsテーブルの中身は次のようになる







2013年9月28日土曜日

fluentdのテスト導入



メタプログラミングRuby勉強会で使ってた本(↑)が終わったので、
fluentdを触って遊ぼうみたいな話になった。
今回は手元でS3プラグインの動作確認まで。

fluentdのインストール

公式ページにしたがい入れた

fluent-plugin-s3のインストール

このページを見たりして入れた

S3プラグインを使う為の今の設定

その後、何度か設定を試したりして次の内容になった。

はまった点

fluentdと関係無いのだが、Macのmaxfilesオプションに引っかかってうまく起動しなかった。http://docs.fluentd.org/articles/before-install#increase-max--of-file-descriptorsをMacに読み替えて設定したらOK。設定の途中で間違えて数を減らしてしまいMacを強制再起動する羽目になった

2013年7月8日月曜日

ActiveRecordでステートマシーン

ここ数ヶ月心に留めつつも触れてなかったstate_machineでいじったので記録

何ができるの?

クラスの状態に応じた振る舞いを簡単に記述できる

Railsで使いたいんだけど?

ActiveRecordと簡単に統合できる

サンプル


参考

GitHub: https://github.com/pluginaweek/state_machine




2013年6月25日火曜日

メタプログラミングRuby勉強会#3

スコープ

Rubyのスコープは、class / module / defの3つのスコープゲートで区切られている
スコープゲートを超えるとスコープが切り替わる。それを超えるにはグローバル変数を使う(当然あまり使わない)、またはスコープのフラット化を使う。

スコープのフラット化の例

普通に書くと次の例はクラス内でaという変数は見えない → エラーになる。

ここで、classを超えるにはClass.newを使う

しかし、まだdefの壁(ゲート)を超えられない.
これを超えるにはdefine_methodを使う。

instance_eval

instance_evalに渡したブロックはselfオブジェクトと同じコンテキストで評価される。
要するにインスタンス変数にアクセスしたり、変更できる。このブロックをコンテキスト探査機を呼ぶ。

Procとlambda

returnの動作と引数の扱いの2つの観点からlambdaの方が使いやすそう。
Proc内のreturnは、Procが定義されたスコープから戻るのでバグとなりやすい。


ブロック、スコープのフラット化、lambdaを使って簡単にDSL書けそう。

2013年5月25日土曜日

メタプログラミングRuby勉強会#2



今日やったことのメモ


動的ディスパッチ

sendを使い、実行時に呼び出しメソッドを決められる。

メソッドの動的定義

define_methodを使いメソッドを定義できる

ゴーストメソッド

method_missingを使い、レシーバに存在しないメソッドを在るかの如く処理できる

動的プロキシ

method_missingを利用して実際の処理を他のメソッドへ委譲する


2013年5月4日土曜日

メタプログラミングRuby勉強会#1



ちょうど仕事と関連しているのでタイムリーな勉強会。
単に手を動かしたせいかもしれないが、充実した時間を過ごせた。

昨日のメインは第一章だったが、オープンクラス等Rubyっぽさを感じられた。


オブジェクトとクラス

オブジェクト自身がインスタンスメソッドを持つ訳ではない。オブジェクトはクラスのメソッドへの参照を持っているだけ。superclassにはモジュールが見えないがancestorsでは見える。

メソッド呼び出し

メソッドが呼び出されると現在のオブジェクトのクラス内でそのメソッドを探す。なければ親クラスを見る。ancestorsを辿れば分かりやすい。

privateメソッド

Rubyでは親クラスのprivateメソッドを呼び出せる。Javaのprotectedみたいなものか。


次回は動的ディスパッチャやmethod_missing等Rubyの沼に潜っていく。




2013年4月28日日曜日

Asset Pipelineについて復習


概要 (ここを見ながら)

  • JavaScriptおよびCSSに対し、結合 and (最小化 or 圧縮)を行うフレームワーク
  • CoffeeScript / Sass / ERBのような他言語で書いても処理してくれる


Rails 3.1より前はJammitおよびSprocketsのような3rdパーティライブラリを使い
実現していた。3.1ではSprocketsとデフォルトで (ActionPackが) 統合されている。

デフォルトではアセットパイプラインが有効になっている。
無効にするには config/application.rb で次のように設定する。
# Enable the asset pipeline
config.assets.enabled = false

圧縮やダイジェストの設定は config/environments/xxxx.rb で設定されてる。
config.assets.compress = true (production.rb)
config.assets.digest = true  (production.rb)

上記を読んで分かったつもりになってはいけない、ということで動作確認


テストアプリケーションを用意

$ rails new assetpipelinetest
$ rails g scaffold book title:string author:string
$ rm public/index.html
$ config/routes.rb
     root :to => 'books#index'
$ rake db:migrate 


ファイルの読み込みを確認 (Development)

次の3ファイルを作成
  • app/assets/javascripts/a.js
  • lib/assets/javascripts/b.js
  • vendor/assets/javascripts/c.js  


起動後にトップページへアクセスし確認、ソースを見るとa.jsだけが読み込まれている。
<script src="/assets/a.js?body=1" type="text/javascript"></script>
しかし、b.jsとc.jsは読み込まれていない。a.jsが読み込まれているのは
application.js (マニフェストファイル) に次の記述があるから。
//= require_tree .

これにより、appliation.jsと同じかサブディレクトリに存在する
JavaScriptやCofeeScriptを自動で読みこむ。

続けてbとcを読み込むようにマニフェストファイル編集
//= require b
//= require c

<script src="/assets/b.js?body=1" type="text/javascript"></script>
<script src="/assets/c.js?body=1" type="text/javascript"></script>

これで3つのjsファイルが読み込まれるようになった。
cssについても、マニフェストファイル (application.css) に書けば読み込まれる。


プリコンパイル (Production)

まずはテーブルを用意
rake db:migrate RAILS_ENV=production

単にWEBrickを起動しただけでは次のエラーが出る。
ActionView::Template::Error (application.css isn't precompiled):

そのため、プリコンパイルせねばならぬ。
rake assets:precompile

これで public/assets にアセットファイルが用意される。
application-00e4a643d1be5b0c4b39be21673d21b2.css
application-4f4e444aeb28497bca7d81506213536e.js

同コマンドで作られるmanifest.ymlには
アセットファイルと実際のファイル名が記述されている。
application.js: application-4f4e444aeb28497bca7d81506213536e.js
application.css: application-00e4a643d1be5b0c4b39be21673d21b2.css

上記はダイジェストが有効な状態での実行結果。
試しに無効化して再度コンパイルすると、manifest.ymlの中は次のようになる。
application.js: application.js
application.css: application.css 

config.assets.compressをfalseにした場合は
application.jsやapplication.cssの中身が圧縮されない。
また、他の特徴として、コンプレッサの指定指定も可能。

とりあえずこんなところか。

2013年4月5日金曜日

Railsアプリがアボートした話 + α


よくあるApache2 + Phusion Passengerの構成で
Railsアプリケーションを動かそうとしたらクラッシュしたので
調査して対応したという話。


# Internal Server Error 500

アクセスしてアプリを触ってたらみんな大好き500エラーが出てた。
なのでApacheのエラーログを見ることに。


# とりあえずログ (抜粋)

[NOTE]
You may have encountered a bug in the Ruby interpreter or extension libraries.
Bug reports are welcome.
For details: http://www.ruby-lang.org/bugreport.html
[ pid=2662 thr=139689980598240 file=ext/apache2/Hooks.cpp:841 time=2013-04-02 14:45:09.779 ]: The backend application (process 6194) did not send a valid HTTP response; instead, it sent nothing at all. It is possible that it has crashed; please check whether there are crashing bugs in this application.
/usr/local/rvm/gems/ruby-1.9.3-p362/gems/activerecord-3.2.11/lib/active_record/relation.rb:241: [BUG] Segmentation fault
ruby 1.9.3p362 (2012-12-25 revision 38607) [x86_64-linux]
-- Control frame information -----------------------------------------------
c:0101 p:0016 s:0578 b:0575 l:000574 d:000574 METHOD /usr/local/rvm/gems/ruby-1.9.3-p362/gems/activerecord-3.2.11/lib/active_record/relation.rb:241
c:0100 p:0026 s:0572 b:0572 l:000571 d:000571 METHOD /usr/local/rvm/gems/ruby-1.9.3-p362/gems/activerecord-3.2.11/lib/active_record/scoping/default.rb:41
c:0099 p:0022 s:0569 b:0569 l:001278 d:000568 LAMBDA /usr/local/rvm/gems/ruby-1.9.3-p362/gems/activerecord-3.2.11/lib/active_record/scoping/named.rb:180
(中略)
7fa5207ac000-7fa5207ad000 r--p 0001f000 ca:01 4240                       /lib64/ld-2.12.so
7fa5207ad000-7fa5207ae000 rw-p 00020000 ca:01 4240                       /lib64/ld-2.12.so
7fa5207ae000-7fa5207af000 rw-p 00000000 00:00 0
7fff2e37c000-7fff2e39d000 rw-p 00000000 00:00 0                          [stack]
7fff2e3ff000-7fff2e400000 r-xp 00000000 00:00 0                          [vdso]
ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0                  [vsyscall]
(中略)
[NOTE]
You may have encountered a bug in the Ruby interpreter or extension libraries.
Bug reports are welcome.
For details: http://www.ruby-lang.org/bugreport.html
[ pid=2665 thr=139689980598240 file=ext/apache2/Hooks.cpp:841 time=2013-04-02 14:52:29.683 ]: The backend application (process 6329) did not send a valid HTTP response; instead, it sent nothing at all. It is possible that it has crashed; please check whether there are crashing bugs in this application.

did not send a valid HTTP responseは多分問題無いということが
他のソースから分かっていたのでこの時点でRuby周りかなという感じだった。
ひとまず、すぐに確認できそうなことから可能性を潰すことにした。


# 再現および状況把握

それ以降も何度も発生したのでサーバリソース周りを見ておくことにした。
sysstat入れて久しぶりにsarコマンド使ってCPU/Memory/IOの様子を確認。

=>異常無し


# 可能性の検討 + 行動

  1. Rubyインタプリタのバグ踏んだ (ログの通り。人間素直が一番)
  2. Passenger, Rails等いずれかのライブラリのバグ (ログからは一度Passengerの可能性も考えた)
  3. ApacheやPassengerがsarで汲み取れない一瞬で何らかの閾値を振り切った (スカウターで確認できない程の一瞬で戦闘力を上げて攻撃されたイメージ)
可能性として1, 2, 3の順に高いと考え、
StackOverflowやら何やらで情報を探すと以下のものに突き当たった。

  • https://github.com/gitlabhq/gitlabhq/issues/2436
  • https://github.com/gitlabhq/gitlabhq/issues/2476

結論としては偶々サーバに入ってたバージョンのRubyが
buggyだったのでアップデートしろということだった。
これ以外にもStackOverflowでログがほぼ同じ投稿があったけど
解決方法がログやテンポラリスペースが云々とあったので無関係と判断。

その後、
Rubyのバージョンを1.9.3p362からp392に上げて、
Apacheのconfigを書き換えた。

$ sudo rvm get stable
$ rvm reload
$ rvm list known
$ sudo rvm autolibs enable
$ sudo rvm upgrade 1.9.3-p362
$ rvmsudo passenger-install-apache2-module

LoadModule passenger_module /usr/local/rvm/gems/ruby-1.9.3-p392/gems/passenger-3.0.19/ext/apache2/mod_passenger.so
PassengerRoot /usr/local/rvm/gems/ruby-1.9.3-p392/gems/passenger-3.0.19
PassengerRuby /usr/local/rvm/wrappers/ruby-1.9.3-p392/ruby

=>クラッシュしなくなった。


# 自分への教訓

メインで使ってるソフトウェアのバグやセキュリティフィックスくらいは
恒常的に意識的に追いかけましょう。


# + α

4/1から新しい会社で働いています。

2012年8月28日火曜日

RubyからS3とSimpleDBを扱う

最近まじめにAWSを触る機会が増えてきたのでメモ
手元のRailsアプリに必要な記述をしてからRailsコンソールで動作確認した。

# SimpleDB

gem設定の追加
# Gemfile
gem 'aws-sdk'
AWS設定の用意
# config/initializers/aws.rb
AWS.config({
    :access_key_id => '<自分のと>',
    :secret_access_key => '<置換しろ>'
})
Modelの作成
#record.rb
class Record < AWS::Record::Base
     string_attr :name
end
コード例
 新規レコードの作成
     Record.new(:name => "First one").save
レコードの検索
     rs = Record.where('name = ?', 'First one')
レコードのname属性値取得
     rs.first.name
レコードの削除
     rs.first.delete       
[参考]


# S3

Gemfileとaws.rbはSimpleDBと同じなので省略

Bucketの取得
- 既存のものがある場合
b = s3.buckets["<bucket name>"]
- 新規作成する場合
b = s3.buckets.create("<bucket name>")
S3オブジェクトの取得
o = b.objects[<key>]
新規作成(或いは更新)の時(中身を書き込む)
o.write("hello") 
既存S3オブジェクトの内容の読み出し
o.read
[参考]
http://docs.amazonwebservices.com/AWSRubySDK/latest/frames.html