pylruをマルチスレッドで使うと「KeyError:」が発生する

githubにissueがないのでここに書く。

以下のような2スレッドで同時にlrucacheに書き込むとエラーが起きる。

test.py

import pylru
import threading
import random

cache = pylru.lrucache(1024)

def f():
    while True:
        key = 'key %s' % (random.randint(1,100000))
        val = 'value'
        cache[key] = val

def main():
    th_1 = threading.Thread(target=f, name="th_1")
    th_2 = threading.Thread(target=f, name="th_2")
    th_1.start()
    th_2.start()

if __name__ == '__main__':
    main()

version 1.0.3の場合

$ python test.py
Exception in thread th_1:
Traceback (most recent call last):
  File "/usr/lib64/python2.6/threading.py", line 532, in __bootstrap_inner
    self.run()
  File "/usr/lib64/python2.6/threading.py", line 484, in run
    self.__target(*self.__args, **self.__kwargs)
  File "test.py", line 11, in f
    cache[key] = val
  File "/usr/lib/python2.6/site-packages/pylru-1.0.3-py2.6.egg/pylru.py", line 145, in __setitem__
    del self.table[node.key]
KeyError: 'key 41240'

version 1.0.9の場合

$ python test.py                                                                                                                                                                     
Exception in thread th_1:
Traceback (most recent call last):
  File "/usr/lib64/python2.6/threading.py", line 532, in __bootstrap_inner
    self.run()
  File "/usr/lib64/python2.6/threading.py", line 484, in run
    self.__target(*self.__args, **self.__kwargs)
  File "test.py", line 11, in f
    cache[key] = val
  File "/usr/lib/python2.6/site-packages/pylru-1.0.9-py2.6.egg/pylru.py", line 145, in __setitem__
    del self.table[node.key]
KeyError: 'key 82904'

ロックをかければ問題が発生しなくなる。 ただロックが大きすぎると性能がでなくなるわけでどう修正したらベストだろうか。明日以降で取り組む。

2016/12/12 追記

Python 3.2からlru_cacheが標準で提供されているので、おとなしく2系から3系に乗り換えることにしよう。 それまではワークアラウンドで。 10.2. functools — 高階関数と呼び出し可能オブジェクトの操作 — Python 3.6.5 ドキュメント functoolsの実装を見るとRLockをつかっているようだ。

曖昧にならない後方参照の記述方法

python正規表現を使って置換処理をするときに、後方参照で少し困ったことが起きた。
一般的に使われる「\1」のような後方参照の直後に数字を続けると当然だけど後方参照の数字がどこまでなのかわからなくなる、というものだ。
解決方法は簡単で「\1」ではなく、「\g{1}」とする。

以下のようなケースは問題ない。

>>> import re  
>>> re.sub(r'(.*) (.*)', r'\1-\2', 'hoge fuga')
'hoge-fuga'

以下のようにすると問題が起きる。

>>> re.sub(r'(.*) (.*)', r'\1-\2012345', 'hoge fuga')
'hoge-\x812345'

解決は以下のようにする。

>>> re.sub(r'(.*) (.*)', r'\g<1>-\g<2>012345', 'hoge fuga')
'hoge-fuga012345'

公式のドキュメントにも書いてある。
http://docs.python.jp/2/library/re.html

pdnsを適当にチューニング

pdnsのチューニングは以下のページが参考になる。
https://doc.powerdns.com/md/authoritative/performance/

細かくやればきりがないと思うけど、receiver-threadsが一番効果がでそうなので、そこからやってみる。

チューニングのきっかけとしては、バックエンドのmysqlへのレイテンシーが非常に大きい環境があり、
その環境にあるDNSサーバがパンクしかけていたことだった。

レイテンシーが大きい場合は並列数を増やすことが効果的だと思ったのだ。

まずは設定値を変えてみる。デフォルトはreceiver-threads=1となっている。

$ sudo diff pdns.conf pdns.conf.20160908                                                                                  
251d250

< receiver-threads=3

以下のシェルスクリプトを使って検証を実施。
gist.github.com

receiver-threadsを上げてテストすると12ぐらいで最良の結果が得られた。
検証の方法が正しいかは保証できないが、適当にチューニングするぐらいならこれでよいのではないかと思う。

上記の公式ページだとreceiver-threadsは3,4ぐらいが適切と書いてあるが、
DBに対する通信のレイテンシーが大きい環境ではそうではないようだ(当たり前か)

2016-01-30 更新

`dnsperf`が大変便利なのでこんなシェル要らないことに気づいた。

pythonのsftpserverを試す

pythonで書いているバッチのテスト用にsftpサーバを簡単に立ち上げたかった。
探してみるとちょうどよいsftpseverというものを見つけたので使い方を含めてメモ。
GitHub - rspivak/sftpserver: A simple single-threaded SFTP server

gist.github.com

ニフティクラウドのロードバランサをAnsibleから操作できるようにしてみた

AWSならbotoと連携するモジュールをAnsibleが正式に提供しているが、
ニフティクラウドSDKがそもそもPythonに対応していないので、Ansible対応は難しい。

ということでAnsibleに対応するために2点実装した。
(1)botoをニフティクラウド用に修正
(2)Ansible用のモジュール作成

(1)はやり始めると途方がないので、ロードバランサからインスタンスの装着/取外しができるところまで実装した。

以下の様なplaybookを書くと動く
gist.github.com


(1)
github.com


(2)
gist.github.com

implementation of smtpd always returning 500 error.

常に500エラーを返すSMTPサーバを実装した。テスト等に使えるはず。
gist.github.com

工夫ポイント: エラーコードの返し方がsmtpdのドキュメントにはなかったので、ソースから特定した。

ログ監視ツール比較

ログ監視を行いたいが、どのツールがよいのか検討したので、簡単にメモする どのツールが良いかというのは、既存環境との組み合わせや、そもそも既存のツールで完結させたいとかそういう要望次第なのでなんとも言えない。 あくまで自分視点で比較をする。

調べた限り以下のツールが出てきた。

  • logmon
  • swatch(simple log watcher)
  • Zabbix
  • fluentd
  • logwatch

logmon

github: https://github.com/moomindani/logmon

IBM製のツール。シンプル。サンプルコードとして提供されている。

正規表現を書いて、一致する行が出てきたら即刻メールが飛んでくる。 ファイル名のパターンや正規表現に関する機能もシンプルなので、細かいところに手が届かない。

所詮サンプルなのでカスタマイズが必要

  • 言語: perl
  • 動作形態: デーモン

swatch(simple log watcher)

URL: https://sourceforge.net/projects/swatch/

イメージ的にはlogmonを改良したもの。 正規表現に一致する行があった時の動作はメールもしくはパイプで任意のコマンドを実行できる。 例えば、以下の記事のようにfluentdと連携することが可能。

http://kyudy.hatenablog.com/entry/2016/06/22/181907

  • 言語: perl
  • 動作形態: デーモン

Zabbix

URL: https://www.zabbix.com/documentation/2.2/jp/manual/config/items/itemtypes/log_items

logmonと変わらないぐらいシンプル。 単純なログ監視をする場合、かつ既にZabbixを運用しているのなら有用かもしれない。

  • 言語: なし(Zabbixのアイテム・トリガー)
  • 動作形態: Zabbixの付属機能

fluentd(grep Filter Plugin等)

URL: http://docs.fluentd.org/articles/filter_grep

grepのfilterとfluent-plugin-mailなどを使えば高度なログ監視が実現できるかもしれない。 fluentdが既に入っているなら良いかもしれない。

ただ、正規表現を追加することを想定する場合には、fluentdのリロードが必要なのが少し気持ち悪い気がした。

要件によっては自分でpluginを書いて見てもよいかも。

※ ただの個人の所感です。

  • 言語: なし(fluentdの設定で記載する)plugin書く場合はruby
  • 動作形態: fluentdの機能の組み合わせ。

logwatch

URL: https://sourceforge.net/projects/logwatch/files/

/var/log以下のログを整形して毎日レポートをくれる。 一般的なsyslogに対応していて、何か起きているとわかりやすく記載してくれるようだ。 内容としては例えばpostfix等でどのぐらいrejectしていたのかなど。

エラーログが出たときにすぐ知りたい、という使い方には対応できない。 他のツールとはその点が大きく異る。

独自アプリのログに対応させるには自分でスクリプト書かないと行けない。

  • 言語: perl
  • 動作形態: cronで定期実行