読書メモ「生物化するコンピュータ」

原題は「Natural Computing」。この本については邦題がすごく良いと思う。本書はいわゆる「IT」「デジタル」な感じとは一線を画す形でコンピュータと関わっている人々に焦点を当てている。

偶然にも、今僕は「遺伝的アルゴリズム」を使ってネットワークからの攻撃を検知するコードを最適化している。この本を読むタイミングとしてはちょうどいい。

生物そのものを研究し、そこから得たヒントやアイデアをコンピューティングに活かすパターン(「遺伝的アルゴリズム」や「ニューラルネットワーク」など)と、生物を使ってコンピュータで行うような処理をしようとするパターン(「DNAコンピューティング」など)が存在している。僕は後者については知識ゼロの状態だったので、この本でとりあえずの入り口を知ることができたという感じ。

20代の頃の一時期、東京大学でバイオインフォマティクスのお手伝いをしていたことがあるので、AGCTなどが出てきたのは懐かしかった。当時は今と比べたら本当に何もできないプログラマだったのでまったくお役に立てなかったことが悔やまれる。研究室の先輩と、秋葉原で30万円くらいでできるだけ高速な自作マシンを組み立て、Perlを使ってゲノムの検索処理などをしていた。今はクラウドが使えるようになったし、きっとバイオインフォマティクスの技術は格段に進化しているんだろうなと思う。当たり前かもしれないがHadoopなども使われているらしい。

p57より引用

コンピュータの授業でといた簡単なパズルの解法は、今の彼の設計技法を予見させるものだった。
(中略)
プロログというプログラミング言語を使って、この課題を書いた。

ちょうど本書のこの部分を読む前の日に、僕はまったく同じパズルを、子供と遊んでいた。「きっとこういうパズルはPrologとかで解けるんだろうな」なんて考えていたら、次の日にまるっきり同じパズルが本に出て来たのでニヤリとした。何かの縁だろうか、などと思ってしまう。宣言型プログラミング言語はとても興味深い存在だと思う。

p70より引用

データベース・プログラムは、K言語として知られるプログラミング・システムに基づいている。ラヴレスはC言語とよく似たプログラミングの授業は落第したが、すぐにこのK言語のパワーに夢中になった。

K言語などというものがあるのか…
Wikipediaの例を見た感じでは、「K言語については見なかったことにしよう」という印象だが、いつかもう少し調べてみる予定。

p77より引用

見つけたかったのは、過去数分間の市場の動きを参考に、次の五分間までの市場の動きを予測できる法則だ。しかし、どのような種類の法則を探せばいいのかがわからない。法則は、例えば一分間隔、五分間隔の市場価格の変動を示す線の勾配といった「属性」で形成される。

普段あまり目にしない、いわゆる「ウォール街のボットたち」を作り上げている人々に関する貴重な情報。僕は金融の世界でコンピュータを使って利益を稼ぐという発想は悪だと思っているが、中でどのような技術が使われているのかについては興味がある。

p96より引用

事故は「非同時的な改良」のために起こることが多い。
(中略)
例えば、1994年、米国の二機の戦闘機が、米国のヘリコプターを撃墜した。その戦闘機は通信システムがアップデートされていたが、ヘリコプターは古いシステムのままだったため、ヘリコプターのパイロットたちには戦闘機からの警告が聞こえなかったのだ。

「非同時的な改良」「局所的な最適化」がもたらす負の側面について。ソフトウェア開発者として心当たりがありすぎる要素である。気をつけるよう自らの肝に銘じておきたい。

p122より引用

彼が提案したのは、DNA鎖を切り貼りする方法でコンピュータをつくり、もっとも古典的な計算モデルである万能チューリング・マシンの動きをシミュレートすることだった。

僕はチューリング・マシンについてろくに理解できておらず、する必要も特にないかと考えていた。しかしこの例のように、非常に基礎的な部分を知っていればこそ応用を思いつく場合がある。やはり基礎は大事だな…と思わされた部分。

本書は部分部分で専門的な単語や解説が登場するため、まったく知識がない場合にはかなり眠くなるというか読むのが厳しいというのが正直な感想。だいぶ飛ばしたページもあるので、そのうち、もう少し知識を付けてから読み直したい。こんなマニアックな書籍を翻訳・出版してくれた講談社の人ありがとうございます。


XSSでインターネットが止まった事件に関連して

意外とみなさんウェブ上の他人管理のウェブサービスにXSSを打ち込むことについては寛容なんですね。これをやめろと言われると「自由が制限される」ように感じる人も多いみたい。僕の感覚とは違うな。週末に会った人に僕の感覚は少数派っぽいと言われてちょい驚いた。

僕と同じくADでオフィスさんに起こったことをリアルタイムで見ている人と、そうでない人の間に統計的に有意な差wがあるのか知りたいですね。

今ってCTFを中心に情報セキュリティのコミュニティはいつになく盛り上がっているように見えていて、僕はその中に入らないけどいいことだなぁと思っている(ADの消失と、その後数年のtessyさんの活動を見ているので)。

でも、ここでそのコミュニティの中の一人が何かしらの弾みで逮捕されてしまったときにどういうことが起こるのか?ということを想像しても、「XSSは打ってOK!」なのかな。

企業の中にはコミュニティの存在とかとは別の世界で生きているサーバー管理者がいて、そのうちの誰かが猛烈に怒ったりすると、XSSとかSQLiとかの区別とは関係なく、トリガーが引かれる可能性がある。今回の銃はISPへの通告程度だったからよかったけど、被害届が発射される可能性も存在するわけで。

まぁ、僕も過去にXSSを打ったことがないわけじゃないので、お前が言うなっていう話ではあるんですが、いちおう少数派の意見として掲載しておきますね。みんな本当に気をつけてください。


HadoopからMongoDBのデータ(BSON)にアクセスする

米国を中心に、オンライン処理はMongoDB、バッチ処理はHadoopという組み合わせが非常にポピュラーになってきている印象である。従来からMongoDB Connector for Hadoopを使うことでHadoopからMongoDBに直接アクセスすることは可能だったが、つい先日、単なる MongoDBのデータ(BSONフォーマットのファイル)がHadoopから読み込めるようになった(また、HadoopのoutputとしてBSON 形式のファイルを使用することも同時に可能になった)。

これはMongoDBのデータベースファイルではなく、mongodumpを使ってダンプされる純粋なBSONファイルであることに注意が必要だ。つまり、HadoopがBSONファイルを読むときには、MongoDBで設定したインデックス等は使用されない。単にデータの塊があり、そのフォーマットがBSONである場合でもHadoopで処理できますよ、ということだ。そのため、条件を指定したデータのみ処理したい場合には、あらかじめ mongodumpする段階でクエリを実行し、目的のデータのみBSONとして取り出しておくとよいと考えられる。

Hadoopが読み込む際には、大きなBSONファイルは当然複数にsplitされる。これがHadoopのウリであり、分散処理が可能となる。試してみた感じでは、分割に使用される一時ファイルなどが入力BSONファイルと同じディレクトリ上に勝手に生成されるようなので、Hadoopが異常終了した際などにはゴミとならないように注意が必要だと思われる。

今回の機能追加によって、MongoDBそのものを稼働させることなく(つまりCPUとメモリのリソースなしで)、MongoDBに溜めたデータを Hadoopで処理できるようになった。これは個人的に非常に大きな可能性を感じさせる進化だ。

Hadoopを夜間等、バッチで走らせる場合、直接MongoDBにアクセスさせてしまうと、複数のノードからのアクセスがMongoDBに集中することで負荷が急激に上がり、オンラインアプリケーション側に影響が出てしまう可能性があった。また、当然HadoopとMongoDBがネットワーク的に繋がっている必要があった。しかし事前に必要な情報のみmongodumpで取り出し、S3等のHadoop処理を行う環境に置いておくことで、低い管理コストでこの課題が解決できるようになった。Amazon EMRはまさにこの使用方法にぴったりのソリューションである。

また、半構造化されたデータをHadoopの出力として用いたい場合にBSONを選択すれば、これをまたMongoDBに戻し、オンライン処理で使うことができる。

試しにBSONに含まれるデータの単語を数えてみた。ソースコードは以下のとおりで、Hadoop1.0.3でテストした。MongoDBの Javaドライバと、MongoDB Connector for Hadoopの両方のjarファイルをクラスパスに含めておく必要がある。


import java.io.IOException;
import java.util.Iterator;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.NullWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapred.FileInputFormat;
import org.apache.hadoop.mapred.FileOutputFormat;
import org.apache.hadoop.mapred.JobClient;
import org.apache.hadoop.mapred.JobConf;
import org.apache.hadoop.mapred.MapReduceBase;
import org.apache.hadoop.mapred.Mapper;
import org.apache.hadoop.mapred.OutputCollector;
import org.apache.hadoop.mapred.Reducer;
import org.apache.hadoop.mapred.Reporter;
import org.apache.hadoop.mapred.TextOutputFormat;

import com.mongodb.hadoop.io.BSONWritable;
import com.mongodb.hadoop.mapred.BSONFileInputFormat;

public class WordCount {
	public static class Map extends MapReduceBase implements
			Mapper<NullWritable, BSONWritable, Text, IntWritable> {
		private final static IntWritable one = new IntWritable(1);

		public void map(NullWritable key, BSONWritable value,
				OutputCollector<Text, IntWritable> output, Reporter reporter)
				throws IOException {
			final java.util.Map valueMap = value.toMap();
			final Iterator p = valueMap.keySet().iterator();
			while( p.hasNext() )
				{
				final Object _key = p.next();
				if( _key.equals( "_id" ) )
					{
					continue;
					}
				final String[] array = ( valueMap.get( _key ) + "" ).split( "[^a-zA-Z0-9]+" );
				for( int i = 0; i < array.length; ++i )
					{
					output.collect(  new Text( array[ i ] ), one );				
					}
				}
			}
		}

	public static class Reduce extends MapReduceBase implements
			Reducer<Text, IntWritable, Text, IntWritable> {
		public void reduce(Text key, Iterator<IntWritable> values,
				OutputCollector<Text, IntWritable> output, Reporter reporter)
				throws IOException {
			int sum = 0;
			while (values.hasNext()) {
				sum += values.next().get();
			}
			output.collect(key, new IntWritable(sum));
		}
	}

	public static void main(String[] args) throws Exception {
		JobConf conf = new JobConf(WordCount.class);
		conf.setJobName("wordcount");

		conf.setOutputKeyClass(Text.class);
		conf.setOutputValueClass(IntWritable.class);

		conf.setMapperClass(Map.class);
		conf.setReducerClass(Reduce.class);

		conf.setInputFormat( BSONFileInputFormat.class );
		conf.setOutputFormat(TextOutputFormat.class);

		FileInputFormat.setInputPaths(conf, new Path(args[0]));
		FileOutputFormat.setOutputPath(conf, new Path(args[1]));

		JobClient.runJob(conf);
	}
}

コネクションプールについて

「コネクションプールがなぜ必要か?」

まず上記の設問自体がおかしくて、コネクションプールが必要(あるいは適している)場面もあれば、まったく不要である場面もある。必ずしもコネクションプールは必要ではない。コネクションプールにはメリデメが存在する。銀の弾丸ではない。

コネクションプールという言葉から始まってしまうとRDBMSとかのイメージが付きまとってしまうが、これはもう少し抽象化して考えると、オブジェクトプールと呼ばれるデザインパターンのひとつとして捉えることもできる。

 

オブジェクトプールのメリットは大きく分けて以下の2つ。
初期化コストや廃棄コストが高いオブジェクト(データベースのコネクションやスレッド)を再利用可能にすることにより、レイテンシ、スループットやシステム負荷等を改善する

・システム中で使用されるリソース数の上限をコントロール可能にする。

ウェブのシステムのように、クライアント側(ウェブサーバ側)のある瞬間のクライアント数が数百、数千といった単位になるシステムにおいては、それぞれのウェブサーバがデータベースにコネクションを張ってしまうと、データベースサーバ側がパンクしてしまう可能性がある。このような場合、コネクションプールがコネクション数の上限をコントロールすることで、これを防ぐことが可能になる。

また、副次的なメリットとして、リソースの使用状況をログに記録しやすくなる。

 

オブジェクトプールのデメリットは以下

・コンポーネントが増える
オブジェクトプールというコンポーネントそのものの存在。無くて済むならその方がシンプルでよい。あること自体がデメリット

・状態を持ってしまう
オブジェクトプールによってシステム中に「状態」を持つ箇所が増えてしまうので、データベース障害時の挙動などを考える場合の複雑性が増す

・アイドル時もメモリを消費する
これは大したデメリットではないが、誰も使っていない時間もリソースを確保しているので、メモリなどを無駄使いしている、と考えることもできる