ページ

2010年12月22日

WebSocketのサーバ間でメッセージをリレーさせるサンプル

どうもです。なぜかPythonのアドベントカレンダーに参加することになっていましたおおたにです。お題が、PythonのWebフレームワークです。えっ、Webフレームワーク?僕はそんなにWebフレームワーク詳しくないし・・・。数年前ならまだしも、最近は便利に使えれば後はそれほど気にしなくなっていました。でも、Webフレームワーク・・・。ということで、WebSocketとFlaskを絡めてサンプルコードを書いたら、どこにもimport flaskってやっていない罠が・・・。ということで、前置き(言い訳)が長くなりました、WebSocketのサーバを複数立てたときに、サーバ間のデータのやりとりをどうするのかというお話です。

まず、WebSocketはHTML5関連で脚光を浴びている機能なので、知っている人も多いでしょう。簡単にいってしまえば、最初にHTTPプロトコルを偽装して、その後データのだだ漏れができるものです。Chromeでは去年サポートされ、Safariも今の最新版でサポートされています。iOS4.2.1でもサポートされているのでiPhoneからでも使えます。IE9では、「何それ?」の悲しい状態ですが、プロトコルの不安定さから仕方ないですね。Firefoxは4からサポートすると期待させておきながら、こんな残念な記事が今朝聞こえてきました。

WebSocketでデータの送受信をすると、Cometなどのロングポーリングよりはインフラに優しい作りになります。でも、ロングポーリングでも同じですが、アプリケーションサーバを複数立てたとき、アプリケーションサーバ間でデータをやりとりする必要がでてきます。リアルタイム性が大きなメリットなので、データベースとかは介せません。そこで有力なのがXMPPだと信じていますが、Google Waveが消えたのと、ブログの一つのエントリじゃ収まらないので、XMPPは忘れます。自前で作るとしたらハブサーバを作って、アプリケーションサーバとハブサーバの間でデータの垂れ流し(リレー)をさせるのが良さそうです。そのときのプロトコルもWebSocketで中身はJSONとか、MessagePackとかもいいかな、と思ったのですが、サンプルコードはライブラリを調べる時間も含めて30分以内に作らないといけないというローカルルールにより、行ベースで自分でソケットを開きます。



で、今回のサンプルは全部geventベースで書いています。非同期処理が同期処理のように書けるのがすごいメリットです。WSGIサーバにもなるので、FlaskでもDjangoでも使えます。で、なぜ、WebSocketを非同期にこだわるかはむか〜し、どこかで書いた気がするので省略。でも、今回のサンプルはある程度ブロックするので効率は悪いです。サンプルコードはここにあります。

まず、ハブサーバのコードです。

from gevent.server import StreamServer

class BCServer:
    def __init__(self):
        self.clients = []

    def received(self, socket, addr):
        fileobj = socket.makefile()
        self.clients.append(fileobj)
        try:
            while True:
                line = fileobj.readline()
                if not line:
                    break
                if line.startswith("quit:"):
                    break
                if line.startswith("msg:"):
                    msg = line.split(":", 1)[1]
                    for fo in self.clients:
                        try:
                            fo.write(msg)
                            fo.flush()
                        except Exception, e:
                            print p
        except Exception, e:
            print e
        finally:
            self.clients.remove(fileobj)


if __name__ == "__main__":
    bcs = BCServer()
    server = StreamServer(("0.0.0.0", 3000), bcs.received)
    server.serve_forever()



geventのsocketが標準ライブラリのsocketと全く同じようにあつかえます。メッセージを垂れ流しているだけなので、単純です。Webアプリケーションの方は、以前のコードとほとんど同じなので、ポイントだけ。全部読みたければ、これ

chat_clients = set()

def read_server(fileobj):
    while True:
        data = fileobj.readline()
        if not data:
            print "end read_server"
            break
        for client in chat_clients:
            client.send(data)

def handle_chat(ws):
    chat_clients.add(ws)
    while True:
        message = ws.wait()
        if message is None:
            break
        fileobj.write("msg:" + message + "\n")
        fileobj.flush()

    chat_clients.remove(ws)


fileobjはハブサーバに接続した後にsocket.makefileしたものです。これだけでメッセージをリレーしてくれます。えっ?信じられないって?それじゃ、動いている画像です。

2010年12月16日

PythonでHTML数値文字参照を文字列に変換

ちょっと前にHTML数値文字参照を文字列に変換したくなりましたが、適当なライブラリはここまでは面倒は見てくれませんでした。HTML数値文字参照、っていう名称が正しいかしませんが、ሴみたいなやつです。これを文字列に変換したいのです。3分間グーグル様に問い合わせても教えて貰えないので、自前で変換することになってしまいました。

import re

def unescape(s):
    pattern = re.compile('(&#x([0-9a-fA-F]{4});)')
    return pattern.sub(lambda x: unichr(int(x.group(2), 16)), s)

それじゃ、その逆っていうことで、

def escape(s):
    return ''.join(("&#x" + hex(ord(c))[2:] + ";"  for c in s))

これで「ほげ」を変換すると、ほげとなります。でもASCIIコードまでエスケープされるのはいやだな〜、ということで、

def escape(s):
    pattern =  = re.compile(u'([\u00ff-\uffff])')
    return pattern.sub(lambda x: "&#x" + hex(ord(x.group(1)))[2:] + ";", s)

これで、「ほげtest」はきっと、u'ほげtest'となるはず。範囲が00ffからでいいかどうかと、maxがffffでいいかはどうかはあるんですがね。

でわでわ

2010年12月14日

MongoDBのメモリ使用量 もうちょっとまじめに計測

昨日の続きです。MapReduce使ったときにメモリをどれくらい使っているのかを、もうちょっとまじめに計測してみました。まずは、データを作るところ。

import pymongo
conn = pymongo.Connection()
db = conn.my_test
table = db.items

def insert():
    for i in range(1, 1000000):
        oid = table.save({"key": i, "value": 1000 % i})

です。割と小さめのデータを100万件放り込んでいます。これを

def map_reduce(max_key):
    # map reduce
    from pymongo.code import Code

    map_code = Code("""
function() {
  emit(this.key, this);
}
"""
)

    reduce_code = Code("""
function(key, values) {
  return {"count": values.length, title: values[0]["title"], url: values[0]["url"]};
}
""")
    result = table.map_reduce(map_code, reduce_code,
                              query = {"key": {"$lte": max_key}})
    return result

で10,100, 1000,10000, 100000, 250000, 500000, 750000, 1000000とmap reduceの対象になるレコードを増やしていって、map reduce処理後のMongoDBのメモリの使用量(RSSの値)をプロットしたものが、この図です。

250000のあたりが少し落ちているのは誤差でしょう。
左の方の10000ぐらいまではほとんど重なっていてわかりませんね。map reduceの処理には80Mぐらいのメモリが必要らしいです。
そこから先はほぼ、比例的にメモリの使用量が増えていきます。map reduceでqueryを制御しないと沢山メモリを食べてくれます。

メモリの消費量が多いのが悪いこととはいえないのですが、レンタルサーバでMongoDBがメモリを食いつぶして落っこちるのは悲しい限りです。レンタルサーバでは大体一時間に16000件のデータが登録されます。今回測定したコードだと20000件のデータでは大体、90Mか100Mぐらいのメモリ使用量でした。実際に動いているデータ構造とサンプルデータ(ちょっと小さめ)で実行すると、メモリの使用量が250Mぐらいでした。300Mぐらいであればすぐに消費してくれそうです。クエリの結果がほぼメモリにすべてのってmap reduceが実行されているのでしょう。

ということで、他のプロセスがメモリも消費することを考えると、メモリがちょっとばかしたりません。490円で動いていますが、やっぱり倍の値段を払わないとだめそうです。

MongoDBはメモリを沢山食います。RDBを使っておけば楽だったかもしれません。

でわでわ。

2010年12月13日

MongoDBのメモリの使用量

とあるところで、MongoDBを使って遊んでいます。一秒間に数個のレコードをインサートし続けています。一時間に一回、集計用に一時間分のデータをMongoDBのMap Reduceを使って処理しています。そのときのメモリの使用量(psで出力された結果のRSSの部分)を一分おきにとって、グラフ化したものがこのグラフです。

最初の小山はあんまり意味がないでしょう。200M byte付近から300M byteぐらいに使用量が急激に上がっているタイミングがMap Reduceで集計している時です。データをとるためにMongoDBを再起動したり、べつのことをちょっとだけしているので、1時間の完全なデータではないですが、50分ぐらいのデータにはなっていると思います。

グラフからインサートを延々と繰り返してもさほどメモリの使用量は増えていません。データの参照もそれほど複雑なことをしたり、負荷があるわけでもありません。

グラフの右の方でまたメモリの使用量が上がっています。大体40Mbyteぐらい増えています。これもMapReduceで集計しているタイミングです。Map Reduceは基本的に、集計対象とするデータ量に応じてメモリをかなり消費するようです。

悲しいことは、これを動かしてるサーバのメモリがとっても少ないので、メモリが足りなくなってしまうことです。

2010年12月9日

WebSocketとプロキシ

プロキシ配下でWebSocketを使おうとしても、通信ができないと思っていました。わざわざ80番ポートをわざわざ使うように変更したぐらいなので、なぜだろうと不思議でした。この記事によると、バグはあるとしても通るはずだと。WebSocketのプロトコルにアップグレードする前にGETのコンテンツ領域(?)でデータを送信し始めるのはHTTPの仕様上よくないし、それをハンドリングでいないプロキシはいるだろう、そのせいで通信ができないということでした。なるほどです。

さて、会社のプロキシもPOSTの時にContent-Lengthがなかったら、POSTされないとか変なことがあったので、GETでデータの垂れ流しなんてもってのほかです。というか、PROXY経由でSubversionをつかおうとしたとき、GET/POST/HEAD以外のメソッドが全然使えない設定になっていた過去があるぐらいです。こっそり直したのですが。

さて、WebSocketの通信は最初CONNECTをするらしいです。会社の環境だとまず、ここで失敗します。HTTPSじゃないとCONNECTしないようにしているみたい。

そんなわけで、プロキシを迂回する経路ができたのでした。

2010年12月6日

Pythonで文字コード判定

昔のトラウマのせいか、文字コードの自動判定はあまり信用していません。できることなら、あまりやりたくないものです。でも、今遊んでいる、じゃなかった作っているものでできれば文字コードを自動判定したくなることもあります。扱っているのはWebページでHTMLです。なので、大抵の場合はHTMLの中に文字コードが書いているので、おそらくそれは信用してもいいでしょう。
HTMLの中に文字コードが書いていない場合は、ちょっと困ります。HTTPヘッダーに文字コードが書かれていればそれを信用するかどうかが迷うところです。あんまり信用したくありません。でも、大きなところではYahoo!Japanのニュースのブログ/意見は文字コードはHTMLにかいていません。HTTPヘッダーにはちゃんと指定してあります。でも、これはYahoo!Japanだからかな。
で仕方なく文字コードの自動判定に落ち着く訳です。Pythonで文字コードの自動判定って何がいいのか、Googleさんに聞くと、Universal Encoding Detectorにしなさいとおっしゃいます。feedparser作っているとこですね。他のはpykfなど日本語に特化したものとかありましたが、日本語だけに特化しているのはちょっとさけたかったので、お告げに従いました。pip install chardetでインストールして、試してみます。手もとにあるちょっといやんなサイトのリストをまわしてみるとちゃんと判定してくれています。こんな感じ。
import chardet
import urllib2

my_urls = [...省略...]
for url in my_urls:
    res = urllib2.urlopen(url)
    print chardet.detect(res.read())

先ほどのYahoo!の場合だと{'confidence': 0.98999999999999999, 'encoding': 'EUC-JP'}と出力されます。確かにあってます。
IBMの文字コードの自動判定するライブラリと比べると、多分100%はあり得ないと思っているので、候補をリストで出してくれたり、フォールバック先を指定できたりするとうれしいかな。フォールバック先はconfidenceの値をみて、閾値以下だと自分でフォールバック先のエンコーディングを使用すればいいのかな?

2010年11月30日

Tweetボタンを動的に作る

最近は至る所でTweetボタンを見かけます。Tweetボタンってこれね。


こんな感じのボタンはよく見かけます。で、今しこしこ作っているものはこの、Tweetボタンが憑いています。そして、WebSocketとかAJAXで動的にごりごりこのTweetボタンをつけたいのであります。Tweetボタン自体の説明は、公式サイトに事細かに書かれていています。基本はhtmlのaタグにクラス名をtwiiter-share-buttonってつけてhttp://platform.twitter.com/widgets.jsをロードしてあげれば、aタグがかっこいい(?)ボタンに生まれ変わります。これって、ドキュメントをロード後のこのクラスのaタグの要素に対して操作しています。ということは、AJAXとか、ドキュメントをロード後に追加したAタグは残念ながら今時のボタンにはなってくれません。

Google先生にお伺いすると、Creating Tweet Buttons Dynamicallyという記事に動的に追加する方法が書いてあります。jQueryを使っていますが、僕の作っているものもjQueryを使っているので、同じようなものです。記事のコードをそのままのせるのも悲しいので、AJAXでHTMLの断片がとれてきたものを加工することにします。

コードはこんな感じ。fragmentはAJAXで取得した断片をinsert_pointというIDの要素に追加しています。
var newelem = $("#insert_point").append(fragment);
newelem.find('a.twitter-share-button').each(function(i) {
    var thisTweetButton = new twttr.TweetButton($(this).get(0));
    thisTweetButton.render();
});

このfragmentの中にAタグの要素があります。こんな感じ。

<a href="http://twitter.com/share" class="twitter-share-button" data-count="none" data-via="liris" data-lang="ja">Tweet</a>

それから

<script type="text/javascript" src="http://platform.twitter.com/widgets.js"></script>

はヘッダーの中に一回書いとけばいいです。これで、動的にTweetボタンが追加されていきます。めでたし、めでたし。
まあ、こんな感じになるだけです。まだ、よく止まります。それから止めます。もし見るんだったらChromeかSafariの最新版かFirefox4.0ベータでプロキシのない環境だとちょっと楽しいかもよ。

でわでわ。

2010年11月22日

mongodbのmap reduceを使ってみた

MongoDBにはMap Reduceを簡単に使う機能があります。それ以外にコレクション(テーブル)にgroupというメソッドが定義されていて、RDBMSのgroup by相当のことができるとマニュアルには書かれています。ただ、次のような怖い注意書きがあります。

注意: 現在のところ、shardの環境では、group()の代わりにmap/reduceを必ず使ってください。
結果はなるべく小さくしてください(10,000キー以内)。大きすぎる場合例外が発生します。
Shard環境では問答無用にMap Reduceを使うしかなさそうです。結果はなるべく小さくしろと言うことですが、10000キーあればそこそこ大きいような気がします。最初の制限の「現在のところ」というのが気にならなくもないですが、処理結果はMapReduceと大差ないし、MapReduceの方がもう少しいろいろできそうな気もします。たとえば、結果は実行後すぐに取得できますが、これをコレクションとしてMongoDBの中に永続化するとかです。なので、groupメソッドはいらねんじゃね?Map Reduceだけで十分じゃね?と思います。

さて、それで、MapReduceですが、map関数とreduce関数をJavaScriptで書くだけです。PythonでもJavaScriptです。はい。それ以外にもfinalizeするときのメソッドも定義できます。マニュアルをよむと、結果で平均値を求めたりするらしいです。reduceの中でやっちゃえばいいような気もするので、あまり使い道を思いつきません。

それじゃ、コードのお時間です。
import pymongo
import pymongo
from pymongo.code import Code
conn = pymongo.Connection()

tbl = conn.my_db.my_table

m = Code("""
function() {
  emit(this.url, this);
}
""")

r = Code("""
function(key, values) {
  var total = 0;
  var len = values.length;
  for (var i=0; i<len; i++) {
    total += values[i].count;
  }
  var url = values[0].url;

  return { count:total, url: url};
}
""")
res = tbl.map_reduce(m, r
for r in res.find():
    print r["value"]["count"]

my_tableは、urlとcountというフィールドを持ちます。それぞれ文字列と数字です。map関数はmです。これは、URLをキーとしてemitしてます。valueはめんどいのでレコード自身です。rがreduce関数になります。これは、valuesはemitしたvalueが配列になって渡されます。一個づつイテレートしながらcountの光景を求めています。終わったら、countとurlを返しています。
map_reduceで実行すると結果として、コレクションが返されます。このコレクションに対して、findなどいろいろできます。結果を永続化するにはout="result"という引数をmap_reduceに追加してあげればよいです。簡単です。
新しく作られたコレクションは、keyとvalueという二つのフィールドからなります。キーはreduce関数に渡されたものです。valueはreduce関数が返したものです。
group関数を使った場合は、結果が配列ですべてオンメモリで返されます。ソートを行うにしても自前でソートしないと行けません。いやんですね。

まあ、groupは使わずにmap reduceでよくね?という話です。
でわでわ。

2010年11月20日

そのうちMacPortsをuninstallする

homebrewでいい気がしてきたので、MacPortsをアンインストールするための来るべき日のためのメモ。アンインストールする方法がMacPortsのページにのっているのは、好感がもてます。
とりあえず、今インストールされているものをメモっておきます。
$ port installed > installed.txt
そのあと、portでインストールしたものをアンインストールします。
$ sudo port -f uninstall installed
最後にディレクトリや残骸のゴミをすてます。
$ sudo rm -rf \
    /opt/local \
    /Applications/DarwinPorts \
    /Applications/MacPorts \
    /Library/LaunchDaemons/org.macports.* \
    /Library/Receipts/DarwinPorts*.pkg \
    /Library/Receipts/MacPorts*.pkg \
    /Library/StartupItems/DarwinPortsStartup \
    /Library/Tcl/darwinports1.0 \
    /Library/Tcl/macports1.0 \
    ~/.macports
よし、これでHomebrewに乗り換えられそうです。

2010年11月19日

flask + gevent + websocket

ざっと3分間Google様で調べたところ、FlaskでWebsocketを扱うものはまだないらしいです。まあ、Flaskじゃなくてもwsgiを使っている何かでもいいのですが・・。

悲しいというか、使いたかったので、とりあえず、書いてみたものがこれです。以前geventでWebSocketを使ったもののコードをちょっとだけ変更したものです。といっても、myappでリクエストを受けて、websocketのパスでなければFlaskのアプリケーションに流し込んでいるだけです。パスの扱いが若干かっこわるいですが、変に処理するよりは単純なものの方がいいです。

以下、コードの抜粋です。chat用のhtmlは前回と同じなので、省略しています。まあ、大した話じゃないですね。

from geventwebsocket.handler import WebSocketHandler
from gevent import pywsgi
import gevent
from flask import Flask, request, flash, render_template, url_for
from flask import session, redirect, g
from flaskext.babel import Babel, gettext

app = Flask(__name__)

chat_html = """
[省略。chat用のhtmlです]
"""

chat_clients = set()

def handle_chat(ws):
    chat_clients.add(ws)
    while True:
        message = ws.wait()  
        if message is None:
            break
        for client in chat_clients:
            client.send(message)

    chat_clients.remove(ws)

@app.route("/")
def index():
    return "hello"

def myapp(environ, start_response):
    path = environ["PATH_INFO"]
    if path == "/":
        return app(environ, start_response)
    elif path == "/chat":
        handle_chat(environ["wsgi.websocket"])
    elif path == "/chat.html":
        start_response("200 OK", [])
        return chat_html
    else:
        return app(environ, start_response)

if __name__ == "__main__":
    server = pywsgi.WSGIServer(("", 5000), myapp, handler_class=WebSocketHandler)
    server.serve_forever()


2010年11月13日

tweepyでtwitterのstreaming APIを使う


OAuthの問題などのせいで、twitter apiのライブラリとしてtweepyを使っています。以前作った「ありえるたん」は実用的ではないので、もうちょっと自分が欲しいというか、やってみたいことがあったので、そのためにStreaming APIを使ってみました。Streaming APIはtweepyでもサポートされているので、そのまま使うだけです。Stream APIって、名前の通り、streamで処理します。接続を張りっぱなしにして、イベントが起これば勝手にデータが流れてきます。Twitterのインフラはどうなっているんでしょう?すごいですね。

Streaming APIは、
1. listenerのオブジェクトを作る
2. Streamオブジェクトを作る
3. filterメソッドなどで検索させる
4. 1で作ったlistenerオブジェクトにイベントがあがるので、各イベントに対して処理を記述する

とう流れになります。


import sys
import tweepy

class StreamListener(tweepy.StreamListener):
    def on_status(self, status):
        print status.user.screen_name.encode("utf-8") + " : " + status.text.encode("utf-8")

def main(argv):
    user = "my_twitter_id"
    passwd = "my_twiter_password"

    listener = StreamListener()
    stream = tweepy.Stream(user, passwd, listener)
    stream.filter(track=["http"])

if __name__ == "__main__":
    try:
        main(sys.argv)
    except KeyboardInterrupt:
        pass

StreamのListenerはon_dataでデータを受信したときにイベントが上がります。on_statusでtweetがヒットしたときの処理が実行されます。他にどんなイベントがあるかは、忘れました。on_dataは引数で渡ってくるものはjson形式の文字列なので、自分でパースしないといけません。on_statusはStatusオブジェクトが渡されます。。
イベントが来たときに、コールバックで処理を記述するだけなので、簡単ですね。きっと。

StreamのコンストラクタにユーザIDとパスワードとリスナを指定します。で、filterメソッドをコールすればtracで指定した文字列を検索して、ヒットすればコールバックが呼ばれます。このAPIはブロックします。asyncでTrueを渡してやると非同期で処理するみたいです。

まあ、とっても気軽にStream APIを使えるのはすてきです。
でわでわ

2010年11月12日

JVM言語のFantom - fantomのfanは大好きのfan

Fantomは最近注目されている(?)新しい言語です。これは、そう、僕らみんなが大好きなJVM上で動きます。Javaは偉大です。(棒読み)。Fantomのfanは楽しいのfanです。これだけで、楽しくなってきました。さすがはJavaです。(やっぱり棒読み)

さて、この言語ですが、シンタックスはJavaっぽいですが、Javaではないです。Javaっぽいので、世界中にうじゃうじゃいるJavaプログラマにはとても取っつきやすい言語です。で、なんでJavaっぽいならJavaじゃいけないの?って、誰もが思います。理由はいくつかありますが、FantomはJVM上で動きますが、それだけじゃなくって、.netのcli上でも動きます。PythonやRubyなど既存の言語で両方で動くというのは聞いたことがありますが、新規の言語で両方で動くというのは珍しいかと思います。

で、Javaじゃだめなの?っていう理由のもう一つですが、基本的にFantomは静的な型付け言語ですが、ローカル変数などは型宣言を明示しなくてもよいです。このあたりは、golangとよく似ていると思います(思想的にもシンタックス的にも)。Java(というかほとんどの静的型付け言語)でめんどいな〜、と思うところが改善されています。インターフェースになる部分には型は明示されていた方がコードは書きやすいですが、ローカル変数までそれをやると冗長なだけです。

あとはAPIが洗練されていると書いてあったりしますが、そのあたりはそんなに主張するところじゃないでしょう。それから、JVM上で動く言語は既存のJava資産が使えるって声高に主張している人がいますが、Thriftをみていると、わざわざJVM上で動かさなくてもいいんじゃないか?と思ったりもします。

それじゃ、前置きが長くなりましたが、それそろ始めます。一応、Mac上で動かしてます。Javaのランタイムはすでに入っているという前提で・・・。まあ、XCode入れれば勝手にはいるし。

1. セットアップ
ダウンロードは、先ほどのページにダウンロードのリンクをクリックして、ダウンロードします。ダウンロード後、アーカイブを展開します(xの部分は適当なバージョン番号)。
$ unzip fantom-x.x.xx.zip
さて、このあと、システム全体にインストールしてもいいのですが、メインで使うわけもないので、やめます。とりあえずは、展開したディレクトリのbinディレクトリにPATHを通せばいいです。
$ export PATH=$path_to_bin_dir:%PATH
binディレクトリの中のファイルには実行属性がついていないので、つけてあげます。で、exeファイルがたくさんあってうっとうしいので、消しちゃいます。
$ cd $path_to_bin_dir
$ chmod +x *
$ rm *.exe
これでおしまい。

2. 動作確認
セットアップが終わったら動作確認です。fanshというしょぼいインタラクティブシェルがついてくるので、それでおきまりのHello, World.です。
$ fansh
Fantom Shell v1.0.55 ('?' for help)
fansh> echo("Hello, World!")
Hello, World!
fansh> quit
$

おー、動きました。

3. ざっとした文法とか・・・
みんなJavaプログラマなので、ここのサンプルコードをみれば、10分で理解できます。JavaプログラマじゃなくってPythonとかそれ以外のプログラマなら8分で理解できます。なので、省略します。ちゃんと勉強したいという頭の固い人は、こちらからどうぞ。
型がすべて大文字で始まるのですが、Voidっていうのは何度見てもキモイとか、言わないの!

4. ちょっとだけ
インタラクティブシェルで書いたHello, Worldをfantomのスクリプトファイルとして書いてみましょう。次のコードをhello.fanとして保存して、fan hello.fanとして実行します。
class MyScript {
    static Void main() {
        echo("Hello, World")
    }
}
まず、ファイルの中のトップレベルはclassでなければなりません。Javaの悪しき風習は守られています。ただし、一つのファイルの中に沢山classをつくれるので、すべての悪を引き継いだわけではありません。
で、実行がめちゃくちゃ遅い。Javaのバイトコードにコンパイルするからです。遅い理由もうなずけます。golangがコンパイルスピードまでこだわったのとは対照的です。

もう一個。
class MyScript {
    static Void main() {
        s := gets
        s = s[0..5]
        echo(s)
        t := Script()
        echo(Type.of(t))
    }
    static Str gets() {
        return "hello, world"
    }
}

なんとなく雰囲気がわかるとおもいます。配列のスライスとか、Javaと違ってすっきりしています。それと、Map型(Pythonの人なら辞書)も[]で表記します。こんな感じ。
[1:"ichi", 2:"ni", 3:"san"]

便利そうなんだけど、空のMapを作るのはどうするんだろう?

5. もっと
fantomはconcurrentがちゃんとサポートしているとか言っています。マルチスレッドで動くときは同じメモリを参照するものは自動で排他制御tかimmutableな変数して扱うとか、Actors model for message passing (Erlang style concurrency)とか、言っています。

それ以外にもGUIをサポートしていたりとか・・・。まあ、Javaの人にはわりと取っつきやすい言語じゃないかな?

2010年11月10日

クラウドエクスポ

アリエルのブース。お昼時で人がいないときにとっています。人がいたときに撮った写真はうまくとれてません。
この写真のブースはバイク王じゃなくって、アリエルのブースです。コンパニオンのおねーちゃんと写真を撮りました。

コードの読まれ方がわかっても工数見積もりに寄与するのか?

「コードの読まれ方が分かった」、工数見積もり精度向上に寄与」という記事は、はっきり言っておもしろくありません。まず、コードリーディングでの結果から導き出されたことを纏めてあるのですが、導き出されたことはごく当たり前のことで、全く新鮮味がありません。新鮮味はないかもしれませんが、定量的にそれらが証明されたらしいということは評価できるとおもいます。「らしい」と言うのは、上の記事からは数字なども示されていないので、判断できなくって、書いてあることを信用するだけだからです。

全体的には悪い記事じゃないんですが、工数見積もりの精度向上とか書かれていると、タイトルとしては気持ちはわかるんですが、身構えちゃうのは心が汚れているからでしょうか? 


「追加、修正行数だけによる工数見積もりは難しい」を実証

とあるんですが、見積もりする方からすれば最初に行数はわからないなので、それをベースに見積もるようなこの表記はちょっとおかしんじゃないかな? と思ったりもします。

そんなことを言っても何も生まれないし、上の表記も完全な間違いじゃいのでアリエルではどんな風に工数見積もりしているかを書いてみます。ちなみに、新機能の追加だけです。バグ修正は調査工数が見積もれないことがあるので範囲外です。

さて、どんな風に工数見積もりしているか?それはほとんど職人の経験によって決めています。で、経験をもとに何を判断しているかと言えば、

1. 影響を受けるモジュール

すべてはAPIのように階層的な構造をもっていて、下のレイヤーは上のレイヤーの知識を知らない状態です。また、OO的に開発していて、隠蔽は進んでいます。といっても影響する範囲はそこそこでるものです。そのあたりをざっと判断します。

2. 変更するソースコードの規模の判定

影響を受けそうな範囲がわかったところで、ソースコードの規模をざっと見積もります。影響を受けるモジュールなんてのは丁寧に時間をかければ調べればわかりますが、 これは、うーん、みんなどうやっているんだろう?こっちは経験としか言いようがないと思います。

3. 単体テストの規模と結合テストの規模

修正や機能追加自体は、アリエルの人の場合はすぐです。ものにもよりますが、一週間は割と長い期間です。 でも、実装の後にテストがあります。単体テストと結合テストの規模は、上の二つを元にやった方が良さそうなことをあらいだして、どれくらいか見積もります。これは、以前のテストケースの量などをいいわけにして、やっぱり経験で決めています。

こんな感じだと思いますが、割と意識せずにやっているので、間違っているかもしれません。うーん、うーん、なんか結局、経験という名目の勘のような気がしてきました。 



2010年11月9日

オフィスの引っ越し前で・・・

アリエルは中目黒には7年ぐらいいました。その間、紆余曲折あったわけですが、人数も増えてきて手狭になって五反田に引っ越したのが2年前です。12月のクリスマス直前に引っ越したので、正確には2年たっていません。

それが訳あって、2週間後に六本木に引っ越すことになりました。理由は聞かないでください。人がこれ以上入れなくなりそうだからです。スペース的に。

つい最近五反田にやってきたつもりなので、とても早くオフィスの引っ越しという印象です。あんまり実感はないのですが、引っ越し用の段ボールが大量に積み上げられているのをみると、えーと、なんて言うか、そのー、一言で言えば、「めんどいの〜」。

僕の持ち物の準備はたぶん、10分ぐらいでおわります。引き出しとか机の上のものを乱雑に段ボールの中に押し込めて、PC関係は適当に包むだけなので・・・。私物じゃないので、壊れても構わないと思っているので、気が楽です。

本とか、会社の荷物は若い衆がきっとやってくれるので、指示を出すだけです。サーバ関係は、マスター浜岸という人がほんとど趣味として面倒見てくれます。 そういえば、ちょっと古いPCが大量に破棄されるので、ほしい人はとりに来てくれればプレゼントしてもらえるかもしれません。古いものは10年近く前の今はなきSotecのマシンもあるので、お一つどうでしょうか?

オフィスの引っ越しの風物詩と言えば、新しいオフィスの席取りですが、なぜか今回は一部の人の独断と偏見できました。偉い人と、偉そうな人の席が決まってから、残りの人の席が勝手に割り振られました。まあ、どこに座るかは強制じゃないので、とりあえずの荷物の届け先だけ確定しておけばいいのです。

うーん、五反田、割と気に入っていたんだけどな・・・。次に引っ越すときはマウンテンビューあたりがいいです。



MongoDBのベンチマーク@おうちのiMac

Membaseのベンチマークが遅かったのですが、VMWareのせいかも?とか、思わなくもないので、MongoDBでも同様におうちのiMacでも計測してみました。コードは、以前の記事のものをほぼ、そのまま使用しています。

で、早速結果のグラフです。

左側はサーバをローカルのマシン上で動かしています。左側はVM上で動かしてます。ローカルではmembaseは動かしていません。ごめんなさい、ごめんなさい。

まず、フェッチのスピードですが、MongoDBとMembaseでほとんと違いがありません。MacBookAirで動かしたときは、MongoDBは0.4秒ぐらいでしたが、それにくらべて速いです。MacBookAirで計ったときのmemcacheとの相対比では2から3倍でしたが、ほぼ同じです。ローカルでは0.2秒でmemcacheに比べて約3倍、でも、vm上だと10倍の開きがあります。
localのフェッチの時間よりVM上のものが1.5倍ぐらい遅いです。CPUというよりディスクIOの違いかもしれません。でも、インサートはlocalよりVM上のものの方が速かったりします。意味がわかりません。不思議です。memcacheもやっぱりローカルのものよりVM上のもののほうが速いです。これもわけわかめ。vm上のものが速くなる理由は全く見つかりません。コンパイラの差?Mac Portsだめ?Gentooのようにコンパイラオプションをいじりまくって最速を目指すべき?

うーん、解釈に困るデータです。ローカルのデータだけにしとけばよかったです。仮想環境は難しいです。

まとめると、MongoDBは検索はmembaseと同じぐらいの遅さで、インサートに関しては、membaseよりはかなり速いです。でも、memcacheに比べると十分遅いので、単純にmemcacheの置き換えにはむきません。Mongoの検索のスピードはmemcacheにくらべて環境に依存して3から10倍遅くなるので、インフラ周りなどで注意が必要です。いずれにしても、membaseは機能の割には遅いので、使わないのがいいでしょう。

iMacのHDDをSSDにして動作を見てみたいと思う今日この頃です。redisのベンチマークもしろという声も聞こえてきますが、めんどいし、もっとやりたいことがあるので、ベンチマークそろそろお開きです。

2010年11月6日

membase、めちゃくちゃ遅い?いや、遅すぎ?

最近はいろいろとベンチマーク的なことをやったりして、それが仕事なのか趣味なのかよく分からない日々を送っています。しかも、会社の人はみんな忙しそうにしているので、暇そうなのはCTOと僕ぐらいです。そんなこんなで、自分で手を動かしている訳ですが・・・。

さて、membaseが気になるお年頃で、membaseとmemcacheで速度比較してみました。以前、repcacheとの速度比較もしたのですが、データをなくしちゃったというか、ほとんど劣化しなかったので、とってもつまらなかったです。membaseもmemcache互換プロトコルをサポートしてくるので、コードを書き換える必要はありません。会社で試したときは、うまく動かなかったのですが、家ではちゃんと動いています。何が違うのでしょう?

まず、membaseの独断と偏見の特徴は次の3つです。
  • クラスタ構成
  • memcache互換プロトコル
  • レプリケーションとデータの永続化
それでは、ベンチマークします。コードは以前の記事で使ったこれです。測定環境は、家のiMacです。会社のマシンより遥かにいいマシンです。えへん。memcache,membaseのサーバはVMWare上のUbuntu 10.10で動かしています。なので、ネットワーク構成がブリッジになっています。

で、membaseはdata bucket(データの保存形式)が二つあります。一つがmembaseのオリジナルのもので、もう一つがmemcacheです。違いがあまりわからないのですが、memcacheはon memoryということでしょうか?

で、測定結果です。

まず、addする時間。1000回addした時の平均ですね。テーブルにするのがめんどいので普通のリストで書きます。ごめんなさい。
  • membase (membase bucket) : 0.32 m sec
  • membase (memcache bucket) : 0.22 m sec
  • memcache : 0.034 m sec
まず、addする時間ですが、memcacheに比べて10倍遅いです。なんど計っても、違いがありません。memcacheのbucketなら多少速いですが、memcacheと比べるとやっぱり遅すぎます。

それでは検索というか、フェッチするスピードです。
  • membase (membase bucket) : 0.30 m sec
  • membase (memcache bucket) : 0.22 m sec
  • memcache : 0.032 m sec
検索もやっぱり一桁遅いです。membaseはとても魅力的な機能を持っていますが、10倍の速度差は許容できないです。多分、一般的にmemcacheより10倍遅ければ、APIが同じでも全体の作りを変えないといけません。
MongoDBがより複雑なことをしておきながらKVSとして使う場合2から3倍の速度劣化しかないのにくらべると、やはりmembaseは納得できる遅さではありません。

ということで、membaseはmemcacheの代替ではなく、使いどころをちゃんと考えないと行けないです。
でわでわ。

2010年11月5日

リーダにふさわしいかチェックしてみた

ちょっと古い記事になりますが、「あなたはリーダーに相応しいか」の7つのリストをやってみました。このチェックリストが本当にリーダーに相応しいかどうかを反映しているか、知りません。

1. 人に好かれたいという思いが強いか

うーん、 いきなりすごい質問です。好かれたいかどうかで言えば、ほとんどの人が好かれたいと答えると思います。でも、説明に書いてあるように必要なことであれば実行する、ということであれば、実行するかな。まあ、そんなに好かれているとは思っていないけど。

2. 民主的な管理体制を築き、合意に基づいて決断を行うというスタイルに価値を見出しているか?

最初の質問と違ってこっちは答えやすいです。少なくとも僕のプロジェクトの運営は民主的ではなかったからです。いいことだとは思っていませんが、すべての最終的な決定は合意ではなかったです。でも、合意に基づいて行った方がモチベーションが上がると思う場合は、合意したように見せかける努力はしたような気がします。時々、誘導したと言われますが。

3. 難しい会話のことを考えると夜も眠れなくなるタイプだろうか?

うーん、そういう理由で眠れなかったことはないな・・・。 

4. 中間管理職というものは、上位の管理職とチームの板挟みにならざるを得なくなるということを理解しているだろうか?

中間じゃなくても、いろいろと板挟みになるような気がする。ちなみに、僕は中間管理職になるんだろうか?

5. 会議の場で立ち上がり、話をしなければならないと考えると、胃が痛くなるだろうか?

日本人に対して英語じゃなければたぶん、胃は痛くないです。そういえば、家族で海外に行ったとき、「パパの英語、ママより下手くそ」と言われました。

6.  思いやりであっても、過ぎたるは及ばざるがごとしとなる場合もあるという事実を受け入れることができるだろうか?

言いたいことがあんまり、わかりません。「リーダーであれば誰でも、遅かれ早かれ、相手にとって嬉しくないことや、不公平なこと、筋の通らないことを言ったり、行ったりする必要が出てくるからだ。」というのは、まあ、そういうものです。 筋が通らないこと、っていうのはちょっと違うと思うけど・・・。

7. 人生において、「バランス」は重要な位置を占めているだろうか?

うーん、仕事を私生活より優先させて、何が残るんだろう?起業したとかいうならともかく、単なる中間管理職に対して何を期待しているんだろう?

 



2010年11月4日

MongoDBのshardとreplica その1

MongoDBのshardingとreplicaについてのメモ。間違っていたらごめんさい。

まず、MongoDBの負荷分散、もしくは、データの分散については、shardingという仕組みで行っています。shard自体は通常のmongodのプロセスです。各shardには、データがchunkの単位で保存されています。chunkは、キーに対してデータの連続性が確保されているので、連続したデータへのアクセスが高速化されます。一つのmongodのプロセスにchunk単位以上のデータがセットされたり、サーバの数が増加すると、他のshardにデータが自動的に移動します。この仕組みだけをみると、shardは増えることはあっても、減るのは苦手のように聞こえます。

shardが自動でデータの分散を行ったり、どこにデータがあるのかを見つけるためにconfigサーバがいます。mongodが実データを扱うのに対して、configサーバはメタデータを扱います。メタデータは、shardなどのサーバの情報とchunkの情報です。configサーバはchunkの情報を更新するために2フェーズコミットを採用しているようです。また、製品レベルで運用する場合は、3つのconfigサーバを別々のマシンで動作させておくように言っています。configサーバがいないと、shardでのchunkの更新は行われなくなります(データの移動などが行われない)。

mongosというプロセスが、shardのフロントエンドに配置されます。mongosはconfigサーバとやりとりして、クライアントからのリクエストなどをどのshardに送信するか決めます(ルーティング)。また、複数のshardにリクエストした場合は、それらの結果を一つに纏めてクライアントに返します。


レプリケーションは各shard単位(replica set)で行われます。各shard内のmongodはprimaryとsecondaryのプロセスにわかれます。mongodのプロセスがreplica setに追加されると、データは自動的にコピーされます。masterのプロセスが落ちた場合は、自動的にsecondaryのmongodがmasterに昇格します。一度昇格すると、設定で降格させられないか、敵前逃亡してプロセスが死なない限りは、masterのままです。複数のsecondaryがいる場合は、どの順番にmasterになるかは、設定によって自動的にきまります。

server1,2,3とあった場合、データAはserver1と2、データBはserver2と3、データCはserver3と1のように分散させて配置するパターンもありますが、MongoDBはshard単位でreplicaを制御するようになっています。
単純なKVSとしてみた場合、configサーバをみれば、データがどこにあるか判別できるのは速度的にすてきかも。

その2に続くかも。でわでわ。

2010年11月2日

MongoDBとmemcacheのパフォーマンス比較

単純なキー・バリューの構造でパフォーマンスがどうなのか、memcacheと比較してみました。mongodbとmemcachedはそれぞれ、ローカルの一台のちびえあたんの中で動かしています。比較するためのコードは、次のところにあります。
Javaのコードは恥ずかしいので公開しません。計測はちびえあたんでやっています。
やっていることは、MongoDBではnoとbodyというフィールドにデータを設定して、10000件登録しています。memcacheでも同様にキーにシーケンシャルな数、値にMongoDBでセットしたものと同じ値をいれてaddしています。
登録にかかる時間は、MongoDBでは1.39秒で、一件あたりにすると0.139ミリ秒です。memcacheは1.86秒で、一件あたり、0.186ミリ秒です。ほぼ誤差の範囲内のような気もしますが、若干MongoDBの方が速いです。
MongoDBはデータベースが空の状態から開始しているので、一番最初はファイルの作成などで遅くなっているので計測から外しています。

次に検索です。MongoDBでは、0から10000までと10000から0までのキーで検索しています。それぞれ、インデックスのありなしでも計測しています。その結果のグラフです。縦軸はミリ秒で、一回あたりの検索にかかった時間です。

インデックスのない状態で検索スピードが大きく違うのは、ファイルの先頭からフルスキャンして、見つかるまでやるからです。find_oneでやっていますが、findで検索するとデータ量に比例して一律遅くなると思います。
これに対してインデックスをはると、データ量にかかわらずほぼ一定の値になります。グラフがつまらないので、そのデータは載せていませんが、一件あたりの検索時間が0.4ミリ秒前後です。
memcacheの検索の時間は大体0.19ミリ秒でした。検索の時間はMongoDBでは2倍から3倍弱遅いです。この性能差をどう見るか・・・?うーん、難しいです。

次はmembaseを計測しよう!と思ってhomebrewのパッケージにはなっていなかったので、やめました。

でわでわ

2010年10月28日

MBA11環境構築編

ちびえあたん(MacBook Air)は、ちっこいのでCPUは非力です。Mac Portsのようなエネルギーを無駄使いするシステムにはちょっときついです。「エコな若者はHomebrewです。」といろいろな人が言うので、Homebrewを使うことになりました。

インストールは、
$ curl -O http://gist.github.com/raw/323731/install_homebrew.rb
$ ruby install_homebrew.rb
です。途中パスワードを聞かれるんで、おそるおそる入力します。しばらくすると終わっています。/usr/localが下々のものでも書き込めるようになっています。

さて、とりあえず、
$ brew install git
でgitがインストールできることを試してみます。brew install mercurialとしたらpipとかpythonをまずインストールしろと怒られます。人はシステムに従います。
$ brew install python
気がつくとpythonがインストールされています。2.7です。2.6がよかったな・・・。次に
$ brew install pip
ちゃんとインストールできるようです。virtualenvを入れようと思って
$ brew install virtaulenv
とすると、pipをインストールした後にpip install virtualenvしろと怒られます。怒るくらいなら、勝手にインストールすればいいのに・・・。でも、やっぱり人はシステムに従います。
$ pip install virtualenv
$ pip install virtualenvwrapper
ここまでやって、Mercurialをインストールしようと思っていたのを思いまして、brew install mercurialってやってもいうことを聞いてくれません。
$ pip install mercurial
がお作法のようです。ここまでがいつもPythonの本体にインストールするモジュールです。あとは、virtualenvの環境下にインストールします。

mercurialをインストールしても/usr/local/binにはインストールされません。なので、
$ ln -s /usr/local/Cellar/python/2.7/bin/hg /usr/local/bin
しておきます。ついでなので、virtualenvの環境を一個追加します。.bashrcに
export VIRTUALENVWRAPPER_PYTHON=/usr/local/bin/python
export WORKON_HOME=$HOME/.virtualenvs
source /usr/local/Cellar/python/2.7/bin/virtualenvwrapper.sh

を追加して、mkvirtualenv mainでメインの環境が一個できあがり。いろいろ抜けていそうな気がしなくもないですが・・・。

27インチiMacにいらいら

今年の夏に我が家にやってきた27インチiMacは画面はとてつもなく広いし、CPUは高速だしとても快適なマシンでした。満足度も極めて高いマシンでした。このまま3年かそれ以上、何もせずにその満足を享受できるはずでした。そう、MacBook Airを店頭で触るまでは・・・。

店頭で触ってみて、MacBook Airと言うよりSSDのすごさに驚かされました。まったくの引っかかりがありません。常に高速で動きます。そして、短い時間店頭で触って分かったことは、ほとんどの作業のボトルネックはハードディスクとのIOにあるんだと分かりました。SSDだとswap in/outを感じさせないぐらい速いというのを見て、メモリサイズもボトルネックじゃなくなりつつあるのかと、驚きました。最もSSDにそんなに頻繁に書き込んで寿命は大丈夫かいなと思わなくもないです。

で、それがそもそもの不幸の始まりでした。でかいデスクトップにはノート以上の性能を求めてしまいます。そして、ハードディスクとのIOの引っかかりがどんどん気になり始めます。SSDにすれば、爆速になるんじゃないかという妄想にとりつかれ始めます。人間の欲望にはキリがありません。いや、そんな大それた話じゃないですね。

Airの起動速度とか、スリープというか、多分ハイバネートからの復帰のスピードとか騒いでいますが、Windowsと違って少なくとも一度立ち上げたらOSのアップデートがかかるまでシャットダウンしないので そこはあんまり重要じゃないかな。普段の操作で全く引っかからないのがうらやましい限りです。

まだ、SSDの容量はそれほどでっかくないですが、Airと同じスピードで512GのSSDが3万円ぐらいになったら欲しいです。



2010年10月26日

gevent的websocket生活

IE9での実装は見送られて悲しいwebsocketで遊んでいたのはもう、一年近く前のことになります。そのときに書いたチャットのサンプルコードですが、サーバサイドはgolangで書いていました。golangはとっても好きな言語なのですが、Python、特にgeventはどうなのかな?と思って調べてみました。単にネタがなくってwebsocketに走ったと言えなくもないです。ごめんなさい。

geventでwebsocketってどうするの?と。twistedであれば、かなり独自のコードを書いて独自の処理を追加しないと行けません。Python界隈ではwsgi全盛の今となってはそれはないよね!と。今時のPythonの人はwsgiのミドルウェアを使ってwebsocketをやるもんだぜ!えっ?geventはどうなって?Flaskでも動かしてやって、イベントループをgeventを使うようにして、monkey patchをあてれば、同期処理で動いてたwebsocketが非同期になって、C10K問題も解決だぜ、ベイビー。おしまい。

まあ、ことはそれほど単純じゃないですし、それだけだとつまらないので、geventでwebsocketを扱うgevet-websocketで書いてみます。昨日、Komodo Editを使うって宣言しておきながら、Emacsで書いています。はい。軟弱ものです。gevent-websockeはpip install gevent-websocketでインストールできます。

基本的にクライアントのコードは一年前とほぼ同じものです。ここにコードのすべてがあります。

from geventwebsocket.handler import WebSocketHandler
from gevent import pywsgi
import gevent

chat_clients = set()

def handle_chat(ws):
    chat_clients.add(ws)
    while True:
        message = ws.wait()
        if message is None:
            break
        for client in chat_clients:
            client.send(message)

    chat_clients.remove(ws)

def app(environ, start_response):
    path = environ["PATH_INFO"]
    if path == "/":
        start_response("200 OK", [("Content-Type", "text/html")])
        return open("chat.html").readlines()
    elif path == "/chat":
        handle_chat(environ["wsgi.websocket"])
    else:
        start_response("404 Not Found", [])
        return []

if __name__ == "__main__":
    server = pywsgi.WSGIServer(("", 5000), app, handler_class=WebSocketHandler)
    server.serve_forever()



geventには、wsgiとpywsgiという似たようなものが二ついます。一時pywsgiはobsoleteの警告が出ていたのに、最新版ではでなくなっています。二つの関係がよく分かりません。pywsgiのWSGIServerにWebSocket用のHandlerを渡します。このHanlderはクライアントがWebSocketとしての接続を要求してきたらWebSocketとして処理できるようになります。それ以外は通常のWSGIとして処理します。
WSGIのミドルウェアと違ってhandlerがenvironにwsgi.websocketにwebsocketのオブジェクトをセットします。
WebSocketで実際のロジックはhandle_chatのところです。めちゃくちゃ簡単ですね。golangのコードと大差ありません。

wsgiのインターフェースでこんな風に長時間の接続を処理するのは向いていないと思っていました。でも、計量スレッドのようなものたちの上で動かせば、そこそこ行けるんじゃないかと思い始めました。でもApacheのような人(同期IOで処理する人)がフロントにいるような環境だとやっぱり無理っぽいな〜、と。

もし、gevent-websocketのコードの説明を始めたら、それはネタがない証拠です。

でわでわ。

工場見学に行ってみた

最近は工場見学が流行っているらしいので、アサヒビール神奈川工場に工場見学に行ってみました。見学後にビールが3杯タダで飲めます。お得です。でも、僕はドライバーなので、見ているだけです。代わりにお子様用ソフトドリンクか、大人用ノンアルコールビールが飲めます。僕はサイダーを飲みました。サイダー好きなんです。工場はかなり田舎にあるので、車で行かなかった場合は電車とバスを乗り継ぎます。バスの値段を見ると、最寄りの駅まで片道1000円ちょっとでした。ビールがタダ、と言うことに釣られていくと、かえって損ですね。

さて、工場見学で中に入って、とあるモニターを見ると、こんな画面が・・・。

なかなか素適です。某会社のOSのブルースクリーンっぽい画面を表示していました。

さて、工場見学は、まあ、子供は楽しかったようです。まりあ先生はかわいかったです。でも、工場の中には人はいませんでした。すべてロボットが猛烈な勢いでビールを造っているそうです。 パッケージングした後の倉庫への搬入までロボットがやっている映像がありました。フォークリフトが無人で動いているらしいです。凄いです。人の手で作られていないのは寂しいとか良くないとか言う思想を僕は持ち合わせていないので、機械でほとんどできるということに単純な驚きを持つだけです。

 見学が終わった後はお土産は、・・・・買いません。キリッ。でも見学の前に軽く食事をしました。となりでは焼き肉のおいしそうなにおいがしていました。足柄牛とかいう高そうな肉も食べられるらしいです。 



2010年10月25日

Komodo Edit 6をインストール

Emacsは使わないと宣言しておきながら、今だにEmacsを使い続けています。意志が弱いです。Emacsを使わないふりをするために、その代替としてKomodo Editを使っていました。Komodo IDEと言う人は売り物で、理不尽に高いです。Komodo Editはそのエディタ部分を抜き出してフリーになっています。OpenKomodoでオープンソースで開発しているとかしていないとかで、宗教的な理由でオープンソースじゃないといけない人でも大丈夫かもです。

さて、そのKomodo Editですが、油断をしていてたらしばらく使っていませんでした。気がつくとバージョン6がでていたので、インストールして、またKomodo EditのユーザになってEmacsを卒業する決意をしました。

なかなか信じて貰えないので、証拠画像です。信じて貰えたでしょうか?

Komodo Editはコード補完やfoldingなど、コードを書く上で必要そうなことはほぼできます。ソースコードのツリービュー(クラスやメソッドなどの一覧)がプラグイン(NST - New Source Tree)でできるのですが、それは僕のところでは動きませんでした。何か別のプラグインとコンフリクトしてんじゃね?と言う人もいるらしいで、iMacにもインストールして確かめてみます。

とりあえず、インストールしただけでまだ、何も試していません。UIが大きく変わるとかで一時期騒がれていたらしいですが、以前のバージョンからUIが何が変わったか分かりません。まあ、ほとんどKomodo Edit使わずにEmacsを使っていた罰です。きっと。

と言うことで、僕は今からEmacsユーザではなくなりました。きっと。


さてさて、Komodoを家のiMacにもインストールしましたが、やっぱりNSTでソースツリーは見えません。やっぱり壊れているようです。でも、インストールした0.49というバージョンはとっても安定しているとか書いています。困ったものです。

2010年10月23日

本をiPhoneとかiPadで読む

iBooksというか、iBooksの本を購入できる環境が中々できないし、Kindleも日本語の本を売ってくれません。でも、電子書籍を読むってどうゆうことか、やってみないとわかりません。種類は多くないですが、iPhoneでもアプリとして本は読めるし、理想書店で種類は多くなくても本を買って読むことができます。Fujisanもあります。ばらばらなのはいやなのですが、KindleなりiBookに統一されのを待つよりも、今ある環境を試すのも一興です。

将来的にはKindleとかiBookになりそうな気がして、本棚が理想書店とかいろんなものに分散されるのがいやだったのですが、大体の本が一度読んだら2回目は読まないので、諦めたともいいます。

さて、今年の夏ぐらいから本を買って読んでいます。まず、iPadは本を読むために買ったのですが、本を読むデバイスとしては僕は気に入っています。理想書店とか、アプリとしての本も快適に読めます。ただ、オライリーからPDFを買ったのですが、PDFだけは不満がのこります。字が小さすぎるのです。ページの余白部分がないぐらい拡大されていれば読みやすいのですが、そうすると、ページをめくるときなどに不満がでます。

その次にiPhoneでも読んでみました。iPadの大きな画面で読みやすさになれた後なので、iPhoneの小さな画面で大量の文字を読むのは不可能とか、快適じゃないとか思い込んでいました。でも、iPadまでとは行かなくても、電車で立ちながら読めるし、悪くはありません。「これから正義の話をしよう」はiPhoneで読みました。

本を読むということで言えば、紙でもiPhoneでもiPadでも何でもいいです。以前にも書きましたが、強いて言えば、 横書きで読めるiPhoneとかiPadのデバイスは紙よりもいいです。

この記事では、 新しいデバイスを否定的にとらえていますが、それは自分のスタイルをデバイスに引きずられているだけのような気がします。まあ、メールなりTwitterなり頻繁にチェックするような人は、紙の本だろうと何だろうと、良くないのかも知れません。僕は、暇なときしかメールとか新着をチェックしないので、特殊なのかも知れません。逆に言えば、メールとか読んだり、Twitterを読んだりしているときは、暇つぶしをしているだけです。

と言うか、本を読んでいる最中にリンクがあったからって言ってそれをクリックしていたら、読みおわらないじゃん。

とりあえず、僕はメールもTwitterもなるべく通知させないようにしています。通知されてもすぐには読まないし。 



2010年10月22日

MacBookAirなんて、か、買わないんだからね

ちっちゃいMacBookAirがでました。至る所で話題になっています。知り合いもポチッと購入しちゃった人もいます。小さくて割と軽いMacBookAirは魅力的ですが、持っているMacBookが壊れそうになるまでは、今回発売されたMacBookAirは買いません。キリッ。

欲しいかどうか聞かれた、それはもちろん欲しいです。でも、スペック的にちょっと微妙です。欲しいのは11インチの方です。13インチは電車の中で使うにはちょっとでかいのです。時々、まわりの人からの攻撃にさらされます。なので、11インチのサイズはとっても魅力で期す。でも、画面の縦は800は欲しいですね。

メモリは4Gまでのせられるのはすばらしいです。でも、CPUがちょっと残念感が漂います。1.4Gとか、1.6GがFSB800MHzでどれくらいのスピードなんでしょう?クロック数だけでも2G欲しいですね。MacPortsをいれると思うのですが、コンパイルのスピードはCPUに依存するしね。母艦でコンパイルして、サブのMacにインストールってできるのかな?

それから、ストレージは、64Gはさすがにきつすぎます。128Gあれば、写真とか動画を入れなければなんとかなりそうな気がします。でも、バッテリがカタログ上、5時間しかもちません。

我慢するとして、11インチでSSD128Gでメモリ4GでCPUを1.6Gのものを選ぶと、13万円ぐらいです。 13インチでSSD128Gでメモリを4Gにしても13万円ぐらいです。しかもCPUはよりはやく、バッテリはより長く、解像度はより高く、でも300gぐらいおもいし、ちょっと大きいです。さてさて、いろいろ迷いどころです。

でも、夏に27インチiMacをかっちゃっているので、 今回は見送ります。MacBookもまだ元気そうだし。



2010年10月21日

プロジェクトのバッファ

プロジェクトで安全のためにある程度のバッファをもってスケジューリングすることはよくあることです。それ自体は悪いことではありません。精神安定上、いいことかもしれません。

アリエルはできてからもう10年ぐらい経つので、その間に数々のプロジェクトがありました。プロジェクトの数とほぼ同じぐらいの数だけ、プロジェクトマネージャがいました。正確に覚えていないのですが、プロジェクトマネージャがいなかったプロジェクトもあったような気がするので、ほぼ同じぐらいという表現です。

プロジェクトが沢山あってそれらがすべて成功した訳じゃないです。もっとも、最初の5年ぐらいは悲惨なものだったかもしれません。まあ、若かったから仕方ないです。アリエルができたときは僕は29だったし。

で、いろいろなプロジェクトを見ていて、明らかにプロジェクトが遅れていて、周りが遅れていると指摘しても平然としているプロジェクトマネージャもいました。僕は小心者なので、平然としていられません。そして、そのプロジェクトマネージャのいい分は、「バッファーがあるから大丈夫です。」「今はバッファを使っているので、何の問題もないです」。言い分はわかるのですが、プロジェクトの運営の最初の方から、もしくは危険そうだとわかり始めても、バッファに過度の期待というか逃げを作ってしまって、結局バッファを食いつぶしてプロジェクトが遅れるというものでした。

プロジェクトが遅れるのはある意味、仕方ないこともあるので、そのことはどうでもいいのですが、バッファがあるからと思考が停止してしまうのは最悪です。その状態でプロジェクトが遅れても誰もかばってくれません。いや、嘘です。かばうこともありますが、それは別の利害関係からかも。

まとめると、プロジェクトマネージャは大変なんです。きっと。いやいや、プロジェクトマネージャじゃなくても、小さなチームのリーダーでも大変なんです。それらを手を出したくても、口だけ出してじっと見るのです。でも、わかってもらえないのです。



2010年10月20日

iMacにメモリを増設したでござる

VMWareを1000円で買ったのですが、iMac 2010midでVMをいくつか走らせるにはメモリが4Gでは若干不安です。いくつVMを走らせるつもりだ?というのもありますが、メモリを増設しました。最近、IO-Dataの2Gx2のモジュールを買おうと思ったのですが、これが実売2万円ぐらいです。ほぞ、買うつもりでいたのですが、いろいろ調べると、CORSAIR DDR3 1333MHz 8GB(4GBx2枚) 2x204pin SODIMM Unbuffered CMSO8GX3M2A1333C9
が1万8千円ぐらいです。4Gで2万円と8Gで18000円じゃ、8Gを買うでしょう。ネット上での評判も悪くありません。というか、気持ち悪いぐらいいいです。

20101019.png ということで、買って増設しました。もともと、4Gのモジュールが乗っていたので、合計12Gです。今のところ、普通に使えています。久しぶりに自分でメモリを増設したので、ドキドキしました。特にマシンが重いし、でかい(27inch)ので落っことさないように慎重です。手順自体はとっても簡単です。ねじの穴をつぶさないように注意するぐらいです。

きっとこれで、iMovieとか触っても不満はなくなるはずです。メモリを16Gにした人もいるみたいですが、そんなには必要ないでしょう。僕も8Gで十分だと思うし。

で、増設してからそれほど時間が経っていませんあが、前よりハードディスクがガリガリいうのが少なくなったような気がします。 

 



VMWareのUbuntuにVMWare Toolsをインストール

ネタがないので、とってもつまらないエントリが続きます。UbuntuをVMWareにインストールしたので、VMWare   Toolsをインストールしました。VMのイメージを作っているときには、VMWare Toolsのダウンロードが遅かったのでスキップしたやつです。

UbuntuにログインしてからVMWareのメニューの[仮想マシン] - [VMware Toolsのインストール]を選択します。すると、UbuntuにToolsのイメージが/media/VMWare Toolsにマウントされるので、その中のtarボールを展開します。

$ tar xf /media/VMware¥ Tools/VMware_Tools_xxx.tgz
$ cd vmware-tools-distrib
$ sudo ./vmware-install.pl

あとでインストラクションに従ってひたすらエンターキーを押すだけです。最初はちゃんと読んでいたのですが、途中で疲れてすっ飛ばしました。
インストールが終わって、とりあえず、再起動しようかと思ったのですが、めんどいのでXだけ再起動。ウインドウのサイズを変更すると、ちゃんとUbuntuのサイズも変わってくれます。

さて、Parallelsのコーヒレンス(であっていたかな?)に対応するユニティを試してみます。ParallelsはWindowsしかできなったと思うのですが、ちゃんとLinux君でも動きました。よかったよかった。

2010年10月19日

VMWareをインストールして、Ubuntu

VMWare Fusionを1000円ほどで買ったのでインストールしました。本当は、Ubuntuサーバを5、6個インストールするつもりだったのですが、それはまた、今度です。VMWare自体のインストールは簡単です。そういえば、英語でVMWareが動いていると思ったら、そうじゃなくって、日本語でした。英語でも日本語でもどっちでもいいけど。

インストール後、Ubuntu 10.10 Desktop日本語Remixをインストールしました。[ファイル] - [新規]でウィザードが立ち上がるので、[ディスクを使用せずにインストール]ボタンを押して、ディスクイメージからインストールします。すると、次の画面がでてきます。
簡易インストールって何かよくわかりませんが、初めてなので、それを選択します。ついでにパスワードも入れておきます。

仮想マシンからホームフォルダにアクセスするは、まあ、そういうものなんでしょうが、アクセスできて困ることはないので、読み書きできる形式にしました。

で、続けるを押すと、続きます。VMWare Toolsをダウンロードするか聞かれるので、ここは、ダウンロードでしょう。



でも、ダウンロードが始まってみると、遅いのです。ダウンロードがおわらないのであります。あきらめます。

あきらめると、次の画面になります。

さて、終了を押すとインストールが始まります。何も設定しなくても勝手にどんどんインストールされていきます。すごいです。待てば良いだけです。

しばらく待ってインストールがおわりました。Ubuntuを立ち上げてログインすると、あれ?英語です。自動インストールしたせいで、デフォルトの言語が英語になっていました。そのぐらい気にしては行けません。
ログイン画面に戻って日本語ロケール、キーボードにしてログインします。今度は日本語です。最初に英語でログインしたおかげで、勝手にできるフォルダ名が英語です。すばらしいです。フォルダ名を変更するかきかれますが、せっかく英語になってくれたのに、切り替えるのはもったいないです。はい。

で、気になって、ターミナルを立ち上げてdateコマンドをうつとPDTというタイムゾーンになっています。カリフォルニアみたいです。さすがはメリケンのソフトです。タイムゾーンを日本語にしました。これで、きっとすべて大丈夫なはず。


VMWare Toolsはそのうちそのうち。

2010年10月15日

VMWare Fusionのアップグレード版、安い


僕はMacBookとiMacをもっています。MacBookにはちょっと前のバージョンのParallelsが入っています。Parallelsは悪くない製品なんですが、一年ごとにバージョンアップをしています。新しいモノ好きなのでバージョンアップ自体はいいことなんですが、バージョンアップに伴ってお金が発生します。それが大体50ドルぐらいです。で、これはかなり大きな出費です。それと、ParallelsのLinuxの対応がちょっといまいちのような気もします。

さて、Macで動くメジャーなVMは、ParallelsとVMWare FusionとVirtual Boxぐらいです。iMacにもVMを入れようと思っていたのですが、まず、VirtualBox。これは、オープンソースで無料でありながら、ちゃんとしたものです。最初は、VirtualBoxをいれて使おうと思っていました。多分、上の3つの中で一番Linuxをちゃんと扱える製品だと思います。でも、悲しいことに、VirtualBoxは名前の前に「Oracle VM」がつきます。一気に使う気が失せました。

Parallelsは高い。特に、バージョンアップ費用。

VMWare Fusionですが、他のプラットフォームは無料なのに、Macだけお金が発生します。Macユーザは金払いがいいからかもしれません。さて、日本語のVMWareのサイトをみても何も書いていないのですが、英語のサイトをみると、今年いっぱいまで9.99ドルでVMWare Fusionの古いバージョン、もしくはParallelsのすべてのバージョンからアップグレードできるって、書いてあります。9.99ドルなので、1ドル85円としても850円ぐらいです。まあ、もともと9.99ドルなので、為替レートとにらめっこしなくてもいいですね。

と言うことで、安かったのでVMWare Fusionを買ってしまいました。未だインストールしてません。

まあ、Parallelsも面白そうな機能はあるんですが、でも面白いだけで多分使わない機能だし・・・。 





2010年10月14日

トラブルシューティングの方法

ソフトウェアの開発にはバグは憑き物です。バグはそこにあるので、末永くうまくつきあっていかなくてはいけません。すごいプログラマであれば、その憑いていたものは別の人に憑くかかも知れません。

さて、最近、他の人がバグと戦っているのを横でみている機会がありました。いや、先週末ぐらいからそんなことしかしていないような気がします。できれば、プロジェクトの終わりの段階だけじゃなくって、もっと速い段階からみてみたいと思うんですが、なかなかそんな時間はありません。

横でバグと戦っているのをみると、とても面白いです。バグを報告する人から、バグの内容を聞くのですが、現象を正しく理解する前に何か作業を始めようとしてしまいます。現象を確認すると、なかなかちゃんと答えられません。まずは、原因とか対処方法とかはいいので、現象を正しく理解することをやんないといけません。

現象が理解できたら、再現性をみないといけません。再現しないものは直ったという確証が得られないのです。でも、現実は悲しいことに特定の環境でしか再現しないものとか、すごく低い確率でしか起きないので、デバッグできなかったり、デバッグしようとすると再現しない嫌らしいものもいます。まあ、そういうものはソースコードを頭の中に入れて、頭の中で再現させるルート見つけたりしましたが、僕はもうそんな芸当ができるほど若くはないです。

コードをみながら再現するルートを見つけようとすることもあります。でも、最近みていた若者はコードをみると、なぜか直そうとか、バグの原因箇所を第六感によって見つけようとします。何もわかっていないのに、どう直すのか、不思議です。六感の信用度もよく分かりません。なので、ちゃんと考えてもバグは生まれるのに、そんな風に直す(?)とさらなるバグの原因になったりします。

で、再現できれば直すのはほとんどのケースが簡単です。ほとんどなので、直せないもの、直すのがめちゃくちゃ大変なものもあります。  

で、以前、僕がバグを解析しているところを若い人に見せて教えようとしたことがあります。結果は見事に失敗でした。思考をすべて言葉にしている訳じゃないので、いくつかをすっ飛ばしていたり、説明するのがもどかしくなったり、理解してもらえなかったり・・・。今回は、指示を出しただけですが、うーん、成功とは言えないような・・・。

結局、トラブルシューティングで言える確実な方法とは、「まずは落ち着け!」。 



2010年10月8日

プログラマの二つの世界なんてあるんだろうか?

プログラマが直面する二つの世界」は とっても面白いんだけど、それ以上にとっても違和感を覚えました。あんまり状況がわかっていないし、僕の働いている環境が特殊なのか、そういう対立するような世界には直面したことがありません。どんなにがんばっても現実的な予算と時間でその要求を実装することがそもそも無理というのはありますが・・・。

さて、二つの世界の住人がいるとしたら、僕はプログラムの設計・実装を重視する方です。そして、必然的に会社としても設計・実装を重視する文化です。でも、それらを重視するとして、顧客の要求も追求しないで一体何を作ろうとしているのか、目的がわかりません。まあ、Chandlerのように何を作ろうしているのかわからないまま、コードをこねくり回していたオープンソースのプログラムもありましたが、そんなのは例外でしょう。 

コードをきれいにすることは、顧客の要求とは違うと言う立場のようです。社内にもそういう風に誤解している人もいます。僕は受託開発の経験がなく、パッケージソフトの開発しか知らないです。パッケージソフトなので、一度限りのコードはほとんどなく、常にメンテナンスのことを考えなければなりません。一度書いたコードは製品がある限りメンテナンスし続けなければなりません。顧客の要求を満たすことは、ある一定のコスト内です。そのコスト内でメンテナンスし続けなければならないです。 メンテナンスが大変なコードはその後のメンテナンスコストの増大につながり、それがその後の価格などに響いてきます。品質とか、機能拡張とかの話にもなりますが、結局、顧客にとってメリットをもたらします。

なので、二つの世界は二律背反なのではなく、リリース期間までを考えたバランスの世界だと思います。 

でも、お客さんがエンジニアを理解してくれないって? そんなことはないんじゃないかな。僕の働いている会社のエンジニアたちは割とお客に理解してもらえているつもりです。



2010年10月6日

iPhoneって本当に直感的?

iPhoneって発売直後とか直感的で使いやすいって言われたりしていました。僕にとっては違和感なく使えて、悪くない製品です。でも、一歩下がったところから冷静にみると、本当に直感的かと言えば、ある程度は直感的だけど、それ以上は独特の慣れが必要です。慣れて言うのは、ちょっとした驚きなどから学習するものです。

さて、iPhoneのアプリとか、iPhone用のWebサイトって結局、どれも似たようなUIやユーザビリティになってきています。厳密には違うのかもしれませんが、すくなくとも僕はそう感じます。なので、新たに作るものは、すでにあるものにあうように作る、UIやユーザビリティを同じように作るのが、ユーザにとっての学習コストや使い勝手からするとよい訳です。

そこで、ほとんど、というか全くiPhoneを使ったことがない人がいきなりiPhoneを使うと、それらのUIやユーザビリティに慣れていません。なので、そこで戸惑いが生じます。戸惑いをなくすために、iPhoneの他のアプリと操作性の一貫性がなくなった方がいいのかもしれません。それはごく短期的な話です。それって、使えば使うほど、いらだつものです。

もう一つ困ったことは、iPhoneの小さな画面にPCのサイトのような画面を持ち込もうとすることです。 iPadなら違和感がないのですが、iPhoneにPCのサイトのを持ち込むと大きな景色を小さな窓から覗いているようで、とてもストレスフルです。僕の会社のCTOも少なくともちょっと前まではこのことがわかっていませんでした。今もわかっていないかもしれませんが・・・。で、実際に自分で使い込んでいないので、言ってもわかってもらえません。すごい割り切りが必要なんですが、割り切れないようです。まあ、すごいUIがあるならそうしますが、そんなに簡単にUIができあるものもないです。

そんな中、「このサイトはこのUIを実現しています」って見せられるものは、うーん、それって、どう考えても使いにくそうだし、かっこ悪いし・・・。僕は大きな景色を小さな窓で覗きたくはないんです。モックを作ればわかってもられるのかもしれません。でも、使いにくいとわかりきっているものを、モックといえども作るのはいやです。まあ、それは技術的に無理だよ、というのもあるんですが・・・。



2010年10月5日

MongoDBで作る20分ブログ

20分かどうかはよく分かりませんが、とりあえず、MongoDBを使ってエントリの登録、表示、編集するだけのブログのサンプルです。WebのフレームワークはFlaskを使っています。それ以外は特別なものはないはずです。コメントの追加は、気が向いたらやります。

まず、MongoDBをPythonから利用するにはpymongoを使いますが、これはMongoDBに対する薄いラッパーです。ORマッパーっぽいというか、MongoDBは全く構造化されていないのでもう少し構造化する者というか、堅いRDBから抜け出して生ぬるいMongoDBに来たのにまた、もうちょっと堅くなろうとするのか、まあそのようなライブラリがいくつかあります。

今回はFlaskを使っていますが、Django好きには、Django-MongoKitというのがいるらしいです。でも、僕はあんまりDjangoを使わないので、おしまい。で、Django-MongoKitというのは、MongoKitをDjangoから使う者です。MongoKitとは、
MongoKit framework try to keep its simplicity when you manage mongodb in python. MongoKit was developed to be fast and light with KISS and DRY in mind. MongoKit brings structured schema and validation layer on top of the great pymongo driver
ということです。構造化したスキーマやバリデーションなどもう少し高レベルのAPIを提供しようというものです。

それ以外には、MongoEngineという人もいるらしいです。基本的にはMongoKitと同じ思想です。MongoEngineのサイトにはMongoEngineはDocument-Object Mapperと書いています。なかなか面白い表記です。

高レベルなAPIもありますが、やっぱり今回はpymongoを直接扱います。

import pymongo
from pymongo.objectid import ObjectId
from flask import Flask, request, flash, render_template, url_for
from flask import session, redirect
from flask import g

def req_start():
    g.mongo = pymongo.Connection()

def req_end(response):
g.mongo.disconnect()
    del g.mongo
    return response

app = Flask(__name__)
app.debug = True
app.before_request(req_start)
app.after_request(req_end)

とりあえず、リクエストが来たらグローバルな値にMongoDBへのコネクションをセットして、リクエストが終わるとMongoDBとの接続を切って変な状態が残らないようにしています。

@app.route("/")
@app.route("/")
def index(oid = None):
    db = g.mongo.blog
    if oid:
        entries = db.entry.find({"_id": ObjectId(oid)})
    else:
        entries = db.entry.find().sort([("created", -1)])

    return render_template("index.html", entries=entries)

一覧とエントリの詳細を表示しています。サボっていますが、許してください。oidが指定されていたらMongoDBの内部IDで検索して、それらを表示します。oidが指定されていなければ、全部取得して日付でソートしています。

@app.route("/save", methods=["POST"])
def save():
    db = g.mongo.blog
    form = request.form
    oid = form.get("oid", None)
    if not oid:
        entry = {}
    else:
        entry = db.entry.find_one({"_id": ObjectId(oid)})
       
    entry["title"] = form["title"]
    entry["body"] = form["body"]
    result = db.entry.save(entry)
    oid = str(result)

    return redirect(url_for("index", oid=oid))

保存も、特に変なところはないですね。バリデーションをサボっているのはご愛敬。データはtitleとbodyしかありません。

テンプレートの中身はgithubのコードをみてください。
で、ここまでがきっと20分です。

コメントは、Embedded Documentにすればあんまり考えずにできます。でも、その場合だとサイドバーをつけたときに、「最近のコメント」を表示できないです。db.commentというテーブルを作って、コメントを2段階で取得した方がいいです。でも、でも、でも。

でわでわ。

2010年10月4日

Pythonで3分MongoDB

PythonでMongoDBで遊びます。shardingとかGridFSとか、MongoDB自体のお話はまた、きっと後日。

まずは、mongodbのインストール。

$ sudo port mongodb

MongoDBを動かします。Cassandraと違って一般ユーザで。

$ mongod  --dbpath data

では、PythonでMongoDBを遊ぶためにpymongoをインストール。

$ pip install pymongo
さて、準備ができたので早速遊びましょう。MongoDBをインストールしたらmongoというインターラクティブシェルも一緒にインストールされます。これを使って、インターラクティブにmongodbを操作できます。でも、残念ながら今回はPythonのインターラクティブシェルで遊びます。
pymongoをインポートしたらMongoDBとのコネクションを取得します。localhostでデフォルトのポート番号で動いているので、Connectionの引数は省略しています。

>>> import pymongo
>>> conn = pymongo.Connection()

はい。それじゃ、MongoDBのデータベースに接続します。今回は、testと言うデータベースです。conn["test"]と書いてもいいです。データベースがあれば既存のものが使われ、なければ作られます。

>>> db = conn.test
>>> db
Database(Connection('localhost', 27017), u'test')

データベースができたらusersと言うテーブル相当のものを作ります。と言ってもアクセスするだけです。

>>> db.users
Collection(Database(Connection('localhost', 27017), u'test'), u'users')

では、nameにlirisでデータを保存します。Pythonの辞書がデータを指定します。最終的にはJSON、本当はBSONにデータはシリアライズされます。BSONについてはそのうち。
スキーマとかテーブルの定義とか難しいことは考えなくても、辞書にぺけぺけデータをセットしておけば、よきに計らって貰えます。この辺がRDBと違っていい加減、いや柔軟なところです。

>>> db.users.save({"name": "liris"})
ObjectId('4ca97e9dd013fd6412000000')

データを保存したので本当に保存されているか確認したくなるのが人情です。早速確認します。いますね。

>>> db.users.find_one()
{u'_id': ObjectId('4ca97e9dd013fd6412000000'), u'name': u'liris'}

それじゃ、もう一人。

>>> db.users.save({"name": "liris", "blog": "http://blog.liris.org"})
ObjectId('4ca97ecfd013fd6412000001')

で、データを確認します。今度はすべてのデータをなめ回してみます。
                                                                               
>>> for user in db.users.find():
...     print user
...
{u'_id': ObjectId('4ca97e9dd013fd6412000000'), u'name': u'liris'}
{u'blog': u'http://blog.liris.org', u'_id': ObjectId('4ca97ecfd013fd6412000001'), u'name': u'liris'}

二匹に増えています。
それじゃ、tagsとして配列でデータを入れてあげます。

>>> db.users.save({"name": "ohtani", "tags": ["liris", "ariel"]})
ObjectId('4ca97fc5d013fd6412000002')
>>> for user in db.users.find():
...     print user
... 
{u'_id': ObjectId('4ca97e9dd013fd6412000000'), u'name': u'liris'}
{u'blog': u'http://blog.liris.org', u'_id': ObjectId('4ca97ecfd013fd6412000001'), u'name': u'liris'}
{u'_id': ObjectId('4ca97fc5d013fd6412000002'), u'name': u'ohtani', u'tags': [u'liris', u'ariel']}

タグの中身で検索です。

>>> db.users.find_one({"tags": "liris"})
{u'_id': ObjectId('4ca97fc5d013fd6412000002'),
 u'name': u'ohtani',
 u'tags': [u'liris', u'ariel']}

辞書の中にさらに辞書を入れてみます(Embedded Document)。

>>> db.users.save({"name": "master", "nature": {"always": u"遅刻", "often" : u"来ない"}})
ObjectId('4ca98098d013fd6412000003')

Embedded Documentの中身で検索するときは、「.」で連結します。ちなみに、Embedded Documentだけのコレクションを扱うことはできないです。

>>> db.users.find_one({"nature.always": u"遅刻"})
{u'_id': ObjectId('4ca98098d013fd6412000003'),
 u'name': u'master',
 u'nature': {u'always': u'遅刻', u'often': u'来ない'}}

lirisさんが2匹もいるとうざいので、こんなやつは一匹だけにします。まずは、いらなさそうなやつをみつけだして、

>>> user = db.users.find_one({"name": "liris", "blog": None})
>>> user
{u'_id': ObjectId('4ca97e9dd013fd6412000000'), u'name': u'liris'}

消えちまえ!の呪文を唱えます。消えています。

>>> db.users.remove(user)
>>> for user in db.users.find():
...     print user
...
{u'blog': u'http://blog.liris.org', u'_id': ObjectId('4ca97ecfd013fd6412000001'), u'name': u'liris'}
{u'_id': ObjectId('4ca97fc5d013fd6412000002'), u'name': u'ohtani', u'tags': [u'liris', u'ariel']}
{u'_id': ObjectId('4ca98098d013fd6412000003'), u'name': u'master', u'nature': {u'always': u'遅刻', u'often': u'来ない'}}

切断しておしまい。

>>> conn.disconnect()

これで、きっと3分です。

会社に白いイヌがいた

触るとしゃべる

2010年9月30日

Magic Trackpadを買って、Magic Mouseがいらなくなる

7月の終わり頃にiMacを買いました。そのときからトラックパッドが欲しかったのですが、ずっと品薄で手に入りませんでした。で、やっと手に入れたのが2週間ほど前です。それ以来、使っています。

ポインティングデバイスは、僕が物心ついた頃にはマウスはあって、普通に使っていたと信じています。そのあと、初期のLets Noteを買って、 トラックボールを使うようになりました。トラックボールは熱狂的なファンがいるようですが、僕はそれほどのファンではありません。Letsを使うときはトラックボールで、デスクトップを使うときはマウスを使っていました。

そして時代は変わって、一部の熱狂的なファンに惜しまれつつも、Letsはまるいトラックパッドになりました。僕はこだわりがなかったので、普通にトラックパッドを使っていました。でも、Letsのトラックパッドは小さいので、オフィスにいるときはマウスをつないでいました。

どこかのタイミングでお仕事で使っていたノートPCがIBMのマメポッチみたいなやつだったのですが、あれは慣れなかったです。あれだけは、ダメでした。

で、LetsのあとにMacBookを買ったのですが、トラックパッドが広いです。広いと使いやすいです。で、マウスは僕にとっては使いにくいものになりました。多分、Letsもパッドがもっと大きければ、マウスをつなげなかったと思います。

で、デスクトップを使うときもいつしかトラックパッドが使えないかなー、と思うようになりました。できればキーボードとトラックパッドの一体型がいいんですが、リンゴの会社は作ってくれなさそうですね。

でも、マジックトラックパッドがきて、iMacに付いてきたMagic Mouseはいらないものになりました。店頭で使ったときはMagic Mouseも結構いい感じだと思っていたのですが、長時間使うと重くて疲れるので、いつしか普通のマウスより使いにくいマウスになっていました。マイティーマウスも使いにくかったので、アップルの作るマウスはダメなのかも知れません。

マジックトラックパッドになって、とても快適です。やっぱり、トラックパッドはいいです。まあ、慣れの問題と言うか、個人の趣味なんですが。

でも、マジックトラックパッドにフィルムをはる人がいるらしいですが、なんでフィルムを貼る必要があるか、よく分かりません。 

さて、使わなくなったマジックマウス、どうしよう? 



2010年9月29日

Google Developer Days 2010に行ってきた

今日はGoogle Developer Days 2010があったので行ってきました。実は行くかどうか、直前まで迷っていたのですが、行くことにしました。今回は3回目の参加です。前回と前々回はGoogle IOの直後でそれをトレースする感じなので、目新しいことはあまりありませんでした。今回はGoogle IOからかなり間があいているので、どうなるんだろうと思っていましたが、良くも悪くも、予想通りかな?

午前中の基調講演は、今日のことをすべてまとめている感じもするので、これだけでもういいかな〜、と感じたりもします。オーストラリアっぽい訛りの人がいました。僕はオーストラリア人の知り合いはいないので、本当にオーストラリアかはわかりません。最初はちょっと戸惑いました。 

お昼を食べながらパネルディスカッションで「未来のソーシャルウェブを占う」というのを聞いていました。まあ、徳力さんがモデレータだったので聞いていただけです。僕はあんまりソーシャルとかは興味がなかったりします。ご飯を食べながらだったので、気軽に聞いていました。感想は、モデレータがいまいちだったような気がします。ごめんなさい、徳力さん。

その後は、「プログラミング言語Go」というのを聞きました。これが残念でした。僕の期待と全く違っただけですが・・・。登場して一年が経つので、成長の軌跡みたいなことかな、と勝手に想像していましたが、Goで簡単なアプリを作って勉強する、みたいな感じでした。一年前であればスライスがどうメモリを扱っているかとか、そこそこ面白かったのかもしれませんが、今更感があります。goroutineとかそういう話もないし・・・。まあ、勝手に期待して勝手に裏切られただけなんですが。でも、goを知らない人にはよかったと思うし、むしろ難しいかも。

そのあと、Google Storage、Prediction API、BigQueryのお話でした。これはそこそこ楽しめました。Google Storageはeventually consistencyじゃなくって、"Read-your-writes" consistencyだと言っていました。eventually consistencyはいい加減さをかっこ良く言ってごまかそうとしているものだと思っていたのですが、それに対抗しているのでしょうか?Read-your-write consistencyってあまり聞いたことがなかったのですが、ググると結構ヒットします。この記事のタイトルはこれです。


Read-your-writes (RYW), aka immediate, consistency 

うーん、タイトルからして面白いですね。Predictionはあまり興味ないのでスルーして、BigQueryは後日遊んでみます。

そのあと、何回か聞いたような気もしますが、「グーグルエンジニアの日常」と言うのを聞きました。これはまた、別のエントリにするかも。で、感想はグーグルは天国のようにも感じないし、グーグルで働きたいとも思わない、かな。確かにすごい環境だとは思うんですよね。おやつと飲み物が無料なのは当たり前として、御飯まで出るとは・・・。ちなみに、アリエルもおやつが毎日でます。 

 



2010年9月28日

電子教科書とかIT教育とか・・・

いろいろなものをかなり周回遅れで書いている気がしないでもないですが・・・。

ちょっと前に電子教科書で教育が云々という話がありました。僕は新しい者好きなので、子供が乱暴に扱っても壊れない電子教科書であればあってもいいと思います。ただ、電子教科書で何が変わるか、と言われると、変わらないんじゃないかな・・・、と。媒体が変わるだけで、そこに本質的な変化はないと思います。双方向とか、コミュニケーションとかメリットを並べ立てることもできると思いますが、今のところ僕には幻想にしかみえません。電子教科書に反対じゃなくって、積極的に賛成じゃないだけです。

さて、電子教科書はそんな感じですが、IT教育とか言うのは、基本的に反対です。 そんなものはなくても全く困りません。まあ、僕の家には変なガジェットであふれかえっていて、それらがパパのおもちゃから子供のおもちゃに変わって言っている特殊な環境だからかも知れません。そうは言っても、たいていのガジェットなりPCは、特に教えなくても勝手に遊んでいるし、いろいろな技も発見しているようです。なので、便利なものであれば学校なんかでわざわざ教えなくても、勝手に使い方を覚える者です。

それから、そういう環境で使うものがWindowsと言うのも良くないです。もっと言えば、その上でOfficeとかホームページを作るためのソフトとか使うのがよくありません。特定の環境をわざわざ学校でうわっつらだけ教えても意味がないです。そんな知識はすぐに役に立たなくなります。そんなものにわざわざコストを払ってまで今更一生懸命やる意味がありません。何かの教育の一環というか、手段で使う分にはいいですが、それが目的化してもあまり意味がないかな。

で、基本的に反対なんですが、ネット上や携帯のマナーや危険を教えることは、 ちゃんとやるべきです。それ以外は、必要ないんじゃないかな?

で、そんなことやる暇があったら、論理的に考える方法とかを勉強すればいいと思っていたんだけど、何かにつけて論理的に反抗されたら嫌だな〜、と。 教育は難しいです。



2010年9月27日

gdataのCalendar APIのメモ

最近、わりかし長い時間gdataのCalendar APIで遊んでいます。まあ、何を作っているかは想像できる人には想像できるかな。
さてそのgdataで終日の予定を作るのはどうすればいいのだろう?ということで、結論から言えば日付データの文字列で時刻がないものということらしいです(ここ)。Python的には、

event.when.append(gdata.calendar.When(start_time="2010-09-27")

のようになります。一日だけの終日の予定は、start_timeだけを記述すればいいようです。では、複数日にわたる終日の予定ってどうなんでしょう?ということで、それは、end_timeを時刻なしで設定すればよいようです。上の一日だけの予定は、次の記述と同義です。

event.when.append(gdata.calendar.When(start_time="2010-09-27",
                                                                  end_time="2010-09-28"))

なので、例えば二日間の終日の予定は、次のようになります。


event.when.append(gdata.calendar.When(start_time="2010-09-27",
                                                                  end_time="2010-09-29"))

つまり、end_timeは終日の日数+1の日付ということになります。まあ、当たり前ですね。

さて、普通の予定はGMTで指定しました。でも、終日の予定には日付しかないです。タイムゾーンを指定するところがありません。終日の予定はその予定に設定されているタイムゾーン、通常はカレンダーの設定でしていしてあるタイムゾーンになります。時刻指定がある場合とない場合で、タイムゾーンの意識の仕方が変わるのはちょっとややこしいですね。

2010年9月23日

個人のブログもちゃんと書くように決意した

個人のブログは多分、2000年頃から書いています。2008年の暑い夏に自宅サーバがお亡くなりになって、それ以降はbloggerで書いています。残念なことは、サーバがお亡くなりになるときにデータがなくなっちゃことですが、まあ、仕方ありません。

その個人のブログですが、最近はなかなか更新できませんでした。理由は、忙しいからではないです。最も、忙しいからと言うのを理由にしたくないだけかも知れません。

さて、理由は多分、疲れたからです。年のせいかもしれません。別の言い方をすれば、ネタがなくなったです。よほど引き出しが多い人じゃない限り、ネタはそのうち底をつくので、ネタを探し続けないといけません。ネタを探して、それで遊んで、と言うところまではそこそこやるんですが、そこから先がめんどーになります。 で、一人で遊ぶだけで満足しちゃって、外にさっぱりアウトプットしなくなります。もったいないです。

アウトプットしないでインプットばかりするのは自慰だと思っているし、アウトプットしないとインプットの量が増えないです。 

なので、まあ、今後は頻度はそれほどあげないで、細々とアウトプットし続けるようにします。 まあ、とりあえず、自分のメモ的なものはなるべく書くようにしようと思う今日この頃です。 

で、個人のブログとこのブログと二つ書いていますが、どちらか片方だけならほぼ毎日書く自信はあるんだけど・・・。 



2010年9月21日

MacPortsのcassandraを一般ユーザで動かそうとしたら困ったよ、の巻

やっぱりMongo DBにしようかな、と迷いつつもcassandraをちまちま動かしたりしていました。今まではルートユーザで動かしていたんですが、めんどいので一般ユーザで動かそうとやっと思うようになりました。タイトルにも書いたように、環境はMacPortsで入れたものです。
環境変数のCASSANDRA_CONFを設定すれば、confのディレクトリが変更されると信じていました。CASSANDRA_CONFをホームディレクトリの下に設定して起動しても/opt/local/share/java/cassandraの下を参照します。/opt/local/bin/cassandraを見ると、次の順にファイルを読み込んで環境変数を設定しています。

/usr/share/cassandra/cassandra.in.sh \
/usr/local/share/cassandra/cassandra.in.sh \
/opt/local/share/java/cassandra/cassandra.in.sh \
~/.cassandra.in.sh \
`dirname $0`/cassandra.in.sh; do

ちゃんと読んでいなかったのですが、後優先で設定を上書きしていくと思ったら、最初に見つかった設定を読み込んでbreakしています。MacPortsでいれると、/opt/local/share/java/cassandra/cassandra.in.shを読み込んでbreakしています。どうりで、カレントディレクトリにcassandara.in.shを置いても読み込んでくれないはずです。

cassandraを使うときに使用する環境変数で、変更しそうなものはCASSANDRA_CONFだけだと思うのですが、/opt/local/share/java/cassandra/cassandra.in.shでは

# The directory where Cassandra's configs live (required)
CASSANDRA_CONF=$cassandra_home/conf

と見事に上書きしてくれています。もう一度/opt/local/bin/cassandraをみると、環境変数でCASSANDRA_INCLUDEを指定していると上の環境変数を指定したファイルは読み込まないので、/opt/local/share/java/cassandra/cassandra.in.shをホームディレクトリのどこかにファイルをおいて、CASSANDRA_INCLUDEでそこを指定します。次にcassandra.in.shのCASSANDRA_CONFをホームディレクトリのconfの置き場所を指定すれば、めでたしめでたし。

ちなみに、confディレクトリのlog4j.propretiesのlog4j.appender.R.Fileとstorage.xmlのCommitLogDirectoryとDataFileDirectoryは正しく設定し直すのは忘れないでね。

最後に、cassandra.in.shで、環境変数にCASSANDRA_CONFが設定されていれば、CASSANDRA_CONFを上書きしないようにすれば、みんなハッピーになれると思うんですが・・・。

2010年9月17日

IE9 betaとその誕生日会と・・・

昨日はIE9 betaが生まれました。家ではWindowsは使わないので、会社に来てからインストールしました。Preview版と同様に軽快に動いてすばらしいです。感想を一言で言うと「IE6,7,8爆発しろ!」と。

さて、そんなIE9ですが、ひっそりと誕生日会が新宿のマイクロソフトであったので行ってきました。行く前に新宿のビックカメラに行ったらMagic Trackpadが沢山売っていたので買ってきました。それはまた、別の機会に。


 

さて、 IE9 betaですが、自分の会社の製品にアクセスすると・・・。JavaScriptが動いてくれませんORZ。プレビュー版では動いていたので悲しい限りです。で、とりあえず互換モードで動かすと、快適です。快適と言っても体感的にはFirefoxとかChromeとか、Safariとかと変わらないです。IE6,7,8が遅いだけなので・・・。

誕生日会ではIE9のUIについてマイクロソフトの人が自慢げに喋っていましたが、普段Chromeを使っているので、すごさを全く感じないです。まあ、それもあるけど、やっぱりIE9のすごさはデモサイトをみせるだけでいいような気もします。スピードのベンチマークは誰かが、自分たちで作ったベンチマークだから自分たちの製品が一番速くなるのはあたりまえとか、1ms速くなったってどうでもいいでしょ!と言っていました。まあ、そんなものです。なので、スピードとかは僕もある一定上であれば気になりません。IE6,7,8がそのラインを越えていないので問題なだけです。

IE9はネットワークキャッシュを最適化しているみたいなことを少し言っていました。興味はあるんですが、言っていることがさっぱり分かりませんでした。まあ、紹介程度の内容なので仕方ないんです。

CanvasがIE9では使えます。これは嬉しいです。でもWebSocketsは残念なことになりそうですが、まあ、仕様の確定とかを考えるといた仕方ないことです。で、一番の驚きは、開発ツールはF12 developer toolという名前らしいです。F12って・・・。

で、IE9はHTML5に向けていろいろできるようになってきますが、ChromeとかFirefoxと違って数ヶ月単位でリリースできないIEは不利なようで、他のブラウザに実験させて仕様が安定したあたりで取り込めばいいので、悪くない戦略かも知れません。でも、多分ギークにとってはちょっとだけ古くさいブラウザと感じるのでしょう。

まあ、HTML5を抜きにしても、速いと言うだけでIEはやればできる子だったのです。もう一度まとめると、「IE6,7,8は爆発しろ」



Flask-Babelのメモ

python界のWebフレームワークと言えばDjangoばかりがもてはやされていますが、僕はFlaskが好きで使っています。いろんなものがgeventベースで動いているので、Djangoは重すぎるのです。さて、国際化についてはTracを作っているところが作っているBabelがありますが、それをFlaskから使いやすくしたものがFlask-Babelです。Flask-Babel関係のメモです。

1. インストール
$ pip install Flask-Bable
です。インストールの仕方って必要?

2. コマンドを使ってメッセージの抽出とか翻訳とか

次の設定ファイルを作ります。pythonのファイルとjinjaのhtmlテンプレートから翻訳するためのメッセージを抜き出すための設定です。

[python: **.py]
[jinja2: **/templates/**.html]
extensions=jinja2.ext.autoescape,jinja2.ext.with_
これをbabel.cfgとして保存します。次にこの設定ファイルをつかって、翻訳すべきメッセージをmessages.potファイルに抜き出します。
$ pybabel extract -F babel.cfg  -o messages.pot .
これはテンプレートなので、ここから各言語ごとにpoファイルをつくります。gettextの国のお話なのでその辺は割愛。最初は初期化します。
$ pybabel init -i messages.pot -d translations -l ja
これは、初期化するものなので、テンプレートを更新したら次のコマンドでpoをアップデートします。
$ pybabel update -i messages.pot -d translations
で、translations/ja/LC_MESSAGESにmessages.poがいるので、この人を翻訳してから、上の方にコメントアウトしてあるLazyの行を削除して、コンパイルします。
$ pybabel compile -d translations
これでmoができます。

3. pythonのコードの変更

話は前後しますが、pythonのコードでgettextを使えるようにしないといけません。まずはbabelをセットアップします。

from flaskext.babel import Babel, gettext
app = Flask(__name__)
app.config.from_pyfile('mybabel.cfg')
babel = Babel(app)

セットアップが終わったので、コードの中で翻訳が必要なところをgettextのメソッドを経由するようにします。

gettext(u"Hello, world!")

さて、言語設定はブラウザから言語を優先する場合は、次のコードを書いてあげます。

@babel.localeselector
def get_locale():
    return request.accept_languages.best_match(['ja', 'ja_JP', 'en'])

これを書かないと、英語しか表示してくれませんでした。セッションの情報が使えるので、セッションからユーザが指定した言語で表示することもできます。それから、jaだけでうまくいくと思っていたのに、IEはja_JPじゃないと日本語なってくれませんでしたIEの言語設定でjaだけのものを追加してあげれば大丈夫なんだけど。

pythonのコードはおしまい

4. jinjaのhtmlテンプレートの変更

jinjaのテンプレートは翻訳が必要な場所を次のように書きます。すでにおまじないをかけているので、何も考えなくてもgettextが使えます。

{{ gettext('Hello, world from jijna html template.') }}

全部、おしまい。


2010年9月16日

ブログのデザインを変えてみた

今までは横幅がちょっと狭くて、もう少し広げたいと思っていましたが、ずーっとそのままになっていました。で、デザインに飽きてきたこともあって、新しいデザインにしてみました。まあ、作業は30分もかかならない程度ですが・・・。ヘッダーの画像の切り取りがちょこっと大変でしたが、まあ、そのぐらいです。画像もどこかに落ちているのを使えばいいのですが、自分で撮った写真を使っています。ちなみに写真の場所は夏休みに泊まったパラオのホテルです。明るめの画像を使ってみました。下が切り取り前の画像です。

ありえるたんをOAuthに対応させたときのメモ

twitterがOAuthになって、今まで使っていたTwythonが使えなくなりました。twythonの中にoauth.pyとかいるので対応していると信じていたのですが、しかたありません。それで別のライブラリ、tweepyを使うことにしました。

1. インストール

easy_install tweepy

ちなみに、僕が会社で使っているtweepyはgithubからとってきて、ちょこっと修正を加えているものです。

2. Twitterへのアプリケーションの登録

OAuthを使ってTwitterのAPIを操作するためにはtwitterにアプリケーションを登録しないといけません。認証に必要なtokenとかを取得するためですね。登録画面から登録します。必要な項目を埋めると、Consumer key、Consumer secretなどが出力されます。

次に認証に使うTokenを取得します。Tokenの取得には、tweepy-exampleにあるgetaccesstoken.pyを使います。この人を実行すると、Consumer keyとConsumer secretの入力を求められます。先ほど取得したものを入力するとブラウザが立ち上がり、pinナンバーが表示されます。コンソール上にはpinナンバーの入力が促されているので、そこにpinナンバーを入力すると、tokenのkeyとsecretが出力されます。これらはメモっておいてください。ちなみに、ブラウザを立ち上げずに、自分でwebにアクセスしてpinナンバーを取得できそうな気がしますが、一回きりしか使わないので、割り切りでしょう。

3. tweepyのオブジェクトを作る

次にtweepyのoauthを使ってTwitter APIを利用できるようにします。

import tweepy

access_key="2で取得したアクセスキー"
access_secret="2で取得したシークレットキー"


auth = tweepy.OAuthHandler(consumer_key, consumer_secret)
auth.set_access_token(access_key, access_secret)
api = tweepy.API(auth_handler=auth)

これで、OAuthを使って認証できるようになりました。ちなみに、この人は、Proxyを通らないので、Proxyがある環境は僕のgithubから持って行ってください。

4.  とりあえず、投稿する

api.update_status("Hello, world!")

5. 効率は無視したfollowしてくれている人をfollowしかえすコード

人数が増えると悲しいことになるコードですが、それはサンプルと言うことで・・・

me = api.me()
friends = me.friends()
followers = me.followers()
for follower in followers:
    if not is_friend(follower, friends):
        api.create_friendship(follower.id)

def is_friend(self, follower, friends):
    follower_id = follower.id
    for f in friends:
        if follower_id == f.id:
            return True
    return False

でわでわ

2010年9月15日

Google Calendarのgdata(python)の雑多なメモ

1. イベントの日付の変更

event.when[0].start_time = "2010-09-15T15:00:00.000Z"
event.when[0].end_time = "2010-09-15T16:00:00.000Z"
2. authorとか人の登録

event.author = atom.Author(email=atom.Email("myaddress@example.com"))

※ cnで表示用の名前とか登録できる

3. 登録とかで例外がでたら・・・

gdata.service.RequestErrorが送出される。配列になっている。で、データは辞書(?)になっていてstatus, reason, bodyからなる。reasonで失敗の原因が書かれている。statusはステータスコードね。大体400番台。bodyはConflictしていればコンフリクトした予定とかがatomの文字列で書かれている。

4. 文字列からイベントのオブジェクトへの変換

まあ、3で発生した例外でConflictしていたイベントの詳細を知るためにやったのですが・・・

event = gdata.calendar.CalendarEventEntryFromString(error["body"])

多分、これで全部。

27インチディスプレーは最狂かもしれない

いつ嫁が新しいiPod nanoを買ってくれと言うか、恐怖の日々を過ごしていますが、それとは関係なく我が家に27inch iMacがやってきたのが一ヶ月ぐらい前です。店頭ではあまり思わなかったのですが、家にもって変える途中や家に設置した後につくづく「でかいな〜」と感じます。子供も嫁もでかいでかいと言います。スペース的には一応、もう一代27inch iMacをおけそうです。

27inch iMacが欲しかったのは、速いMacが欲しかった訳ではなく、でかいディスプレーが欲しかったからです。まあ、そのディスプレーを使うためにはそれなりのハードも必要な訳ですが・・・。さて、そのディスプレーの中で何を表示しているかと言えば、まあ、横に3つに区切られていて、半分弱のスペースにブラウザがいます。リファレンスの参照とか、いろいろですね。のこりのスペースを2分する形で、ブラウザの隣に大きな声では言えませんが、Emacsがいます。Aquamacsはすぐに落ちたりするので、普通のCocoa Emacsです。その隣にターミナルがいます。それらが重なることなく閲覧、作業できるので、とても快適です。ちょっと高い買い物でしたが、とても満足です。会社のディスプレーを一番長くみるので、会社のディスプレーもでかくしたくなります。いや、会社のディスプレーこそでかくするべきです。でも、こんなでかいディスプレーがならんでいるオフィスの威圧感を想像すると、うーん。難しいですね。

さて、でかいディスプレーは快適なんですが、今の僕の能力ではこれ以上のでかいディスプレーは必要ありません。サブディスプレーとかで何か表示をしても処理しきれそうにありません。まあ、それもEmacsを使っているからそう感じるだけかもしれません。Eclipseを使えばまた、違った感想を持つかもしれませんが、潔癖性の僕は家のiMacにEclipseを入れる気はしません。

あとは、メモリを8Gにしたいのですが、メモリを買っても取り付けるのが大変そうなので、躊躇しています。まあ、Virtual Machineを動かさなければメモリが足りなくなることもなさそうだし(多分)、もう少し我慢の子です。

iMacの不満は・・・、Magic Mouseです。まあ、もともとTrackpad派で、なおかつ家電量販店でTrackpadが入荷されてこないという不満もありますが、Magic Mouseは重いです。重いのでしばらく使っているとフラストレーションがたまります。画面がでかい分、移動距離が増えて、重さがなにげに効いてきます。多分、電池が重いんだと思うけど・・・。Apple StoreからTrackpadを買っちゃいそうな自分がいます。



2010年9月14日

コンビニに負けないようにチキンライスを作った

先先週末のこと、お昼にコンビニのオムライスを買って公園で食べさせました。コンビニと言ってもローカルのコンビニで、裏に厨房があってそこで料理を作っています。ひょっとしてコンビニじゃないのかな?まあ、そのようなところでオムライスを買って食べさせました。子供が一口食べると、「うわっ!おいしい」と言います。 ちょっともらって食べると、そこそこおいしいのですが、コンビニごときに負けているようで悔しいです。子供には、コンビニの味覚より、我が家の味(そんなものがあるかは疑問ですが・・・)を覚えさせないといけない!と義憤に駆られて先週末に、チキンライスを作りました。えーと、冷蔵庫の中のもので作ったのですが、オムライスを人数分作ろうとすると、冷蔵庫の中の卵の大部分を消費してしまうので、チキンライスだけです。

とりあえず、ご飯を堅めに炊いて、タマネギと肉とウィンナーソーセージやらを炒めて、ご飯を入れて秘伝の調味料で味付けして、トマトケチャップいれて炒めて・・・。と、普通のチキンライスを作ります。で、「どうだ!」って出すと、「熱いけどおいしい。」「でも、ウィンナーソーセージいらない」って。子供はウィンナーソーセージが好きだと思って入れたのに、自分で全部食べました。オムライスの卵は、卵をふわっ、ってすることに神経を集中しますが、チキンライスだけだと楽でいいです。

まあ、よかった、よかった。きっと、コンビニには勝ったに違いないです。



2010年9月10日

いつの間にか新卒採用をしようとしていた

僕は新卒の採用には積極的ではないのですが、それは、プログラマにとっては新卒とか中途とかの区分けはそれほど重要じゃないと思っているからです。いい人がいるからとったら、たまたま新卒だったと言うだけです。今月号のSoftware Designでインタビューを受けているうさみみも、ぶらぶらしていて面白そうだからとったら、たまたま大学を卒業したばかりだったと言うだけです。

そうは言っても世の中、就職難らしいです。大学生の3割とか4割も就職できない人がいるらしいです。まわりに困っている人はいないので、どれほど大変なのかはわかりません。一部では起業しろとか、海外に行けとか言う意見もあるようですが、それは極端に走りすぎです。そんな状況と関係あるのかないのか、さっぱり分かりませんが、アリエルでも正式に新卒の募集を始めたらしいです。しかも、12年度の話じゃなくって11年度、今度の4月に向けてです。遅いですね。ただ、時間が短いのでアリエルのホームページ上でしか応募していません。リクルートのサイトとにらめっこしても出てこないと思います。

募集ページにいろいろ書いていますが、まあ、一通りの職種は募集しているみたいです。開発も募集しています。でも、開発だけはすでにプログラマの人しかとるつもりはないので、プログラマじゃない人はごめんなさい。あんまり書くと、「そんなこと言わないでください!」と怒られるので、このぐらいで。

それと、夏休みにやってきた飯田さんも紹介されています。まあ、就職まではちょっと、と言う人も、学生の間、バイトでくるのもいいかも。ちなみに、年内に六本木に引っ越すので、五反田で見かけなくなっても夜逃げではないので安心してください。

 



2010年9月7日

遅刻はよくない。でも・・・

ちょっと前に知り合いが会社の人が遅刻してくるのをブログで嘆いていました。ちなみに、知り合いは、同じ会社の人じゃないよ。本人は、立場的にも深刻なんだろうけど、他人事としては笑ってしまいました。まあ、どこも同じだな〜、と。で、多分、ロータスはさらにひどかった・・・。それに比べたら、今の人たちはまじめです。偉いです。

と、まあ、悪いところと比較しても意味がないですが・・・。さらに悪いところ(もうない会社なので、悪かったところ)を知っていると、その辺の感覚におおらかになるものです。でも、やっぱり、ルールはルールなので、守らないといけません。単純に人数が増えてくると、あるところでリミッターをつけないと組織が機能しなくなるのも事実です。それが遅刻しないことを厳守することかも知れないし、別の方法かも知れません。どの方法がベストかは企業の文化などによるので何とも言えません。で、それとは別に、周りに影響することもあります。それは、そのせいでミーティングができなかったり、心理的な影響です。心理的な影響は受ける方が悪いと言う人もいますが、人間はロボットではないので、影響を受けて当たり前だと思うし、何らかの形で企業の空気を変えていくものです。

と、年寄り臭いことを思う反面、僕が社会人になって、最初に遅刻したのは、入社して一週間もたっていない頃だったと思います。僕の同期で、入社式に遅刻してくる人もいたような・・・。まあ、僕は常習犯ではない(ではなかった)ですが、 やっぱり偉そうなことは言えないな〜。なので、心の中で葛藤しますが、やっぱりダメなものはダメです。 

ちなみに、僕は今は心を入れ替えたのか、年のせいなのか、他の人よりは早めに出社しています。 



2010年9月3日

コンソールだらけの驚愕の事実

9月になったことだし、会社に新しい人が入ってきました。入って最初にすることは、開発環境の構築です。もともとの開発環境の大本を作ったのは僕ですが、その後、誰かに引き継いだ後、どうなったのか見てきませんでした。僕が作ったときは、Eclipseからすべてを行う手順になっていました。

新しい人が困ったように作業しているのを後ろから眺めていると、黒いDOS窓が見えます。何をやっているのか不思議です。新しい人の隣に座っている人にちょっと作業をお願いして、作業が終わるの待っている間に新しい人が何をしているのか見ていました。

「でぃぷろいって言うのがエラーが出てできないんですぅ」と。「どれどれ見せてみ」 と見てみると、TomcatがProgram Files以下にあって、パーミッションではじかれています。まあ、誰でも書き込めるように設定して終わりなんですが、製品のビルドとかディプロイをコマンドラインからやっています。ビックリです。今でも僕はEclipse上からすべて作業しています。まあ、僕の場合は、インストーラでtomcatをインストールしないし、Program Filesの下にはいれないし、そもそも、tomcatが5匹ぐらいいるし・・・。まあそれは置いておいても、コマンドで操作しているのは驚きです。

数年ぶりにドキュメントをみると、おー、コマンドラインからビルドしろって書いてある(僕が書いたんじゃないよ)。しかも、DOS窓は管理者権限で起動しろだと・・・。 あり得ない・・・。Eclipseからビルドすれば、エラーがあればすぐに修正できるし、そのままデバッグもできるし。

まわりを見回してみると、みんなコマンドラインからビルドしているらしい。変態の集団だ。まあ、時々Eclipseが遅くなったりするので、それを回避してさっさと開発できるようにするためらしいですが・・・。 



2010年9月1日

gdataでGoogle Calendarにイベントの登録

登録は後で書こうと思っていて、プログラムができてしまうといつしか忘れるものです。なので、ちょっとだけメモ。コードはめんどいので実際の僕の書いたコードをそのまま。

1. 登録

container = "/calendar/feeds/%s/private/full" % cal_id
GDATA_DATETIME_FORMAT = "%Y-%m-%dT%H:%M:%S.000Z"

new_evt = gdata.calendar.CalendarEventEntry()
new_evt.title = atom.Title(text=cal["summary"])
new_evt.content = atom.Content(text=cal["description"])
new_evt.where.append(gdata.calendar.Where(value_string=cal["location"]))
new_evt.uid = gdata.calendar.UID(value=cal["uid"])

dts = cal["dtstart"]
dte = cal["dtend"]
# TimeZoneをごにょごにょしてdtstartとdtendをGMTでセット
    
start_time = dtstart.strftime(GDATA_DATETIME_FORMAT)
end_time =  dtend.strftime(GDATA_DATETIME_FORMAT)
new_evt.when.append(
    gdata.calendar.When(
        start_time = start_time,
        end_time = end_time))
evt = self.client.InsertEvent(new_evt, container)



containerのcal_idでどのカレンダーにイベントを登録するか指定できます。defaultにしとけば、デフォルトのカレンダーに登録されます。あとは、うーん、あんまり面白くないです。

startとendのフォーマット指定がよくわかりません。なにも考えないとGMTになります。フォーマットの000Zのところをいじくれば、ローカルタイムになってくれそうな気もしますが、自前でGMTに変換する方が楽だったのでGMTで指定しています。

で、uidですが、ドキュメントには、

Indicates the globally unique identifier (UID) of the event as defined in Section 4.8.4.7 of RFC 2445.

Must be a globally unique string and must follow specifications in Section 4.8.4.7 of RFC 2445.

とあります。多分、外部のツールが識別用に与えてもいいものだと信じて使っています。

2. 更新

InsertEventがUpdateEvent(event.GetEditLink().href, event)になるだけです。eventは検索したりして取得したイベントのオブジェクトです。それだけです。


あとは、処理をするときに緑色のスレッドを複数動かして並立すれば、とりあえずの目標は達成だな。次はexchangeだ。

ServerMan@VPSのメモリが増えた!

僕がこっそり使っているServerMan@VPSのメモリが増えました。だましだまし使っても、256Mのメモリは割とつらいものがありました。何もチューニングしないで使うと、256Mをわずかにはみ出ることがあったので、うれしいです。

でも、常に512M使える訳じゃなくって、利用状況によって使えるメモリ量がかわるみたいです。ちょっと不思議。増える分にはいいですが、減る時って使っていたメモリはどこに行くのかな?単にディスクに追いやられるだけのような気もしますが。

それ以外にも少し変更になっていますが、メモリ以外は僕にとってはどうでもいい機能です。

2010年8月31日

学生のころのプログラミングの力

会社ではなんだか、新入社員を採ろうとしているようです。当然、開発の人もです。僕は他の職種についてはよく分かりませんが、プログラマについては新入社員はとる必要がないと思っています。プログラマであれば、未だ学生であろうが、既に卒業していようが、いつでも採用すればいいだけで、新入社員という枠にくくる必要はないのかな・・・、と。とは言っても、社会人経験がないと、最初の名刺の渡し方とかの練習は必要ですが・・・。なので、どっかで新入社員を募集するかも知れませんが、そんなのは関係なく、会社のWebページの採用のところから申し込めばいいのです。そっちの方が自分でちゃんと調べた感がでて、評価が高くなるかも。

で、もう、十数年前のころ、僕が学生だった頃も就職は大変だったらしいですが、今の方がもっと大変なのかも知れません。就職活動もとてもがんばった訳じゃないので、特殊な環境だったのかも知れません。

で、就職活動(実質一社しか受けていない。もう一社は面接で断ったので)を経てプログラマになったのですが、学生のころは周りにプログラマが沢山いたわけではないです。なので、全体の中での自分の位置づけが全く分かりませんでした。今は、ネットの中に情報は氾濫しているし、人のコードもごろごろ落ちています。で、それらを書いた人たちに会うのも比較的容易です。でも、ちょっと前まではそうしたことがちょっと敷居が高かったので、自分の位置づけが分かりませんでした。まあ言い訳かも知れません。位置づけが分からないので、プログラマとしての自信がありませんでした。今も、本心としては自信はないのですが・・・。

なので、自分のレベルは一番下だと信じ込んでいました。就職すると、プログラマの人はうじゃうじゃいました。同期の人も、先輩も。で、自分より下の人は見ないので、その環境でもやっぱり僕は一番下だと信じ込んでいました。うーん、劣等感の塊だったような気がしてきた。 と言っても、周りの人は全くそんな風に感じていなかったと思いますが・・・。

で、自分のことを一番下だと思っているのに、面接なれしていない時にその時の自分を面接したら・・・。多分、採用していないでしょう。キリッ。

まあ、今はブログとかでいろいろ評価しやすいので、面接受けまくるより、ちゃんとした技術系のブログを書いて、技術力とか自称しているような会社を受ければいいじゃないかな。そうすれば、面接でおおぽかしない限りなんとかなると思う。 



2010年8月28日

誰にも理解されないこと

どんなに説明しても、悲しいくらい誰にも理解されないことがあります。その中の一つに半休があります。一日会社を休むんじゃなくって、半日だけ休むというやつです。僕は半休が好きで、無意味に半休をとって突発的に休みたくなるときがあります。一日中休みたいんじゃなくって、半日だけ休みたいのです。

このことを会社で話すと、「全休でいいじゃん」と、みんな納得してくれません。どうせ休むんなら一日休むというのがすべての人の意見でした。でも、僕は全休じゃなく半休がいいのです。

半休で何をするかと言えば、うーん、基本的には何もしません。僕が覚えている限りでは、初代iPhoneを買ったときが最初です。でも、このときは、契約が遅れて全休になっちゃいました。その次がiPhone 3GSを買ったときです。このときは、半休をとってお店にiPhone 3GSを見にいったのです。買うつもりはなかったのですが、なぜか買って会社に行きました。それから、2回ぐらい半休をとったことがあります。

半休をとる場合は午前休です。たぶん、午後に休みを入れたい場合は、お客さんに会う時間を調整して、直帰しちゃうから必要ないのかも。休みたい理由は、

1. 朝ゆっくり寝ていてもいいという安心感、心の余裕。でも普段通りに起きます

2. お昼頃に会社でみんな一生懸命仕事をしているんだと思いつつ、のんびり電車に揺られている倒錯感。

3. 会社に行く前に、ちょっと寄り道するわくわく感。(家をでる時間は大体同じです) 

すばらしいじゃないですか?でも、誰にも理解されません。会社のakayamanが今週の月曜日に午前休をとりました。「やっぱり分からん」と。 うーん、いつも通りに起きなかったか、いつも通りに家を出なかったせいだ。それから、寄り道せずに会社に来ちゃうからだ。

それとも、僕はちょっと寄り道すれば江ノ島とか鎌倉に行けちゃうから違うのか? 

と言うことで、みんなで午前休をとって、会社に行く途中でどこかで寄り道してみてはどうでしょうか? 



2010年8月27日

gdataでGoogle Calendarのイベントの検索とデータの登録、ついでに削除

ふざけたタイトルのエントリですが、昨日の続きです。

1. 検索

昨日のエントリでは、カレンダーのイベントの取得は、GetCalendarEventFeedを使って取得しました。この人はお手軽なんですが、取得した一覧がどういうものかよく分かりません。まず、このAPIはデフォルトでは25件しか結果を返しません。これはmax_resultsで制御できるようです。続きを取得したければ、eventのGetNextLinkを使えと書いています。このへんの制御は負荷の問題もあるのでしかたないです。取得する25件がどういう順番なのかわからないのが不満です。ドキュメントを読めば書いてあるかもしれませんが、無意味に全部そうなめしてもうれしくありません。前後ひと月分のデータをハンドリングしたいだけなので、期間を限定して取得します。

query = gdata.calendar.service.CalendarEventQuery(calendar_id, "private", "full")
now = time.time()
query.start_min = time.strftime("%Y-%m-%d", time.gmtime("%Y%m%e", now))
query.start_max = time.strftime("%Y-%m-%d", time.gmtime("%Y%m%e", now+60*60*24*31))
query.max_results = 256
feed = self.client.CalendarQuery(query)

これで、feed.entryにひと月分入っています。256件超えるとその分はとれませんが、そんなに僕は忙しくないので気にしません。

2. ついでに削除

まあ、削除は簡単です。

event = feed.entry[0]
client.DeleteEvent(event.GetEditLink().href)

client.DeleteEvent(event)とかの方がスマートに思いますが、まあ、ほかのところもこんな感じなのでなれるしかありません。

3. データの登録

データの登録はめんどくさいので明日。


それ以外で、あるカレンダーとGoogle Calendarでデータのマッピングをどうするかで、icalのuidとgoogleのeventのidをマッピングすればいいのですが、そのためにはマッピングテーブルを別に持たないといけません。マルチスケジューラ、通称マルスケはマッピングテーブルを外部に持っています。gdataのcalendarにはuidがあるので、それでマッピングできると思ったのですが、どうなんでしょう?あと、googleのイベントのlinkにicalのurlを持たせてマッピングしようかと思ったのですが、こっちはますますよくわかりません。

ということで、のんびりと遊んでいます。いや、お仕事がなぜか忙しんだが・・・。誰かの呪いに違いない。

2010年8月26日

pythonでgdata

googleさんのgdataを使ってGoogle Calendarをごにょごにょしてみました。gdataのAPIはversion 2.0と1.0の二つあるみたいです。version2.0はJavaと.netしか言語をサポートしていません。Pythonは1.0系しかないです。ちょっと悲しいですが、できることがそれほど大きく変わる訳じゃないので、気にしません。

さて、インストールはやっぱりいつものeasy_install gdataです。でも、ソースコードをダウンロードしてsampleディレクトリをみると、だいたいAPIの仕組みがわかります。よいサンプルコードです。すばらしいです。さて、それじゃ、順番に。

1. ログイン

import gdata.calendar.service

client = gdata.calendar.service.CalendarService()
client.email = "my_google_email_id"
client.password = "my_password"
client.ProgrammaticLogin()

です。ちょっと、4行目、5行目あたりがきもいですが、気にしません。

2. カレンダーの一覧を取得

feed = client.GetOwnCalendarsFeed()
for calendar in feed.entry:
    print calendar.title.text

これだけです。かえってくるものはatomです。atomのフォーマットができた頃は、こんな無意味に汎用的なもの、誰が使うんだ?blogの更新に特化しておけばいいのに・・・、と思っていましたが、gdataをみていると僕の考えは浅はかでした。

3. カレンダーのエントリーを取得

feed = client.GetCalendarEventFeed()
for event in feed.entry:
    print event.title.text

これで、デフォルトカレンダーの予定の一覧がとれて出力されます。calendarの一覧の取得方法と対比すると、フォーマットなどが類推しやすいですね。

さて、特定のカレンダーの中のエントリを取得するにはどうするんだろう?と言うことで、ドキュメントらしきものを読みましたがわかりません。GetCalendarEventFeedのuriのデフォルト引数をみると/calender/feeds/default/private/fullとなっています。多分defaultをカレンダーのIDを指定すればいいはず。あらかじめわかっている場合はそれを直接書けばいいですが、カレンダーの一覧から適当に選んだものを指定したいので、どうしよう?

ということで、

cal_id = calendar.id.text
cal_id = cal_id[cal_id.rindex("/")-1:]
feed = client.GetCalendarEventFeed("/calendar/feeds/%s/private/full" % cal_id)

としました。ちょっと違和感がありますが、まあよしとしましょう。目的の動作ができました。

2010年8月25日

icalendarとPythonと・・・

ちょっとわけあって、iCalendarのファイルをパースしたくなりました。会社ではグループウェアとか作っているのに、僕が直接iCalのファイルを操作するのは初めてです。世の中そんなものです。会社はJavaですが、個人の不満を解消するのにわざわざJavaで何かを作る気はしません。やっぱり、Pythonになっちゃいます。まあ、そのうち、会社に捨てるかもしれませんが。
さて、python icalendarで何がよいかGoogle様にお伺いをたてると、iCalendar for Pythonのお達しがくだりました。ページをみるとバージョンが1.2で更新日が2006年です。死んだプロジェクト?とか思いましたが、代替がなさそうなのでeasy_install icalendarでインストールすると、2.1がインストールされました。あれっ?と思ってみると去年の年末に更新されています。完全に死んだ訳じゃなさそうですが、ホームページが更新されないと不安になります。まあ、最初は僕のお遊びなので気にしないことにします。

それでは、icalのファイルをパースしてみます。

import icalendar

cal = icalendar.Calendar.from_string(open("mycal.ics").read())
events = cal.walk("vevent")
for evt in events:
    print evt["summary"]

のようにすれば、summary(タイトル)が一覧されます。日付の扱いがまだよく分かりませんが、おいおい。それではまた。

2010年8月23日

トゥクトゥクと言う乗り物に乗ったぞ

夏です。暑い日が続きます。家から5分ぐらいのところに海水浴場がありますが、ほとんどが原住民です。大磯や茅ヶ崎の海水浴場にも行くことがありますが、ほとんどが原住民です。サーフィンする人は遠くから来ることもあるらしいですが、それはよく分かりません。まあ、そんな感じの夏の近所ですが、先週末にトゥクトゥクと言う乗り物にのりました。ホテルの送迎用の乗り物らしいです。

公園で子供と遊んでいたら、見かけぬ変なバイクのようなものが止まっています。子供と近づいて見ていると、乗せてくれました。ホテルの送迎用の乗り物らしいです。でも、とっても暇しているらしいです。朝から晩まで、乗せるべき人がいないので、近所の人とおしゃべりして、近所の人をのせてその辺をドライブしているそうです。夏だから少しは客がいるんじゃ?って聞くと、「いないね〜。お盆のころはちょっとは人がいたけど、まあ、暇だよ〜」「暇だからこうやってそこらへんの人を乗せて暇を潰しているんだけどね」。

うーん、そもそも、僕が遊んでいるあたりはお客は全く来そうにないところですが、なぜそんなところにいるんだろう?いや、だからか?とか思っちゃうのですが、ホテルの経営状況とは裏腹にのんびりしたおっちゃんで、嫌いではないです。この乗り物のおかげで、近所では割と有名人です。まあ、サーファーとか、そこらへんのおっちゃん、おばちゃんは割とそういう人が多いですが。

経営的にはこれはどうなんだろう?と思いつつ、地域のコミュニケーションとしてはとてもおもしろかも。いや、ちょっと前から見かけていて、何か知りたかったので、良かったです。乗っていた人は、やっぱり近所の原住民でした。

で、そんなに遊んでいるんなら、駅と海の間(1kmぐらいある)を往復すれば人も多いのでそこそこの宣伝になるような気もするけど、まあ、いろいろやっちゃいけない理由があるのかも知れません。一人100円とかなら乗りたいかも。

まとめると、僕はアメリカに行ったときにも消防車に乗せてもらったりしたので、変なものに乗せてもらえる運命なのかもしれません。ちなみに、日本でも子供と消防署にいけば、時間が空いていれば救急車や消防車に乗せてもらえます。少なくとも僕の近所と江ノ島のあたりはそうでした。

 



2010年8月20日

データストアの方法とか・・・

アリエルの最初の製品はP2P型のグループウェアで、データはXMLとしてファイルシステム上の保存しているとはどこかで書いたような気がします。今の主力製品はP2PではなくてWebアプリケーションとしてグループウェアを作っています。Lotusのころから考えると、グループウェアが好きなのか、一番知っているからたまたまそうなったのか・・・。

そのグループウェアですがデータストアにはOracle様のRDBMSを使っています。言語はJavaで作っているので、結局、いろいろなものがOracleに集約されていくように感じます。XMLではなく、RDBMSを選択したのはパフォーマンス上の理由です。開発が始まった4年か5年前にもオブジェクトデータベースとか、変なデータベースがありました。もっと前のあり得るができる前後の2000年ぐらいには、これからはXMLだと言われていましたが、RDBMSは未だに主流です。RDBMSをXMLにマッピングするのは無理があるとか、蔑まれたりもして、オジェクとデータベースだ、と聞いたこともありますが、やっぱりまだ、RDBMSです。NoSQLでキーバリューストアだと見せかけて、MySQLもPostgresも面白い動きを見せていて、やっぱり目が離せません。この後にインデクシングの話をしようかと思ったのですが、それはまた、別の機会に。

さて、RDBMSを選択しましたが、当初のもくろみから、テーブル構造はころころかわるものを想定していました。 これは、Notes/Dominoを強く意識していたためです。Notesほどエンドユーザコンピューティングじゃなくてもいいのですが、もっと簡単にユーザがアプリケーションを開発できる環境を提供したく、そのためにはテーブル構造が固いのは不都合だったからです。

で、最初の実装では、NoSQLのキーバリューストアのように、キーバリューを保持するテーブルにすべてを保存して、あとはそれらを適当にインデクシングできるようにしてパフォーマンスを稼ごうという戦略でした。この戦略はある程度まではうまくいきましたが、ある一定量のデータを超えると次第に遅くなっていきました。まあ、そりゃそうですね。

で、その次に考えたのが、テーブルをもう少し分割して、データタイプに応じて分割したりするものです。こちらは最初の方法の延長線上なので比較的簡単なのですが、本質的に同じ問題をはらんでいます。以前、とあるシステムで月ごとにテーブルが分割されていてとても扱いにくかったので、それはさけたかったのです。(月ごとにテーブルを分割するのが適切なアプリケーションではなかったのに)

で、その次のものが今の形です。それは、テーブルを動的にどんどん変更してきます。動いている最中にテーブルを変えるので、とってもいやんです。しかも、クラスタリングしてアプリケーションサーバーが複数あると、動いているのが不思議なくらいです。いや、動くように作っているんですがね。で、カラムが増える分には割と問題ないのですが、減るときは、こっちもあんまり問題ないですね。でも、データタイプが変わるときは、うーん、がんばっています。 

で、データの保存の仕方は変わるんですが、その上で動くアプリケーションはほとんど変更を加えていません。アプリケーションはデータストアを全く意識する必要がないのはすてきなことです。データベースをよくわからないエンジニアも、ごりごり何かを書いています。これがフレームワークがすごいところなんですね。

ということで、次はまた、思いついたことを適当に書いています。 

 

 



2010年8月19日

師匠は誰だ?

柴田さんに「アプレンティスシップ・パターン
」をもらって読んでから、社内では師匠と弟子という関係がちょっとだけ意識されています。「プログラマーは一子相伝で技を伝承するので、簡単には弟子をとらない」とよく分からないことを言う人もいますが、アリエルの場合、新入社員をとらないので本当のところは不明です。ちなみに、新入社員はとらないけど、社会人経験がなくてもプログラマーならいつでもとるので、応募してください。

さて、弟子候補がそもそもいないので、かつての自分たちに当てはめてみることになります。CTOに「師匠はいましたか?」と聞くと、「う〜ん、いなかったです」と。社内のめぼしい人に聞いてみましたが、100%の人がいないと答えました。「大谷さんは?」と聞かれたので、とりあえず、「僕の師匠はCTOです」 と笑顔で答えたら、真顔で「おおたにさんは弟子とは認めません。そもそも何も教えていないし」と一瞬で破門、と言うか、弟子入りを拒否されました。

まあ、師匠はいなくても仲間というか、ライバルというか、そんな関係の人がいたという人は、当社調査で約78%いました。誰かに教えてもらうと言うより、誰かと一緒にと言う方が多いのかも知れません。僕も後者でした。

それじゃ、師匠と弟子っていらないんじゃないか?ということになりそうですが、まずは、アリエルは特殊な環境なので、僕の知っているもう一つの特殊な環境のLotusはどうだったかというと、友達の何人かは、先輩に当たる人に細かく教えてもらっていました。遠くから見ていただけなので真実は不明ですが、僕から見るとそのようにうつりました。放置プレーされていた僕からすると、うらやましかったような、うざいような、どうでもいいような、微妙な心境でした。まあ、80%の時間を遊んでいたので、懇切丁寧に教えられると遊べなくなるので、やっぱりいやですね。後に別の人から聞いたところ、その友達は「最初は使えなかったけど、何年かしたら使えるようになった」らしいです。さらにその後どうなかったかは、プログラマじゃなくなりました。それは日本の環境のせいなのか、本人が飽きたからなのか、なんらかの壁のせいなのかは不明です。アプレンティスシップ・パターンにもドロップアウトというパターンはあるので、これが悪いことではないのでしょう。

まあ、師匠はいない人は多いですが、その場合は、仲間のような人の存在が多いのかも知れません。えっ?うちのCTO?彼は孤独を愛する人です。何度頼んでも、僕を弟子にとってくれません。 



2010年8月11日

KeyValueストアとか・・・

あまり、会社の製品について書いたりしていないので、ちょっとだけ。もともとの発端は、「最近、NoSQLとかKey Value Storeとか流行っているよね。でも、それらがどう分散されているかと言うことを除けば、アリエルってkey value store好きだよね。もしくは大谷さんが好きなのか・・・」と言うCTOのお言葉です。多分、僕じゃなくってアリエルです。

で、時はさかのぼること10年前。アリエルはP2Pのグループウェアを作っていました。その時の文書(スケジュールとか掲示板の文書とか)は、それぞれにキーが割り振られていて、XMLのドキュメントとして扱っていました。大昔に、Unix Magazineにそのへんの仕組みを書いたことがありますが、多分、忘れ去られています。この場合の文書が、Valueに相当します。まあ、アリエルのシステムが特異というわけじゃなく、割と一般的にだと信じています。

さて、アリエルの最初の製品はP2Pのシステムです。これは、Key Value Storeがインターネットに分散されていると言うことです。で、最初にアリエルがつまずいたところが、一覧で見たときにパフォーマンスが追いつかないとことです。 ちょっと前のKey Valueの動向を見ていていると、昔の記憶がよみがえるようで感慨深いです。

で、文書という概念、もしくはValueという概念は今の製品にも受け継がれているのですが、これはもっと古いです。ZopeのZODBというオブジェクトデータベース由来だという噂もありますが、それは嘘です。基本的にはLotus Notes/Domino由来です。新入社員として入ったせいで、大きな呪縛があるのかも知れません。Notesにはとても影響されています。

Key Value Storeは最近出てきた凄いものという受けとめ方もありますが、確かにCassandraとかVoltDBとか面白いんですが、Key Value Store自体はdbmとか大昔からある仕組みです。それをどう使うかは新しいかも知れません。新しいものを追い求めるのも大事ですが、ちょっと立ち止まるのもまた面白いかも。 

とか、そんな昔話はどうでもよくって、今作っている製品の仕組みを書こうと思っていたんだけど、それはまた、今度。