Posted at 2012-12-04 00:21:22 under テクノロジ (by key)

いつものように走り書きスンマセン。環境はnginx 1.1.19-1ubuntu0.1, mongodb-10gen 2.2.1, td-agent 1.1.10.3-1です。

nginxの設定とポイント。

  • nginxでJSONっぽいフォーマットのログを定義
  • request_time, upstream_response_timeを定義してnginxの処理時間を出力
  • 時刻のフィールドはなんでも良いが、ここではISO8601文字列にしてある(JSONだし)

td-agent.confの設定とポイント。

  • 取り込みformatはjsonにする
  • ISO8601形式はそのまま読み込めないのでtime_formatでフォーマットを指定する 参考

これでモリモリとmongodbにログが記録されていきます。ほっときっぱなしでディスク溢れて死なせないよう気をつけなければ…。


2013/1/2 追記。

fluentdが lexical error: inside a string, '' occurs before a character which it may not. というエラーを吐いてました。 具体的には$requestに含まれる文字がparse出来ずにコケました。 ユーザ入力の環境変数を取り扱う際は、対象となる変数をエスケープしておく必要があります。 nginx一般の変数としては $request, $http_referer, $http_user_agent辺りでしょうか。

HttpSetMiscModule ( 説明 )のset_quote_json_strを使ってエスケープするのが簡単そうでしたが、locationディレクティブ内でしか使えないので採用できません。

これから対応策考えます…。


2013/1/2 夜追記。

結局性能計測ログを別に分けました。

  • 通常のアクセスログ → apache互換形式
  • 性能計測ログ → CSV形式

性能測定ログ nginx.conf:

log_format  performance  '$time_iso8601,"$request",$request_time,$upstream_response_time';

性能測定ログ td-agent.conf:

<source>
  type tail
  path /var/log/nginx/performance.log
  pos_file /var/log/fluent/nginx.performance_log.pos

  tag nginx.performance_log

  format csv
  keys time,request,response_time,upstream_response_time
  time_key time
  time_format %Y-%m-%dT%H:%M:%S%z
</source>

nginxからJSON形式で出力できればベストですが、標準モジュールでは対応できそうにない(上記問題が解決できそうにない)ため出力そのものを見送りました。 HttpSetMiscModuleを改造すれば解決できそうではあるんですけども、開発コスト、トラブルリスク、維持継続の面でやや厳しいかなと。

Tags: mongodb, nginx
Posted at 2012-03-31 00:54:53 under テクノロジ (by key)

MongoDBのニュースレターで MongoEngine を知った。

てっきり MongoDB Engine のことかと思ってサイトを覗いてみたら、 サイトの体裁がぜんぜん違うし10gen official evangelistのソフトウェアということだし、どうやら違うプロダクトである模様。 ちょっと気になって調べてみたところわりと素性が良さそう。

schemalessなデータベースを利用していて困るのはデータセット内に何があるかわからなくなることなので、個人的には保存時に何かしらのバリデーションが必要だと思ってます。 スキーマを定義可能なライブラリはいくつかありますが、django-mongodb-engineはdjangoモデルの拡張フィールドセットだけ、 mongokitはスキーマ定義が出来るものの埋め込みドキュメントがサポートされておらず、帯に短し襷に長しといった状況です。

で、mongoengineのドキュメントを読んだ限りではその辺りをクリアしており、なかなか良さそうだな〜という判断。

直近で使う機会がありそうなので、いろいろ試してまた紹介したいと思います。

Tags: mongodb, python
Posted at 2012-01-22 18:04:31 under テクノロジ (by key)

MyBikeに特定の位置情報をマスクする機能を追加しました。

どんなところで使っているかというと、GarminのGPSログをアップロードして地図や自転車のデータを見られる機能があるんですが、 例えば自宅から会社まで移動する際のログを取り続けると、始点終点を含めたポリラインが地図に描画されてしまい、自宅も会社もモロバレになってしまいます。

内部的にはこんな構造でデータを格納しています。クエリ一発で1行帰ってくるからすごく高速だし、 単純なループでグラフライブラリやGoogle Maps APIに渡すことが出来るので、システム全体としての負荷は激減しました。

activity_document = {
    'records' [
        {
            'timestamp': '2012-01-03T13:32:57',
            'speed': 35.0,
            'location': [135.0, 45.0],
        },
        {
            'timestamp': '2012-01-03T13:32:58',
            'speed': 36.0,
            'location': [135.0, 45.0],
        },
        {
            'timestamp': '2012-01-03T13:32:59',
            'speed': 38.0,
            'location': [135.0, 45.0],
        },
    ]
}
db.activity_record.insert(activity_documents)

MongoDBには位置情報を取り扱うための2Dインデックスというものがあって、 長方形や円に含まれる ドキュメントを 取得することができます。 次のように計算することで、自宅やオフィスを隠蔽できるなと考えたわけです。

位置情報全体 - 隠したい位置情報 = 表示する位置情報

ドキュメント内に情報を直列で持っている(recordsは配列になっている)ので records.location にインデックスを張ればいいや〜と簡単に考えていたのですが、 MongoDBの理解が浅くて罠にハマりました…。

罠。

  • nested arrayにはインデックスが張れない
  • embedded objectのみを取り出す方法が無い

こんなことが出来ません:

#records内のobjectにインデックスを指定…は出来ない
# 本来は records.0.location などとすべきだから。
db.activity_record.ensureIndex({'records.location':'2d'})

※MongoDB 2.0からインデックス作れるらしい。が、後述の問題は残る。

こんなことも出来ません:

# 経度、緯度から近い情報を取得…と言ってもドキュメントが丸っと帰ってくる
# 意味なし
db.activity_record.find({'location': {'$near': [130.0, 45.0]}})

仕方が無いので、データが増えることを覚悟の上で、時間と位置情報だけを持つコレクションを追加しました。 以下のドキュメントをデータ数分、activity_timestamp_locationというコレクションに保存します。

timestamp_location_doc = {
    'timestamp': '2012-01-03T13:32:59',
    'location': [135.0, 45.0]
}
db.activity_timestamp_location.insert(timestamp_location_doc)

最終的にGoogle Maps APIに渡す際は、次のように処理をしています。

# 隠すべき位置情報と時刻を取得する

# 隠したい場所の緯度経度、半径(km)
longitude, latitude = 135.0, 45.0
distance = 1

# 地球の半径
earthRadius = 6371

# 隠したい場所と周囲を指定して、位置と時刻を検索
results = db.activity_timestamp_location.find({
    'location': {
        "$within": {
            "$centerSphere": [[longitude, latitude], distance / earthRadius]
        }
    }
})
hidden_timestamp = [result['timestamp'] for result in results]

# 全てのデータから、隠したい場所の時刻を除外する
locations = [record['location'] for record in activity_record_document['records'] if record['timestamp'] not in hidden_timestamp]

GPS機器を使用した場合には(少なくともGarminは)時刻をキーとして利用できるので、 それを利用してマッチングをかけています。

ループ回して隠してる辺りがイマイチですが、速度的にはストレスを感じるほどでは無いのでしばらくこのままで行こうと思います。


環境。

  • MongoDB 2.0.2
  • pymongo 2.1.1

Tags: mongodb
Posted at 2011-08-10 00:02:08 under テクノロジ (by key)

UPDATE 2012/1/21

こちらのブログエントリは内容が古いものになりました。 2012年11月以降のdjango-mongodb-engineでDjango 1.3のデータベースオペレータに対応したので、特別な対応なくsouthと連携できます。 ( このへんの , パッチ


Southを利用したくて色々調べていたんですが、 純正SouthをDjango MongoDB Engineと一緒に使うと次のようなエラーが出ます。:

There is no South database module for your database backend south.db.None.
Please either choose a supported database, check for SOUTH_DATABASE_ADAPTER[S] settings,
or remove South from INSTALLED_APPS

その時のsettings.pyはこんなかんじ(一部抜粋):

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.sqlite3',
        'NAME': 'django.db',
        'USER': '',
        'PASSWORD': '',
        'HOST': '',
        'PORT': '',
        },
    'mongodb': {
        'ENGINE':'django_mongodb_engine',
        'NAME':'dummy',
        'OPTIONS': {
            'slave_okay':True,
        },
        'SUPPORTS_TRANSACTIONS': False,
       },
   }

southはDATABASES.iteritem()を総ナメしてENGINE属性があればマイグレーションが必要なデータベースとして認識するようです。 SOUTH_DATABASE_ADAPTERSへの記載は、DATABASESに登録されているデータベースで、 south上のどのデータベースドライバを利用するかを設定するものでした。 対応しているデータベースはsouth.db.engine_modulesにまとめられています。

で、 DATABASES['mongodb']['ENGINE'] = 'django_mongodb_engine' が設定されているとき、 engine_modulesに記載が無くsouth.db.Noneが参照されるため、 python manage.py syncdb 実行時にエラーが出力されて停止していまします。

south本体に手を入れるのは本意ではなかったのですが、手を入れないと直らなそうだったので パッチを書いてみました 。 DATABASESに書いてあっても、SOUTH_DATABASE_ADAPTERSに記載がなければ無視するような仕様です。

つたない英語でpull-requestを送ってみましたが、良ければ取り込まれるだろうし、ダメならそのままかなと。 ま、手元では上手くいってるのでよしとします。

Tags: django, mongodb
Posted at 2011-03-07 14:10:12 under テクノロジ (by key)

Debian/Ubuntu(自分はDebianで検証)の10gen謹製aptlineとMongoDBパッケージの名称が変わった。

旧:

deb http://downloads.mongodb.org/distros/debian 5.0 10gen
パッケージ名はmongodb-stable

新:

deb http://downloads-distro.mongodb.org/repo/debian-sysvinit dist 10gen
パッケージ名はmongodb-10gen

単純にアップグレードするだけならバックアップなど要らずサクっとアップデート出来るのだが、レプリカセットを構築していて新旧のパッケージが混在するような状況になると レプリカセットが壊れます 。どうもリンクしてるライブラリが異なるためかお互いに通信できなくなるようで、レプリカセットが停止してしまいます(mongoコマンドを使うと文字コードがうんにゃらというエラーが出る)。

きちんとパッケージを揃え、ノードを立ち上げてあげればデータの欠損など無く復旧できるので軽度な問題だと思いますが、ハマると泣けるのでメモ。

Tags: mongodb