2018-02-01

【重要】ricollabサービス終了のお知らせ

はてなブックマーク   livedoor clip

いつもricollabをご利用いただき、ありがとうございます。

この度、誠に勝手ながら2018年2月28日をもちまして「ricollab blog」「郵便番号検索サービス」の各サービスを終了させていただきます。

日頃より利用いただいております皆様にはご迷惑をおかけすることとなり、誠に申し訳ございません。長らくのご愛顧をいただき、厚くお礼申し上げます。

ricollabは、2008年より運営を続けて参りました。しかし、この数年で利用者も大幅に減少しており、サービスとしての役割を終えたと考え、終了という判断に至りました。ご愛用頂いている利用者の皆さまにはご迷惑をおかけしますが、何卒ご理解頂きたく存じます。

■郵便番号検索APIのご紹介

2010-04-26

MySQL Proxyでできること、できないこと

はてなブックマーク   livedoor clip

こんにちは。リコーの井上です。今回から私もこのブログを書かせていただくことになりました。よろしくお願いします。

今回はMySQL Proxyについて書きたいと思います。MySQLを利用していると、WebアプリケーションなどのMySQLクライアントからサーバに送られるクエリを解析したい場合があります。あるいは、複数のMySQLサーバに処理を振り分けたいということがあります。

MySQL Proxyはその名の通り、MySQLクライアントとMySQLサーバの間に入って、接続やクエリで取り交わされるデータを参照・加工して、いろいろと役立つことをさせようというものです。動作内容は軽量スクリプト言語Luaにより柔軟に記述できますが、機能的制限もあります。

このエントリではMySQL Proxyとはどういうものか、どういうことができそうなのか、できないのかということを感じ取っていただければと思っています。

必要なものとインストール

MySQL Proxyを一からビルドするにはlibevent, glib, Luaなどの依存ライブラリについてもビルドする必要があります。気軽に試すには、ダウンロードサイトからお使いのOS用のバイナリパッケージをダウンロードするのがおすすめです。バイナリパッケージはtarballで提供されているので、これを/opt以下などに展開して利用します。

Red Hat系ディストリビューション用に、/etc/init.dに置くスクリプトなども別途公開されています。

ここでの動作確認は以下の組み合わせで行いました。

  • CentOS 5.4
  • MySQL Proxy 0.8.0(Red Hat Enterprise Linux用バイナリ)
  • MySQL 5.1.45

簡単なLuaスクリプト

MySQL Proxyの大きな特長は、軽量スクリプト言語Luaを利用して任意の処理を記述できることにあります。逆に、Luaを記述しなければ指定されたMySQLサーバに交互に接続を振り分ける単純なロードバランサとなります。

MySQL Proxyでクライアントから発行されたクエリの内容をファイルに出力するLuaスクリプトは以下のようになります。


 1  function read_query(packet)
 2    local fh
 3    if string.byte(packet) == proxy.COM_QUERY then
 4      fh = io.open("/var/tmp/query.log", "a")
 5      fh:write(string.sub(packet, 2) .. "\n")
 6      fh:close()
 7    end
 8  end

スクリプトをlog.luaという名前で保存し、以下のようにMySQL Proxyを起動します。


% /opt/mysql-proxy/bin/mysql-proxy \
          --proxy-backend-addresses=127.0.0.1:3306 \
          --proxy-lua-script=`pwd`/log.lua

オプション --proxy-backend-addresses で中継先のバックエンドつまりMySQLサーバを、 --proxy-lua-script でLuaスクリプトファイル名を指定します。Luaファイル名はフルパスで指定することに注意してください。

MySQLクライアントを起動してデータベースにSELECTを発行してみましょう。MySQL ProxyはデフォルトでTCPの4040ポートを利用します。なお、接続するDBとSELECTするテーブルは事前に作成しておいてください(ここではhogeとitems)。


% mysql -u root -p -h 127.0.0.1 -P 4040 hoge
Enter password:
Welcome to the MySQL monitor.  Commands end with ; or \g.
...(省略)...
mysql> select * from items;
...(省略)...
mysql> exit
Bye
%

この後、 /var/tmp/query.log を開くと以下のような内容が記録されています。


select @@version_comment limit 1
select * from items

1行目はMySQLクライアントがサーバに接続したときに自動的に発行している文です。2行目は発行したSELECT文がそのまま出力されています。exitはクエリではないので、ログは出力されていません。

スクリプトの内容

MySQL ProxyはLua関数により拡張できるポイントをいくつか用意しており、read_queryはその一つです。read_query関数はクライアントからのリクエストがきたときに、そのデータが入ったパケットを引数として呼び出されます。

2行目ではファイルハンドルを格納するローカル変数を定義しています。localキーワードをつけないと変数はグローバルとなり、他のリクエストから呼ばれたLua関数の中でも共通に利用されることになります。なお、関数の外で定義されたローカル変数は、クライアントとの同一コネクションを通して有効です。

3行目のif文では、パケットの最初のバイトを見て、リクエストがクエリなのかどうかを確認しています。MySQL ProxyではCOM_QUERY, COM_QUITといったパケットのタイプを示す定数が定義されています。これをパケットの先頭バイトとマッチすることでパケットを識別することができます。なおstring.byte(str, i, j)はi文字目からj文字目の内部コードを数値として多値で返します。i, jのデフォルト値は1で、文字列の最初1文字ということになります。

4~6行目は実際にファイルを開き、クエリの内容を書き込んでいます。Luaの関数 string.subを使って、関数に渡されたパケットから2バイト目以降を取り出しています。

余談ですが、string.sub() の 「.」 はstringインスタンスのメソッドを呼び出しているように見えますが、Luaはクラスを持ちません。 string は「テーブル」と呼ばれるいわゆる連想配列で、Lua処理系が提供する文字列関数を格納しています。 string.sub() はキー”sub”に対応して格納された関数を呼び出すという意味になります。つまり string["sub"]() と同じ意味です。

MySQL Proxyを使いこなすにはLuaを理解する必要があります。とはいえ、Luaは大変理解しやすい言語なので、Lua 5.1 Reference Manual(日本語訳)などにざっと目を通すだけでかなりのことができるようになるでしょう。

カスタマイズできる箇所とその内容

MySQL Proxyが用意している拡張のための関数は以下の7つです。各関数からアクセスできるデータなどについてはスクリプティングのリファレンスマニュアルに詳しく書かれています。

接続に関わる関数

connect_server
クライアントからMySQL Proxyに接続が行われた際、バックエンドとの接続を行う前に呼び出されます。実際に接続および認証を行うサーバを選択できる唯一の場所で、落ちているとマークされているバックエンドを避けるといったことができます。ただしクライアントに関する情報は利用できません。
read_handshake
MySQL Proxyとバックエンドとの接続が行われた後、バックエンドから送り返されたハンドシェイクのための情報をクライアントに転送する前に呼び出されます。クライアントのIPなどの情報が得られる状態になっているので、IPを見て切断するといった制御ができます。
read_auth
ハンドシェイクに続き、クライアントから受けとった認証情報をバックエンドに転送する前に呼び出されます。クライアント側のユーザ名にアクセスできますが、認証が成功するのかどうかはこの時点ではわかりません。
read_auth_result
バックエンドでの認証が終了した後、バックエンドから受けとった認証結果をクライアントに通知する前に呼び出されます。認証の成否がわかるので、認証成功(失敗)後に行いたい処理を記述できます。

認証が終了したコネクションはそのままそのクライアントからのリクエストで利用してもいいですし、バックエンドと結びつけられたコネクションプールに入れて後から利用することもできます。

リクエストに関わる関数

read_query
クライアントから何らかのリクエストが送られたとき、それをバックエンドに転送する前に呼び出されます。「簡単なLuaスクリプト」で利用していたのもこの関数です。SQLを解析して書き換えたり、結果を作って返したりできます。
read_query_result
クエリ結果がバックエンドから返されたとき、それをクライアントに転送する前に呼び出されます。結果を操作したり任意の結果を返すようにすることができます。この関数は結果セットにアクセスしたい旨を前述のread_query()の中で明示的に指定しなければ呼び出されません。

その他の関数

disconnect_client
サーバとクライアントの接続が切断されるときに呼び出されます。実際には切断せずにコネクションをプールするといった処理を記述します。

コネクションプールへのアクセス

少し細かい話になりますが、MySQL Proxyのスクリプトで一番わかりにくいと感じたのがコネクションプールとのやりとりなので、説明を加えておきます。これは proxy.connection.backend_ndx という変数によって制御します。

現在利用している(バックエンドとの)コネクションをコネクションプールに入れるには、 proxy.connection.backend_ndx に0を代入します。 代入した段階でプールへの格納が行われます。

逆にプールにあるコネクションを利用するには、対応するバックエンドの番号 (proxy.global.backends配列の添え字) を proxy.connection.backend_ndx に代入します。 代入と同時に、プールにあるアイドルなコネクションのどれか一つが、現在のコンテキストとなっているクライアントとのコネクションに対応づけられます。

Lua層のこの変数への代入が、Cレベルではコネクションプールを操作する関数呼び出しを起こすような実装となっている、ということを覚えておくと多少理解しやすくなります。

MySQL Proxyでできること、できないこと

MySQL Proxyの用途として、サイトでは以下のようなものが例として挙げられています。

  • 負荷分散
  • フェイルオーバー(冗長化)
  • クエリの解析
  • クエリのフィルタリングや書き換え

Luaでかなり自由なことができそうなのですが、現行のバージョンでは機能にいくつかの制限があり、これらを念頭に置いて使い方を考えなければなりません。

  • Luaスクリプトが呼ばれるのはクライアントがアクションを起こしたときのみです。そのため、クライアントからの指示なしに必要な数のコネクションを作ってプールすることや、バックエンドの生死確認をアクティブに行うといったことはできません。
  • コネクションプールからのコネクションの割り当ては接続してきたユーザの権限に関係なく行われるようです。コネクションの割り当て時にCHANGE_USERコマンドで権限を変更することもできるようなのですが、期待通りの動作を確認できませんでした。そのため、権限の高いユーザと低いユーザが混在するような利用法はうまく動作しません。
  • これはコネクションプールをアプリケーションと独立に持つ場合の宿命かも知れませんが、コネクション固有の状態をうまくサポートできません。プールに入っているコネクションに固有の状態を設定しても、次にそのクライアントに対して同じコネクションが割り当てられる保証はないからです。コネクション固有の状態としては、文字エンコーディングやセッション変数、そしてテンポラリテーブルなどがあります。

R/W Splittingへの適用

MySQL Proxyの利用法として、更新系と参照系のクエリをそれぞれマスタとスレーブに振り分けて負荷分散する、いわゆるR/W Splittingがあります。これが完璧に動作すれば、アプリ側での制御が不要になるため開発が楽になります。MySQL Proxyのパッケージに含まれるサンプルスクリプトの中にも rw-splitting.lua としてコンセプト実証のコードが入っています。(0.8.0に付属のものはいくつかバグがあってそのままでは動作しません。例: Bug #39629)

実現方法は、認証済みのコネクションをマスタ・スレーブそれぞれに対して一定数プールしておき、クエリがきた段階で書き込みならマスタへの、読み込みならスレーブへのコネクションを使うというものです。LAST_INSERT_ID()やトランザクションに対する考慮もされています。

しかし、前節に書いたような制限があるため、バグを修正して使う場合でもアプリ側で以下のような注意が必要になります。

  • 文字エンコーディングなどコネクション固有になる設定はバックエンド側で一括して設定する
  • MySQL Proxy起動初期にはコネクションプールに書き込みのためのコネクションが不足することがあるため、エラー時に再接続するようアプリ側で制御する

R/W SplittingについてはMySQL Proxy RW Splittingに制限を含めた記載があります。

まとめ

以上、MySQL Proxyの機能やその制限について書いてみました。開発においてクエリを解析したり、特定の条件で書き換えたりということには利用できるレベルになっていると思います。ただ、MySQLサーバ群の手前に配置してすべてのアプリからの読み書きを分散、という使い方はまだ荷が重いようです。今後の開発に期待したいところです。

2009-11-30

MySQLパーティショニングについて(その2:性能検証編)

はてなブックマーク   livedoor clip

こんにちは、濱田です。

前回から時間が経ってしまいましたが、今回は「性能検証編」ということで、パーティションドテーブルに対して実際にデータを挿入・参照することでパーティショニングの性能面を検証してみようと思います。

性能検証環境

使用したマシンのスペックは以下の通りです。

OS
CentOS 5.3 32bit (on Windows XP Pro SP3 32bit via VMware Server 2.0.0)
CPU
Core2 Duo E8300 2.83GHz (VMには1CPUを割り当て)
Memory
3.25GB (VMには512MBを割り当て)

MySQL のバージョンおよび設定は以下の通りです。なお、MySQL サーバおよびクライアントは同一マシン上で動作させました。

MySQL
5.1.35-community (設定は my-medium.cnf をそのまま使用)

また、パーティションドテーブルのスキーマは以下の通りです。

CREATE TABLE logs (
    id INT NOT NULL AUTO_INCREMENT,
    client_name VARCHAR(255) NOT NULL,
    log_data VARCHAR(1024) NOT NULL,
    logged_at DATETIME NOT NULL,
    PRIMARY KEY(id, logged_at),
    INDEX(client_name)
) ENGINE=MyISAM
PARTITION BY RANGE( TO_DAYS(logged_at) ) (
    PARTITION p200901 VALUES LESS THAN ( TO_DAYS('2009-02-01') ),
    PARTITION p200902 VALUES LESS THAN ( TO_DAYS('2009-03-01') ),
    PARTITION p200903 VALUES LESS THAN ( TO_DAYS('2009-04-01') ),
    PARTITION p200904 VALUES LESS THAN ( TO_DAYS('2009-05-01') ),
    PARTITION p200905 VALUES LESS THAN ( TO_DAYS('2009-06-01') ),
    PARTITION p200906 VALUES LESS THAN ( TO_DAYS('2009-07-01') ),
    PARTITION p200907 VALUES LESS THAN ( TO_DAYS('2009-08-01') ),
    PARTITION p200908 VALUES LESS THAN ( TO_DAYS('2009-09-01') ),
    PARTITION p200909 VALUES LESS THAN ( TO_DAYS('2009-10-01') ),
    PARTITION p200910 VALUES LESS THAN ( TO_DAYS('2009-11-01') ),
    PARTITION p200911 VALUES LESS THAN ( TO_DAYS('2009-12-01') ),
    PARTITION p200912 VALUES LESS THAN ( TO_DAYS('2010-01-01') ),
    PARTITION pmax VALUES LESS THAN MAXVALUE
);

データ挿入の性能検証

検証内容

データ挿入の性能検証には下記 INSERT 文を1万回連続で発行し、それに要した時間を測定しました。INSERT 文の「?」の部分には ‘client_1′ ~ ‘client_1000′ がそれぞれ10回ずつ入ります。(実際には上記の処理を行なう Ruby スクリプトを作成し、その実行時間を time コマンドで測定しました)

INSERT INTO logs (client_name, log_data, logged_at) \
VALUES (?, 'test log data', '2009-11-01');

logs テーブルの事前条件として、以下の2パターンで測定しました。

条件1
1件もレコードが格納されていない状態
条件2
1~10月の各月に1000クライアント×1000件分のログデータ(計1000万件)が格納されている状態

  • logged_at : 10通り (’2009-01-01′ ~ ‘2009-10-01′)
  • client_name : 1000通り (’client_1′ ~ ‘client_1000′)
  • 上記組み合わせをそれぞれ1000件ずつ

検証結果

結果は図1のようになりました。

result_insert_myisam.png
図1 : データ挿入の性能検証結果 (MyISAM)

条件1を見ると、パーティションあり/なしで処理時間が殆ど変わっていません。これはデータ挿入時にパーティション振り分け処理のオーバーヘッドが殆どないことを示しています。今回振り分け処理に用いたパーティショニング表現は TO_DAYS(logged_at) であり、この程度の処理ではパーティション振り分けのオーバーヘッドを全く気にしなくてよいことが分かります。

逆に言えば、パーティショニング表現に時間のかかる処理を用いてしまうとそこがボトルネックになってしまいます。それが気になる場合は、以下のように BENCHMARK() 関数を用いて事前にパーティショニング表現の処理時間を確認しておくとよいと思います。

mysql> SELECT BENCHMARK(10000, TO_DAYS('20091101') );
+---------------------------------------+
| benchmark(10000, TO_DAYS('20091101')) |
+---------------------------------------+
|                                     0 |
+---------------------------------------+
1 row in set (0.00 sec)

また条件1、2での処理時間を比較すると、パーティションなしの場合は処理時間が増加しているのに対し、パーティションありの場合は処理時間が殆ど変わっていないことが分かります。これはインデックスデータがパーティション毎に独立して作成されることを示しています。今回のスキーマで作成されるインデックスは (id, logged_at) の複合インデックスと client_name ですが、このうち client_name はユニークなカラムではありません。そのため、事前に格納されているレコード数が多いほどインデックスデータ(BTREE 構造)の更新処理に時間がかかり、故にデータ挿入処理も遅くなります。

パーティションなしの場合に事前に0件と1000万件格納されている場合とで挿入時間に差が出ているのはそのためです。しかし、パーティションありの場合、条件2において p200911 (以降)には1件もレコードが格納されていない状態であるため、logged_at が ‘2009-11-01′ (以降)のデータを挿入する場合はテーブルが空の場合と同じ処理時間になるのです。つまり、ログ情報のような日毎に蓄積されるデータを格納する場合、月別にパーティションを設定しておくことで、レコード数増加に伴い累積的に遅くなるデータ挿入処理の影響を一ヶ月分に抑えられることが分かります。

なお、ストレージエンジンを InnoDB にして同じ検証を行なってみましたが、MyISAM の場合と同様の結果になりました。(図2)

result_insert_innodb.png
図2 : データ挿入の性能検証結果 (InnoDB)

データ参照の性能検証

検証内容

データ参照の性能検証には、MySQL クライアントから下記2パターンの SELECT 文を1回発行し、それに要した時間を測定しました。

条件1
client_name 指定なしで11月分のログデータ参照する

SELECT * FROM logs WHERE logged_at >= '2009-11-01' \
AND logged_at < '2009-12-01';
条件2
client_name 指定ありで11月分のログデータ参照する

SELECT * FROM logs WHERE logged_at >= '2009-11-01' \
AND logged_at < '2009-12-01' AND client_name = 'client_1';

logs テーブルの事前条件は、データ挿入の性能検証(条件2)を行なったときの状態としました。つまり、1~10月の各月に1000クライアント×1000件分のログデータが、11月に1000クライアント×10件分のログデータが格納されている状態(計1001万件)です。

検証結果

結果は図3のようになりました。

result_select_myisam.png
図3 : データ参照の性能検証結果 (MyISAM)

条件1を見ると、パーティションありの方が処理時間が劇的に速いことが分かります。これはパーティションなしでは全レコード(1001万件)を参照しているのに対し、パーティションありでは p200911 のレコード(1万件)のみを参照しているためです。これはオプティマイザが SELECT 文中のクエリ logged_at >= ‘2009-11-01′ AND logged_at < ‘2009-12-01′ から判断した結果です。理論的には検索件数が1001万から1万に減るので処理時間も1/1000になって欲しいところですが、そうなっていないのはオプティマイザの判断処理等によるオーバーヘッドの影響かもしれません。

なお、SELECT 文の前に EXPLAIN PARTITIONS を付けることで、参照時にアクセスするパーティションを調べることができます。(これを見るとなぜか p200912 にもアクセスしていることが分かります。なぜそうなるのかは分かりませんが…)

mysql> EXPLAIN PARTITIONS SELECT * FROM logs
    -> WHERE logged_at >= '2009-11-01'
    -> AND logged_at < '2009-12-01'\G
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: logs
   partitions: p200911,p200912
         type: ALL
possible_keys: NULL
          key: NULL
      key_len: NULL
          ref: NULL
         rows: 10010000
        Extra: Using where

また、条件2を見ると、インデックス併用時にもパーティションありの方が処理時間が劇的に速いことが分かります。この場合、まずオプティマイザが参照に必要なパーティションを判断し、そこからさらに該当するパーティションのインデックス情報を使って参照するデータ数を絞り込んでいるようです。ここまでいくと、データ参照のコストがほぼゼロになりました。

なお、ストレージエンジンを InnoDB にして同じ検証を行なってみましたが、こちらも MyISAM の場合と同様の結果になりました。(図4)

result_select_innodb.png
図4 : データ参照の性能検証結果 (InnoDB)

まとめ

本エントリでは、パーティションドテーブルに対して実際にデータを挿入・参照することでパーティショニングの性能面を検証しました。その結果、今回検証した条件ではデータ挿入・参照のいずれにおいてもパーティショニングしない場合に比べて性能が向上することが分かりました。

このように、パーティショニングは上手に利用すればいいことずくめとなる機能です。本エントリがパーティショニング活用に役立てれば幸いです。

2009-10-21

MySQLパーティショニングについて(その1:基本知識編)

はてなブックマーク   livedoor clip

初めまして、リコーの濱田です。このたび私も本ブログを担当することになりました。今後ともよろしくお願いいたします。

本エントリではデータベースに関する技術トピックとして、MySQL 5.1 から導入された機能であるパーティショニングについて書こうと思います。少し長くなりそうなので、「基本知識編」「性能検証編」の2回に分けて書くことにします。

今回は「基本知識編」として、パーティショニングの概要と基本的な使い方について紹介します。

パーティショニングの概要

パーティショニングとは、事前に設定されたルールに従ってデータをパーティションと呼ばれる部分的なテーブルに分割する仕組みです。

データ挿入時には、設定ルールに従ってデータが該当するパーティションに自動的に振り分けられます。データ参照時には、オプティマイザがクエリから必要なパーティションを判断し、該当するパーティションのみにアクセスします。これらは MySQL の内部で行なわれるため、データの操作においてパーティショニングを意識する必要はありません。
partitioning
パーティショニングを利用することで、以下の利点が得られます。

  • 大量のデータを処理することによる性能上のボトルネックの発生を抑えられる
  • テーブルサイズに上限がある場合(MyISAMなど)でも、その上限を超える量のデータを格納できる

パーティションドテーブルの作成

それでは実際にパーティションドテーブル(パーティショニングされたテーブル)を作成してみます。パーティショニングにはいくつかの種類がありますが、ここでは RANGE パーティショニングについて説明します。

パーティションドテーブルの作成は簡単で、通常のテーブル作成文の後にパーティション設定ルールの記述を追加するだけです。以下の例は、複数クライアントのログ情報を月別に分けて格納するパーティションドテーブルの作成例です。

CREATE TABLE logs (
    id INT NOT NULL AUTO_INCREMENT,
    client_name VARCHAR(32) NOT NULL,
    log_data VARCHAR(1024) NOT NULL,
    logged_at DATETIME NOT NULL,
    PRIMARY KEY(id, logged_at),
    INDEX(client_name)
) ENGINE=MyISAM
PARTITION BY RANGE( TO_DAYS(logged_at) ) (
    PARTITION p200910 VALUES LESS THAN ( TO_DAYS('2009-11-01') ),
    PARTITION p200911 VALUES LESS THAN ( TO_DAYS('2009-12-01') ),
    PARTITION p200912 VALUES LESS THAN ( TO_DAYS('2010-01-01') ),
    PARTITION pmax VALUES LESS THAN MAXVALUE
);

PARTITION BY RANGE 以下がパーティション設定ルールです。上記の例では p200910、p200911、p200912、pmax という名前の4つのパーティションが作成され、TO_DAYS(logged_at) の値を基にデータが各パーティションに振り分けられる設定になっています。例えば、logged_at が ‘2009-11-01′ より小さいデータは p200910 に格納されるといった具合です。ここで、振り分けに利用される表現(上記例では TO_DAYS(logged_at) )を「パーティショニング表現」と呼びます。

パーティション設定ルールの最後の行の “VALUES LESS THAN MAXVALUE” は「キャッチオール」節と呼ばれるものです。MAXVALUE は表現可能な最大整数値を表すので、logged_at が ‘2010-01-01′ 以上となるデータは全て pmax に格納されます。これが無いと、logged_at が ‘2010-01-01′ 以上となるデータを格納する場合にエラーが起こります。

なお、パーティションドテーブルの作成に際してはいくつかの制約があります。以下に代表的なものを挙げておきます。

  • パーティショニング表現に用いられるカラムはテーブル内に存在するプライマリキー(またはユニークキー)の一部でなければならない
    • プライマリキー(またはユニークキー)が存在しない場合は例外
  • パーティション表現は連続した整数値をとるものでなければならない
    • 日付データを利用する場合、TO_DAYS() 関数などを使って整数値に直す必要がある
  • 作成できるパーティションの上限は1テーブルにつき1024個

パーティションの追加

既存のパーティションドテーブルから更にパーティションを追加する方法は2つあります。

  • ADD PARTITION を使って新規パーティションを追加する方法
  • REORGANIZE PARTITION を使って既存のパーティションを再分割する方法

ただし、既存のパーティションドテーブルに「キャッチオール」節が存在する場合は後者の方法しか選択肢がありません。

以下にそれぞれの方法について説明します。

ADD PARTITION を使う方法

既存のパーティションドテーブルにパーティションを新規追加するには以下のようにします。

ALTER TABLE logs ADD PARTITION (
    PARTITION p201001 VALUES LESS THAN ( TO_DAYS('2010-02-01') )
);

注意点として、RANGE パーティショニングされているテーブルにおいて、ADD PARTITION は既存パーティションの「後」にしかパーティション追加ができません(「前」や「間」はNGです)。そのため、「キャッチオール」節(MAXVALUE)が既に存在する場合は(MAXVALUE より後の値は存在し得ないため)、この方法が使えません。

REORGANIZE PARTITION を使う方法

既存のパーティションを再分割するには以下のようにします。この例では、pmax を p201001 と、(新しい) pmax に再分割しています。

ALTER TABLE logs REORGANIZE PARTITION pmax INTO (
    PARTITION p201001 VALUES LESS THAN ( TO_DAYS('2010-02-01') ),
    PARTITION pmax VALUES LESS THAN MAXVALUE
);

REORGANIZE PARTITION を行なうと、既存のデータが再分割後のパーティションに適切に振り分けられます。例えば上記の例では、旧 pmax に格納されていたデータのうち logged_at が ‘2009-02-01′ より小さいものは p201001 に、’2009-02-01′ 以上のものは 新 pmax に格納されます。この再振り分け処理により、既存データが大量にある場合は REORGANIZE PARTITION の実行にそれなりの時間がかかります。

REORGANIZE PARTITION は、既存パーティションの結合にも利用できます。例えば、p200910 と p200911 を結合したい場合は以下のようにします。

ALTER TABLE logs REORGANIZE PARTITION p200910, p200911 INTO (
    PARTITION p200910_11 VALUES LESS THAN ( TO_DAYS('2009-12-01') )
);

さらに、既存の複数パーティションを新しい複数パーティションに再構成することもできます。

ALTER TABLE logs REORGANIZE PARTITION p200910, p200911, p200911, pmax INTO (
    PARTITION p200909_10 VALUES LESS THAN ( TO_DAYS('2009-11-01') ),
    PARTITION p200911_12 VALUES LESS THAN ( TO_DAYS('2010-01-01') ),
    PARTITION pmax VALUES LESS THAN MAXVALUE
);

パーティションの削除

パーティションドテーブルから特定のパーティションを削除するには、以下のようにしてパーティションごと DROP します。

ALTER TABLE logs DROP PARTITION p200912;

上記の例では、パーティション p200912 および p200912 に格納されているデータが全て削除されます。また、上記例の実行前に p200910、p200911、p200912、pmax のパーティションがあった場合、p200912 が削除されたことで logged_at が ‘2009-12-01′ 以上となるデータは全て pmax に格納されるようになります。

パーティションの削除は内部的にテーブルの DROP とほぼ同じであるため、(ストレージエンジンが MyISAM や InnoDB の場合であれば)格納されているデータ件数によらず一瞬で処理が完了します。

パーティショニングの解除

パーティションドテーブルからパーティションを取り除き、通常のテーブルにするには以下のようにします。

ALTER TABLE logs REMOVE PARTITIONING;

DROP PARTITION とは異なり、各パーティションに格納されているデータは削除されません。各パーティションのデータを一つのテーブルにマージするため、既存データが大量にある場合は REMOVE PARTITIONING の実行にそれなりの時間がかかります。

まとめ

本エントリでは、MySQL 5.1 から導入された機能であるパーティショニングについて概要を説明し、RANGE パーティショニングによるパーティションドテーブルの作成、パーティションの追加・削除、パーティショニングの解除方法について具体例を交えて説明しました。RANGE パーティショニング以外のパーティショニング方法などパーティショニングについてより深く知りたい方は、MySQL 5.1 リファレンスマニュアル :: 15 パーティショニング を一読されると良いと思います。

次回のエントリでは、パーティションドテーブルに対して実際にデータを挿入・参照することでパーティショニングの性能面を検証してみようと思います。お楽しみに!

2009-10-15

進化する携帯電話

はてなブックマーク   livedoor clip

はじめまして。リコーの梅原です。今回から私もブログを書くことになりました。宜しくお願いします。私の一つ目のエントリーは、Androidについて書こうと思います。

Android携帯

国内初のAndroid搭載端末HT-03Aが2009年7月にNTT docomoから発売されました。皆様ご存知でしょうか?

海外でもMotorola CLIQHTC HeroLG-GW62等の端末が続々とリリースされていて、最近ではDellもAndroid搭載のスマートフォンを発売すると報道されています。Android端末が世界中で増えつつありますね。

実は私もHT-03Aの購入者の一人なのですが、携帯電話の変更連絡を友人や家族にしたときに、多くの人から「何それ?」と言われてしまいました。いやいや、これは今までに無い素晴らしい携帯電話なんです。

今回は、私が感じたAndroid携帯の魅力を3つに絞って皆様にお伝えできればと思います。

魅力1 携帯電話もリアルタイムの世界へ

まずAndroid携帯を使っていて驚かされる事はデータの同期および通知がリアルタイムであるということです。

一つ目のデータの同期とはGmail、アドレス帳、Google Calendar等のアプリケーションのローカルデータとGoogleのクラウド上にあるデータがリアルタイムに同期するということです。

今までの同期といえば、デバイスとPCをUSBでつないで・・・、という面倒な作業が頭をよぎりますが、何もしなくてもインターネット経由で勝手に同期してくれます。例えば、携帯電話で作成しているメールの内容をリアルタイムにPCで確認することができます。これなら携帯電話で作成した文書を途中からPCで編集!ってことも簡単です。他にもPCでアドレス帳を編集したり顔写真を追加したりしていると、気がついたときには携帯電話にデータ・画像が反映されています。データの同期がリアルタイムであるというメリットはおわかりいただけたでしょうか。

二つ目のデータの通知というのは、Gmail、GTalk、Twitter等の複数のアプリケーションからリアルタイムにデータが通知されるということです。

これはAndroidの特徴でもある「マルチタスク」が威力を発揮しています。例えば、Gmailでメールを作成しているときに、GTalkからのメッセージ受信通知、Twitterの発言通知、アプリケーションのアップデート通知等々、ありとあらゆる情報がどんどん通知されてきて、それが容易にタスクバーで把握可能です。

今までの携帯電話では考えられない世界だと思いませんか?Googleはリアルタイムな通信基盤を構築しようとしています。Androidはその一部です。この周辺技術を理解していくとリアルタイム通信というのが重要であることがわかるので、関連するChrome OSGoogle Waveも紹介したいところですが、紙面の都合で次回以降に回します。

魅力2 オープンな携帯電話

次の魅力はAndroidがオープンソース、ロイヤリティーフリーであるということです。

Androidは携帯電話のためのものだけではありません。Androidはフルスタックソフトウェアであり、OS、ミドルウェア、アプリケーションフレームワークをモバイル機器(組み込み機器)向けに提供しているのです。メーカーは携帯電話をはじめとするモバイル機器を開発し、その上で動くアプリケーションを3rdパーティーが開発することが可能になるというわけです。

携帯電話だけでなく、情報家電、車載機器、セットトップボックス等がAndroidへの新しいプラットフォームへ移行することで、OSのロイヤリティがかからない上に機器による開発環境の違いも吸収できることになります。エミュレーターも充実しているので開発コストも低く抑えられることでしょう。そして言語はJava。私はJavaが好きなのでかなり魅力を感じています(笑)。

How Android works: The big picture」の記事のAndroidのシステムアーキテクチャを見てみると、Web開発者であればご存知の「WebKit」や「SQLite」を初めとするライブラリとアプリケーションフレームワークがありとても魅力的です。携帯電話の開発といえばDoja、MIDP、starプロファイルが頭をよぎるのですが、自由度や機能面でいうと全然違います。「All application are created equal」というように、電話帳アプリケーションだって置きかえられるくらいの高い自由度を備えてます。もちろんユーザインタフェースはiPhoneにはまだまだ及びませんが、洗練されたUIを作る事も可能でしょう。

これはAndroidプラットフォームの魅力のほんの一部です。これからどんどん新しい端末がローコストで出てくるのが楽しみですね。

魅力3 強力なアプリケーション

AndroLib.comの統計によると2009年10月1日現在、約1万2000本のアプリケーションが登録されています。統計を見ると面白いですね。毎日約50もの新しいアプリケーションが生まれています。

そして、未完成の携帯電話をどんどん自分に最適な機能を持つアプリケーションをインストールしてカスタマイズしていくわけです。今までの携帯電話より愛着が湧きますよね。アプリケーションは新着ランキングを見て気に入ったアプリケーションをインストールするのが一番良いと思います。

最後に私の愛用しているアプリケーションの一部を紹介して終わりにしたいと思います。
Android携帯をお持ちの方はこのアプリをインストールしてみてください。

OpenWnnフリック入力対応版
 フリック入力に対応しており、素早く文字入力が可能です。最近は絵文字入力に対応しています。(標準だと絵文字入力はできないんです)

Any CutUltimateFaves 
 さまざまなショートカットを作成できます

PicSay 
 画像編集ならこれが便利です

e電話帳
 今までの携帯電話の検索に近い形になるので重宝しています

QR我風 
 自分の連絡先のQRコードを作成できます

路線ドロイド 
 乗り換え案内はこれを使っています

WiFi OnOff 
 WiFiの切り替えを簡単にすることができます

2009-09-24

Mock と Stub について

はてなブックマーク   livedoor clip

初めまして、リコーの沖田です。この度私もこの blog を書くことになりました。以後よろしくお願いいたします。

みなさんテストは好きですか?私も含めて私の同僚は皆テストが大好きなので、しばしばテストの議論で白熱しすぎてしまいます。今日はそのテストの中から Mock(モック) と Stub(スタブ) について書いてみたいと思います。

Test Double

まずテストにおける Mock と Stub についてですが、これらは Test Double という概念の一部です。Double とは代役という意味で、テスト対象となるシステムが依存する外部のコンポーネントの代わりに、それらしく振舞ってくれるコンポーネントを代役として利用しようということです。

例えば Web アプリの Controller の単体テストがしたい場合に、Model の実装が完了するまでテストができないっていうのでは大変ですよね。そこで、Model の Mock や Stub を利用することにより、Model の影響を受けることなく、Controller の単体テストだけに専念することができます。

このように Test Double を利用することで、依存を排除し、テストを効率良く作成・実行することができます。

Test Double についての詳細は「xUnit Test Patterns(Gerard Meszaros, 2007)」という本や
http://xunitpatterns.com/Test%20Double.html
に書いてあります。なお、Mock と Stub 以外にも Spy, Fake, Dummy 等の Test Double もあるとのことです。

Mock と Stub の違い

さて、では Mock と Stub の違いはなんでしょうか。

先ほどの Test Double の定義では、Mock はテスト時の呼び出しの仕様をあらわしたもので、Stub はテスト時の呼び出しに対して決められた値を返すものです。

これを単に Mock は呼び出しの verification をして、Stub はそれをしないと認識してしまうと、どのようにして Mock と Stub を使い分けるかわからなくなってしまいます。さらにいえば、Mock と Stub が区別されている理由さえわからなくなります。

Mock と Stub の違いはテストの観点の違いです。相互作用(振る舞い)中心のテストに利用するのがMockで、状態中心のテストに利用するのがStubです。

相互作用中心のテストとはテスト対象のシステムと外部のコンポーネントとの間で正しいやり取りがされるかのテスト、いわばプロトコルのテストです。外部のコンポーネントは Mock により置き換えられ、システムから正しい呼び出しがなされているかを監視します。
したがって、例えばWebアプリの Controller の単体テストにおいて相互作用中心のテストが正しく行われてパスしているならば、Model が正しく実装された時に Controller が正しく動作するということが、Model が実装されなくても保障されます。

一方状態中心のテストとはテスト対象のシステムが正しい結果を返すかというテストです。したがって、最終的に返ってくる結果だけが重要なのですが、その過程において外部のシステムとの統合が面倒な時に Stub を利用してテストを簡単にします。なので、 Stub は信頼できる外部のコンポーネント(例えばDBとかLDAPとか)に対して適応するといいと思います。

相互作用中心のテストと状態中心のテスト

さて相互作用中心のテストと状態中心のテスト2種類の観点でのテストが出てきましたが、どちらのテストをやればよいのでしょうか? 結論からいうと両方やるべきです。

例えば BDD(Behavior Driven Development) では、その名前から振る舞いすなわち相互作用中心のテストだけをやればいいと錯覚してしまいます。たしかに Mock を利用して振る舞いの仕様を作成するのが BDD での spec 作成の大半を占めます。しかし、仕様書の観点からすると、振る舞いすなわちアルゴリズムだけの記述だけでは不十分で、入出力の例が書いてある必要があります。その入出力例を記述するのが状態中心のテストなので、両方やるべきなのです。

もちろん、コンポーネント毎に相互作用中心のテストと状態中心のテストのどちらに比重をおくべきかを考えることが重要になるわけですが。

RR(Double Ruby)

さて Mock を使った RSpec のテストを実際に書いてみようと思います。RSpec には標準で Mock が用意されているのですが、今回は RR(Double Ruby) というライブラリを利用してSinatraで作る blog のテストを書いてみようと思います。(記事の都合上基本的な Sinatra, RSpec, DataMapper の説明は省略しています)

まず RSpec の Mock を RR の Mock で置き換える設定をします。これで rspec の Mock が RRの Mock に置き換えられます。また、Sinatra のテストをするため、Rack::Test::Methods を include しています。

Spec::Runner.configure do |config|
  config.include Rack::Test::Methods
  config.mock_with :rr
end

次に、’/posts/1′ にアクセスすると、DataMapper により ID が 1 の記事が読み込まれ、その article が表示されるという仕様を書いてみます。

describe '/posts/:id' do
  it 'should load Posts with id' do
    mock(Post).get!('1') { mock(Post.new).article { 'article of 1' } }
    get('/posts/1')
    last_response.body.should == 'article of 1'
  end
end

Post は DataMapper で作成した Model です。その Post の Mock に対して get! が呼ばれることを verification しています。中括弧の中は article が呼ばれた時の返り値を指定しています。では、このテストが落ちることを確認したら実装をします。

def '/posts/:id' do
  post = Post.get!(params[:id])
  post.article
end

実装後テストをしてみると、実際にDBに値を入れなくても(DBの状態にかかわらず)テストが通ります。前述したように外部への依存がなく効率の良いテストができる ことが実感できます。

ちなみに RSpec の Mock を利用する場合はこのようにかけます。

Post.should_receive(:get!).with('1')\
  .and_return mock(:article => 'article of 1')

RR の方が実際のコードに近く、かつ短く書けるので素敵です。さらにいえば mock(Post.new) の部分を Machinist を利用して

Post.make_unsaved(:article => 'article of 1')

と書くとさらに素敵なのですが、Machinist については次回以降ということにします。

また、私の同僚が RR は RSpec に比べて matcher の機能が足りない部分があるので、既存の rspec での Mock を RR に置き換える時に困ることがあると言っていました。そこで RSpec と RR(0.10.2) の matcher を見比べたところ例えば hash_including という matcher で差異が見られました。

RSpec の matcher では hash_including は

Foo.should_receive(:color).with(:red, :blue => false)

のように Hash の key の存在確認と key value の組み合わせの両方が指定できます。
一方、RR では

mock(Foo).color(:red => true, :blue => false)

のように key value の組み合わせでしか指定できません。

これの解決には例えば satisfy という matcher を利用するか、RR::WildcardMatchers::HashIncluding.wildcard_match? を書き換えるかすればいいと思います。ここでは、satisfy を利用する例を紹介しておこうと思います。

def hash_has_keys(*args)
  satisfy do |hash|
    args.all? do |arg|
      arg.is_a?(Hash) ? arg.all? {|k, v| hash[k] == v } : hash.has_key?(arg)
    end
  end
end

これを以下のような感じで hash_including という matcherの代わりに利用すれば、

mock(Foo).color(hash_has_keys(:red, {:blue => false, :yellow => false}))

RSpec の hash_including と同様に使えます。

ただし、こうすると matcher が失敗した場合に、

unexpected method invocation:
  color({:red=>true, :green=>false, :blue=>false})
expected invocations:
- color(satisfy {block}) 

と表示されてエラーメッセージがいまいちわかりにくくなってしまうので注意してください。

長くなりましたので、最後にまとめますと、本エントリでは

  • Test Doubleの定義に基づく Mock と Stub の違い
  • Mock を利用する相互作用中心のテストと Stub を利用することができる状態中心のテストの使い分け
  • 相互作用中心のテストを RR の Mock を使って書く例および RR の tips

について説明しました。

2009-08-18

モダンなXML出力方法について

はてなブックマーク   livedoor clip

XHTML2は残念なことになってしまいましたが、HTML5にしろフィードにしろ、XML構文が重要なことにはかわりのない昨今みなさんいかがお過しですか。ずいぶんとエントリの間隔が空いてしまって申し訳ありません。

さて、XMLの出力には

  • to_xml()のようなメソッドを呼ぶ
  • RubyのBuilderのようなDSLを使う
  • テンプレートエンジンを使う
  • 手書き

などの方法が考えられますが、今回の本題はこれらの方法によって生成される最終的なXML文書(インスタンス)はどう表現されるべきか、についてです。

エンコーディングはUTF-8で

これはもう説明不要ですね。UTF-8が処理できない環境というのは本当に少くなりました。モバイルや組込み等でどうしても対応できない場合以外はUTF-8を使うのが望ましいと思います。

実体参照の件

実体参照というのはXML内で文書の一部に名前を付けて再利用する仕組です。たとえば「&」という文字(文字はXML文書を構成する最小単位です)を出力するときは、XML文書に直接記述することができないので「&amp;」とエスケープします。XML仕様では&や<など5種類の実体参照がビルトインで定義されていますが(これを定義済み実体と呼びます)、DTDを使うことで自分で好きな実体参照を定義することもできます。たとえばハートマーク(♥)を出すために「&hearts;」と書くというのはよく知られているでしょう。この「&hearts;」という実体参照はHTMLのDTDで定義されています。

HTMLのDTDで定義されている実体参照を使うためには、DOCTYPE宣言(「<!DOCTYPE」で始まるものです)でHTML/XHTMLのDTDを読み込まなければなりません。HTMLで定義されている実体参照は個別のファイルで定義されているので()、特にHTMLでない文書型を使う場合でもこれを個別に読み込めばHTMLで定義されている実体参照を使うこともできます。

しかし、モダンなXML文書ではDOCTYPE宣言を付けないものもあります。たとえばAtom仕様にはDTDがありません。仕様にDTDがないとしても、DOCTYPE宣言を付けることは可能ですが、通常のライブラリはAtomフィードを出力するときにDOCTYPE宣言を付けないのが一般的です。

このような場合にハート記号を使いたくなったらどうすればいいのでしょうか。まずはその文字を直接入力できないか考えてみましょう。ハート記号を表示するのにわざわざ実体参照を使う必然性はないのです。エンコーディングがUTF-8であれば、その文字をそのまま出力すればよいでしょう。

もし何らかの理由でどうしても直接入力したくない場合は、実体参照を使うのではなく数値文字参照を使うのがよいでしょう。たとえばハート記号は&#9829;です。

xml:baseは使わずに絶対URIを利用する

xml:base属性を使うと相対URIを解決するための基底URIを設定できるのですが、
これをきちんと解釈してくれないクライアントやライブラリがいるのも事実です。
なので、ハイパーリンクとしてURIを埋め込むときには、たとえ同じサイト上
のリンクでも絶対URI(「http://」で始まるURI)を使うのがよいでしょう。

2009-03-04

第一回 ライブドア テクニカルセミナーに参加しました

はてなブックマーク   livedoor clip

日野原です。

週をまたいでしまいましたが、先週末にlivedoor主催のテクニカルセミナーに参加してきました。
検索するとすでにいろいろな方のレポートが見つけられますね。月曜日は別に片付けなくてはいけない仕事があったので相当出遅れてしまいました。やっぱり週末に清書しておけば良かったかと反省しております。…いや、休日に仕事しちゃいけません。ああ悩ましい。
ということで、あまり遅くなってもつまらないのでセミナー中に取っていたメモを整形だけしてスピード優先で掲載します。
後日発表資料が公開されたら、資料と一緒に見ていただけるとよりわかりやすいかと思います。
なお、私が聞いて解釈したとおりに書いているので、間違ったことが書いてあっても発表内容が間違っていたとは限りませんので察してください。指摘をいただけるとさらに幸いです。

セミナーは社員の方の発表はバックエンドの話が中心で、非常におもしろかったです。また、ゲスト講演はWinnyの金子さんとその弁護団事務局長の壇さんで、普段聞けないような話が聞けて刺激的でした。

ただ、壇さんの講演に関しては専門外の法律の話である上に、普通の文字量のプレゼン資料を「高橋メソッド風に(本人談)」進められてしまったのでメモを取る方も追いつけず、十分な報告ができません。申し訳ありません。

開会の挨拶

livedoor テクニカルセミナー 2009 Open & Share
株式会社ライブドア技術部会

司会 伊勢さん
足下の悪い中ありがとうございます。

アンケートにご協力ください。

「Open & Share」だから撮影や録音はご自由にどうぞ。でもうるさくて迷惑なのはやめてください。
ブログに書いてもオーケーです。
ちなみにねとらじで生中継中なので、質問などオフレコにしたければその旨言ってください。
何かあったら黒いストラップの社員まで。

「Practical Cicindela 実践ちちんでら」

メディア事業部 ブログビジネスユニット 井原さん

最近edgeラボで公開したレコメンデーションエンジン。
実際に運用中で実践経験豊富なので、その運用例を紹介する。

井原さんは「ちちんでら」と呼んでいるけれど、社内では「ししんでら」と呼ばれている。

レコメンデーションは脇役なので、応答速度が重要。
新しいデータがすぐに反映されるかも重要なので夜間バッチ処理とかはよくない。
その辺を考慮した作りになっている。

入力はいったんバッファに入れられ、バッチ処理で一次テーブルに入る。
これは入力の処理を軽くするためで、一次テーブルに後からいろいろ情報を足していく。
集計はできる限りバッチでやって中間集計テーブルを作る。
その後、現在使用中のテーブルと中間集計テーブルをrenameして入れ替える。

応答時間は平均数ミリ秒、ちょっと複雑な処理でも20~30ミリ秒で返すことができている。

livedoorクリップでの事例

一日1万2千件くらいの記事がクリップされる。
ひとつのページに4、5件のクリップがついたころが一番見られるので、そのころまでにレコメンドを準備する。

最新1時間、通常のレコメンド(このページをクリップした人はこのページもクリップしています)、タグを考慮したレコメンド(このタグはこのページにも付いています)の3段階に分けてレコメンドを作る。

「~のトップページ」とか、いかにもクリップされがちなページはレコメンドしない。

同人ダウンロード販売サイトでの事例

同人ダウンロード販売サイトではレーティングに基づいたレコメンドや、独特の配慮をしている。
たとえばほかの作家を薦めすぎないとか、一般向け商品のページで成人向け商品を薦めないとか。

レーティングの得点を正規化したり、フィルタリングをしたりという処理が入る。

レコメンド経由の販売が4%くらいあるらしい。

you bride(結婚情報サービス)での事例

アクションをまったくしない人同士をつなげる工夫をしている。

たとえば、「Eさんを選んでいる男性はDさんを選んでいる」のほかに、「同じ属性の同姓が選んでいる異性と、同じ属性の異性」を薦める。

具体的には、何もアクションを起こさないCさんと同じような属性の人たちが選んだ女性Eさんと同じ属性のFさんをCさんに薦める。

この方式の採用により申し込みが6.5%アップ、交際率が2~4%アップした。

ストレートなレコメンドを採用したときには3割とか1割とか上がってるので、それに比べると地味だけど。

伊勢さんより補足。

livedoorのedge ラボで公開しているから使ってね!

質疑

  • Aさん
    • 中間結果の数は制限かけているか?
      • フィルタリングのときにパフォーマンスの目的でlimitをかけることはある。
    • ランダム要素はあるか?
      • レコメンドの結果にランダム要素はないけれど、10件出た中からランダムで2件薦めるとかで一見ランダムに見える工夫はしている。
  • Bさん
    • ニュースのおすすめに使ってる。apacheのログベースでやっているけどドキュメントがまだ無い。
      • ごめんなさい。
    • IPアドレスをユーザとしてみているけど、なんかいいやり方は無いか?
      • mod_usertrackでcookieベースでやっている。これだとcookieが文字列で入ってくるが、それをintに変換して性能向上の工夫をしている。
    • 特にモバイルだとIPが同じになるが何か良い方法はないか。
      • モバイルではやってないので「こうすればいい」というのはない。アプリ側でセッションIDを使うとかするのかな。
    • HTTPだと遅いかと思ってDB直でたたこうとしてるけど、どうしてる?
      • livedoorでもHTTPでやっている。

「ライブドアブログ公式攻撃マニュアル」

引き続き井原さん。

2chのコピペブログへの無差別DOS攻撃があった際、社内勉強会で話した内容から。

2008/3/7 22:00攻撃が開始された。

しかしすでに対策済みで、10秒くらい連続アクセスするとアク禁になるようにしてあった。

そのとき、LDブログチームは送別会中だった。

結果、2chでほめられた。

どうすれば落ちるかの部分は社外秘のため発表できません!

まとめ

  • ブログでお金儲けたっていいじゃないか。
  • livedoorのブログサーバはそんなに簡単に落ちるほど単純じゃない。

伊勢さんより補足。

去年の2008/07号の日経SYSTEMSでコラムになっているので読んでください。

「インサイド livedoor Blog」

メディア事業部 ブログビジネスユニット 垣内さん

agenda

  • サーバ構成
  • apache独自モジュール
  • 管理画面
  • 新サービス

2003年11月オープンで、5年ちょっと経つ。
キャッシュとかを抜いて16TBのデータがある。

サーバ構成

構成図をこんなに細かく出していいのかな。
(発表資料が無いとこのあたりは意味不明です、すみません)
この構成図からは検索サーバ等は省いてる。
CMSは管理画面

portal/cmsはlivedoor標準な構成になっている

blog側(攻撃をうけやすいところ)
blog-wwwではApache2.2 mod_ldblog_mapper2 mod_include (SSI)を使っている。

blog-app (コメント、トラバを受ける場所)
apache2.2 mod_proxy_balancerも使っている。後はsledge。

DB
mysql
user-cluster DB
user単位のパーティショニングで1~2万ユーザ×150クラスタ。
クラスタリングは手動で、運用でカバーしている。

ストレージ
商用でNFSマウント。
DBの内容をhtmlに変換した静的ファイルをキャッシュとしておいてる。

イメージストレージ
容量を安く増やすために自作している。
WebDAVでアクセスする。
どのサーバに何のファイルがあるかをapache + 自作module + mysqlで解決している。
解決の部分とかreplicationはapache moduleだから強い。
ほかのサービスで新版を試験運用中。

サーバ構成のまとめ

  • OSはBSDとかLinuxとかばらばら。
  • 回数の多い処理はmoduleにして性能を上げている。

独自モジュール

mod_counter
画像カウンタ。

mod_ldblog_mapper2
URLからNFSマウントされたストレージのパスを計算する。
memcachedやDBから引いた結果を環境変数に入れている。
キャッシュがあれば mod_rewrite でキャッシュに飛び、無ければblog-appに行く。
キャッシュがあればほとんどApacheで完結するのでDDOS攻撃とかに強い。
ストレージに入った画像はgzipで圧縮されるけれど、その辺もの処理もフロントでやっている。

静的ファイルキャッシュについて
htmlファイルを全部いっぺんにキャッシュしてしまうとよく変更される部分(新着記事、新着コメント)にキャッシュ再構築の頻度が依存する。
→個別ファイルにしてmod_includeでSSIしている。

キャッシュの再構築はファイルを消すだけ。作るのはアクセスがあったとき。

お知らせメールなどqueueの必要な処理には、TheSchwartzを使っている。

新管理画面

今までEUC-JPだったのをUTF-8にした。
蓄積データも新規ユーザはUTF-8。

文字コード多重化
blog単位でcharsetを切り替えられる様になっている。
マルチブログで二つのblogを持ってる場合、EUCとUTF-8と共存したりできる。
複雑になったので、ORMでアクセサを切り替える仕組みを仕込んだ。

URL変更
シングルサインオンをlivedoor.comと分離するのが目的。
OpenID連携にした。
新サービスの準備のため。

新サービス

livedoor Blog ASP

ブログのドメイン名を変えられる。
既存のlivedoor Blogのいろいろな機能と素直に連携できるのが売り。

OpenIDについて
RPをやるわけではないが、裏で使っている。

AXでいろいろ情報を交換している。sregではなくAXを使ったのは、sregだとuser_idをうまく取れなかったため。

IEのURL長の制限の対策もしている。

パートナー募集中。

質疑

  • Cさん
    • 画像のサーバが別になっていることに関して、blogと一緒に投稿された画像には公開範囲の限定があると思うけど、mod_accesstokenを使っているか?
      • blogではmod_accesstokenは使ってない。
    • mod_accesstokenの導入事例はある?
      • コスプレサイトのCure
      • 会社で見るときには注意してください。ファンクラブに登録すると効果がわかります。
  • Dさん
    • 守るマニュアル教えてください。
      • 資料中にもあったように、10秒の連続アクセスで防御される(mod_dos_detectorみたいなもの)
      • 他にはYAPC ASIA2008 で発表したスパムチャンプルーも使ってる。
      • 詳細には言えない。ごめんなさい。
  • 伊勢さん
    • blog ASPはいつから?
      • 4月から

「ライブドアのネットワークとトラフィックパターン」

ネットワーク事業部コアネットワークグループ 市川さん

agenda

  • DATAHOTEL(データセンター)ネットワーク概要
  • iDC ISPの立場から見た傾向
  • 最近の話題
  • IPv6

概要

DATAHOTELではiDC(データセンター)、公衆無線LAN、企業向けISP(ライブドアBB)とトランジットサービスをやっている。

サービスごとに別に接続してるのでどれかに障碍があってもほかには影響しない。

DATAHOTEL内の冗長化はメッシュ型だが、ライブドアBBは拠点が離れてて難しいのでリング型になっている。

トラフィックの傾向

一般的なプロバイダのトラフィックはユーザへの下りがメイン。
平日

  • 20時から始まり24時がピークで、そこから減少。
  • 朝のメールチェック時に小さなピークがある。
  • 昼休みにもちょっとピーク。

休日

  • 昼の12時から増え始めて24~25時がピーク。

P2Pのトラフィック

  • 上りと下りがほぼ同じ。
  • livedoorでは帯域制限は基本的にかけていない。

iDC(データセンター)のトラフィック

  • ISPと逆になる。
  • ピークになる時間帯は同じ。

ルータのメンテナンスをするのはトラフィックの落ち込む2時から5時くらい。

休日は一日中ネットをやっているユーザが多いらしい。
「土日」じゃなくて「休日」。

トラフィックの例:コンテンツダウンロード販売系のラック

「これがピーク」と言う時間帯が無い。一番少ない時間帯でもそこそこの量が流れている。
アキバ系は強い。
ピークで1G弱。

トラフィックの例:ポータル系のトラフィック

livedoor Blog

  • 0時過ぎの落ち方が緩やか。ユーザに夜更かしする若者が多いのか?

livedoor ねとらじ

  • お昼休みピークが無い。
  • 就業時間後にまた下がる。
  • 仕事中にBGMとして聴いてるのかな?
  • 夜に最大値を迎えるのは変わらない。

インターネットのトピック

1/31~2/1にかけてGoogleが正常なサイトをフィッシングと誤認する障碍が発生したが、livedoorのトラフィックにはほとんど影響はなかった。

年末年始、12/31~1/1には夜中の大きなピークが無い。0時前後に「あけおめ」書き込みか?という小さなピークがある程度。

2/17 01:30~02:30ごろにインターネットに障害。
上位回線で回線が切断されたりしたらしい。
livedoorの上流では幸い影響なかったが、インターネットって意外と脆弱だという印象を受けた。

IPv6に関して

実験だからお客さんには出してないが、WIDEとつながっている。

2ちゃんのIPv6板を運用している。IPv6がreachableな人は書き込みができるから書いてみてください。

IPv6のトラフィックは少ない。20kとか1Mとか、バックボーンでは考えられない量。

Tokyo6to4というprojectに参加してみてね。

伊勢さんより補足

  • ネットユーザって初詣行くんだね。意外と信心深いのかな。
  • DLサイト、平日は少なくて休日は多いってことから同人ファンはサラリーマンが多いってのがわかるね。

質疑

  • 伊勢さん
    • 18:00-20:00のトラフィックの落ち込みの原因はわかる?
      • ご飯?
    • 正解。「飯落ち」と呼ばれる現象。
    • blogのトラフィックってお昼にキューンとあがる。みんな見ているんですね。

「P2Pコンテンツ配信技術の現状」

金子さん

自己紹介
(株) Dreamboat 技術顧問
コンテンツ配信システムSkeedCastの開発中。

agenda

  • P2Pの紹介
  • WinnyとSkeedCast
  • SkeedCast

P2Pの紹介

P2P(型ファイル共有ソフト)の発展
第一世代 Napsterなど
→ハイブリッド(P2Pなのはデータ転送のみ)
第二世代 Gnuteraなど
→pure P2P(検索もP2P)
第三世代 Winnyなど
→キャッシュ型(転送キャッシュ)
第四世代
→商用(P2P教科書(インプレス)による)
インプレスによる分類は技術的な分類ではない。
金子さんによると第四世代にはSkeedCastやWinny2が該当し、ノード群をサーバとみなしてクラサバ的挙動をする

Winny→SkeedCast

Winny1
→ファイル共有ソフト
Winny2
→もっと汎用的なものを目指した。BBSをP2Pでやる。BBS部分だけ第4世代的。

Winny2ははるかな高みを目指していた。
Winny1の落穂ひろいにWinny2の要素を入れていったのがSkeedCast

SkeedCastの特徴

P2Pノードがサーバになる→分散サーバとP2Pのハイブリッド

Winnyにも速度による上下関係はあったが、SkeedCastではもっっとノードの役割を意識している。

アップローダ(投入ノード)、中継(共有ノード)、ダウンローダ(レシーバ)の3つがあり、ダウンローダは中継もできる。

投入ノードは課金とかDRMとかを考えてる。
共有ノードはUNIX、レシーバはWindowsで動く。

共有ノードはコントロールノードからコントロール可能。
キャッシュコントロールとか。
ある人気コンテンツを優先的にキャッシュに入れるとかもできる。

P2Pは不安定という弱点があるので、サーバのトラフィックが予定外に増えすぎたときにユーザノードを中継ノードとして動かしてしのげる。P2Pのいいところ。

SkeedCastはファイル共有ではなくコンテンツ配信のシステムなので、普段はCDN的に動作して、サーバが大変なときにP2P的に動作して凌ぐ。

特徴
Winnyはハッシュを多用していたが、SkeedCastは代わりにデジタル署名を多用する。
ホワイトリストのフィルタリングやキャッシュのライフタイム管理に使う。

コンテンツにデジタル署名をする。
コンテンツプロバイダが秘密鍵を持ち、流通段階で署名をチェックして問題があれば流通させないといったコントロールが可能。
公開鍵をホワイトリストとして使う(後述)

流通フィルタリング
Winnyはダウンロードフィルタ、無視フィルタ(ブラックリスト)の二つのフィルタがあった。
SkeedCastはダウンロードフィルタ、無視フィルタ、許可フィルタ(ホワイトリスト)の三つのフィルタを持つ。
許可フィルタは公開鍵の束なので偽装は不可能。

再中継の制御
Winnyはどこかにキャッシュがあると再中継可能だが、SkeedCastは投入ノードがコントロール可能。

レシーバ(PCに入れるソフト)
Webと連携する。
ブラウザでリンクをクリックすると起動する。
SkeedReceiverはWebサーバとして動作し、裏でCacheとしてダウンロードする。
また、SkeedReceiverがストリーミングサーバになって、メディアプレーヤで視聴する。ストリーミングだけどローカルにあるからシークもできる。

展開可能フラグがたってればCacheをファイルに展開することもできるよ。

視聴のコントロールにはDRMを使う。

言ってみれば巨大なProxyになる。

(株)Dreamboatではほかにもいろいろやってるから使ってください。
社内ネットワークでの配信もできる。

質疑

  • 伊勢さん
    • receiverはプラグイン? デーモン?
      • 常駐はしない。ブラウザのプラグイン経由で必要なときに起動して、終わったら終了する。タスクバーにも出るので、気になったら消すこともできる。
    • ヒントをもらった。ありがとうございます。ストリーミングサーバのランニングコストは馬鹿にならないから。
  • Eさん
    • ダウンロード可能性は投入ノードで管理といってたけど、ダウンロード済みのものをレシーバでストリーミングするかどうかはチェックするのか?
      • ダウンロード済みの物はWinny的な感じで処理している。詳細は想像してください。

「デジタルコンテンツ配信の法的問題」

壇さん
金子さんの弁護団の事務局長

P2P教科書は私も書いているので買って読んでください。

cocologでblogを書いています。

Livedoorには壇さんを誹謗中傷するwikiがある。

ネットのせいでコンテンツ産業は損害を受けてるってホント!?

CDやレコードはの売り上げは落ちてるけど、着うたとか増えてるから消費シフトと見るべきではないか。
映画の興行収入は年ごとに上がったり下がったりしている。
結論:ネットは関係無いんじゃないか? むしろうまく使えば利益もたらすんじゃないか。

著作権について

厳密には著作権と言う名前の権利は無い。
いろいろな法律で規定されてるいろいろな権利をまとめて著作権と総称してる。

Winnyは大容量コンテンツの配信を可能にした。
それまでは映画一本流すなんてできなかった。

(以下、追いつけなかった部分はキーワードのみで失礼します。)
プロバイダ責任制限法
カラオケ法理とか
私的目的での複製
著作権侵害はP2Pに固有の問題ではない。ニコ動とか。
アメリカ:セーフハーバーとかDMCAとか
クラブキャッツアイ事件
カラオケ法理がP2Pのファイルローグに適用された。

開発者、提供者が刑事事件の当事者になったのは日本、韓国、台湾のみ。
有罪になってるのは金子さんと台湾の1件(Kuro事件)のみ。
金子さんは確定してないので最高裁まででひっくり返すよ!

Winny事件地裁判決における幇助成立の基準
証拠として提出されたWinnyの利用状況がなぜかアンケート結果。

よくある誤解
Winnyに匿名性があるから有罪?
→匿名性は関係ない。
金子さんの目的が著作権侵害蔓延だったから有罪?
→そんなことのためにわざわざソフトを開発するわけ無い。

IP-TVのニーズ
海外で日本の番組を見たい
東京ローカルを地方で見たい。
しかし地方免許制度の枠組みでしか見られない。

IP-TVの訴訟
録画ネットと選撮見撮は著作権者側が勝っているけど、まねきTVとロクラクはサービス提供者側が勝っている。

著作権の問題
カラオケ法理の適用範囲
非親告罪化
ダウンロード違法化
日本版フェアユース(ぜひとも立法化すべき)
ただし裁判所の判断にゆだねることになるので危険性はある

質疑

  • Aさん
    • 日本の著作権関連団体は強い主張を繰り返していて、徐々にその主張に引きずられている気がするが、彼らの言うような方向にこのまま行ったらどうなる?
      • アメリカから制度変えろといわれて終わるんじゃないか。
  • 伊勢さん
    • SkeedCastと連携しろなんてあったけどメディア事業部の担当役員さん、どう思う?
      • やりたい。

閉会の挨拶

代表取締役社長 出澤さん

みなさん、雨の中ありがとうございます。
金子さん、壇さん、ありがとうございます。
新規サービス(最後の質疑のSkeedCastの連携)も決まってうれしいねw

Open & Shareという企業理念でやっています。

今後ともがんばるのでよろしくお願いします。

2009-02-09

はてなダイアリーAtomPubレビュー: その3 設計編

はてなブックマーク   livedoor clip

さて、このシリーズも今回で最後です。遅れに遅れて申し訳ありません…ちなみに昨年の12/22付けで仕様書が改訂され、私が指摘したところが直っていました。どうもありがとうございます>中の人

最後は設計編です。

今回、私がはてなダイアリーAtomPubの仕様を見ていて、設計面で疑問に思ったのは次の2点です。

  • X-HATENA-PUBLISH HTTP ヘッダ
  • エントリ文書の対称性

以下ではそれぞれについて具体的に検討してみます。

X-HATENA-PUBLISHヘッダ

いくつかのブログでも指摘されていますが、はてなダイアリーAtomPubではapp:draft 要素を利用しません。

はてなダイアリーAtomPub仕様書によると、以下のような理由があるそうです。

はてなダイアリーAtomPubでは、AtomPubで規定されているapp:draft要素を使用しません。はてなダイアリーでは日記エントリと下書きエントリを別々に管理しています。そのため、日記エントリと下書きエントリを一つのコレクションとして扱い、app:draft要素によって下書きかどうかを区別するような構成は不自然になります。むしろ、下書きエントリを日記エントリとして公開するという操作を行う仕組みであるほうが自然であると考え、以下のような方法をとっています。

なるほど、コレクションを分ける際の実装上の都合なわけですね。

これは、これで一つの設計判断だと考えます。コレクション間のエントリのコピーや移動は AtomPub では定義されていませんので、何らかの拡張仕様が必要になります。WebDAV の MOVE を使うなど、マニアックな方法も考えられますが、独自ヘッダは悪くない判断だと思います。

ただし2点、よくない点があります。

1点目は、X-HATENA-PUBLISH の値の仕様です。このヘッダが取りうる値は「1」なのですが、いったいこれは何を意味するのでしょうか。おそらく内部的に Perl の “1″ としてそのまま実装されているのでしょうが、これはたとえば “true” にするなど、もう少し可読性を考慮した方がよいでしょう。

また、”1″ 以外の値を値を入れたときの挙動も不明です。たとえば “111″, “01″, “hoge” などを入れたらどうなるのでしょうか。

2点目は、PUT メソッドの挙動です。

はてなダイアリーAtomPubの仕様では、下書きを公開するときは既存の下書きエントリに対して X-HATENA-PUBLISH を 1 にした PUT を送ります。このリクエストが成功すると、レスポンスとして 201 Created が返り、Location ヘッダに公開されたブログコレクションのエントリが入ります。これは少し違和感を感じるところです。

HTTP/1.1 における PUT の仕様は以下のとおりです。

PUT メソッドは、同封されたエンティティを供給される Request-URI の元に保存するように要求する。 Request-URI が既に存在するリソースを参照している場合は、同封されるエンティティはオリジンサーバにあるそれの修正版とみなされるべきである。 Request-URI が既存のリソースを指していない場合に、その URI がリクエストしているユーザエージェントによって新しいリソースとして定義する事ができる時は、オリジンサーバはその URI にリソースを作成できる。新しいリソースが作成された場合、オリジンサーバは 201 (Created) レスポンスをもってユーザエージェントに知らせなければならない。既存のリソースが更新された場合は、リクエストが成功し終了した事を示すために 200 (OK) か 204 (No Content) のいずれかのレスポンスコードを送るべきである。

つまり PUT のレスポンスは

  • その URI が存在しない場合はリソースを新規作成して 201 を返す
  • その URI が存在する場合は、リソースを変更して 200 か 204 を返す

となります。これは SHALL (べきである)なので、絶対に従わなければならないことはありませんが、いずれにせよ既存の URI に対しての PUT で 201 が返るというのは、仕様上のグレーゾーンでクライアントを混乱させる可能性があります。

私だったらどうするか

もし私がこの機能を設計するなら以下のように考えます。

下書き機能は AtomPub の app:draft 要素に非常に近い(というかほぼ同じ)機能であるため、いかにこれを活かすかを考えます。今回の問題はドラフト状態を公開すると URI が変更になってしまうところなのですが、これをいかに違和感なくクライアントに教えるか、が課題です。

PUT すると URI が変更される(リソースが移動するイメージ)というのがクライアントからの一番の違和感だと思うので、そこをどうにかするようにしたいですね。そのためには、ドラフトの URI を残しつつ、そのリソースに別の URI (エイリアス)を付与するのが良いように思います。

これには HTTP に標準で付いてくる Content-Location ヘッダを使うことで解決できそうです。この方式を採用したときのリクエスト・レスポンスは以下のようになります。

PUT /yohei/atom/draft/12345678 HTTP/1.1
Host: d.hatena.ne.jp

<entry xmlns="http://www.w3.org/2005/Atom"
    xmlns:app="http://www.w3.org/2007/app">
  <title>テスト</title>
  <app:control>
    <app:draft>yes</app:draft>
  </app:control>
  ...
HTTP/1.1 200 OK
Content-Location: http://d.hatena.ne.jp/yohei/20081224/abcdefg

/draft 以下の URI も依然として有効ですが(おそらく作者だけがアクセスできるリソースになるでしょう)、ダイアリーの方のリソースも別名で有効になります。

もちろん、サーバ側の実装の都合で、ドラフトエントリを公開するとドラフトコレクションからは必ず消さないといけないのかもしれません。上記のような理想形で設計したとおりに実装できるわけではないですが、一つのヒントとして提案してみます。

エントリ文書の対称性

2点目はエントリ文書の対称性についてです。
現在の仕様では、ブログコレクションで日記エントリを更新するとき、以下の
ようなリクエストを送ります。

PUT /yohei/atom/blog/20090203/xxx HTTP/1.1
Host: d.hatena.ne.jp
Content-Type: application/atom+xml; type=entry; charset=utf-8

<entry xmlns="http://www.w3.org/2005/Atom">
  <updated>2009-02-03T00:00:00Z</updated>
  <id>tag:d.hatena.ne.jp,2008:diary-yohei-20081226-p1
  <author><name>yohei</name></author>
  <title>test</title>
  <content type="text">this is a test</content>
</entry>

これに対して、以下のようなレスポンスが返ってきます。

HTTP/1.0 200 OK
Date: Wed, 04 Feb 2009 04:13:59 GMT
Server: Apache/2.2.3 (CentOS)
Content-Length: 694
Content-Type: application/atom+xml;type=entry; charset=utf-8

<?xml version="1.0" encoding="utf-8"?>
<entry xmlns="http://www.w3.org/2005/Atom">
  <updated>2008-12-26T00:00:00+09:00</updated>
  <published>2008-12-26T00:00:00+09:00</published>
  <app:edited xmlns:app="http://www.w3.org/2007/app">2008-12-26T00:00:00+09:00</app:edited>
  <id>tag:d.hatena.ne.jp,2008:diary-yohei-20081226-p1</id>
  <link rel="edit" href="http://d.hatena.ne.jp/yohei/atom/blog/20081226/p1"/>
  <link rel="alternate" type="text/html" href="http://d.hatena.ne.jp/yohei/20081226/p1"/>
  <author>
    <name>yohei</name>
  </author>
  <title>test</title>
  <content type="html">
                &lt;div class="section"&gt;
                        &lt;p&gt;this is a test&lt;/p&gt;
                &lt;/div&gt;
</content>
</entry>

このリソースを再び GET してみると以下のようになります。

GET http://d.hatena.ne.jp/yohei/atom/blog/20081226/p1 HTTP/1.1
Host: d.hatena.ne.jp
HTTP/1.0 200 OK
Date: Wed, 04 Feb 2009 05:13:52 GMT
Server: Apache/2.2.3 (CentOS)
Content-Length: 793
Content-Type: application/atom+xml;type=entry; charset=utf-8

<?xml version="1.0" encoding="utf-8"?>
<entry xmlns="http://www.w3.org/2005/Atom">
  <updated>2008-12-26T00:00:00+09:00</updated>
  <published>2008-12-26T00:00:00+09:00</published>
  <app:edited xmlns:app="http://www.w3.org/2007/app">2008-12-26T00:00:00+09:00</app:edited>
  <id>tag:d.hatena.ne.jp,2008:diary-yohei-20081226-p1
  <link rel="edit" href="http://d.hatena.ne.jp/yohei/atom/blog/20081226/p1"/>
  <link rel="alternate" type="text/html" href="http://d.hatena.ne.jp/yohei/20081226/p1"/>
  <author>
    <name>yohei</name>
  </author>
  <title>test</title>
  <content type="html">
                &lt;div class="section"&gt;
                        &lt;p&gt;this is a test&lt;/p&gt;
                &lt;/div&gt;
</content>
  <hatena:syntax xmlns:hatena="http://www.hatena.ne.jp/info/xmlns#">this is a test</hatena:syntax>
</entry>

ブログコレクションのエントリを編集するときは、以下のことに気をつける必要があります。

  • 追加・更新を行うときは /entry/content/@type は “text” ではてな記法を入れる
  • サーバから取得できる /entry/content は @type=”html” ではてな記法がHTML にレンダリングされたものが入り、/entry/hatena:syntax にはてな記法が入る

これはクライアント側ではてな記法を入れる要素について注意しながら、はてな独自のロジックを実装しなければならない点で、あまり良い設計ではないと言えるでしょう。

この問題を解決する方法はいくつか考えられますが、完璧なソリューションは存在しないと思います。私が即興で考えたソリューションのそれぞれのメリット・デメリットを挙げておきます。

/entry/content に常にはてな記法を入れる

Pros
既存の AtomPub クライアントをほぼそのまま利用可能
Cons
既存のブログクライアントのユーザははてな記法を使うことを理解しなければならない
フィードをフィードリーダーで見たときにはてな記法が出てしまう

/entry/hatena:syntax を常に使うようにする

Pros
content を HTML にできる
Cons
AtomPub クライアントが hatena:syntax を理解しなければ更新ができない

まとめ

先日、会社の超先輩エンジニア(ソフトだけじゃなくてエレキよりの設計も含めて全般されてきた方です)の方と話をしたのですが、そのときに「設計はバランスだよ」という言葉をもらいました。

今回の件でもわかるとおり、設計というのはあちらを立てればこちらが立たず、ということが多く、全てを満足させることは不可能です。

私はよく「REST 的に理想的な設計にするにはどうしたらいいんでしょう」というような質問を受けるのですが、正直なところ理想ばかり追いかけても仕方がないだろう、と思います。REST はあくまでも設計のためのヒントなのであり、それ自体が目的になってしまったら本末転倒です。

高い理想をバックグラウンドに、現実的でバランスの取れた設計をするのが優れたエンジニアなのではないでしょうか。

2008-12-03

プロキシ認証の通し方まとめ その2

はてなブックマーク   livedoor clip

お久しぶりです、日野原です。

読者の皆さんは山本のレビューの設計編を楽しみにしていらっしゃると思いますが、彼は最近何やら忙しく飛び歩いていて、ここの記事を書く時間がとれそうにないのでもう少々お待ちください。

私はこの3ヶ月の間にまたプロキシの設定をしなければいけないケースに遭遇したので追記しておきます。

今回もプロキシの情報は同じとします。

ホスト proxy.example.com
ポート 8080
ユーザ foo
パスワード bar

sudo 編

まず、前回基本編で挙げた

export http_proxy=http://foo:bar@proxy.example.com:8080

ですが、これの適用範囲が広がりました。

rubygems編で「sudo では環境変数を引き継げない」と書いてしまったのですが、これは私の無知で、実は設定で引き継ぐ環境変数を指定できることがわかりました。

設定するためには、/etc/sudoers の env_keep という変数に引き継ぎたい環境変数を指定します。

具体的には、

Defaults env_keep = "COLORS DISPLAY HOSTNAME HISTSIZE INPUTRC KDEDIR \
         LS_COLORS MAIL PS1 PS2 QTDIR USERNAME \
         LANG LC_ADDRESS LC_CTYPE LC_COLLATE LC_IDENTIFICATION \
         LC_MEASUREMENT LC_MESSAGES LC_MONETARY LC_NAME LC_NUMERIC \
         LC_PAPER LC_TELEPHONE LC_TIME LC_ALL LANGUAGE LINGUAS \
         _XKB_CHARSET XAUTHORITY"

という箇所があるので、これの最後の行を

         _XKB_CHARSET XAUTHORITY http_proxy NO_PROXY"

と変更します。
これで rubygems や yum を sudo で動かすときには別途プロキシを設定する必要がなくなりました。

ちなみに /etc/sudoers を編集するときには chmod して編集して戻してなんてせずに、visudo を使ってくださいね。

pear / pecl 編

PHP のモジュールを追加するときに使う pear や pecl は、プロキシ対応を独自でやっているので設定しなくてはいけません。

設定するには次のようにします。

% pear config-set http_proxy=http://foo:bar@proxy.example.com:8080

ちなみに、これで設定すると pecl にも反映されるので pear だけ設定すれば OK です。
試しに確認してみましょう。

% pecl config-get http_proxy
http://foo:bar@proxy.example.com:8080

air 編

Adobe の air のアプリケーションを作って配布するためには .air ファイルにパッケージングする必要がありますが、その際にタイムスタンプを付与するためにタイムスタンプサーバにアクセスします。そう、ここでプロキシの壁に阻まれるのです。

そこで、プロキシの内側にタイムスタンプサーバがある場合は -tsa オプションに url を指定し、無い場合にはやむを得ないので -tsa none と指定してタイムスタンプの付与を省略します。
タイムスタンプを省略した場合は署名証明書の有効期限切れ後にインストールできなくなってしまいますが、正式に製品としてリリースするときまでは考えなくても大丈夫でしょう。

ちなみにこれは http ではなく、RFC3161で定義された Time-Stamp Protocol(TSP) で、318番のポートを使用するので -Dhttp.proxyHost 等で設定してもダメです。

socksで通れば -DsocksProxyHost を指定することでできるかもしれませんが、私の環境ではまだうまく行っていません。

今回は前回の Chrome のような目玉はありませんでしたが、また何かあったら追加していきます。