5月28日に開催された東京Ruby会議11で「アプリケーションへのRubyインタープリターの組み込み」と題して、アプリケーションにRubyを組み込む実装について話しました。
関連リンク:
内容は前述のスライドや発表動画を参照してください。ここでは発表後の質疑応答の内容について補足します。
ruby_sysinit()
は呼ばなくていいの?「ruby_sysinit()
は呼ばなくていいの?」に対する当日の回答は「ruby_sysinit()
の説明は省略した」だったのですが、どうして省略したかを補足します。
Rubyインタプリターを組み込んだアプリケーションの1つであるruby
コマンドの実装(main.c
)を見るとruby_sysinit()
を呼んでいます。そのため、Rubyインタプリターの初期化には必要なAPIにみえます。
しかし、ruby_sysinit()
のコメントには次のように書いています。ざっくり言うとruby
コマンドを初期化するためのもので、Rubyインタプリターを組み込むときはこの関数を呼ぶんじゃなくて自分で初期化してね、と言っています。
Initializes the process for ruby(1).
This function assumes this process is ruby(1) and it has just started. Usually programs that embeds CRuby interpreter should not call this function, and should do their own initialization.
中身を見ると、コマンドライン引数と標準入出力を初期化しています。Windowsで動く場合はもっと初期化しています。
Rubyインタプリターを組み込んだアプリケーション例として紹介したmilter managerではruby_sysinit()
を呼んでいません。コマンドライン引数は自前で処理しています。
ただ、ruby_sysinit()
を呼ばないというのはmilter managerがWindowsをサポートしていないからできることです。Windows用の初期化をしているrb_w32_sysinit()
をチラ見するとわかりますが、この関数がやっている処理のうち、自分に必要な分はどこかを判断してそれらを自前で実装することは難しいでしょう。(Windowsに詳しい人ならそうでもないかもしれません。)
そのため、Windowsでも動くアプリケーションにRubyインタプリターを組み込む場合はruby_sysinit()
を呼ぶのがよいでしょう。(コメントでは呼ぶなと書いていますが。)
RUBY_INIT_STACK
を呼ばなくていいんじゃない?スライドで言うと以下のページの実装についての質問です。
以下のようにしてembedded_init()
内でapplication_main()
を呼ぶようにすればいいんじゃない?という話です。
int
main(void)
{
embedded_ruby_module = dlopen();
embedded_ruby_init = dlsym(embedded_ruby_module, "embedded_init");
embedded_ruby_init(application_main);
}
void
embedded_init(main_func application_main)
{
RUBY_INIT_STACK;
/* ... */
application_main();
}
これに対する回答は「RubyインタプリターとPythonインタプリターを一緒に組み込めないのでやりたいことを実現できない」でした。一緒に組み込む時のイメージは次の通りです。
void
main(void)
{
embedded_ruby_module = dlopen();
embedded_ruby_init = dlsym(embedded_ruby_module, "embedded_init");
embedded_ruby_init(application_main);
/* ↓はアプリケーションのメイン関数の前に実行しないといけないけど、
↑でメイン関数が実行されちゃう。 */
embedded_python_module = dlopen();
embedded_python_init = dlsym(embedded_python_module, "embedded_init");
embedded_python_init(application_main);
}
それに対する別案が「他のインタプリターの初期化もやる関数を渡せば?」でした。こんなイメージです。
void
main(void)
{
embedded_ruby_module = dlopen();
embedded_ruby_init = dlsym(embedded_ruby_module, "embedded_init");
embedded_python_module = dlopen();
embedded_python_init = dlsym(embedded_python_module, "embedded_init");
init_func init_functions[] = {
embedded_python_init,
application_main,
NULL
};
embedded_ruby_init(run_init_functions, init_functions);
}
void
run_init_functions(init_func *init_functions)
{
if (init_functions[0]) {
init_functions[0]();
run_init_functions(init_functions + 1);
} else {
application_main();
}
}
質疑応答はここで時間切れでした。
たしかにこれで動きそうです。
会議後にはこんなアイディアもありました。
そういえば #tkrk11 の RUBY_INIT_STACK の話だけど、アドレスを引数として渡せるようなインターフェイスになれば万事解決なんじゃないかと思いましたまる
— Kazuho Oku (@kazuho) 2016年5月30日
これはどういうことかというと、アプリケーション側ではRUBY_INIT_STACK
に必要なデータを用意するだけにして、実際の呼び出しは別の共有ライブラリーの方に移す、という実装にすればいいんじゃない?ということです。
RUBY_INIT_STACK
はruby_init_stack()
というアドレスを引数にとるAPIを呼び出しているだけなので、このアイディアも実現できます。こんなイメージです。
void
main(void)
{
int address;
embedded_ruby_module = dlopen();
embedded_ruby_init = dlsym(embedded_ruby_module, "embedded_init");
embedded_ruby_init(&address);
application_main();
}
void
embedded_init(int *address)
{
ruby_init_stack((VALUE *)address);
/* ... */
}
milter managerでも実装できます。(アプリケーション本体に手を入れられないケースではこの方法の実装は難しいでしょう。)
「アプリケーションがRUBY_INIT_STACK
を呼ばないといけない問題」は複数の方法で解決できますね!このような場で話をするといろんなアイディアを聞けて便利ですね!みなさんも積極的に実装の話をしてはいかがでしょうか。
RUBY_INIT_STACK
問題が解決したので、複数言語のインタプリターを組み込んだケースのことを想像してみたところ、次はfork
で問題にあたりそうです。Ruby以外の言語が複数のスレッドを作っている場合が問題になりそうです。
質疑応答では触れられませんでしたが、外部のライブラリーが使用しているメモリー量をRuby(mrubyもCRubyも)のGCシステムが知らないために適切なタイミングでGCが動かない問題についても補足します。
発表では「外部からRubyのGCシステムにメモリー使用量を通知する仕組みが必要だと思う」と話しました。
CRubyのTypedData
にはメモリー使用量を返すAPIがありますが、それは使えないはずです。それを使うと、GCシステム側が情報をpullする仕組みになるからです。pullする仕組みにすると、いつpullするの?pullした情報をどうやって管理するの?あたりが大変になりそうです。
通知する仕組みだとこれらの問題を解決できそうです。
ということで、gc-triggerという拡張ライブラリーを作ってみました。このライブラリーは他の拡張ライブラリーに使用メモリー通知用のAPIを提供します。他の拡張ライブラリーがこのAPIを使ってメモリー使用量を通知すると、適切なタイミングでGC.start
を実行します。
本来はCで提供しているAPIを呼んだほうがよいですが、テスト用・説明用にRubyのAPIも用意したので、そっちを使って使い方を説明します。
ここでは、例としてrcairoを使います。rcairoは画像を扱うからです。画像を扱う拡張ライブラリーはRuby管理外のメモリー使用量が大きめなので、この問題に遭遇しやすいケースなのです。
次のコードは1000個画像オブジェクトを生成します。どの画像オブジェクトも参照されていないのですぐにGCで回収できます。
require "cairo"
width = 6000
height = 6000
1000.times do |i|
Cairo::ImageSurface.new(:argb32, width, height)
end
gc-triggerでメモリー使用量の増減を通知するコードは次のようになります。
require "gc-trigger"
require "cairo"
def image_surface_finalizer(size)
lambda do |id|
GCTrigger.update_memory_usage(-size)
end
end
def create_image_surface(width, height)
size = 4 * width * height
surface = Cairo::ImageSurface.new(:argb32, width, height)
GCTrigger.update_memory_usage(size)
ObjectSpace.define_finalizer(surface, image_surface_finalizer(size))
surface
end
width = 6000
height = 6000
1000.times do |i|
create_image_surface(width, height)
end
画像オブジェクトのサイズは、単純化して「1画素のサイズ(4バイト)×縦×横」で計算しています。
それぞれの場合でのメモリー使用量をグラフにすると次のようになります。従来の方はメモリー使用量が安定しませんが、gc-triggerを使った方はメモリー使用量が一定になります。
発表で話したアイディアを動くようにしてみました。これで通じるでしょうか。
発表でも紹介しましたが、OSS Gateワークショップ2016-05-28を東京Ruby会議11と同時開催しました。東京Ruby会議11でポスターを展示していたので、そこではじめて知ったという方も多かったのではないでしょうか。
OSS GateおよびOSS Gateワークショップを紹介していて痛感したことは「口頭での説明でないとちゃんと伝えられない」ということでした。たとえば、「私はすごくないのでメンターできないです。。。」に対して口頭では伝わるように回答できますが、資料では伝わらないです。これだとスケールしないので口頭での説明でなくても伝わる資料の作成が必要です。
ポスターを展示しての紹介は想像以上に効果があり、OSS Gateワークショップ2016-07-30の登録者が10人以上増えました。東京Ruby会議11実行委員会に協力してもらえて本当によかったです。ありがとうございました。
ちなみに、想像以上に登録者が増えたおかげでメンターが足りなそうです。OSS開発に参加したことがある人(技術力不問)でOSSの開発に参加する人が増えるといいなぁと思う人は「メンター」としてOSS Gateワークショップ2016-07-30に登録してください。すでに定員オーバーでキャンセル待ちでの登録になりますが、気にしないでください。キャンセル待ち扱いでも気にせずに当日来てください。入れます。
5月28日に開催された東京Ruby会議11での発表とOSS Gateワークショップ2016-05-28について補足しました。
東京Ruby会議11の参加者アンケートはまだ回答を受け付けているので参加した方はぜひ回答してください。
6月9日に「MySQLとPostgreSQLと日本語全文検索2」というイベントを開催しました。今回もDMM.comラボさんに会場を提供してもらいました。当日のツイートはMySQLとPostgreSQLと日本語全文検索2 - Togetterまとめにまとまっています。
2月9日に開催した1回目のイベントでは次の2つのことについて紹介しました。
2回目となる今回は次のことについて紹介しました。
ここではMroonga(むるんが)とPGroonga(ぴーじーるんが)の1歩進んだ使い方について少し紹介します。
関連リンク:
今回の発表ではMroongaとPGroongaのオススメの使い方およびレプリケーションまわりについて紹介しました。
Mroongaは次のように使うのがオススメです。
IN BOOLEAN MODE
と*D+
プラグマを使う詳細はスライドに書いていますが、以下のようなSQLで使うということです。
CREATE TABLE items (
title text,
FULLTEXT INDEX (title)
-- ↑をCOMMENTでカスタマイズできるが
-- まずはデフォルトで使うのがオススメ
) ENGINE=Mroonga
DEFAULT CHARSET=utf8mb4;
SELECT * FROM items
WHERE MATCH (title)
-- ↓ *D+プラグマを使ってデフォルトでANDにする
AGAINST ('*D+ 激安 人気' IN BOOLEAN MODE);
-- ↑ ブーリアンモードを使う
-- Web検索エンジンのような使い勝手になる
また、ストレージモードを使うための工夫としてスレーブだけをMroongaにしてレプリケーションする構成とその設定方法も紹介しました。ストレージモードではトランザクションを使えませんが、この構成にするとトランザクションを使わなくてもよくなります。
PGroongaは次のように使うのがオススメです。
search_path
を設定した上で@@
演算子を使う詳細はスライドに書いていますが、以下のようなSQLで使うということです。
CREATE TABLE items (
-- ↓主キーを用意する
id integer PRIMARY KEY,
title text,
);
CREATE INDEX pgroonga_items_index
ON items
-- ↓ インデックスに主キーを含める
USING pgroonga (id, title);
-- パラメーターは指定せずデフォルトで使う
ALTER DATABASE db1
-- ↓ search_pathにpgroongaスキーマを入れ、pg_catalogスキーマよりも先にする
SET search_path TO "$user",public,pgroonga,pg_catalog;
SELECT *,
-- ↓検索スコアーを取得(このために主キーが必要)
pgroonga.score(items) AS score
FROM items
-- ↓ @@演算子を使う
WHERE title @@ "激安 人気"
ORDER BY score DESC;
PGroongaのレプリケーションについても説明しました。
PGroongaはPostgreSQL標準のレプリケーション機能を使えませんが、pglogicalと組み合わせたレプリケーションは使えます。なお、PostgreSQL 9.6以降ではPostgreSQL標準のレプリケーション機能を使えるようにPGroongaの開発を進めています。
PostgreSQLでの日本語全文検索を実現する方法としてPGroongaに興味のある方は、PGroongaをWindows用のサーバーログ管理ソフトであるVVAULT AUDITでの利用事例「VVAULT AUDITにおけるPGROONGAの利用」も合わせて参照してください。
6月9日に開催された「MySQLとPostgreSQLと日本語全文検索2」でのMroongaとPGroongaの発表内容について紹介しました。
MySQL・PostgreSQLで日本語全文検索したい場合はぜひこれらを検討してみてください。
誰でもSSL/TLS証明書を無償で取得できることを標榜しているサービスとして、Let's Encryptがあります。2016年4月に正式サービスの提供が開始されたことから、実際に試してみた人も多いことでしょう。
今回は、Let's Encryptをcloudapp.azure.comで使おうとするときのハマりポイントを紹介します。
2016/06/17 Kazuhiro NISHIYAMAさんに記事内容の誤りをletsencrypt の Rate Limit についてというエントリで指摘していただきました。ありがとうございます。Rate Limitの回避方法の言及が不十分だった箇所、適用例外についての記述が誤っている箇所、ステージング環境を利用する場合のコマンドラインオプションがある旨の3点について記述をあらためました。
cloudapp.azure.comはAzureで仮想マシンを立ちあげて、公開するときの既定のドメインです。 次のようなルールでDNSを設定できます。このうちロケーションは東日本リージョンであればjapaneastになります。
お使いのディストリビューションにcertbotがあればそれを、ない場合や最新のcertbotが利用したければ、certbotのサイトからクライアントをダウンロードします。
$ wget https://dl.eff.org/certbot-auto
$ chmod a+x certbot-auto
あとは次のようにダウンロードしたスクリプトを実行するだけ、と思うかも知れません。
$ ./certbot-auto
Azure上とはいえ、Linuxディストリビューションを使う分には何も問題なさそうではありますが、ここにハマりポイントがあります。
実際に実行してみると、次のようなエラーに遭遇しました。(2016年6月時点)
There were too many requests of a given type :: Error creating new cert :: Too many certificates already issued for: azure.com
Please see the logfiles in /var/log/letsencrypt for more details.
エラーメッセージで検索してみると、このエラーに遭遇している人が他にもいることがわかりました。
この投稿からたどれるリンクには、Let's Encryptの制限に関するものがありました。
細かな制限はいろいろありますが、どうやら特定ドメインに対しては、週に20個のSSL/TLSサーバー証明書しか発行しないようです。そのため*.cloudapp.azure.comなどのように、皆がこぞってSSL/TLSサーバー証明書を取得しに行くような場合、見事に制限にひっかかってしまいます。
これを回避するには別途独自にドメインを取得したり、Dynamic DNSやIaaSなどを利用している場合はサービスプロバイダーに対応(後述)してもらったりしなければなりません。
2016/06/17訂正:DDNSやIaaSについての言及を追加。
*.cloudapp.azure.comで上記の制限がかかるとなると、大分厳しいように思えます。
申請すれば、制限を緩和してもらえるというような情報もありましたが、本当にそういったものはあるのでしょうか。
実際に疑問に思って質問しているケースがありました。
LE Tech AdvisorであるJacob氏の発言を読む限りでは、残念ながら個別に申請して例外扱いしてもらう方法はなさそうです。
ただし、後から教えてもらったのですが、DDNS Rate LimitedやToo many certificates already issued for dynamic dns provider sub domainで言及されているように、次の条件を満たせば例外扱いされるようです。
public_suffix_list.dat
へazure.comを追加する *1ただし、上記の条件を満たすのはすぐにというわけにはいきません。
2016/06/17訂正:個別に申請する方法がない点、サービスプロバイダーに対応してもらう方法を追記。
ここまで厳しいのは、あくまで新規取得の場合です。 更新する場合や、再取得の場合は影響はないようです。
正式なSSL/TLS証明書じゃなくても、certbotを試してみたい、というのであればステージング環境があります。
こちらは、ステージング環境だけあって、制限がゆるいです。*.cloudapp.azure.comでも問題なくSSL/TLSサーバー証明書を取得できます。
その場合には次のコマンドラインオプションを使います。
--test-cert, --staging
Use the staging server to obtain test (invalid) certs;
equivalent to --server https://acme-
staging.api.letsencrypt.org/directory (default: False)
2016/06/17訂正:パッチをあてなくてもコマンドラインオプションでできるので、そちらを紹介するように訂正。
この方法で取得した場合、認証局が「Fake LE Intermediate X1」になります。 正式版の「Let's Encrypt Authority X3」ではありません。アクセスするにはブラウザのセキュリティ例外などの設定が必要です。
今回は、Let's Encryptを*.cloudapp.azure.comの配下で取得しようとすると、制限にひっかかってしまうというハマりポイントの事例を紹介しました。 ただし、この情報は2016年6月時点のものなので今後改善されるかも知れません。
*1 実際にazurewebsites.netやazure-mobile.net、cloudapp.netは登録されています。
クリアコードは「OSS開発支援サービス」の提供を開始します。
「OSS開発支援サービス」はその名の通り「お客さんがOSSを開発すること・お客さんがOSSの開発に参加することを支援するサービス」です。以下は支援の仕方の一例です。実際にこのサービスを提供するときは、以下の例をそのまま全部提供するのではなく、お客さんの現状にあった適切な支援はなにかを考えるところからお客さんと一緒に取り組みます。
企業としてOSSを開発すること・OSSの開発に参加することにはメリットがあります。たとえば次のようなメリットです。
もちろん、すべての企業にとってこのようなメリットがあるわけではありません。このようなメリットがある企業もあるということです。そして、メリットがある企業を支援するのがこの「OSS開発支援サービス」です。
企業としてOSSの開発に取り組みたいという場合はお問い合わせください。どのように取り組むのがよさそうか、どんな支援が適切そうかなどを相談するところから一緒に取り組んでいきましょう。
サービスの紹介は以上です。以下はどうして「OSS開発支援サービス」を提供することにしたかについての話になります。興味があれば続きもご覧ください。
実はクリアコードはすでに何年も前から同様のサービスを提供しています。
例として「お客さんが開発しているOSSの開発にクリアコードが参加する」の事例をいくつか紹介します。
Senna(Groongaの前身の全文検索エンジン)は未来検索ブラジルさんが開発しているOSSです。Sennaの開発に参加し始めたのは2008年です。
Hatoholはミラクル・リナックスさんが開発しているOSSです。Hatoholの開発に参加し始めたのは2013年です。
FluentdはTreasure Dataさんが開発しているOSSです。Fluentdの開発に参加し始めたのは2015年です。
今回「OSS開発支援サービス」という名前をつけてサービス提供を始めた理由は次の2つです。
前者に関してはこのようにブログを書いたり、問い合わせフォームに「OSS開発支援について」という項目を追加したりしています。飲食店で外にメニューがあると入るかどうかを決めやすい、というような感じです。
後者に関しては少し詳しく説明する必要があります。
OSS Gateは「OSS開発に参加する人を増やす取り組み」です。現在はそのための1つの方法としてOSS Gate ワークショップだけを開催しています。
OSS Gateワークショップは「OSS開発に参加したことがない人」向けに作られたものです。実際、そのような人たちがOSS Gateワークショップに参加し、OSS開発に参加するきっかけになったと言っています。
OSS Gateは、趣味であろうと仕事であろうと用途や目的は関係なくOSS開発に参加する人を増やしたいという取り組みです。しかし、仕事でOSS開発に参加する人を増やす、という観点ではリーチしにくい部分があります。そこをクリアコードでフォローできないかと考えています。
たとえば、OSS Gateワークショップの開催日と開催人数です。
現在、OSS Gateワークショップは奇数月の最終土曜日に開催しています。休日なので企業としてOSS開発に参加したいという場合に利用することは難しいです。ワークショップの内容はだれでも自由に使えるライセンスで公開しているので、自分たちで開催することもできますが、まだOSS開発に参加している人がいない場合は難しいでしょう。
OSS Gateワークショップの参加人数は徐々に増えていて、次回の7月30日開催分にはキャンセル待ちがでています。企業としてOSS開発に参加したい場合、企業内の開発者が一緒にワークショップに参加したくなりますが、多くの人数が一気に同日のワークショップに参加することは難しい状況です。
OSS Gateワークショップを業務時間中にその企業向けに開催するサービスをクリアコードが提供することで、企業での開催を推進します。これによりOSS Gateでリーチしにくい部分をフォローします。
OSS GateはまだOSS Gateワークショップだけに注力している段階です。OSS Gateワークショップに参加した人が、その後のOSS開発に関して相談できるなにかを提供したいね、という話もでていますが、まだその実現には至っていません。(興味のある方はOSS Gateのチャットで相談しましょう!)
企業でOSS Gateワークショップをやった場合も「OSS開発に参加する最初の一歩を踏み出した後のフォロー」が必要になることがあります。その場合はやはり業務時間にフォローして欲しくなるのですが、OSS Gateではまだそこをフォローできません。OSS Gateでフォローできるようになっても、企業内で秘密の情報を交えながらフォローした方がいいケースはフォローできません。そのようなケースはクリアコードでフォローします。
先日、Speeeさん向けにOSS Gateワークショップを開催しました。詳細はSpeeeさんのレポートとOSS Gateにあるレポートを参照してください。
このとき、「OSS Gateワークショップの次の一歩のサポートもあるとうれしい」というフィードバックをもらいました。おそらくSpeeeさん以外もそう思うでしょう。クリアコードは以前からそのあたりをフォローするサービスを提供しています。そのサービスでOSS開発に参加する人が増えることはクリアコードとしてもOSS Gateとしてもうれしいことです。必要な人たちにこのサービスが届くように「OSS開発支援サービス」としてわかりやすくしました。
長くなりましたが、「OSS開発支援サービス」という名前をつけてサービス提供を始めた理由である次の2つについて補足しました。
「OSS開発支援サービス」の提供を開始したことを宣言しました。
このサービスを利用したい、自分たちも同様のサービスを提供したい(クリアコードとしてもOSS GateとしてもOSSの開発に参加する人が増えることはうれしいことなので、それをやりたいという企業を歓迎します)など「OSS開発支援サービス」に興味のある方はぜひお問い合わせください。相談しましょう。