ジョギング
- 香椎浜 x 2 周
日課
- (腕立て x 30 + 腹筋 x 30) x 3
なんだか
疲れた。
を弄っています。
Elasticsearch でも簡単に始めることが出来ます。もちろん、Amazon Elasticsearch Service でも無問題(IAM の処理が必要となる場合があります)です。尚、dd-trace-py については導入済みという体で進めます。
本記事を書くにあたり利用した環境は以下の通りです。
$ python --version Python 2.7.13 $ pip list --format=columns | egrep 'elasticsearch|ddtrace' ddtrace 0.8.0 elasticsearch 5.3.0
尚、Amazon Elasticsearch Service のアクセスポリシーは IP アドレスによるアクセス制御で利用します。
以下のモジュールを利用します。
Elastic 謹製です。
今回は Elasticsearch に読み込ませた 20 万件程度のレストランデータを検索するコマンドラインツールを作ってみたので、そのコマンドラインツールを dd-trace-py を利用して各処理を追跡して可視化してみたいと思います。
超ざっくりで Elasticsearch のクエリチューニングは一切行っていませんが、一応、以下のように --name
や --location
オプションで指定したキーワードを含むレストランのリストがダーッと標準出力に表示されます。
$ python restaurant-search.py --name '焼き鳥' --location '福岡' 2> /dev/null 焼き鳥 一郎 | 福岡市博多区美野島2-11-14 | 焼き鳥の屋台 花山 | 福岡市東区箱崎1 | 焼き鳥 たかつ 三苫店 | 福岡市東区三苫1-16-44 | 焼き鳥 木鶏 | 福岡市中央区平尾4-5-15日之出第2平尾ビル | 炭火焼き鳥 ちびっこ大将 | 福岡市中央区高砂2-11-33北村コーポ 1F | 焼き鳥 いわた | 福岡市中央区渡辺通5-24-37レジデンス江崎 2F | 博多水炊きと焼き鳥 鳥善 | 福岡市中央区薬院2-15-2ルミエール薬院1F |
ちなみに、レストランデータは[こちら(https://github.com/livedoor/datasets)のデータを利用させて頂き、stream2es で Amazon Elasticsearch Service に放り込んでいます。
以下のように elasticsearch-py にパッチを当てるだけで Elasticsearch へのクエリを追跡出来るようになります。
from ddtrace import Pin, patch, patch_all patch(elasticsearch=True)
ソースコードを大幅に手をいれる必要が無いのが嬉しいですね。
今回は Elasticsearch へのクエリ以外に、検索処理を担う関数をはじめ、スクリプトの各種関数についてもパフォーマンスをトレースしてみたいと思いますので、tracer.wrap を利用してみたいと思います。
tracer.wrap は関数全体をトレースするデコレータで、以下のように関数の上にちょこんとのっけるだけで、関数の処理結果を Datadog に送信することが出来るようになります。
... @tracer.wrap('scroll_index', service='sample-app') def scroll_index(scroll_size, sid): try: while (scroll_size > 0): page = es().scroll(scroll_id=sid, scroll='2m') sid = page['_scroll_id'] scroll_size = len(page['hits']['hits']) output(page['hits']['hits']) except Exception, e: print e ...
ちなみに、デコレータとは対象となる関数をいじることなく、その関数の挙動にてを加える手法という認識で、上記だと scroll_index(scroll_size, sid):
という関数に対して、@tracer.wrap
という関数でデコレート(装飾)することになります。
もつ
と 福岡
で検索してみます。
$ python restaurant-search.py --name 'もつ' --location '福岡' 2> /dev/null もつもつ天神店 | 福岡市中央区天神3−3−5久保田ビル2F | もつ壙 | 福岡市博多区中洲3-3-3 | ... ... 二十四 永徳屋 博多もつ鍋 | 福岡市博多区美野島1-4-32松永ビル1F |
さすが、もつ鍋王国の博多です。
span = tracer.current_span() span.set_tag('name', unicode(name, 'utf-8', 'ignore'))
上記のように span.set_tag
で Span に対して任意のタグを付与することが出来ます。
もつ
と 福岡
そして 禁煙
で検索します。
$ python restaurant-search.py --name '焼肉' --location '福岡' --keyword '禁煙' 2> /dev/null 焼肉処 國 | 福岡市南区野間2-7-13ヴィラージュ野間 1F | 席数 25席 (掘りごたつ4つとカウンター5席) 駐車場 無 予約 予約可 貸切 可 (20人以下可) 禁煙・喫煙 全面喫煙可 カード 不可
一軒しかヒットしないのは寂しいですが、以下のように検索に利用したキーワードが span のタグとして確認することが出来ます。
Elasticsearch の検索が関数の殆どの時間を使っていることがひと目で判ります。
dd-trace-py を使うことで、アプリケーションの関数毎のパフォーマンスやデータベースへのアクセス時間をソースコードに殆ど手を加える事無く Datadog で監視出来るというのは嬉しい限りです。
{ "query": { "bool": { "must": [ { "term": { "name": "name" } }, { "term": { "description": "keyword" } }, { "term": { "address": "location" } } ] } } }
一行で欲しい時がある。
jq の -c
オプションを使えば一瞬で。
$ echo '{ > "query": { > "bool": { > "must": [ > { > "term": { > "name": "name" > } > }, > { > "term": { > "description": "keyword" > } > }, > { > "term": { > "address": "location" > } > } > ] > } > } > }' | jq -c {"query":{"bool":{"must":[{"term":{"name":"name"}},{"term":{"description":"keyword"}},{"term":{"address":"location"}}]}}}
意図した通りに一行で頂きました。
{"query":{"bool":{"must":[{"term":{"name":"name"}},{"term":{"description":"keyword"}},{"term":{"address":"location"}}]}}}
ありがとう。
$ jq --version jq-1.5 $ jq --help jq - commandline JSON processor [version 1.5] Usage: jq [options] <jq filter> [file...] jq is a tool for processing JSON inputs, applying the given filter to its JSON text inputs and producing the filter's results as JSON on standard output. The simplest filter is ., which is the identity filter, copying jq's input to its output unmodified (except for formatting). For more advanced filters see the jq(1) manpage ("man jq") and/or https://stedolan.github.io/jq Some of the options include: -c compact instead of pretty-printed output; -n use `null` as the single input value; -e set the exit status code based on the output; -s read (slurp) all inputs into an array; apply filter to it; -r output raw strings, not JSON texts; -R read raw strings, not JSON texts; -C colorize JSON; -M monochrome (don't colorize JSON); -S sort keys of objects on output; --tab use tabs for indentation; --arg a v set variable $a to value <v>; --argjson a v set variable $a to JSON value <v>; --slurpfile a f set variable $a to an array of JSON texts read from <f>; See the manpage for more options.