DB操作ツール Emacs DBI を作ってみた

去年からほそぼそと作ってきた、EmacsからDBを操作できるツール Emacs DBI を紹介します。

Emacs DBI の簡単な紹介

このツールの目的は、クロスプラットフォームで便利なDB操作環境を実現することです。 pgAdmin や MySQL Query Browser のようなGUIの良さをCUIで実現してみようとしてみました。すなわち、ぼくのかんがえたさいきょうのDBツールです。ちなみに、このツールにとってEmacsはただの実行環境です。Emacs使わない人でも使うと便利だと思います。



データベース画面



e2wmで3ペインの画面

機能概要

以下のような機能があります。

  • EmacsとDB接続可能なPerlが動けばターミナルでも何処でも動く
  • DB定義、テーブル定義がすぐ見れる
  • auto-complete によるSQL補完
    • 接続先DBにからキーワード、型名、テーブル名、カラム名などを補完
  • 対応DB(この日記時点)

簡単に仕組みを説明しますと、EmacsのUIから自前RPC(SWANKっぽいS式のプロトコル)でPerlに接続し、PerlDBIを使ってDB接続するというものです。

インストール

Perlのモジュールとelispを入れる必要があります。

どちらもパッケージシステムを使うことによって、コマンド一発で入れることが出来るのですが、環境によってはうまく動かないかもしれません。多分そんなに難しくないとは思いますが、EmacsPerl/CPANの知識があるとスムーズです。

手元での動作確認は、以下の環境で確認しています。

CPAN

以下のCPANモジュールを入れます。

cpan 使うときのコマンド例

cpan RPC::EPC::Service DBI DBD::SQLite DBD::Pg DBD::mysql

自分の所ではシステム全体のCPANと分けるために cpanm を使っています。

LinuxMacならこれで入ると思います。Windowsで手軽に使うにはActivePerlがいいみたいです。
Cygwinを使っている人は多いと思いますが、Cygwin PerlPerl以外の依存関係を自力解決する必要があります。

Windows : Cygwin Perl
CygwinのSetup.exeから、Perlgcc-4 や SQLite などの開発パッケージを入れて、CPANコマンドで入れると入るはずです。CygwinCPANが遅いのでちょっと時間がかかります。その場合は、 cpanm を使うと速くていいです。

CPANでのビルドに失敗した場合は、build.logを見ながら依存ライブラリを追加すると大丈夫みたいです。

Windows : ActivePerl
(2012/03/07 修正)
ActivePerlppmで一発インストールできます!

ppm install RPC-EPC-Service
Marmalade を使ったelispの全自動インストール

Marmalade package システムが入っていれば、 M-x list-package して、「edbi」というパッケージを探してインストール操作するだけです。必要な依存ライブラリやPerlプログラムがすべてインストールされます。あとは設定へ進んでください。

elispの手動インストール

Marmaladeを使わない場合のインストール方法です。
まずは以下の elisp をインストールします。

  • deferred.el
  • concurrent.el
  • ctable.el
  • epc.el
  • edbi.el
;; auto-installを使う場合
(auto-install-from-url "https://github.com/kiwanami/emacs-deferred/raw/master/deferred.el")
(auto-install-from-url "https://github.com/kiwanami/emacs-deferred/raw/master/concurrent.el")
(auto-install-from-url "https://github.com/kiwanami/emacs-ctable/raw/master/ctable.el")
(auto-install-from-url "https://github.com/kiwanami/emacs-epc/raw/master/epc.el")
(auto-install-from-url "https://github.com/kiwanami/emacs-edbi/raw/master/edbi.el")

後は perl のプログラムが必要です。
以下のリンクから edbi-bridge.pl をダウンロードして edbi.el と同じ所に置いてください

設定と動作確認

scratchバッファで (require 'edbi) とやって、 M-x 'edbi:open-db-viewer としてDB接続画面が出ればひとまずOKです。ここでエラーが出る場合は、必要な elisp が入っていない可能性があります。

次回起動時から使えるように、 ~/.emacs.d/init.el などの各自の設定ファイルに以下のように追加します。

(autoload 'edbi:open-db-viewer "edbi")

使い方説明

各画面を簡単に説明します。後で画面遷移についてもまとめます。

接続

M-x edbi:open-db-viewer で接続ダイアログが開きます。



接続ダイアログ

「Data Source」にはDBIで接続するときの書き方で、接続先のDB情報を書きます。以下、書き方の例です。

「User Name」「Auth」には、接続のためのユーザー名とパスワードを入れます。ユーザー名やパスワードが必要ないDBは入力する必要はありません。

一度接続確立したら、Data Source と User Name は履歴に残りますので、次回からは「History」をクリックするかボタンの上でRETすると選択できます。履歴は edbi:ds-history-file に保存されます。(履歴の編集機能は現在ありません。)

接続の際にエラーが発生すると、エラーの内容が上の方に表示されます。接続情報に間違いがなければインストールに問題があるかもしれません。トラブルシューティングの項を参照してください。

DB定義画面

Emacs DBI のホーム的な画面です。接続先のテーブルやビューの一覧が表示されます。
ここから各テーブルの定義やクエリーの内容を表示します。



DB定義画面

主なキーバインドです。

キー 動作
j,k,n,p 選択の上下移動
SPC 選択したテーブルの定義表示
RET 選択したテーブルのデータ表示
c クエリーエディタ開く
g 最新の情報に更新
q Emacs DBI 終了

(詳しくは edbi:dbview-keymap と ctbl:table-mode-map を参照してください。)

テーブル定義画面

テーブルやビューの定義を表示します。カラムの名前・型・サイズや主キーやNOT NULLなどの情報を見ることができます。



テーブル定義画面

主なキーバインドです。

キー 動作
j,k, n,p 上下移動
V テーブルのデータ表示
c クエリーエディタ開く
g 最新の情報に更新
q バッファkill

(詳しくは edbi:dbview-table-keymap と ctbl:table-mode-map を参照してください。)

クエリーエディタ画面

このバッファでSQLを書きます。C-c C-c でSQL実行、 M-p / M-n で過去に実行したSQLを表示できます。
sql-mode をベースにしたメジャーモードになっていて、適当なシンタックスハイライトと、auto-completeが入っていれば補完が効いて、Emacsのエディタの能力を生かして効率よくSQLを書くことができます。



エディタ画面

主なキーバインドです。

キー 動作
C-c C-c SQL実行
M-p 履歴もどる
M-n 履歴すすむ
C-q q Emacs DBI 終了

(詳しくはedbi:sql-mode-map を参照してください。)

現在のところ、SQLの履歴はEmacsを終了すると消えてしまいます。

クエリー結果画面

SQLの結果を見やすいテーブルで表示します。



結果画面

主なキーバインドです。

キー 動作
j,k,n,p 上下移動
h,l,b,f 左右移動
a,e 左端、右端移動
q バッファkill

SELECTでないSQLの場合は、影響を受けた行数が表示されます。
(ただし、DBIドライバの実装によってはなんだかよく分からない値になることがあります。)

画面遷移

これまでの画面の遷移をまとめると以下のようになります。



画面遷移

終了、接続の後始末

Emacs DBI を終了するには、DB定義画面(*edbi-dbviewer* バッファ)で q を押します。その際、関連するバッファも一緒に消してしまっていいかどうかの確認が出ます。普通は消してしまっていいと思います。

DB定義画面を普通にkillしてしまうと、DB接続だけが残ってしまう場合があります。そんな時は、 M-x epc:manager とすると、現在接続中のEPCが一覧で出てきます。この画面で必要ない接続の行に移動して「D」を押すと接続を消すことができます。EPCについては別のエントリでやると思います。



EPC接続画面

トラブルシュート

予想されるトラブルと対応方法を簡単にまとめました。
近所に EmacsPerlハッカーが一人はいると思いますので、うまく動かない場合は相談されるといいと思います。

  • LC_ALLとかLANGのロケールが不明というエラー
    • (setenv "LC_ALL" "ja_JP.utf8") とか評価すると動くかもしれません。
  • 「file-error Searching for program そのようなファイルやディレクトリはありません perl
    • Emacsからperlが見つけられないというエラーです。
    • Perlをインストールするか、Emacsから見えるところにパスを通す必要があります。
  • 「error Server may raise an error : Can't open perl script "〜/edbi-bridge.pl": そのようなファイルやディレクトリはありません (以下略」
    • edbi-bridge.pl が見つからないというエラーです
    • edbi.el と同じディレクトリにあれば大丈夫のはずです
    • うまく行かない場合は、手動で edbi:driver-libpath を edbi-bridge.pl のあるディレクトリに設定してみてください。
  • 「error Server may raise an error : Can't locate RPC/EPC/Service.pm in @INC (@INC contains: ...(以下略」
    • Perlモジュールが見つからないというエラーです
    • RPC::EPC::Service などのインストールか、インストール先のCPANディレクトリが見えてない可能性があります
    • 環境によっては AnyEvent が動かない場合もあるようです
  • その他
    • バグや要望などは github の issue に書いていただくと、対応するかもしれません。ご期待にそえなかった場合はすいません。
    • バグの場合はSQLiteで動くけどこのDBでは動かないみたいな調査があると大変助かります。

まだまだ開発途上ですので、バグや不便なところがたくさんあると思います。

カスタマイズ

変数、フック、キーマップ
  • テーブル簡易データ表示の LIMIT 件数。デフォルトは50。
    • edbi:dbview-show-table-data-default-limit
  • 長い文字列の切り捨て文字数。デフォルトは 50。
    • edbi:query-result-column-max-width
  • 固定ヘッダーか、バッファ内ヘッダーか。デフォルトは t (固定ヘッダー)
    • edbi:query-result-fix-header
    • 固定だと縦スクロールしてもヘッダーが表示されるが、横スクロールすると切れてしまう
    • バッファ内ヘッダーだと横スクロールしても問題ないが、縦スクロールで切れてしまう
  • クエリー編集バッファの mode-hook
    • edbi:sql-mode-hook
  • キーマップ
    • ctbl:table-mode-map : テーブルの基本キーマップ
    • edbi:dbview-keymap : DB定義画面のキーマップ
    • edbi:dbview-table-keymap : テーブル定義画面のキーマップ
    • edbi:sql-mode-map : クエリー編集画面のキーマップ
    • edbi:dbview-query-result-keymap : クエリー結果画面のキーマップ
e2wmとの連携

e2wmのパースペクティブにすることで、GUIアプリのようにウインドウの構成を固定して使うことができます。



e2wm 画面

利用方法は、以下の elisp を e2wm の後で読み込むように設定します。

この例では「Super+D」でDB画面になるように設定しています。

キー一発でコードとDB操作を切り替えることができるので、Webアプリ開発のようなコードとDBを頻繁に参照するような開発に威力を発揮します。

別のDBMSへの対応

現在のところ、SQLite, MySQL, Postgresql と、id:buzztaiki さんにパッチをいただいた Oracle に対応しています。これ以外のDBMSについては、PerlのDBD(DBIのドライバ)があれば原理的に対応可能です。ただし、DBDやDBMSによってAPIの仕様や返り値がかなり異なるため、そのままでは動かない可能性が高いです。

他のDBMSへの対応を追加する方法は以下のようです。

  • githubの edbi-demo.el をポチポチ実行しながらDBDからの返り値を確認
  • 他のDBMSのものを参考にしながら edbi:dbd 構造体を定義
  • edbi:dbd-register で登録して接続しなおして動作確認

もし、いい感じに追加対応ができたら、 pull request を送っていただけたら取り込みたいと思います。

今後の展開

以下、自分が現在考えている希望です。実現するかどうかは未定です。

  • 対応DBの追加
    • ADO.NET, odbc とか
    • mongoDBなど、DBI以外のDBもつながると便利そう
  • テーブル定義画面の充実
    • インデックス情報の取得 (現在SQLiteしか取れてない)
    • Foreign Key 情報
  • クエリー関係
    • よく使うSQLの保存機能、Anythingで選択とか
    • 段階的データ取得(現在は全部取ってきてしまう)
    • LIMITはかっこ悪い(fetchを使ってるので、実際にはプログラムから途中で止めることが出来る)
    • auto-complete で文脈をみて補完できると良さそう
  • データ操作機能
    • レコードの詳細表示(長いテキストは切れるので)
    • テーブルにINSERTする機能

あとがき

そもそも、このツールをつくろうと思ったのは、Linux用の良いDB操作用GUIツールが無かったからです。

開発の時はSQLをたくさん書くので、補完もシンタックスハイライトも欲しいですし、テーブル定義も頻繁に見たいです。コンソールだとそのあたりのサポートが全く無いので不便でした。

Emacs には sql-mode や inferior な sql-interactive-mode もありますが、自分にとっては console がちょっと便利になった程度でしかないので、コンソールに比べてメリットが感じられませんでした。

  • コンソールUIの問題
    • 結果表示が貧弱
    • コマンド打たないと何も見えない
      • DBMSによってコマンドが異なる
    • 途中改行するSQLが書きづらい

一方、GUIのアプリは、結果がテーブルの形で表示されるので見やすく、またテーブルの定義情報へのアクセスも容易です。さらに、GUIでテーブルやDBMSの管理が行えたり、Explainの結果を見やすく整形できたり、便利な機能がたくさんあります。

自分が使ったり試したりしたことがあるDB操作のGUIツールには以下のものがあります。

Windows にはたくさんGUIツールがあるのですが、Mac版は殆どありません。LinuxMacよりはましですが、常用できるツールはほとんど無いです。IDE統合のツールはどのOSでも動いて便利そうなのですが、わざわざこのためにIDE起動したくありません。

軽くて、どのOSでも同じように動いて、GUIのツールぐらい便利に使えて、Emacs並にエディタが強力なDB操作ツールが欲しいのです。

ということで、無いなら作ろうという事で、去年の calfw が一段落した後から作り始めました。途中、派生ツールとして epc / ctable などが出来ました。

今では日常的にEmacs DBIを使っていますが、大変幸せな毎日を送っています。

次回予定やお知らせ

以下の内容を予定しています。

  • Emacs用汎用RPCスタック epc
  • 汎用テーブルビュー ctable
  • Emacs用のDB接続API edbi

また、3/31の Fukuoka Perl Workshop #21 にて、Emacs DBI や epc の内部の動きなどについて発表しようかなと考えています。@lestrrat さんや @nekokak さんも来られるそうですので、ぜひどうぞ!

オライリー:プログラミングC# 第4版 メモ

プログラミングC#―C#2.0/.NET2.0/Visual Studio2005対応

プログラミングC#―C#2.0/.NET2.0/Visual Studio2005対応

プログラミング C# 第4版を最近スキャンしたのですが、以前読んだ時の付箋がたくさん貼ってありました。読み返してみたら今でも相当面白かったので、メモを淡々と書いてみます。

現在は第6版なので、以下のメモには現在の仕様と異なっている部分があるかもしれません。ご注意ください。

  • p26 値型(value type)と参照型(reference type)の説明
    • →地味に各章への伏線がはってある
  • p28 スタックとヒープ
    • コラムでスタックとヒープについての説明
  • p29 組み込み型同士の変換
    • "キャストとは「おい!コンパイラ!私は自分が何をやってるのかちゃんとわかってるよ」と伝えること"
  • p33 シンボル定数(const)
    • シンボル定数という呼び方
  • p36 列挙型の変換 cast
    • 指定しないと0から始まって1ずつ増える。途中指定するとそこから増える。
  • p45 goto / label
    • "goto文は、通常別のcaseにジャンプするときに使われる"
    • サンプルコードが政治
  • p53 空白文字と中括弧
    • コラムで空白文字と中括弧のスタイルについて
    • 趣味の問題であり、VSの設定で調整可能。
  • p55 四則演算演算子
    • "C#は、整数の割算をするとき、小学4年生ぐらいの子供が行うように割ります。" → つまり切り捨て
  • p67 クラスとオブジェクトについて
    • クラスは型であり、同時にカプセル化の手段である
    • "クラスとオブジェクトの違いは、犬という概念と足元に座っている特定の犬との間の違いになぞらえることができます。犬とは何なのかという定義を相手にして、これを取ってこいといいつけて遊ぶことはできません。遊び相手にできるのは特定のインスタンスです。"
    • "プログラミングに関する古い冗談に「電球を交換するために何人のオブジェクト指向プログラマが必要でしょうか?」、「ひとりも要りません。電球に、自身を交換するように指示すればよいのです」というものがあります(「ひとりも要りません。なぜなら、Microsoftが暗闇というものの基準そのものを変えてしまったからです」という答もあります)。"
  • p70 アクセス修飾子
    • public, private とかの表。アセンブリも一つの境界になる。
  • p71 アクセス修飾子は明示的に書くべき
    • "明示的に書いておくと、それは考えた結果の決定であるという事が伝わりますし、自己記述的にも出来ます。"
  • p73 VB6 の Dim, New と C# の new
    • VB6では Dim と New を同じ行で使うと性能が悪くなるらしい
  • p73 単純型の既定値
    • char の既定値は '\0'
  • p78 C#の「thisポインタ」
    • C#では「thisポインタ」は誤解のもとになるので、正しくは「this参照」を使う
    • "本書ではthis参照というように使い分けるようにしています。言い間違えた個所があれば、それぞれに25セントの罰金をチャリティに寄付しましょう。"
  • p80 staticメソッドはインスタンスから呼べない
    • Javaとは違う
  • p80 オブジェクトの名前 = ヒープ上の無名のインスタンスへの参照の値に付けられた名前
    • "本書において、オブジェクトの名前という表現をする場合、「ヒープ上の無名のインスタンスへの参照の値につけられた名前」を省略しているということを記憶の隅に置いておいてください。"
  • p85 デストラクタの動作
    • GCの動きを具体的に書いている
  • p87 using文
    • try - catch - finally よりも確実
  • p93 出力用の out, 参照渡し ref の例
    • Javaと同様、基本的に引数は「値渡し」。
    • out や ref を使うことで引数で値を返すことが出来る。
  • p99 set アクセサ
  • p102 readonly の代入ルール
    • コンストラクタ内では代入できる
  • p105 クラス階層の説明
    • 本文で陥りがちな悪い例を示して、コラムで自分で批判
    • →これで分かる人は面白いけど、分からない人は混乱しそう
  • p109 overrideキーワード
    • オーバーライドするには override って付ける必要がある
  • p111 既定コンストラクタ自動生成
  • p113 new virtual キーワード
    • new virtual を使うと、基本クラスが同名の派生クラスのメソッドを呼ばないように出来る
    • 規模が大きくなってメソッド名がぶつかる事故が起きて、意図せず基本クラスの動作を破壊することを防ぐらしい。
    • →うーん。余計な迷路を増やしただけにも見えるし、ちょっと気持ち悪い。
  • p122 ボックス化とボックス化解除
    • JavaのAutoboxingのように似た挙動をする違う値に変換されるのではなくて、単にObject型で包まれる。
    • 明示的にボックス化解除が必要。解除の際に null や型が違う場合に備えて try が必要。
    • JavaのAutoboxingは最悪だと思っているので、C#のボックス化はまだまし。
  • p123 C#入れ子クラス
    • JavaインスタンスにひもづいたインナークラスはC#にはない。あるのは Java でいう static なインナークラスのみ。
  • p127 比較演算子オーバーライド
    • C++では対になる演算子は<か=を用意するだけでよいが、C#では対になる演算子は両方定義する必要がある
  • p137 構造体は値渡し、参照にするにはボックス化が必要
    • →クラスと構造体の使い分けは難しそう
  • p140 構造体はnewしなくても使えるが、推奨されない
    • →何のためにあるのか?
  • p143 C#ではインタフェースに定数が書けない
  • p144 Mix In の話
    • 例のアイスクリーム屋の小話
  • p147 インタフェースでのアクセサ
    • publicしかありえないので、アクセス修飾子を書くとコンパイルエラー
  • p159 as演算子。isでチェックしてキャストする
    • →cast必要なときは便利そう
  • p161 抽象クラスかインタフェースか
    • Microsoft社内ではバージョン管理の都合からインタフェースよりも抽象クラスを好む人がいる
    • インタフェースはインタフェースと実装クラスの2つが必要。めんどう。抽象クラスであれば virtual 付けるだけでいい。
    • "なんということでしょう!"
  • p163 キャスト先の型によって動作が変わる!
    • インスタンスが同じでも、キャスト先のインタフェースの型によって動作を変えることが出来る
    • →サンプルコードは的確で分かりやすく、本文で説明されているメリットは理解できるが、それによるデメリットの方が大きくないのか?ちょっと気持ち悪い。
  • p166 インタフェース名を指定して実装するときはアクセス修飾子を付けられない
    • インタフェースはpublic以外ありえないから
  • p168 インタフェース名を指定して実装することで、インタフェースメンバの選択的公開が出来る、クラスのセマンティクスを守る
    • →熟考の上での選択だと思うけど、そんな複雑なことがどうしても必要なことがC#界隈にはあるのだろう。
  • p207 IEnumerableとyield
    • →見た目かなり違うけど、中身はRubyのyieldっぽい
  • p210 ジェネリクスの型の制約
    • →型の制約の書き方はどの言語も大変そう
  • p233 Dictionaryのキーをオブジェクトにするならキーのオブジェクトを変更してはいけない!
    • オブジェクトの中身を変更するとGetHashCodeが変わるので、オブジェクトをハッシュのキーにして変更すると探せなくなる
    • →ついやってしまいそう。こわい。
  • p236 stringとStringは等価
    • stringの内容を変更すると新しいオブジェクトの参照が帰る
  • p237 逐語的文字列リテラル
    • 基本的にエスケープされないが、ダブルクオートはエスケープする必要がある
    • JavaEmacs Lispにも欲しい
  • p242 case ignore な比較
    • "ここでは、1行を短くすることで結果の出力が書籍のページにきちんと収まるように、WriteLine()を2回呼出しています。" ??
  • p244 string の == はオーバーライドされているので中身で比較される
    • Javaもそうだったらよかった
  • p249 split の params キーワードの例
    • 可変長引数
  • p263 例外について
    • バグとエラーと例外の区別をつけよう
    • →いい話
  • p267 すべての例外をキャッチする書き方
  • p274 finally ブロックの制限
    • return, break などのジャンプを使うとエラー
  • p311 event キーワード
  • p316 delegate の匿名メソッド
    • lambda
  • p318 マルチキャストデリゲートの明示的な実行
    • →ここまでくると、わざわざ言語でサポートする必要があったのか疑問
  • p321 デリゲートの非同期実行
    • →話は面白いけど、多分説明が分かりにくそう
  • p341 コラム 亀は続く
    • スチーブン・ホーキング氏の小話
    • "「あなたはなかなかよくできた子ね。しかし亀は亀の上にいるのですよ。その繰り返しです。」"
  • p367 ADO.NET
  • p397 UIデザイン
    • "筆者はUIを完璧にデザインできるプログラマはいないと考えます。"
  • p400 ASP.NET
    • IsPostBack でポストされたかどうかの判断
    • →うーん。昔だからしょうがないかも。
  • p444 アセンブリとセキュリティ境界
    • 2つのアセンブリにまたがって型を定義することはできない。型の参照は出来る。
  • p452 デフォルトではプライベートアセンブリ
    • DLL地獄の解決策
  • p480 マーシャリングの説明
    • 約1ページに渡って、マーシャリングとチャンネルについてスター・トレックで説明
    • "例えば、カーク船長が文明の発展に重大な影響を与えるようなことを伝達しようとするとき、最初のシンクはこれを拒絶します。"
    • Prime Directive の知識が前提!!!!
  • p497 RPCのサンプルコードのコメント
    • "「ただそこに立ち、待つばかりの者もまた主に使えし者である」(ミルトン)"
    • キリスト教の知識が前提。深い。
  • p526 訳者注:デッドロック
    • アメリカでは「deadlock」、ヨーロッパでは「deadly embrace」が多い。ただし、embraceは2つのプロセス間の場合のみに使う。
  • p553 シングルスレッドでブロックするサーバーソケットのサンプルコードについて
    • "電話を目の前にして、鳴り出すのをいつまでも待っているような状態です。このモデルは1週間に2接続程度のサーバーであれば問題ないでしょうが、実際のアプリケーションとしてはほとんど使い物になりません。"
  • p612 fixed キーワード
    • fixedを使うとGCによって変更されないメモリアドレスのポインタを取得できる


この本はサイ本や詳説正規表現と並んでとても楽しかったです。

C/C++の方面から見るとそんなに不思議ではないけど、Java側から見ると落とし穴が多いと思います。Javaと字面は似てるけど、全く同じのつもりで業務でコード書いてる人(SE PGとかでよく見る)は気を付けた方が良いと思いました。(Lispに比べたら大した違いではないけど。。。)

Emacs で IPC や RPC (Emacs Advent Calendar jp: 2011)

Emacs Advent Calendar jp: 2011 : ATND 6日目の記事のはずでしたが、手違いでかぶってしまい、いろいろあって遅くなってしまいました。
皆様、乱してしまってすみません。

今回の記事では、EmacsでのIPC(Inter-Process Communication)やRPC(Remote Procedure Call)の方法について、自分の知っている範囲でまとめてみます。IPCは、プログラムの間で通信する仕組みです。RPCは、既に起動しているプログラムの一部(関数やサブルーチン)を別のプログラムから呼び出す仕組みです。一般的にIPCの枠にRPCが含まれます。

EmacsにはどんなIPC/RPCの実現方法があるのか、実際の応用例などを5分程度で簡単にまとめてみます。

Emacsの標準IPC手段

以下のようなものがあります。(Emacs 23 以上の場合)

ひと通り揃っていて、大体色々出来ますし、D-Bus以外はプラットフォーム中立の事を考えて作られています。

しかしながら、本格的なプログラミング環境としては細かい制御が出来ず、悔しい思いをすることが多いです。特に、共有ライブラリにリンクして、高機能・高パフォーマンスな通信手段(DB直接続とかmsgpackとか)を使うことが出来ません。

そのため、一般的には別の中継プログラムを作成して、パイプなどで通信してやりたい機能を実現するということが多いです。このあたりの歴史や状況については、 id:m2ym さんの文書 http://cx4a.org/pub/emacs-is-dead.ja.html が詳しいです。*1

IPC/RPC いろいろ

Emacsで行われている IPC/RPC の手段について簡単に紹介してみます。

コマンド起動

プログラムを起動し、結果を標準出力などからテキスト等で受け取る方式です。
同期的に呼べば簡単に使えますし、外部プログラムだけでデバッグもできるので楽です。
一方で、外部プログラムや設定がOSや環境に依存して動かないこともあります。が、他の方法よりもはるかに問題解決が楽です。

    • Wanderlust から bogofilter, bsfilter など
      • 文字列からスパム判定
    • GCCSense
      • C/C++などの補完、定義ジャンプなど
    • rsense
      • Ruby型推論による補完、定義ジャンプなど
      • 別にサーバーが動く
    • flymake
      • 非同期で実行、通信はしてなくて結果を受け取るだけ
      • バックグラウンドでコンパイル → エラー表示
    • その他にもたくさんある
パイプ・ソケット

バックグラウンドで外部プログラムを起動し、パイプやソケットなどで通信する方法です。
非同期プログラミングが大変ですが、常に起動しているため応答の性能も良く、また外部プログラム側も状態を持つことができるので、出来ることが大幅に増えます。

(2011/12/09 TCPソケットのところにパイプ通信が混じっていたのを修正)

D-Bus

D-BusLinux Desktop 上ではかなりデファクトに近いIPC環境になっています。

1対1の通信だけではなく、必要なプロセスにイベント通知させたり、オブジェクトのシリアライズ、サービスのインスペクションなども出来るため、IPCとしてはかなり高機能です。

skype.el でも使っていますが、これを積極的に利用したアプリケーションはほとんど無く、正直 D-BusEmacsでサポートする必要はなかった(もっと他にやることがあった?)のではないかとか思っています。

emacsclient

起動中のEmacsにファイルをオープンさせるような用途が圧倒的ですが、S式を送り込んで評価させたり出来るため、手軽なRPCとして利用できます。

HTTP / Webサービス

最近はインターネット上のサービスと通信する機会が多くなってきました。REST APIに直接アクセスしたり、XML-RPCなどで機能を手軽に呼び出すことが出来ます。

xml-rpc.el のように、標準のurlパッケージを使ってEmacs単独で通信することも出来ますが、るびきちさんも指摘する通り、細かいバグやSSL・プロキシ経由の通信で問題があるので、自前実装する場合は curl, wget を使う方が堅実だと思います。

Webサービスと通信する場合に問題になるのは認証です。一般的に以下の方法があります。

今後、Webサービスとの通信は増えてくると思いますので、この辺りの環境が整備されてくると便利になりそうです。

EPC に続く

これまでのIPC/RPCはプリミティブすぎる(パイプとかD-Busとか)か、特定の目的のための専用スタック(pymacsとかel4rとか)なので、別のプログラムを気軽に呼び出して使えるような、汎用のRPCスタックがありません。

例えば、EmacsはDBに直接接続することが出来ませんが、DBに接続して自由にSQLを発行してデータをやり取りできると、大変夢が広がります。さらに、これがPythonRubyなどの専用スタックではなく、インタフェースは固定のまま、自分の好きな言語で拡張できると大変素敵です。

ということで、現在 EPC(Emacs Procedure Call)なるものをつくっています。YAPC Asia 2011の時にデモしたような、EmacsからPerlを自由に呼んでやりたい放題なことを実現できるようにする予定です。

Emacs - Perl 間はだいたい動くのですが、いきなり CPAN に上げる前に PrePAN でレビューをお願いしたいと思っています。

プロトコルの詳細などのドキュメントが固まり次第、お知らせする予定です。

おわり

本当にすみません。Advent Calendar は小粒なネタで毎日続けることが重要だと思いました。来年も参加するときは気をつけようと思います。

やっぱり1時間ぐらいかかりました。

*1:ちなみに、自分も Emacs 自体が単純に肥大化していくことにはちょっと違和感があります。単純に機能が増えるよりも、パッケージや汎用的な通信手段で簡単に拡張できることに集中したほうがいいと思っています。

Nginx Inside Memo (カーネル/VM Advent Calendar)

この記事は http://atnd.org/events/21910 のために書かれたものです。
一つ前の5日目は @sora_h さんの钱柜娱乐_钱柜娱乐平台_钱柜娱乐777(唯一)官方网站でした。明日は、 @master_q さんの予定です。

nginx の内部の仕組みやモジュールの作り方について簡単に紹介してみます。

最近 nginx のコードを読んでハックを試みました。 nginx のコードについてはほとんどドキュメントがなく、日本語でも見かけなかったため(もし書いている人がいたらすみません)、後から続く人にとって多少でも参考になれるようメモをまとめました。(2011/12/06 デバッグについて少し追記)

全部読みきったわけではないので、コードを読む上でのとりかかりぐらいの情報です。もし、間違いがあれば教えてください。
このメモは stable version である Nginx-1.0.10 のコードを対象にしています。

参考情報

  • Joshua Zhu's Blog, Nginx Internals (Slides & Video)
    • 英語と中国語ですが、大変参考になりました。結構読んだ後に見つけたのですが、プレゼンのスライドなので読まないと分からないことが多かったです。

全体アーキテクチャ

各地で説明されているように、クライアントのリクエストをさばくWorkerプロセスと、Workerプロセスを管理して、全体の起動や終了などを管理するMasterプロセスからなる。




WorkerとMaster

WorkerプロセスとMasterプロセスは、UNIXシグナルや共有メモリ、ドメインソケットなど(環境によって違う)で通信する。

各Workerはシングルプロセス*1のシンプルなイベントドリブンな設計になっている。




イベントドリブン動作イメージ (ngx_process_events_and_timers関数)

nginxが受け取るイベントには、ネットワークのソケットの接続開始・終了や、読み出し・書き込み可能状態の変更、シグナルなどの通知などがある。ひとつのプロセスでIOを多重化し、可能な限り処理を非同期に行うことで、各地で評判の高いスループットが実現している。OS(FreeBSDとか)によってはファイルのIOも非同期に行うことができる。

各OSや実行環境でのイベント操作の抽象化のために ngx_event_actions_t という構造体が定義されている。この構造体のメンバにイベント操作の関数ポインタ入っている。




ngx_event_actions_t

個別のイベントについては ngx_event_t でイベントの情報と操作がセットで定義されていて、各地の実装で実際のイベント情報が生成される。

コードレイアウトと起動

コードのレイアウトはシンプルで直感的。見たまま。

  • src
    • core
    • event
    • http
    • mail
    • misc
    • os/unix

エントリポイントは /src/core/nginx.c の main 関数。内容はごく普通のデーモンサーバー。

  • 処理流れ
    • 引数処理
    • デーモン化
    • 環境変数設定して worker フォーク
    • 各モジュール初期化
    • メインループ突入

この中で cycle という単語がよく出てくる。プロセスサイクルの略だと思われる。大体 worker/master プロセスのメインの処理やデータを表す感じ。




ngx_cycle_t の主なメンバ

高速化の努力

全部読んでないけど、なるほどと思った。

イベントドリブン

全体の設計が徹底的にイベントドリブン。非同期に出来るものであれば、何でもこのアーキテクチャに乗って回すことが出来る。後述のモジュール実装でも出てくる。

独自メモリアロケーション

イベントオブジェクトやバッファなど、各地で細かくメモリの割り当て・開放が行われるので、多分かなり効果ありそう。

Rope的バッファ

バッファをリンクリストで連結。イベントドリブンな仕組みと相性が良さそう。

その他自前ライブラリ
  • 文字列
  • キュー、ハッシュ、木(赤黒木、Radix木)
  • 時間計算

やっぱり自前だと速いのかもしれない。

HTTP処理

処理フェーズ状態遷移

HTTPの細かい処理を状態遷移マシンで抽象化。

  • 状態一覧 (ngx_http_init_phase_handlers 周辺で定義)

各状態でやるべき処理が明らかになって見通しが良い。ただし、状態遷移マシンの実装はハック的。

フィルター

よくある出力の抽象化。リンクリストで実装。
設定で柔軟に組み合わせられる。

モジュール

各パーツをモジュールという入れ物に入れて、統一的に扱う仕組み。(ここは抽象度が高くて範囲が広く、いたる所 void* だらけで完全に把握できてない。)

ngx_xxx_module_t が各モジュールごとにあり、そこから各モジュールの設定ファイルの解釈、状態(コンテキスト)にアクセスできる。




主なモジュール構造体 ngx_module_t のメンバ(未完成。。。)

設定ファイルの解釈、起動時の初期化、終了時の処理などが統一的に定義されていて、大体どのモジュールも同じような書き方になっている。

モジュールには core, event, http, mail の4種類がある。各モジュールの依存関係の図が欲しい。

モジュール開発について

nginx にはたくさんモジュールがある。が、本家サイトにはモジュール開発の情報がない。

デバッガ

本体やモジュールが落ちる場合はデバッガを使って原因を調査する。Workerを調査する場合は、追跡しやすくするために worker プロセスは1つにしておくほうが良い。

デバッガを使って本体を起動するか、起動中のPIDを調べてアタッチする。Linuxだとgdbやnemiverを使ってやる感じ。
設定ファイルのパースの状況やモジュールの状態がどうなっているかなどがよく分かる。

gdbで起動させる場合は、フォークしたWorkerプロセスをフォローしてもらうために、以下のコマンドを実行しておく。

set follow-fork-mode child

結局やりたかったこと

これまで業務で DRBD などを用いて大容量冗長化ファイルシステムを用いてきたが、ファイル容量のスケールアップが辛く、また運用のコストも高いので、初期コスト低い・簡単運用・スケールアップ可能な分散ファイルシステムを探している。

最近、 MongoDB の分散FSである GridFS に注目している。

nginx-gridfs というモジュールがあり、これを使うと GridFS に直接つなげることが出来る。単純なローカルファイルへのリクエストとのパフォーマンス比較ではもちろん圧倒的に負ける。

しかしながら、他の分散ファイルシステムと比較した場合のパフォーマンスについては自明ではない。そこで、手元で修正したり、ハックしながら調査中。

GridFS との接続が同期通信なので、ここを非同期にするとスループットが上がるのではないかと考えている。もしくはキャッシュをうまくやる。でも C で実装するのは辛いので mighttpd など、他のプランも考えたい。

GlusterFS も調査したい。

その他高速Webサーバー

まとめ

つかれました。

*1:マルチスレッドで動かすことも出来るみたい

org-mode のリファレンス検索 anything-orgcard

最近ローカルでの開発作業のメモに org-mode を使っています。単なるテキストファイルの延長から始めているので、そんなに高度な機能は使っていません。

org-modeは機能が多いのでリファレンスのお世話になることが多いのですが、Emacs上のキーバインドを検索できる神ツール descbinds-anything が全く役に立ちません。

こんな風にバインド先の関数名がキー名の関数なので意味がないです!*1



org-キー名になってうれしくない

そこで、 orgmode.org のサイトにあるリファレンスカードのファイルを使って、anythingで機能からキーバインドを探すことのできるツールを作りました。こんな感じになります。



機能で探すことができる

やっつけ実装なので選択しても実行はしません。ミニバッファに表示しておくので、自分でキーを入力します。

インストールは load-path に置いて (require 'anything-orgcard) してください。

M-x aoc:anything-orgcard で起動しますので、適当なキーバインドに入れておくといいと思います。手元では、以下のように org-mode のキーマップに入れるようにしています。

(eval-after-load "org"
  '(progn
     (define-key org-mode-map (kbd "M-4") 'aoc:anything-orgcard)
   ))

*1:その場の状況から適切な機能にバインドされるという設計だからなのですが、これはちょっとあんまりです。せっかくキーバインドに統一的な意味を持たせて空気を読む設計にしているのだから、抽象的な動作を表す関数名を付けるべきだったかなと思います。

Gentoo派になりました

8月〜9月ごろからメインマシンの X201s の Ubuntu10.10 が急速に不安定になってきました。原因はよく分かりません。最初、排気口が詰まったことによる熱暴走かと思っていましたが、熱の問題を解決しても状況はあまり改善せず、9月末には1日10回以上再起動するような状況になり、とても生活できない状況になったのでOSを入れ替えることにしました。

11.10までのつなぎとしてGentooでも使ってみようと思い、WDの安いハードディスクを買ってきてGentooを入れてみました。結果、相当よかったので今もそのままGentooで使っています。以下、Gentoo導入のメモです。かなりいいかげんで、細かいところはアドリブでやったのであんまり覚えていませんが、何かの参考になるといいですね。

Gentoo インストール

ブートと展開用ファイルだけが入ったCDを準備。手順をひとつひとつ確認しながらすすめるのでかなり時間がかかった。別環境に2回目インストールしたときは再起動まで1時間ぐらい。練習すれば放置してあるPCをGentooにしてしまうのに10分かからないくらいまで行けるのかも。

Xの起動ではまった。結局、自分で xorg.conf 書いてはダメだということと、 /etc/make.conf に INPUT_DEVICES="evdev" が必要だった。これらの情報は日本語ドキュメントと英語ドキュメントで内容が違っていたので、日本語で概要を掴んで英語ドキュメントで詳細を見る方が確実そう。

カーネルコンパイルが楽。すごい楽。FreeBSD 並。10年以上 Linux をやっていて make menuconfig から make modules_install までこけなかったのは初めて。一方で Ubuntu ではカーネルのビルドに成功したことがほとんど無い。 Ubuntu は独自にカーネルを調整してるらしいので独自コンパイルが難しいのかも。

/etc/fstab の /boot のオプションに noauto が入っているのに気がつかずに /boot の内容が反映されずにしばらく grub ではまってた。

あとはひたすら emerge してもとのUbuntuで出来ていた環境に近づけていくだけ。naota先生の資料が大変役に立ちました。

メインメモリは 6GByte あるので /dev/shm の容量は 5GByte にした。大物 LibreOfficeコンパイルが 3GByte ちょっと使うので多めに設定。

途中、libpngの更新ではまった。soライブラリのシンボリックリンクが足りなかったので自分で足した。何というか、 emerge のエラーが親切というかログも素直なので問題解決がしやすい。

WD の IntelliPark 問題

ディスクアクセスの時に2秒くらい固まるのは気のせいかなと思っていたけど、やっぱり気のせいでなかった。

るびきちさんもはまったこのIntelliPark地雷を踏んでしまった。3000円くらいの安物だったので、よく調べずに買ってしまった。ハードディスクの値段というより、Gentooインストールにかけた時間と労力が悔しかったのでなんとか対応した。

方法は、非推奨ツールのwdidle3を使う方法。

USBメモリDOSブートディスク作成。DOSのブートはいろいろ試したけども、結局 Linux 上の makebootfat を使う方法が一番確実だった。

wdidle3でハードディスクの設定を見てみると4秒アイドルでヘッド退避・回転停止の設定だった。4秒ごとにシステムが2秒固まる設定に殺意を覚えた。すぐにwdidle3/dでタイマーを無効にした。一応普段は固まらなくなったが、バッテリー駆動中に固まる時がある。今後の課題。

ATOK X3 インストール失敗 → ibus/mozc へ

こちらを参考に頑張った。

しかしながら iiim のサーバーが起きない。ダイナミックリンクの問題みたいだけど、2日ぐらいいろいろ頑張ったけどだめだった。あきらめて ibus/mozc にした。全然問題なかった。。。

unzip/file-roller 文字化け

スーツとしては日本語SJISファイル名入りのZIPが扱えないと困る。unzipに自分でパッチするのかと思ったら、unzip の最新 ebuild の mask 外して natspec 入れると解決した。

/etc/portage/package.use
=app-arch/unzip-6.0-r2 natspec

/etc/portage/package.accept_keywords
>=dev-libs/libnatspec-0.2.6 ~amd64
=app-arch/unzip-6.0-r2 ~amd64

GNOME/Compiz

すんなり入った。screenlets もそのまま移行。これでもとのデスクトップ環境がもどった。

そこへ naota 先生から Gentoo の compiz 終了かものニュース。

nvidia とか ati とかのボード持ってないので、メインでサポートは出来ないけど、パッケージを単に追従させるとかだったら出来るかもしれない。あとで desktop-effect overlay をチェックしてみる。

VMwareVirtualBox

VMware Server の放置っぷりに困ってたので、この機会に VirtualBox に移行。設定ファイルとイメージを変換するだけで完了。超快適。今までの VMware の速度はなんだったのか。。。

USB2.0の対応は一応出来てはいるけども、iPadの同期はちょっと不安定。ipadなどの同期はWifi経由か、iOSのアップグレードなどのクリティカルな更新はMacを使ったほうが良さそう。

iPadアドホックルーター

X201sを有線でつないでいるときに、iPadからも無線でアドホック接続してX201sをルータにして外に出たい。
こちらを参考に、dhcpdとiptablesを入れて設定。有線側のDHCPを混乱させないように注意。

dhcpd, iptables 起動は手動で行って、iPadDNSはめんどくさいので 8.8.8.8 とかを使う。

電子辞書 EBView の ebuild

Emacs の lookup でもいいのだけど、ウインドウの構成が崩されるのが嫌なので EBView をよく使っている。

Portageに無かったので最初は自前コンパイルしてたけど、 思い切って ebuild 書いてみた。

実際には app-dicts/eblook の ebuild を参考に書いた。dpkgよりは楽だった。

GNOME-Do の migemoebuild

GNOME-Doもパッチ作って migemo の use いれて ebuild 作成。ebuildはまだ自信がないところがあるので、naota先生のebuild読み講座で勉強中。

おわりに

最近のマシンはスペックが昔より良くなったので、 emerge はそんなにつらくないです。
サーバーも FreeBSD から Gentoo に乗り換えてしまおうかと思ってます。

Gentoo 良いです。 ArchLinux もいいらしいですね。

[perl] YAPC::Asia 2011 参加。LTしました。

以前からすごく参加してみたかったYAPCに、今年やっと行けました。
Perlは言語よりも、やっぱりそのコミュニティにすごい興味がありました。
結果、会場の勢いのあるコミュニティの空気と、素晴らしいスタッフによる運営にとても感動しました。おつかれさまです。ありがとうございました。

こんなイベントを企画運営できると面白いだろううなと思いました。

レポートなどは以下をどうぞ。

LT : Emacs, Perl and Async

Fukuoka.pm に肩を押されてLTにも挑戦してみました。準備があまり間に合わなくて、とてもいっぱいいっぱいでした。最後のデモは準備不足でUIを省略してミニバッファでやっていたのですが、プロジェクターでは画面の下が切れて見えてなかったので、かなり残念な感じになってしまいました。でも(後続の深町さんと同様)Perlの会でLispをやるというネタだったので、そこは達成できましたと思います!!!

この技術(EPC:Emacs Procedure Call)については後ほど形にして記事にすると思います。

しばらく YAPC 熱にうなされそうです。