FirefoxをはじめとしたMozilla製品の技術サポートを提供していますが、希に、自社製品の一部としてFirefoxを顧客向けに配布したいというご相談を頂くことがあります。結論から先に言うと、このような場合にはFirefoxをソースから独自にビルドする必要があります。Installerというソフトウェアを開発・公開しており、これを使うと、「自組織向けにカスタマイズされたFirefox」と呼べるひとそろいの環境を手軽に展開することができます。Installerを使ったカスタマイズのご案内をしておりますが、このようなケースは「自組織内で使うための限定的な再配布」にあたるため、Mozilla公式で配布されているFirefoxのインストーラを使用しても問題はありません。Installerベースで作成して提供したいというご相談を頂く事があります。このようなケースは「自組織外への再配布」となるため、ライセンスの都合上、そこに「Firefox」というブランドが付与された製品を同梱することはできません。そこで登場するのが「ノーブランド版」です。「Firefox」というブランドが付与されていない「Firefoxのソースに基づいた別製品」を配布する分には、上記のような制約はありません。そのようなバイナリは一般公開されていないため、Firefoxのソースを使って自分で作る必要があります。以上が、Firefoxの独自ビルド作成の動機ということになります。(なので、正確にはこの記事の趣旨は「Firefoxのソースに基づいた別製品のビルド方法」と言うことになります。Instructionsから辿ることができます。buildを見ると必要な情報が手に入ります。以下の手順も、基本的にはこれらのページから得られる情報に基づいています。ビルド環境の構築Windows上でFirefoxをビルドするために必要なソフトウェアは、すべて無料で入手できます。Communityインストール時にオプション項目の指定が可能ですが、すべてのオプションを無効にして差し支えありません。SDKが特にファイルサイズが大きく、ダウンロードにはそれなりに時間がかかります。ソースの入手Firefoxのソースコードはスナップショットのtarballとしても入手できますが、ここではMercurialのリポジトリをcloneする方法で手順を解説します。環境構築の際に導入したMozillaBuildはMinGWのコマンドラインコンソールを含んでおり、ビルドの作業はこのコンソールから行います。ダブルクリックしてコンソールを開きましょう。コンソールを開いたら、リポジトリをcloneするためのディレクトリを用意します。/c/mozilla-source次に、Firefoxのリポジトリをcloneします。Firefoxやその他のMozilla製品のリポジトリはhttps://hg.mozilla.org/で公開されています。Nightlyの最新版やESR版など、どのバージョンをビルドしたいかによってどのリポジトリをcloneするかが変わります。ESRを含むリリース版のFirefoxのソースはreleases/以下にあり、ESRの場合はreleases/mozilla-esr38のようにバージョン番号を含むパスのリポジトリ、一般向けのリリース版の場合はreleases/mozilla-releaseのリポジトリです。38.2.1ESRをビルドしたいので、releases/mozilla-esr38をcloneします。https://hg.mozilla.org/releases/mozilla-esr38リポジトリの規模が数GBと大きいので、cloneにはそれなりの時間がかかります。Firefox本体のリポジトリをcloneできたら、次は言語リソースのリポジトリをcloneします。言語リソースのリポジトリは言語ごとに分かれており、ESRを含むリリース版のFirefoxの日本語用言語リソースはreleases/l10n/mozilla-release/jaです。jacloneする際に、hgコマンドの3番目の引数としてディレクトリ名を、明示的に言語コード名のjaと指定します。これは、MozillaBuildでのビルド時には言語リソースが言語コード名に基づいて検索されるためです。ビルドの準備cloneしたリポジトリは、次のリリースに向けての作業が進行している状態になっています。38.2.1ESRのように特定のバージョンをビルドするには、タグに基づいてそのリビジョンのソースをチェックアウトする必要があります。リポジトリに現在含まれている各リリースのタグは、以下の要領で確認できます。38.2.1ESRのタグはFIREFOX_38_2_1esr_RELEASEです。Firefox本体のリポジトリと言語リソースのリポジトリでタグ名は共通なので、それぞれチェックアウトします。$TARGET次に、ビルドオプションを指定します。ビルドオプションの指定はFirefox本体のリポジトリのトップレベルのディレクトリに.mozconfigという名前のテキストファイルとして保存します。今回の例ではc/mozilla-source/mozilla-esr38/.mozconfigの位置です。38.2.1ESRでabout:buildconfigを開いて確認できるビルドオプション公式のビルドで使用している設定を参考にできます。Firefoxではabout:buildconfigを開くとそのバイナリの元になったビルドオプションの一覧を見ることができますが、実際の.mozconfigには、それらに加えて環境変数の指定なども必要です。Studioのランタイムライブラリがインストーラに含まれないなど、一般ユーザの環境で使用するには不都合があるビルド結果となってしまいます。38.2.1ESRなので、オンラインで例を見る場合はmozilla-esr38リポジトリの物を参照して下さい)にファイルがあります。Studioのインストール先パスなどが実際の物と異なっています。38.2.1ESRのabout:buildconfigに列挙されているオプション群から必要でない物を除外し、ローカライズに必要なオプションを足した物が、以下の例です(行頭の#はコメントアウトです)。--with-l10n-base=/c/mozilla-source--with-l10n-baseで指定するパスは、言語リソースのリポジトリのパスではなく、その1つ上位のディレクトリのパスです。言語リソースがあることを期待します。準備ができたら、いよいよビルドです。buildを実行すればビルドが始まります。ビルドにどの程度時間がかかったかを計測するために、timeコマンドを使うことをお薦めします。buildビルドに使用するマシンの性能にもよりますが、弊社で使用した環境は以下の通りで、ビルド時間はおよそ1時間でした。64bitビルドが完了したら、本当に動作するか確かめてみましょう。以下のコマンドを実行すると、ビルドされたFirefoxが起動します。アプリケーション名は「Nightly」になっているはずです。run正しく動作することを確認できたら、インストーラを作成しましょう。これは以下のコマンドで行えます。installerできあがったインストーラは、カレントディレクトリから見てobj-i686-pc-mingw32/dist/install/sea/の位置、フルパスではC:\mozilla-source\mozilla-esr38\obj-i686-pc-mingw32\dist\install\sea\の位置に出力されます。ノーブランド化する実は、この段階ではまだ「ノーブランド版」とは言いにくい状態です。いうのも、言語リソース内で「Firefox」と決め打ちされている箇所があるため、このままではUI上のあちこちで「Firefox」という名前を目にすることになってしまうからです。いうわけで、ソース中に残っている「Firefox」という文字列を置換してみましょう。これはsedを使って簡単に行えます。(なお、MinGWに同梱されているsedで-iオプションを使うと、ファイルのパーミッションが意図せず変更されてしまうという問題が起こります。回避するにはで解説されている手順を参考に、あらかじめC:\mozilla-sourceおよびその配下のすべてのファイルについてUsersにフルコントロールの権限を与えておきましょう。Browser」、省略した短い名前を「Browser」にすると仮定すると、一括置換の操作は以下のように行えます。done短い名前には空白文字を含められない事に注意が必要です。Firefox」と書かれている箇所の置換は容易ですが、「Nightly」と書かれている部分はそれが長い名前なのか短い名前なのかが場合によってまちまちなので、ここでは確実に短い名前と断定できる部分(例えばMOZ_APP_DISPLAYNAMEやbrandShorterNameの値など)は短い名前にして、それ以外は長い名前にしています。grepとすると現在のリビジョンではなく履歴に対する検索となるため、リビジョンによっては検索漏れが出てしまいます。ここでは通常のgrepを使っています。この方法は「Nightly」または「Firefox」という文字列を無差別に置換しているため、文脈的におかしな箇所が残っていることがあります。米国およびその他の国における商標です。」のような記述になっているかも知れませんので、こういった例は適宜手作業で自然な内容に書き直す必要があります。その一例として、「about:rights」の内容が挙げられます。about:rightsには、使用者自身の権利に関する情報、具体的にはプライバシーポリシーであったり、ノーブランド版のFirefoxと組み合わせて使用するWebサービスそのものの利用規約であったりが表示される事になっています。この情報は初期状態ではプレースホルダ的な内容になっているため、自分で書き換える必要があります。38ESRでも同じ位置にあります)を直接書き換えるか、そこで参照しているエンティティを定義している言語リソースの項目を書き換えます。特定言語のビルドしか提供しないのであれば、aboutRights-unbranded.xhtmlだけを直接書き換えれば十分でしょう。文章に日本語などの非ASCII文字を含める場合は、ファイルの文字エンコーディングはUTF-8にする必要があります。画像の差し替えも行えます。ブランドに関する画像はFirefox本体のリポジトリのbrowser/branding/nightly以下にある物が使われるので、必要であればこれらの画像も差し替えておきます。これらの手順で変更できるのは表示上のアプリケーション名のみで、この状態のノーブランド版Firefoxを通常のFirefoxと同時に運用する際には、プロファイルを明示的に分ける必要があるなど、いくつか気をつけないといけない点があります。いった点を気にしなくてもよくするためには、内部的なアプリケーション名まで含めたノーブランド化が必要となります。ソース上でのノーブランド化を終えたら、もう一度ビルドします。configureを手動で実行して、configureをやり直す必要があります。configureの条件が変わるせいで、ここまででビルド済みのバイナリは大部分が使えなくなるため、ビルド時間は新規にビルドした時と同程度かかります。前述の弊社環境では45分ほどでした。言語リソースの不自然な箇所を直しただけだったり、画像を差し替えただけだったりの場合は、configureし直す必要はありません。installer)今度は変更が影響する部分だけ再ビルドされるため、全体のビルドに比べて短時間で完了します。前述の弊社環境では7分ほどでした。ノーブランド版のベースにするバージョンを更新するノーブランド版は、1回作成して提供すれば終わりという性質の物ではありません。38.4ESRといった要領で、その後のセキュリティアップデートにも追従していく必要があります。セキュリティアップデートの反映は、以下の手順で行います。ノーブランド化のための変更を元に戻す。リポジトリを最新の状態に更新する。セキュリティアップデート版のタグをチェックアウトする。ノーブランド化のための変更を再反映する。shelveを使えます。shelveを有効化しておいて下さい。popによく似た機能です。unshelveすると退避していた変更をもう一度反映できます。shelveで変更を取り消しておく必要があります。pullリポジトリが最新の状態に更新されたら、新しいリリースのタグ名を調べてチェックアウトします。38.3.0ESRであれば、恐らくタグ名はFIREFOX_38_3_0esr_RELEASEになっているはずです。38.3.0ESRをチェックアウトする例です。shelveで退避しておいたノーブランド化のための変更を書き戻します。unshelveの結果が期待通りになっていないかもしれません。そのような場合は改めて、ノーブランド化の作業をやり直して下さい。Firefox本体を更新したら、同様の手順で言語リソースも更新します。unshelveその後、インストーラを作り直します。installer)直前までできていたビルドが失敗するようになってしまったときは?変更があった箇所だけの差分ビルドは、必要最小限の時間でビルドを完了できるため手軽ですが、変更点が期待通りに検出されなかった場合などに、configureやビルドの段階でエラーになることがあります。そのような時は、ビルド結果が格納されているディレクトリ(obj-i686-pc-mingw32)を削除してもう一度完全なビルドをやり直すとうまくいきます。38.2.1ESRをノーブランド化した物を独自にビルドするための手順を簡単に解説しました。ちなみに、ここからさらにソースコードに手を加えれば、より自由度の高いカスタマイズが可能になりますが、そうすると元のFirefoxのソースからかけ離れていくため、以後のセキュリティアップデートへの追従が非常に大変な事になります。基本的にはソースコードに対して行う変更は最小限に留めて、カスタマイズはアドオンや設定ファイルの組み合わせで行うようにすると良いでしょう。X、Windowsなど複数のプラットフォームに対応するプログラムをC/C++で開発する場合、GCCを利用すると便利です。例に挙げたすべてのプラットフォームに対応している上に、クロスコンパイルもできるからです。Windows向けバイナリーのクロスコンパイルは便利です。開発環境(たとえばGNU/Linux)上でビルドできるので、環境を切り替える手間が減って開発効率があがります。クロスコンパイル関連のことについてはここでは説明しません。GNU/LinuxでWindows用バイナリをビルドする方法や近日中にまとめる予定のCygwinのSSHサーバーに公開鍵認証でログインする方法を参照してください。これに該当するソフトウェアに全文検索エンジンのGroongaがあります。Groongaは全文検索サーバーとしても動作します。サーバーは長期間動作するプロセスで、状況を確認したり問題を調査するためにログを出力する機能は必須です。ログは確認しやすくないと活用されなくなってしまうため、運用しているシステムとうまく連携するのがベターです。UNIX系のシステムなら/var/log/以下のファイルに出力したりSyslogに出力したり、Windowsならイベントログに出力したり、という具合です。システムと連携する場合、どうしてもその環境特有のコードになってしまいます。ここでは、Windowsでイベントログにログを出力する機能を実現する方法を説明します。Studioは使わずにmingw-w64だけで実現するという制限をつけることにします。理由は、そうしないとクロスコンパイルできないからです。以下Windowsのイベントログについて調べた結果を情報源(MSDN)付きで説明していますが、説明している人はWindowsに詳しくない人なので誤った理解を説明しているかもしれません。疑問に思った箇所は情報源を参照してください。API具体的な実現方法を説明する前にイベントログ関連のAPIを説明します。Windowsイベントを出力するには次のどちらかのAPIを使います。2000用に開発されたAPIです。APIの上位互換になっています。APIはログを出力するだけならコードを書くだけで別途ツールは必要ありません。Compilerの略)というツールが必要です。キレイに表示しなくてもよいならツールは必要ありません。APIは別途ツールが必要です。APIではログを出力するアプリケーションのことをプロバイダーと呼んでいます。プロバイダーには2種類あります。provider)それぞれログを出力するために使うAPIも違いますし、使うツールも違います。ログを出力することを「イベントを書く」と呼んでいるので、以降ではそのように書いている箇所があります。compilerの略。FormatはMOFと略される。)というツールが必要です。APIでキレイにログを表示するために使うツールと同じ)というツールを使います。APIしかありません。そのままではログはキレイに表示されません。この回避策については後で触れます。Studioではありません。APIを使ったログ出力の実装方法を説明します。ログをキレイに表示するための回避策については次のセクションで説明します。APIでログを出力するために使うAPIは次の3つです。RegisterEventSource()ReportEvent()DeregisterEventSource()RegisterEventSource()でReportEvent()を実行するときに使うハンドルを取得して、ReportEvent()でログを出力します。ログは何回出力してもかまいません。ログを出力し終わったらDeregisterEventSource()でRegisterEventSource()で取得したハンドルを解放するという流れです。コードでいうと次のような流れです。*/);DeregisterEventSource(event_source);このコードを動くようにするために最低限決めなければいけないことは次のことです。イベントソース名ログの種類メッセージイベントソース名はアプリケーション名にするとよいでしょう。Groongaの場合は"Groonga"です。情報(知っておくといいよ、という情報。どのポート番号で起動した、とか。選ぶことになるでしょう。Groongaではこの3つのどれかだけを使っています。メッセージはログに出力するメッセージです。次のように決めたとします。found"」このときのコードは次のようになります。ReportEvent()の引数が多いですが、ほとんどの引数は0やNULLといった値を使うことができます。0;}次のようにmingw-w64でクロスコンパイルします。log.cこのlog.exeをWindowsにコピーして動かすとWindowsのイベントログに出力できます。ビューアーで確認すると次のようになります。found」というメッセージの前に「ソース"MyApp"からの...」という説明が書いています。これが「キレイにログが表示されない」ということです。これの回避方法は次のセクションで説明します。WevtUtil.exeというツールを使うとコマンドラインからでも確認できます。(XMLは見やすいように整形していますが実際は1行です。</EventData></Event>オプションの説明は省略します。/?」で確認できるのでそちらを参照してください。ポイントは「/f:xml」です。これはXMLで出力するという意味です。found」)を確認できません。これは「キレイにログが表示されない」と関係しているのですが、それの説明も次のセクションに回します。ここで示したコードと引数で「イベントソース名」、「ログの種類」、「メッセージ」を出力できるコードをkou/windows-event-log-loggerに置いてあるので参考にしてください。キレイにログを表示する方法Windowsのイベントログはログを出力する側(たとえばアプリケーション)とログを読む側(たとえばイベントビューアー)で別になっています。ログを出力する側は必要な情報を出力します。ログを読む側は出力されたログをいい感じに表示します。いい感じとは、たとえば国際化して表示するということです。exist.」と表示して、日本語ユーザー向けには「『logger.exe』というファイルがありません。」と表示するというようなことです。イベントビューアーで見たときに「ソース"MyApp"からの...」という説明がでるのは、いい感じに表示できていないということを表しています。いい感じに表示するためには追加の情報が必要です。それがメッセージファイルです。メッセージファイルには「このイベントのメッセージはこんな風に表示する」という情報が入っています。それが見つかるとイベントビューアーは「ソース"MyApp"からの...」という説明が消えます。そのメッセージファイルを用意するにはMC.exeというツールを使いながらその情報が入ったDLLを作成する必要があります。それだとクロスコンパイルできないので別の方法を2つ紹介します。他のマルチプラットフォーム対応のフリーソフトウェアが提供しているDLLを利用する。Windows組み込みのDLLを利用する。最初の方法は他のマルチプラットフォーム対応のフリーソフトウェアが提供しているメッセージファイルの情報が入ったDLLを利用する方法です。Windowsのイベントログの作法に従うなら、アプリケーションはメッセージを組み立てるために必要な情報だけを出力し、メッセージファイルの方でそれを組み立ててメッセージにします。アプリケーションからはファイル名だけを出力し、メッセージファイルには「『ここにファイル名を入れる』というファイルが見つかりませんでした。」というテンプレートを用意しておくということです。その方法に従うと他のプラットフォームでうまくログを出力できません。他のプラットフォームではアプリケーションがメッセージを組み立てることが多いからです。そのため、マルチプラットフォーム対応のフリーソフトウェアはメッセージの内容をアプリケーション側で組み立てて、メッセージファイルには「『ここにそのままメッセージを入れる』」というテンプレートを用意し、すべてのメッセージをアプリケーション側で組み立てています。PostgreSQLもそのような実装になっていて、そのようなメッセージファイルの情報が入ったDLLを提供しています。PostgreSQLが提供しているDLL(PostgreSQLのバイナリーのzipをダウンロードして展開した中にあるpgsql\lib\pgevent.dll)を利用する場合は次のようにします。管理者権限で実行しないと失敗するので注意してください。pgsql\lib\pgevent.dllイベントビューアーを見てください。「ソース"MyApp"からの...」という説明が消えています。注意点は一度登録したらDLL(この場合はpgevent.dll)の場所を変えてはいけないということです。レジストリーにこのDLLのパスが書き込まれているからです。他のマルチプラットフォーム対応のフリーソフトウェアのDLLを使えば自分のアプリケーションではDLLを作成する必要はありませんでした。いちいちDLLを登録するのは面倒です。いうことで別の方法です。別の方法も方針は前述の方法と一緒で、「『ここにそのままメッセージを入れる』」というテンプレートを利用するのですが、そのテンプレートを持ってくる方法が違います。Windowsに標準で入っているたくさんのテンプレートの中の、たまたまそのようなテンプレートになっているテンプレートを使います。この方法を使うとメッセージ以外の情報(たとえばカテゴリーとか)がおかしなことになります。メッセージだけに興味があるのでその他は気にしないと割り切るならアリでしょう。この方法を使っているのはApacheです。Apacheはnetmsg.dllを使っています。このDLLの中のイベントID3299のメッセージのテンプレートが「『ここにそのままメッセージを入れる1』『ここにそのままメッセージを入れる2』...『ここにそのままメッセージを入れる9』」というものなのです。興味がある人はApacheのソースのserver/mpm/winnt/nt_eventlog.cをみてください。Studioを使わずにmingw-w64だけでクロスコンパイルできるWindowsのイベントログ出力機能の実装方法について説明しました。同じようなことをしようとして調べているフリーソフトウェア開発者の役に立つ情報になることを期待します。Groongaはこの間リリースされた5.0.7からWindowsのイベントログ出力機能が実装されています。WindowsでもGroongaを使ってみてください。GNU/Linux上で作る方法PGroongaでのJSON検索の実装方法2015-09-10クリアコードとフリーソフトウェアとビジネスクリアコードの理念は、フリーソフトウェアとビジネスの両立です。クリアコードは2015年7月1日から10期目に入っています。クリアコードが設立当初から大事にしていることの1つにフリーソフトウェア(ユーザーが自由に使えるソフトウェア)があります。それを大事にするにあたって「継続できること」という制約をつけています。言い方を変えると、「お金を稼ぐ」ということです。フリーソフトウェアを大事にしているという響きだけ聞くと、お金度外視でひたすらフリーソフトウェアを大事にしているイメージを持つかもしれません。お金を稼がないと継続できないので会社は潰れます。それでは大事にすることができなくなります。いうことで、クリアコードはどうやってフリーソフトウェアを大事にすることとお金を稼ぐことを両立させようとしているかを整理します。お金を稼ぐ方法クリアコードが使っているお金を稼ぐ方法は次の通りです。フリーソフトウェアのインストール・設定を支援することの対価としてお金をもらう方法です。Mozilla製品(FirefoxとThunderbird)でよくある方法です。他のフリーソフトウェアの導入支援をすることもありますが、Mozilla製品が一番多いです。この方法は大まかにいうと次のような流れになります。依頼者が実現したいことをよく聞く聞いた内容を理解し整理するどのようにしたら実現できるかを検討する実現方法を提案する導入する導入支援の依頼者は対象ソフトウェア周辺に関する知識が不足しているためクリアコードに依頼します。そのため、クリアコードは自分たちが持っている知識を活かして、適切な実現方法を提案し、実際に依頼者が実現したい状態を達成することが大事になります。知識があっても、依頼者が実現したい状態と違う状態を達成してしまっては適切な支援ができたとは言えません。そのため、依頼者が実現したい内容を理解することは非常に重要です。この流れの中でフリーソフトウェアを大事にする方法は次の通りです。できるだけこれらを実現するように依頼者と調整しながら進めます。例:↑のリンクに書いています。導入支援中に見つけた問題を報告・修正フリーソフトウェアを大事にするということは「開発に参加する・寄付する」というイメージがあるかもしれません。それら以外にも、関連情報を公開したり、ソフトウェアを広めたり、困っているユーザーを助けることなども大事にすることです。することで開発者たちの作業が減ることにつながるからです。そのため、これらはどれもフリーソフトウェアを大事にすることにつながります。フリーソフトウェアを大事にするとき、依頼者のことを忘れてはいけません。クリアコードがフリーソフトウェアを大事にする行動をすると、依頼者にとってもよい状態になることを大事にします。しないと、依頼者の満足度が下がって継続して一緒に仕事をできないからです。仕事にならないとお金を稼げません。ここに挙げた方法では次のような観点で依頼者にとってもよい状態になると説明して理解を求めます。他の導入支援のときにすでに公開していた情報を更新することができ、依頼者は更新された情報を無償で利用できます。(たとえば、Firefox/Thunderbirdのバージョンアップがあった場合など。した場合は依頼者は無償でその恩恵を受けられます。(たとえば、Firefox/Thunderbirdのバージョンアップに追従する作業など。作業時間を短縮でき、費用を少なくできます。導入支援中に見つけた問題を報告・修正クリアコードが独自で調査・修正してパッチをメンテナンスするよりも、メンテナンスコストが下がるので、長い期間でみると費用を少なくできます。(導入して終わりではないので、長い期間をみるのは妥当なことです。プラグインの変更、プラグインの新規作成などの作業の対価としてお金をもらう方法です。他のお金を稼ぐ方法である、導入支援、サポート、受託開発の一環として実施することが多いです。依頼者固有の内容については公開しませんが、一般的に使えそうな知識やプログラムは、依頼者に説明して理解を得た上でできるだけ公開します。このときの方針は前述の導入支援のときと同様です。「クリアコードはこういうポリシーなのでこうしていいですよね?」ではなく、「依頼者にとってもメリットがあることなのでこうしていいですか?」というところを大事にするということです。サポートサポートとは運用中に発生した問題の調査、開発時の技術的問題の解決支援などの対価としてお金をもらう方法です。Webで検索すると「OSSサポートサービス」を提供している会社がいくつか見つかりますが、それらと同様のお金の稼ぎ方です。Mozilla製品は導入支援後にそのままサポートを提供するケースが多いです。受託開発でシステムを構築した場合はそのシステムのサポートを提供することがあります。一般ユーザーから直接問い合わせる形でのサポート(ヘルプデスクと呼ぶのでしょうか)は提供しておらず、情報システム部門の方経由、SIerさん経由、既存のサポートサービスのエスカレーション先としてサポートを提供します。サポートは基本的にリモートから提供しています。依頼者のところにいって調べることはせず、情報を提供してもらい、それらを使って問題を解決します。「行ったほうが早い」ときは行きます。リモートでのサポート提供ではどういった情報を提供してもらいたいかを適切に伝えることが重要になります。しないと闇雲に時間が過ぎていくからです。なると問題が発生している期間が長くなりますし、依頼者の負担も増えます。結果として、依頼者の満足度が下がり、サポートサービスの利用を打ち切るでしょう。リモートで調査するというのはフリーソフトウェアの開発では普通のことなので、フリーソフトウェア開発で培った知見を活かします。いう知見かというと、どういうときにこのケースが発生する可能性があり、それを確かめるためにはどういう情報を知ればよいか、また、その情報をいかに少ない負担で取得してもらうか、というのを考えるということです。そのために、状況を整理する、ソースから問題が発生する可能性のあるケースを洗い出す技術などが必要になります。具体例としていくつか過去の記事を挙げます。問題の原因調査のためのログ収集のセオリー:ログを調査に有効活用するときのポイントサポートのときのフリーソフトウェアを大事にする方法は導入支援と同じですが、「導入支援中に見つけた問題を報告・修正」が多くなります。多くの場合、なにか問題があって問い合わせがきています。依頼者が欲しいソフトウェアを開発する対価としてお金をもらう方法です。契約方法は次のどちらかのパターンが多いです。最初は請負契約で、お互いに信頼できることを確認できていて準委任契約の方が向いていそうな内容(たとえば、作業開始時にすべての作業内容が決まらないとき)なら準委任契約にする準委任契約が向いていそうな内容のときは最初から準委任契約にする開発するソフトウェアは、すべてがフリーソフトウェアの場合もありますし、一部だけフリーソフトウェアの場合もありますし、すべてがプロプライエタリなソフトウェアの場合もあります。仕事を受けるときはフリーソフトウェアになる度合いが大きい仕事を優先します。開発するソフトウェアがすべてプロプライエタリなソフトウェアの場合でも、フリーソフトウェアを活用します。Rails、Sinatraなどのフリーソフトウェアを活用しますし、GNU/Linux上でマルチメディアを再生するアプリケーションを開発する場合はGStreamerを活用します。これはフリーソフトウェアを大事にするクリアコードとしての最低限譲れないラインです。活用するだけでなく、問題点や改良点が見つかった場合は開発元に報告する、知見を公開するなどでよりフリーソフトウェアを大事にする活動につなげようとします。このときもクリアコードの都合だけを考えないことが大事なポイントです。依頼者にとってもメリットがある形、少なくともデメリットがない形での実現を目指します。最低限のラインは依頼者が満足することです。依頼者と約束した機能を約束した妥当な期間内に実現していること、依頼者に素早く適切な回答を返し続けることで良好な関係を築いていること、などが満足につながります。前に汚いコードで作ってしまった・本来必要ない作業も(気づかずに)実施しているなどの理由で、機能の実現にかかる時間が「長い印象」を与えることが続けば依頼者の満足度は下がっていくでしょう。(妥当な理由を依頼者が理解できるように説明できれば「長い印象」はもたれないでしょう。)依頼者にとってのメリットが見いだせない場合(たとえば、それをやっていると時間が無くなって欲しいものが実現できない場合)は、業務の空き時間や趣味のフリーソフトウェア開発のときに開発元に報告します。業務の空き時間を作るためには効率よく仕事をします。空き時間ができたクリアコードのメンバーは空き時間がない他のメンバーが溜めている問題を代わりに開発元に報告します。クリアコードのメンバーはもともと好きでフリーソフトウェアの開発をしてきた人たちです。そんな人たちが、クリアコードに入ったから趣味のフリーソフトウェア開発をやめた、となるのはクリアコードとしてはうれしくありません。好きでフリーソフトウェアの開発をしていた人が減るからです。クリアコードがフリーソフトウェアを大事にする方法として、メンバーが趣味に使える時間を増やすという方法があります。クリアコードは基本的に残業をしない働き方にしています。1年ほど前から有給休暇の有効期限切れ日数が0になるような取り組みを進めていて、今ではほとんどのメンバーが有効期限が切れる前に使い切っています。業務時間の一部を好きなように使ってよいという会社もありますが、クリアコードは休みの日を増やし、メンバーがフリーソフトウェアの開発に使える時間を増やすことでフリーソフトウェアを大事にしています。まとめクリアコードがどうやってフリーソフトウェアを大事にすることとお金を稼ぐことを両立させようとしているかを整理しました。クリアコードは、依頼者のことを大事にし、これからも継続してフリーソフトウェアを大事にしていきます。vol.4ワークライフバランス推進の取り組み2015-09-17PGroongaでのJSON検索の実装方法PGroonga(ぴーじーるんが)はPostgreSQLから全文検索エンジンGroonga(ぐるんが)を使えるようにするためのPostgreSQLの拡張機能です。PGroongaを使うとPostgreSQLに格納したデータに対して高速な全文検索を実現できます。PostgreSQLは標準では日本語テキストを全文検索できません。LIKEでシーケンシャルサーチする必要があり、レコード数・テキストサイズが増えるほど性能が劣化します。PGroongaを導入することで大量の日本語テキストデータに対しても高速に全文検索できます。そんなPGroongaですが、2015年9月29日(肉の日)にリリースされた0.9.0からjsonb型をサポートしました。サポートしたというのはjsonb型のデータをインデックスを使って高速に検索できるようになったということです。jsonb型というのはPostgreSQLでJSONを保存するための型の1つです。JSONを正規化する点が特徴で、生のJSONよりも扱いやすいです。JSONでは次の2つはどちらも正しいです。Objects」には次のように名前は一意である「べき」としているだけだからです。unique.その後の説明には、名前が重複している場合の挙動は実装依存だと書いてあります。duplicates.jsonbは正規化処理の中で名前を一意にする(最後の名前と値のペアだけを使う)処理もあります。すると、扱うときは名前が重複しているかも!?と考えずに済むので扱いやすいのです。この記事では、PGroongaでどうやってjsonb型をサポートしているかを説明します。何ができるのかを説明し、その後、実現方法を説明します。できることPGroongaではjsonb型のデータに対してインデックスを使って次の検索をできます。全部の値に対する範囲検索(a)全部の値に対する全文検索(a)(b)PostgreSQLが提供する機能(GINとjsonb_ops)を使ってjsonb用インデックスを作成した場合、「(a)」と書いている操作は実現できません。JsQueryという拡張機能を使うとPostgreSQLが提供する機能を使う場合よりも高度な検索を実現できますが、それでも「(b)」と書いている操作(全文検索)は実現できません。このように、PGroongaを使うとjsonb型のデータに対して、既存の機能よりもより高度な検索をインデックスを使って高速に実現できます。全文検索対象の値が入っているキーがわかっている場合は、式に対するインデックスを使うことでjsonb型のデータの値に対して全文検索できます。参考までに、それぞれの検索がどのような検索になるか例を示します。検索対象のJSONとして次の2つのJSONがあるとします。"world"}クエリーが次の場合は両方ヒットします。どちらもトップのオブジェクトに「a」というキーがあり、その値が「"hello"」だからです。"world"}」だけヒットします。片方はトップのオブジェクトに「b」というキーが存在しないからです。"world"}指定したキーの値に対する完全一致検索この検索は「指定したjsonb型のデータを含んでいるレコードを検索」のサブセットなので省略します。指定したキーの値に対する範囲検索(a)検索対象のJSONとして次の2つのJSONがあるとします。300}「code」の値が200番台という条件で検索できるということです。300」という条件で検索できるイメージです。200}だけがヒットします。指定したキーの値に対する全文検索(a)(b)検索対象のJSONとして次の2つのJSONがあるとします。"サーバーがダウンしました"}「message」の値に「起動」というテキストを含むかという条件で検索できるということです。"サーバーが起動しました"}だけがヒットします。JSONとして次の2つのJSONがあるとします。[200]}JSONのどこかに「200」という数値があるかという条件で検索できるということです。この場合はどちらにもヒットします。「200」がどこにあるかは問わないからです。全部の値に対する範囲検索(a)検索対象のJSONとして次の2つのJSONがあるとします。[299]}JSONのどこかに200以上300未満という数値があるかという条件で検索できるということです。この場合はどちらにもヒットします。最初のJSONには「200」があり、2つめのJSONには「299」があるからです。全部の値に対する全文検索(a)(b)検索対象のJSONとして次の2つのJSONがあるとします。["起動失敗"]}JSONのどこかに「起動」というテキストを含む値があるかという条件で検索できるということです。この場合はどちらもヒットします。最初のJSONは「"起動しました"」に「起動」が含まれていて、2つめのJSONは「"起動失敗"」に「起動」が含まれているからです。実現方法だいぶ前置きが長くなりましたが、このような検索をインデックスを使って高速に実現するためにどのように実装しているかを説明します。ポイントは次の2点です。JSONの値をそれぞれ1つのデータとして分割する内部で複数のインデックスを組み合わせるまず、それぞれ1つのデータとして分割するということについて説明します。次のJSONを考えます。"world"}このJSONには「"hello"」と「"world"」という2つの値があるので、次のように2つのデータに分割します。Bという書き方は「Aというパスの値はBだよ」ということを示したつもりです。"world"Groongaにはストレージ機能があり、RDBMSのようにテーブルを作り、そこにレコードを保存することができます。PGroongaはそれぞれの値を1つのレコードとして保存します。このレコードを保存しているテーブルを「Valuesテーブル」と呼んでいます。今回の例だと次のようなレコードを保存するということです。"world"これだけだとJSONの値は見つけられてもその値を持っている(PostgreSQLの)レコードを見つけることができません。そのため、もう1つテーブルを作ります。「Sourcesテーブル」と呼んでいるもので、インデックス対象のデータを管理しています。このテーブルのレコードがインデックス対象のレコードに対応します。このSourcesテーブルの各レコードにValuesテーブルへの参照を格納します。元のJSONがどの値から成り立っていたかを示しています。Valuesテーブルへの参照にはGroongaのベクター型のカラムを使います。これは、PostgreSQLでいう配列型です。今回の例では次のようなレコードを保存するということです。valuesカラムの中に入っている1も2も前述のValuesテーブルのレコードのIDです。(ctidというのはPostgreSQL特有のIDでデータの実体を識別するIDのようなものだと思ってください。2]あとは、次のような流れで検索できるようにするだけです。Valuesテーブルから検索(たとえば、「.aカラムの値が"hello"」という完全一致検索)1.で見つかったレコードをvaluesカラムに持つレコードをSourcesテーブルから検索2.で見つかったレコードからctidカラムの値を参照し、PostgreSQLのレコードを特定するこの、1.と2.を高速に実現するために「内部で複数のインデックスを組み合わせる」ということをしています。Valuesテーブルを考えます。"hello"」という条件を評価すればよいことになります。これを高速に実現するためにpathとstringにインデックスを用意します。Valuesテーブルから検索」を高速に実現できるようになります。1.で見つかったレコードをvaluesカラムに持つレコードをSourcesテーブルから検索」を高速に実現するためのインデックスを用意します。これは、Valuesテーブルにインデックスカラムindexを追加します。indexにはこのレコードを参照しているSourcesテーブルのレコードのIDのリストが入ります。(インデックスカラムは転置インデックスでレコードのIDのリストはポスティングリストだと言えばわかる人にはわかります。)今の例ではどちらの値もSourcesテーブルのID100のレコードから参照されているので、どちらも[100]になります。1.で見つかったレコードをvaluesカラムに持つレコードをSourcesテーブルから検索」を高速に実現できます。indexカラムに入っているレコードIDのリストが求めるものだからです。今の例ではValuesテーブルの次のレコードがヒットしています。[100]このレコードを含んでいるSourcesテーブルのレコードは100だけです。indexに100だけが入っているからです。2.で見つかったレコードからctidカラムの値を参照し、PostgreSQLのレコードを特定する」は簡単です。ctidカラムの値を返すだけだからです。今の例だと[1,1]を返すだけです。2]このように、次の2つを組み合わせてjsonb型の値を高速に実現できるようにしています。JSONの値をそれぞれ1つのデータとして分割する内部で複数のインデックスを組み合わせるこの実現方法の利点この実現方法の利点を説明します。この実現方法ではJSONそのものをそのまま検索しません。JSON内のそれぞれの値を別のレコードに分割し、それぞれの値に絞って検索します。これが範囲検索や全文検索といった高度な検索を使える理由です。先ほどの例では次のように値を保存しています。"hello"と条件を指定できます。"hello"とすれば簡単に全文検索をすることができます。(Groongaでは@は全文検索の演算子です。200とすれば範囲検索になります。単なる表なので、いつも通りインデックスを用意できます。操作に合わせたインデックスを作ればその操作を高速に実現できるというわけです。文字列の前方一致検索をできるインデックスを用意すれば高速に文字列を前方一致検索できます。この「表である」ということには別の利点もあります。JSONのどこにある値か関係なく検索できるという点です。"hello"」となります。JSONで値を格納したいということは、データの構造がブレることがある場合が多いでしょう。"○○"」とすれば実現できます。この実現方法の欠点この実現方法の欠点はディスク使用量が多いことです。値を分解してGroongaに保存しているので、PostgreSQLが持っているデータを2重に持っています。1つのJSONには複数の値があるので、Valuesテーブルのレコード数が多くなります。これもデータが増える原因になります。Valuesテーブルのサイズを小さくする工夫はしています。違うJSONの中に、同じパスで同じ値がある場合は同じValuesレコードを共有します。多くの場合、ある程度同じ構造や同じ値がでてくると予想していて、この工夫は効果があると期待しています。Webサーバーのログを考えます。次のようなJSONがたくさん保存されることでしょう。"/favicon.png"}.codeに注目してください。値がすべて200です。HTTPのステータスコードは200や404や500などよく使われる値に偏ります。同じパスで同じ値のときにValuesレコードを共有すると.codeが200という値は共有できます。このようにValuesテーブルの肥大化を防ぐ工夫をしています。説明しなかったこと説明を単純にするために文字列型の値だけを例にして説明しました。JSONでは文字列型以外に数値型と真偽値型があります。それらをどう扱っているかの説明は省略しました。JSON内の配列をどう扱っているか、パスをより簡単に指定するための工夫も省略しました。実はpathカラムにはもう少し違う形でパスが入っているということの説明も省略しました。速度面についても説明していません。使い方(SQLの書き方)についても説明していません。まとめPGroongaは0.9.0でjsonb型の検索をサポートしました。その実現方法を説明しました。興味がでてきた方はドキュメントを参考にして使ってみてください。使い方(SQLの書き方)を説明しています。わからないことがあったらGitHubのissueやメーリングリスト、チャットで質問してください。ある程度大きなデータで性能を測定し、それを公開してくれると大変励みになるのでよろしくおねがいします。4.0とGFDLのデュアルライセンスで自由に利用できます。クリアコードはプログラミングが楽しいソフトウェア開発者を1名募集しています。クリアコードは「クリアコードをいい感じにする人」を1名募集しています。クリアコードはフリーソフトウェア開発で培った技術力を提供しています。Thunderbird)とRubyとGroonga(全文検索)に関連した開発を得意としています。