2008年7月アーカイブ

Ruby on Railsのcaches_actionでキャッシュヒットした時に、リクエストログを保存したりしたい。

初めてRailsのキャッシュ機構を触るわけですが、caches_actionとやらでは、キャッシュヒット時点でフィルタ処理を中断(終了)する様です。after_filterでログ記録処理を行っているアクションに対して、caches_actionでキャッシュを利用する場合にどうするのが一般的なのでしょうか。

とりあえず、キャッシュヒット後の挙動であるrender_for_textをオーバーライドして、rendered_action_cacheとアクション名を見て後処理を云々とかしてみましたが、無理矢理すぎて嫌な感じです。

_withなんたらを使うのか、alias_なんたらでbeforeをのっとってやればいいのか。いまだにRailsを理解しきれていないので、何が普通の手段なのかよくわかりません。

ActionController::Caching::Actions::ActionCacheFilter#beforeを、alias_methodだか何だかを使ってbefore_hogeにリネームして、自前のbeforeを書いて、そのbeforeの中でbefore_hogeと適当な処理を実行する様にしたらいいのでしょうか。

「負荷軽減のためにキャッシュ使いたいけど、after_filterも使いたい」という場合の、一般的な方法ってどんなでしょうか?


まあ、純粋なHTTPのリクエストログを記録したいだけなら、(後でまとめて)Apacheログをパースしてやればいいわけですがね。それはそれで、いろいろと面倒があったりするので、今回はリアルタイムでの記録にこだわってみたかったりします。

gist(およびgithub)初利用

周辺サービス(?)の華やかさに誘われて、gitびいきになろうか検討中。

コードは漢数字(全角数字)を数値に変換するRubyスクリプトです。解き方やら慣習やらには大分自身がありませんが、主題はgist(github)なので気にしないでおきましょう。

コードを保存して"embed"からscriptタグをコピーして貼付け。楽。

知らなかった。

autocomplete="off"となっていると、パスワードマネージャが働かないんですね。

MERGEテーブルが抱えているMyISAMテーブルをリネームした場合、何かが原因でテーブルが壊れたと見なされる様子。

例えば、parentというMERGEテーブルがあり、childというMyISAMテーブルを抱えているとします。そして、child_tempというMyISAMテーブルを作成していくらかのINSERTを行った後、"RENAME TABLE child TO child_old, child_temp TO child;"としてリネームします。そうすると、childが壊れたと見なされる事があります。これは、必ずというわけでもないので、理由がよくわかりません。

アクティブな MERGE テーブルで使用されているテーブルに対して RENAME TABLE を実行すると、テーブルが破損するおそれがある。これは MySQL 4.1.x で修正される予定。
MySQL :: MySQL 4.1 リファレンスマニュアル :: 7.2.1 MERGE テーブルの問題

MySQLのドキュメントに、それらしい記述がありますが、利用しているバージョンは5.0系なので、ドキュメント通りに事が進んでいるとすれば、修正されて問題がなくなっているはずです(そうでもない?)。

開いているMERGE テーブルにマップされた全てのテーブルに対して、 WHERE 条項、 REPAIR TABLE、 TRUNCATE TABLE、 OPTIMIZE TABLE、また ANALYZE TABLEがない DROP TABLE、 ALTER TABLE、 DELETEを使う事はできません。それをすると MERGE テーブルは元のテーブルを参照する事があるので、好ましくない結果をもたらす可能性があります。このような事を防ぐのに一番簡単なのは、FLUSH TABLES ステートメントを事前に発行する事によって、全ての MERGE テーブルを閉じておくという方法です。
MySQL :: MySQL 5.1 リファレンスマニュアル :: 13.6.1 MERGE テーブルの問題点

また、上記のような記述もあります。微妙にRENAME TABLEは外れている気がしますが、それらしい記述です。

.MRG ファイルを変更し、MERGE テーブルとこれを構成するすべてのテーブルに対して FLUSH TABLE を発行することで、ストレージエンジンが新しい定義ファイルを読み取るようにする。
MySQL :: MySQL 4.1 リファレンスマニュアル :: 7.2 MERGE テーブル

ついでに、上記のような記述もありました。

さて、「壊れた」と書きましたが、症状としては、レコードサイズやデータファイルのサイズが一致しないというものでした。さらに、観察してみると、どうやら、RENAME TABLE前のテーブル情報をそのまま抱え続けている様です。

どうやら、テーブル情報を「何か」が抱え続けている事が原因で、テーブルが破損したと見なされる様です。(ソースを確認したわけではないので)確証はありませんが、"RENAME TABLE"後にMERGEテーブルとMyISAMテーブルの両方をFLUSH TABLEする様にしたところ、「今のところは」テーブル破損が確認されなくなりました。

ふとした事から、レプリケーション周りの調査をしているところ。

MySQL4.1.1から、特定のDBやテーブルのみをレプリケーション対象とする事ができる様になったらしい事を知りました。
MySQL :: MySQL 4.1 リファレンスマニュアル :: 4.11.6 レプリケーションスタートアップオプション

`SHOW SLAVE STATUS \G`で、'Replicate_%'な項目が確認できますが、これらがそのオプションで指定されているものです。が、これらをどうやって指定するのか、微妙にわかりません。

webのマニュアルを見る限り、mysqldの起動オプションで指定すればよいような印象ですが、どうなんでしょう。

# Replicate_Do_Table, Replicate_Ignore_Table, Replicate_Wild_Do_Table, Replicate_Wild_Ignore_Table

--replicate-do-table、--replicate-ignore-table、--replicate-wild-do-table、--replicate-wild-ignore_table の各オプションでテーブルが指定されていれば、その一覧。

これらのフィールドは、MySQL 4.1.1 で導入された。
MySQL :: MySQL 4.1 リファレンスマニュアル :: 4.11.6 レプリケーションスタートアップオプション

`man mysqld`で`mysqld --verbose --help`したら何かが見れる事を知ったので、その中から'--replicate-*'なものを抽出してみました。

  --replicate-do-db=name
                      Tells the slave thread to restrict replication to the
                      specified database. To specify more than one database,
                      use the directive multiple times, once for each database.
                      Note that this will only work if you do not use
                      cross-database queries such as UPDATE some_db.some_table
                      SET foo='bar' while having selected a different or no
                      database. If you need cross database updates to work,
                      make sure you have 3.23.28 or later, and use
                      replicate-wild-do-table=db_name.%.
  --replicate-do-table=name
                      Tells the slave thread to restrict replication to the
                      specified table. To specify more than one table, use the
                      directive multiple times, once for each table. This will
                      work for cross-database updates, in contrast to
                      replicate-do-db.
  --replicate-ignore-db=name
                      Tells the slave thread to not replicate to the specified
                      database. To specify more than one database to ignore,
                      use the directive multiple times, once for each database.
                      This option will not work if you use cross database
                      updates. If you need cross database updates to work, make
                      sure you have 3.23.28 or later, and use
                      replicate-wild-ignore-table=db_name.%.
  --replicate-ignore-table=name
                      Tells the slave thread to not replicate to the specified
                      table. To specify more than one table to ignore, use the
                      directive multiple times, once for each table. This will
                      work for cross-datbase updates, in contrast to
                      replicate-ignore-db.
  --replicate-rewrite-db=name
                      Updates to a database with a different name than the
                      original. Example:
                      replicate-rewrite-db=master_db_name->slave_db_name.
  --replicate-same-server-id
                      In replication, if set to 1, do not skip events having
                      our server id. Default value is 0 (to break infinite
                      loops in circular replication). Can't be set to 1 if
                      --log-slave-updates is used.
  --replicate-wild-do-table=name
                      Tells the slave thread to restrict replication to the
                      tables that match the specified wildcard pattern. To
                      specify more than one table, use the directive multiple
                      times, once for each table. This will work for
                      cross-database updates. Example:
                      replicate-wild-do-table=foo%.bar% will replicate only
                      updates to tables in all databases that start with foo
                      and whose table names start with bar.
  --replicate-wild-ignore-table=name
                      Tells the slave thread to not replicate to the tables
                      that match the given wildcard pattern. To specify more
                      than one table to ignore, use the directive multiple
                      times, once for each table. This will work for
                      cross-database updates. Example:
                      replicate-wild-ignore-table=foo%.bar% will not do updates
                      to tables in databases that start with foo and whose
                      table names start with bar.

mysqld実行時に渡せるオプションは、my.cnfに記述できるんでしたっけ。頭のハイフン削ったものが設定値の名前だった気がしないでもないですが、覚えていません。

試してみればすぐわかる事ではありますが、全く知らないオプションの存在をしったので、勢いでメモです。Railsみたく、複数環境で多数のDBを使う場合、productionのものだけをレプリケーション対象とした方がいいのでしょうかね。障害でスレーブがマスタになったとき、本番用のproduction環境以外のDBが存在しないか空だったとしても、何も困らないですね。即座にテストが実行できない問題はありますが、たいした事でもない様に思います。

'--replicate-wild-do-table=%_production.%'あたりでしょうかね。


'--binlog-do-db=db_name'か'--binlog-ignore-db=db_name'を使って、そもそも、バイナリログに書き出さない方法もありか。

mac miniの熱暴走ひどい

会社で購入したmac miniが熱暴走気味。

検証&ミーティング用マシンとして購入したので、常時稼働させています。いつごろからか、突然応答しなくなる事が頻発するようになりました。ひたすら強制シャットダウン(電源長押し)で対応してきましたが、どうもファンが機能していなかったようです。

Mac mini: PMU をリセットする方法
上記ページにある通りにした結果、起動直後にファンがものすごい勢いで回り、背面の排気口から清々しい程に排気される様になりました。

後は、気持ち程度に、金属製ブックエンドの上にのせてみたり、ファン制御のソフトを入れてみたりしました。

(夏真っ盛りなだけに)これで解決するのか不明ですが、しばらく様子をみつつ、できる事を試してみようと思います。

Appleの整備済製品

会社で教えてもらった、Apple Storeの大安売りコーナー(?)。

つい最近知りました。

アップル整備済製品とは、店頭商品、返品商品、初期不良品などを修理調整し、新品水準並みの品質を確認したアップル認定製品です。新製品同様、1年間の特別保証書をお付けしております。
Apple Store (Japan) - 特別限定販売

新品大好きなので、自宅用のメインmacとして使おうとは思いませんが、何かの検証用としてはちょうどよいのかもしれません。

24インチを2台で、1900x1200x2。

これで、本体の1900x1200とあわせて、3面ディスプレイが実現。

GXM Control Panelというソフトウェアが添付CDに入っていないせいで、Dual Headの使い方がわかりませんでしたが、Matroxのサイトを調べてなんとか解決。システム環境設定から、GXM Control Panelを開いて2*1900x1200 60Hzのみを有効にしました。

後は、1番と3番のコネクタにディスプレイを接続すれば、Dual Headができるわけですが、これが最初のシステム起動時には1面しか映らず難儀しました。

何度かコネクタをあれこれ差し替えてみたりしたわけですが、結局、1番と3番の組み合わせで問題ありませんでした。なんでしょう。システム終了から起動じゃなくて、再起動だと駄目だとかですかね。終了にした気がするんですけど。

使い心地は、正直微妙な気もします。3800x1200を2台に分けて出力しているので、全画面表示をすると当然3800x1200になるわけです。そのためのデバイスだと言われればそれまでなんですが、1画面を広げたいわけではなくて、3つの作業領域が欲しいわけなので。

まあ、今わかっている不満な点は使い方で解決する気もするので、なんとかなりそうな気もしています。

とりあえず、今は無事に導入ができてほっとしてます。

GXM Control Panelを見つけるのに苦労したので、メモがてら手順を記録しておきます。

  1. Matrox Graphics - Homeの"Support"タブの"GXM Support Center"を選択
  2. STEP1でMac、STEP2でTripleHead2Go Digital Editionを選択
  3. 左のメニューから"Download Software"を選択
  4. "Matrox GXM Control Panel"をダウンロード

こんな面倒な手順を踏まなくてもダウンロードできる気がしますが、これしかわかりませんでした。

TripleHead2Go Digital Edition

3面ディスプレイが実現するのかも。

ずっと、「ポート1つにつき、1つのディスプレイ」という考え方をしていましたが、「1つのポートで、複数のディスプレイ」という考え方もありますね。"TripleHead2Go Digital Edition"というのを使うと、DVI-IかミニDVI-Sub15ピンを使って、最大3つのディスプレイに出力できるとか。

Matrox Graphics - Support - GXM System Compatibility
↑MacBookProへの対応を見てみると、3840x1200 (2x 1920x1200)か3840x1024 (3x 1280x1024)で利用できるようです。枚数増やすと、1枚あたりの解像度が小さくなるわけですね。

PowerMac時代に使っていた古いシネマディスプレイが余っているので、本体+2枚でちょうどいい気がします。

そもそも、WikiのXML-RPCの仕様で未定義?

Hikiのページは、本文を空にして保存すると、ページが削除されます。"hiki/command.rb"の"Hiki::Command#cmd_save"で、text.empty?として処理を分岐(削除処理につなげる)ているようです。

XML-RPCでも同様に空の本文でリクエストした場合に、ページ削除になると思い、"wiki.putPage"に空の"content"を与えてみましたが、空の本文でページが更新されるだけでした。"hiki/xmlrpc.rb"の"wiki.putPage"ハンドラ内を見てみると、保存処理が"plugin.save"で実行されている事がわかります。

"hiki/plugin.rb"の"Hiki::Plugin#save"を見てみると、先述の"Hiki::Command#cmd_save"と異なり、本文が何であれ保存されるようです。

過去の議論などを確認していないのでなぜこうなっているのかわかりませんが、一般にWikiをRPCなどでページ削除する事はないのでしょうか。それとも、自分が見落としていて、実はページ削除の方法は提供されているのでしょうか。

とりあえず、今やっている遊びには必要なので、無理矢理ページを削除する処理を追加してごまかします。

diff -NBaur hiki/plugin.rb hiki.1/plugin.rb
--- hiki/plugin.rb	2006-09-02 15:36:57.000000000 +0900
+++ hiki.1/plugin.rb	2008-07-06 17:40:10.000000000 +0900
@@ -295,6 +295,12 @@
       result
     end
 
+    def delete( page )
+      @db.delete( page )
+      @db.delete_cache( page )
+      delete_proc
+    end
+
     def admin?
       ( @user == @conf.admin_name ) || @conf.password.empty?
     end
diff -NBaur hiki/xmlrpc.rb hiki.1/xmlrpc.rb
--- hiki/xmlrpc.rb	2007-03-14 17:49:00.000000000 +0900
+++ hiki.1/xmlrpc.rb	2008-07-06 17:40:10.000000000 +0900
@@ -65,16 +65,20 @@
 
         md5hex = attributes['md5hex'] || db.md5hex( page )
         update_timestamp = !attributes['minoredit']
-        unless plugin.save( page, content.gsub( /\r/, '' ), md5hex, update_timestamp )
-          raise XMLRPC::FaultException.new(11, "save failed.")
-        end
-        keyword = attributes['keyword'] || db.get_attribute( page, :keyword )
-        title = attributes['title']
-        attr = [[:keyword, keyword.uniq], [:editor, plugin.user]]
-        attr << [:title, title] if title
-        db.set_attribute(page, attr)
-        if plugin.admin? && attributes.has_key?( 'freeze' )
-          db.freeze_page( page, attributes['freeze'] ? true : false)
+        if content.empty?
+          plugin.delete( page )
+        else
+          unless plugin.save( page, content.gsub( /\r/, '' ), md5hex, update_timestamp )
+            raise XMLRPC::FaultException.new(11, "save failed.")
+          end
+          keyword = attributes['keyword'] || db.get_attribute( page, :keyword )
+          title = attributes['title']
+          attr = [[:keyword, keyword.uniq], [:editor, plugin.user]]
+          attr << [:title, title] if title
+          db.set_attribute(page, attr)
+          if plugin.admin? && attributes.has_key?( 'freeze' )
+            db.freeze_page( page, attributes['freeze'] ? true : false)
+          end
         end
         true
       end
 

デバッガを使える様になろうと、まずはツールから物色中。

それで、rdebug(ruby-debug)をEmacsから使ってみることにしたわけですが、デバッガの普通な使い方を知らないので、ソースが見えなくて何をしていいのかわからなくなります(listで周辺ソースが見られるのはわかりました)。

そんな状態なので、左にソース、右にデバッガという状態を作りたいなと思ったわけです。

Googleから、rdebug.elというそれっぽいelispを見つけました。"M-x rdebug"でrdebugがEmacs上で起動します。それっぽいです。

ただ、1面ウィンドウの状態から起動すると、当然(?)全面デバッグウィンドウになります。これを、「"M-x rdebug"するだけで、左ソース右デバッガ」となるようにしたいわけです。

rdebug.el自体をいじることなく、きれいに挙動をいじる方法がわからなかったので、rdebug.elを直接書き換えました。ひどく異臭を感じますが、よくわからないなで我慢します。

--- rdebug.el.orig      2008-07-05 21:40:24.000000000 +0900
+++ rdebug.el   2008-07-05 21:21:51.000000000 +0900
@@ -109,6 +109,7 @@
                                  (concat rdebug-command-name " "))
                                nil nil
                                '(gud-rdebug-history . 1))))
+  (setq source-buffer (current-buffer))
   
   (if (not (fboundp 'gud-overload-functions))
       (gud-common-init command-line 'gud-rdebug-massage-args
@@ -133,4 +134,9 @@
       (set-marker comint-last-output-start (point)))
   (set (make-local-variable 'paragraph-start) comint-prompt-regexp)
   (run-hooks 'rdebug-mode-hook)
+  ;;; split and switch
+  (setq debugging-buffer (current-buffer))
+  (switch-to-buffer source-buffer)
+  (split-window-horizontally)
+  (switch-to-buffer-other-window debugging-buffer)
   )
  1. カレントバッファを別名記憶(ソース)
  2. デバッガのバッファを別名記憶(デバッガ)
  3. バッファをソースのバッファに切り替える
  4. ウィンドウを左右に割る
  5. 右のウィンドウのバッファをデバッガに切り替えてそこに移動

やった事は上記のような感じです。激しく異臭を放っています。

ここまでやって思ったんですが、gdbの場合はそもそも、自動でウィンドウが割れたり、ソースのウィンドウとデバッガのウィンドウが連動したり、あれやこれやと、至れり尽くせりだったりするんでしょうか?
ウノウラボ Unoh Labs: gdbの使い方

rdebug(ruby-debug)をEmacsから使う場合の、正しい方法や拡張ツールなど、どこかで提供されてたりしませんかね?


ちなみに、ようやくrcodetoolsをEmacsから使う様にしました。irbで複数行を扱うのは自分には無理です。かといって、捨てファイルを作って実行というのもアレ過ぎなので。Google経由でみつけた、"*ruby-scratch*"なelispをウィンドウは縦割りにして使わせてもらってます。

蛇足ですが、splitの縦(vertically)と横(horizontally)で混乱してます。split-window-verticallyとsplit-window-horizontallyの意味を間違えて、しばらくあれこれしてたのは内緒。縦に分けるのは上下に分けるで、横に分けるのは左右に分ける、と。正直、verticallyと見ただけで、「縦割り=上から下に切り割る事」だと思ってました。
垂直(vertically)方向に分割すると切れ目は水平に入って、水平(horizontally)方向に分割すると切れ目は垂直に入るわけですね。
ただ、未だに混乱が残ってます。「縦に割る」といった場合、「刀を上段からまっすぐ振り下ろす」的なイメージがありますが、間違いでしょうか?もう、自分が日本生まれで日本育ちの純日本人であることが信じられません。。。

最初から提供されているタスクのロールを変更するにはどうすればいいんだろうか。
注) 手元のCapistranoのバージョンは2.4.3です。

例えば、deploy:defaultを再定義して、":roles => :app"を指定したとします。再定義したdeploy:defaultの中で、標準定義のままのdeploy:updateを実行した場合を考えます。

deploy名前空間内の標準定義タスクは、いずれもロール未指定です("capistrano/recipes/deploy.rb"を見る限り)。特に何もせずにcapコマンドを使ってタスクを実行した場合、「定義済みロールすべて」に対して実行されます。

タスクを実行する対象となるロールは、3段階の優先度を持って決定されます。"lib/capistrano/configuration/servers.rb"の38行にその処理が書かれています。

roles  = role_list_from(ENV['ROLES'] || options[:roles] || self.roles.keys)
  1. 環境変数ROLES
  2. タスク定義時に指定したoptions[:roles]
  3. deploy.rbなどで定義したロール

タスク定義時にロールを指定せず、「特に何もせず」にタスクを実行すれば、3番目の"self.roles.keys"からロールを取得するため、定義されているすべてのロールが対象となるわけです。

さて、ここからが本題ですが、「ロール未指定な既存タスク」のロールを環境変数ROLESを使わずに指定するにはどうすればいいのでしょうか。

暫定的な対策として、タスク実行時にタスクのオプションを書き換える方法があります。定義されたタスクはTaskDefinitionのインスタンスとして保管されます。TaskDefinitionのインスタンスは、定義時に指定した:rolesなどのオプションを、@optionsというインスタンス変数で保持します。そして、ネームスペースであるCapistrano::Configuration::Namespacesは、TaskDefinitionのインスタンスを格納する@tasksというインスタンス変数を持ちます。つまりは、タスク定義の中から"tasks[:task_name].options[:roles]"で指定ロールにアクセス可能だという事です。

と、まあ、ふとした事から気になったので追いかけてみました。本当は、どうするのが正解なんでしょうかね。


Capistranoを詳しく調査/利用したことはないので、いろんな意味で的はずれかもしれませんが、ご容赦。

プロフィール

このアーカイブについて

このページには、2008年7月に書かれたブログ記事が新しい順に公開されています。

前のアーカイブは2008年6月です。

次のアーカイブは2008年8月です。

最近のコンテンツはインデックスページで見られます。過去に書かれたものはアーカイブのページで見られます。