RubyでRedis使ってて便利だったgem

・redis-objectsの「Redis::Lock」

redis-objectsがまず便利なんだけど、その中で「Redis::Lock」。
処理が平行して実行されないようにしたい時など、処理の一部をロックしたい時に役立つ。

これは、redis公式の

http://redis.io/commands/setnx

これに則ってる。

簡単にいうと、SETNX(セットされていたかどうかで0or1が帰る処理)でキーを取得して、取得されていたら解放されるまでポーリング(もしくはタイムアウトを設定してタイムアウトさせる)

require 'redis-objects'
@lock = Redis::Lock.new('lock-key', timeout:0.1)
begin
  @lock.lock do # lockの最初に"setnx" "lock-key"を実行し、lockを獲得
    sleep(10)
  end           # lockの最後に"del" "lock-key"を実行し、lockを解放
rescue Redis::Lock::LockTimeout
  p 'ロック獲得できない時の処理'
end

ちなみにlockのtimeoutを設定しない場合、timeout:5(秒)となる。
また、0.1秒ごとにSETNXでキーの解放をチェックする。

`lock': Timeout on lock lock-key exceeded 5 sec (Redis::Lock::LockTimeout)
・Resqueで同じパラメータのものはenqueしない「Resque::Plugins::UniqueJob」

https://github.com/jayniz/resque-loner

Redisというか、Resqueなんだけど。

同じパラメータのものは一度しかenqueしてほしくない時に使う。

仕組みとしては、ざっくり言うと「queue名」「パラメータ(をmd5等で一意のIDにしたもの)」をキーとしたものををenque時にredisにSETし、重複するものが既にSETされていればenqueしないという仕組み。


resque:loners:queue:cache_sweeps:job:5ac5a005253450606aa9bc3b3d52ea5b

ただ、これはenqueueの重複を防ぐだけでqueueの重複実行を防ぐものではない(queueが取り出され実行された時点で、次のenqueueは受け付けるので)。
なので、重複実行には上記のlock等を使うべき。

「つくって学ぶプログラミング言語 RubyによるScheme処理系の実装」を読みはじめた

1.2 はじめてのプログラムの評価のところ。

サンプルのパーサをrubyで書いてはいるんだけど、scheme風にするために(is_a? Arrayしてるのをあえてdef list?定義してたり)で書かれていて読むのがつらかった。
とりあえず動きだけ把握したいなーと思って、rubyだけのシンプルな感じで書いてみる。

def primetive_fun(exp)
  {:+ => lambda{|x, y| x + y},
   :- => lambda{|x, y| x - y},
   :* => lambda{|x, y| x * y},
  }[exp]
end

def _eval(exp)
  case exp
  when Array
    fun = _eval(exp[0])
    args = exp[1..-1].map{|x|_eval(x)}
    fun.call(*args)
  when Numeric
    exp
  else
    primetive_fun(exp)
  end
end

p _eval([:+ ,[:-, 1, 5],13])

=>9

「なるほどUnixプロセス ― Rubyで学ぶUnixの基礎」を読んだ

良い本だった。Ruby関係ない人も、Unix的な仕組みの理解につながると思う。

この本の内容でそれほど触れているわけでは無いが、普段なにげなく叩いているコマンド実行時の「パイプライン」。
これの理解がコマンド同士の入力、出力をつなげるくらいしか理解できてなかったが、この本読んだきっかけで実際どういうう動きになってるのか多少整理できた。


たとえば下記のようなコマンドを入力した時、どういう挙動でデータの受け渡しがなされるのか。


ls -l|grep hoge

■実行された時の流れ

1.パイプ用のファイル(ディスクリプタ)を作る

「ls -l」から「grep hoge」への標準出力を渡すため、パイプ用のファイルを作る。
ファイルとは言ってもディスク上に書き込まれるものではなくメモリ上でのただの識別子(ファイルディスクリプタ
メモリ上なので、基本的には高速なやりとりが期待できる。

2.コマンド実行用のプロセスをそれぞれのコマンドごとに立ち上げる(fork)

コマンドの実行は、それぞれ1つのプロセス内で行われる。
「ls -l」 と「grep hoge」を実行するためのプロセスをそれぞれfork(2)する
(fork(2)実行時、作ったパイプのファイルディスクリプタdup(複製)して渡す。パイプにはread,writeそれぞれの口があるので、それが親と子プロセスどちらかになるように片方を閉じる。そうしないと、書き込み、読み込み処理の終了判定が壊れる)

3.パイプでのやりとりのために、出力を置き換える

それぞれのコマンドの標準入力、標準出力の参照を、パイプに置き換える。
forkしてできあがったプロセスは、それぞれexec(2)して「ls -l」 、「grep hoge」に姿を変える(exec(2)はプロセスを指定の実行コマンドに完全に置き換える )

4.実行される

それぞれのコマンドは単体で動作する時と同じように、標準入力、標準出力を使っての挙動をとる
書き込み、読み込みは随時行われる(tail -f file |grep hogeってやっても随時結果が出力されていくのはこのため)

このwikiの「Unixにおけるforkの重要性」、わかりやすいな。
http://ja.wikipedia.org/wiki/Fork

■割り込み時された処理(Ctrl+cでの中断など)

また例としてこのコマンドで。


ls -l|grep hoge

このコマンド実行時、例えば何かシグナル(例えばCtrl+c)などを送ると、「ls -al」と「grep hoge」の実行プロセスはどちらも終了される。
これは、「ls -al」と「grep hoge」のプロセス両方にシグナルが転送されたから。
なぜシグナルが転送されるかというと、このパイプラインでつながれたそれぞれのプロセス(コマンド)が、同じ「プロセスグループ」となるから。

また、「プロセスグループ」のさらに上に「セッショングループ」というのが存在する。
さらにこのセッショングループには「セッションリーダー」が存在し、コマンド実行時には基本的にログインシェルとなりそう。
なので、ログインシェルが終了し「セッションリーダー」がいなくなった時、そのログインシェルの「セッショングループ」内の全てのプロセスグループにもシグナルが転送され、同じように終了する事ができる。

xmpp4rで日本語のチャットルームに入ると deadlock detected

xmppなチャットルームにrubyからメッセージを送りたいと思い、
http://d.hatena.ne.jp/gnarl/20120810/1344564294
を参考にして下記のスクリプトを書いたけど、

# -*- coding: utf-8 -*-
require "xmpp4r"
require 'xmpp4r/muc'

chat_room = "ほげ-会議室@conference.im.hoge.jp/motsat"

client = Jabber::Client.new(Jabber::JID.new("motsat@im.hoge.jp"))
client.connect
client.auth("mypassword")

# チャットルームに入る
muc = Jabber::MUC::MUCClient.new(client)
muc.join(chat_room)
message = Jabber::Message.new(chat_room, "test")
muc.send(message)
client.close

なんか

muc.join(chat_room)

の所で

.rvm/rubies/ruby-1.9.3-p125/lib/ruby/1.9.1/thread.rb:71:in `sleep': deadlock detected (fatal)

こんなエラーが出る。特に自分が書いた部分でthreadのdeadlockさせるようなことしてないしなーと思って

 Jabber::debug = true

をして詳細表示させてみたら、


REXML::ParseException
#
.rvm/rubies/ruby-1.9.2-p290/lib/ruby/1.9.1/rexml/source.rb:212:in `match'
xmppでやりとりしてるxmlのパースが失敗してたらしい・・・。エンコーディングがまずいと。
で、ググったら異国の地で同じコケかたしてる人がいた。
https://github.com/ln/xmpp4r/issues/3
ruby1.9以上はencoding部分でこける可能性があるっぽい。

   # Encoding patch
    require 'socket'
    class TCPSocket
        def external_encoding
            Encoding::BINARY
        end
    end

    require 'rexml/source'
    class REXML::IOSource
        alias_method :encoding_assign, :encoding=
        def encoding=(value)
            encoding_assign(value) if value
        end
    end

    begin
        # OpenSSL is optional and can be missing
        require 'openssl'
        class OpenSSL::SSL::SSLSocket
            def external_encoding
                Encoding::BINARY
            end
        end
    rescue
    end

このパッチをあてたらら解決。たぶん実行環境のrubyなりその他?の文字コード設定にも寄るのだと思う。
とりあえず投稿できた。

Arduino

最近Arduinoが気になってたので、手を出してみることにした。
いろいろ頑張れば、Webサーバと連動したり障害物を判定して動いたりするような電子工作ができるらしい!

そもそもArduinoって?

電子工作。
プログラミング(Java風のオブジェクト指向言語「Processing」。Javaを単純化したようなもの)

何ができるのか

電子工作。っていうと学生の宿題っぽいけど、センサー、ネットワーク通信、音声などの電子なものが一通り使えるので工夫次第ではそこらのおもちゃより高度なものが作れる。
Webも得意な人だと、
ツイッターを監視する『Twitter Monkey』とか。作ったり。 
最近話題の『Arduino』とは一体何なの?何ができるの?まとめ

必要なもの

すごい事やろうと思うといろいろいるんだろうけど、まずはこのあたり。

■初心者キット(最低限の部品)

Arduinoをはじめようキット

■USBケーブル

USB2.0ケーブル(ABタイプ)がいる。PCとArduinoつなぐから。俺はこれが最初無くて、Arduino前にして生殺し。

■入門書

たのしい電子工作 Arduinoで電子工作をはじめよう!
Arduinoをはじめよう 第2版 (Make:PROJECTS)
あとは時間とやる気。ある程度部品を準備して試行錯誤して、Webなんかの情報よりも少ない情報をたよりにしてみたいな感じだから時間の無い社畜系の人は無理ぽい。これを機に社畜やめると良い。

今さらrails debuggerの便利な機能

いつもpry-railsデバッグしててあんまりdebugger使ってなかったんだけど、
debuggerって多機能だし、その機能ってどんなんあるだろうと。
で、その中でup、disp、infoって便利だと思った。

up

呼び出し元を見る。
ある箇所でおかしな事になってるけど、元の処理のどこかが悪そう
例)ある箇所にdebugger→up→up…で呼び出し元を探ってinfo argsなりでデバッグ

disp (式)

任意の式を実行毎に評価する。
例えば、disp hoge(2)とすると、nextやupする毎にhoge(2)の実行結果を表示する。
何かの処理結果を常に監視したい時等に役立つ。
(undisp)
dispを解除(disp設定時の番号指定)

info (name)

infoに下記のnameを指定し、情報を見る。下の2つ以外にもいろいろある。
variables - ローカル変数(引数も)、インスタンス変数、selfを見る
display - displayでセットした内容

ついで

基本コマンド


next
ステップアウト(メソッドの中に入らず1行実行)
step
ステップイン(メソッドの中に入って1行実行)
continue
次のbreakpoint※1まで

.rdebugrc

~/.rdebugrcにデフォの設定を書いておくと便利


set autoeval
set autolist
set autoreload