プログラマ行進曲第二章

主にソフトウェア関連の技術をネタにした記事を執筆するためのブログ

redisのコマンドが失敗した時にfailするようにするansibleのtaskを作る

仕事で直面したちょっとしたtipsを掘り起こしてブログにする形式の記事です。

プロダクトでredashを使っていて、その関係でAWSのElastiCache(redis)を使っているのですが、redashの内部で使われているCeleryがCPUを食い潰すくらい暴走することがあったりして時折再起動する必要があり、再起動した後redisをflushdbする必要があるのですが、その際に直面した問題がありました。

どういう問題かというと、redisをflushdbしたとき、成功しようが失敗しようがexit codeが0になってしまい、ansibleのtaskでredis flushdbしたときにそのままでは失敗した時にfailして落ちるという動作にならないということです。

細かい検証をしたわけでは無く、仕事でやったときはredis-cliではなくncコマンドを利用してflushdbするようにしていたので、全般的にそうなのか分かりませんが、今手元のPCで軽く確認した限りではerrorと出るようなコマンドをredis-cliで実行してもexit codeが0のままでした。

$ redis-cli flushdb
OK
$ echo $?
0

$ redis-cli flushdb -n 0
(error) ERR syntax error
$ echo $?
0

単にコマンドをマニュアルで叩く形式なら出力結果を目で確認し、失敗したら再度実行すればいいのですが、今回は長いansibleのplaybookの中にある一つのansibleのtaskとして利用したかったので、flushdbが失敗したらその場でansibleが失敗するようにしたかったのです。

失敗を検知したかったといっても、失敗した時にもexit codeが0が返ってくる状況でどうやればいいのか分からなかったのですが、検索して見たらredisの公式ドキュメントに解決の糸口になる情報が書かれていました。

https://redis.io/topics/protocol#resp-errors

RESP has a specific data type for errors. Actually errors are exactly like RESP Simple Strings, but the first character is a minus '-' character instead of a plus. The real difference between Simple Strings and Errors in RESP is that errors are treated by clients as exceptions, and the string that composes the Error type is the error message itself. The basic format is:

"-Error message\r\n"

要するに、コマンドが失敗したら、返ってくるエラーの頭の文字が - になるということです。

これを利用して、以下のようにansibleのfailモジュールを組み合わせれば、redisのコマンドが失敗した時にansibleがfailするtaskを実装できます。

以下のコマンドは実際に仕事で書いたansibleのtaskで、面倒なので行頭の空白は調整してないです。

       - name: Execute redis flushdb
         shell: <省略>
         register: redis_command_result

       - fail:
           # The first character of redis RESP Errors is minus '-' character.
           # See https://redis.io/topics/protocol#resp-errors
           msg: "Fail because redis flushdb failed."
         with_items: "{{ redis_command_result.stdout_lines }}"
         when: item|first == "-"

jinja2のfilterのfirstを利用して、返ってくるエラー文字列の各行の頭に - が存在したときはfailするというようにして解決しました。

マニュアルで実行するときは特に問題ならないことがansible経由だと問題になることが多々あって面倒ですね。