2012-10-28

pyspa 2012-10 3日目 〜リファクタリング〜

眠い...

昨日はチューニングっぽいことをした後、眠りにつきました。今日はもう直接仕事をするのではなく、コードのエントロピィを下げる作業をしました。

たとえば、テストコードを見てもらったときに「これは、mock.patch を使いすぎだろwww」と指摘された箇所があります。

class API_UpdateCountsTest(TestBedTestCase):
    @patch('poll.api.get')
    @patch('backend.api.update_options_in_datastore')
    @patch('backend.api.calculate_deltas')
    @patch('google.appengine.api.taskqueue.Queue')
    def test(self, Queue, calculate, update, get_poll):
        ...
        self.assertEqual(Queue.call_count, 3)
        self.assertEqual(Queue.call_args_list[0], ((backend_api.QUEUE_NAMES[0],), {}))
        self.assertEqual(queue.lease_tasks.call_count, 2)
        self.assertEqual(queue.lease_tasks.call_args, ((backend_api.LEASE_SECONDS, backend_api.MAX_TASKS),  {}))
        ... (以下、大量の assert)

まあ、ひどい。テスト対象の関数の中で、いろんなことをしていて、かつ、それが丸見えになっているのが原因です。手順が多いのは仕方ないのだけれど、データの受け渡しがたくさん丸見えなのがよくない。

def _update_counts(queue_id):
    queue_name = QUEUE_NAMES[queue_id]
    queue = taskqueue.Queue(queue_name)
    tasks = queue.lease_tasks(LEASE_SECONDS, MAX_TASKS)
    vote_count = len(tasks)
    option_count = 0
    if tasks:
        payloads = [json.loads(task.payload) for task in tasks]
        deltas = calculate_deltas(payloads)
        option_count = len(deltas)
        update_options_in_datastore(deltas)
        # handle done
        for data in payloads:
            ... (まさかの20行くらい)...
        # delete task
        queue.delete_tasks(tasks)


ひどす。急ぎで性能を上げるために、コードを追加/削除/変更をしたときに、とにかくテストを書いて通すようにした残骸が残っています。こうやってエントロピィが増加するわけです。エントロピィの意味分かってませんが。

というわけで、以下のように変更。

def _update_counts(queue_id):
    data = dequeue(queue_id)
    if not tasks:
        return
    update_options(data)
    update_dones(data)

それでも patch は 3つくらい必要ですが、assert がシンプルになります。

そんな作業をしていたら、夜が明け、海辺まで散歩し、温泉に浸かり、朝食を食べて、もうすぐ10時になります。解散して、帰宅して寝ます。

2012-10-27

pyspa 2012-10 2日目

Python 温泉 2012-10 の1日目 は、Google App Engine の応答が悪くてふて寝で終わったわけですが、午前3時ごろに監視ツールから「正常に戻りました」メールが届いて起床しました。

というわけで、意地になっているチューニングを継続しました。データの流れは、

フロントエンド → タスクキュー → バックエンドインスタンス

になっていました。フロントエンドは自動スケールで台数がいくらでも多くなります。タスクキューとバックエンドインスタンスは 1:1 対応していて、 lease_tasks() している最中に、フロントエンドが add() するのに時間がかかるように見えます。

てなわけで、タスクキュー数とバックエンドインスタンス数 を N:1 にしてみたら、ちょっと応答がよくなってる感じもありつつ、しかし、あんまり優位な差ではないなあ、というところです。ひととおりの変更をしたので、月曜にちゃんと負荷かけてみます。

細々とした修正をしました。月曜に備えて、明日はゆっくりしようと思っています。リファクタリングとか。

pyspa 2012-10 1日目

本日から Python 温泉 2012-10 に参加しています。当初はだらだらと「いつかやる」リストに入っていることをやろうと考えていました。が、仕事で作っているプロダクトの性能要件が、昨日の夜に上がってしまったので、そのあたりをやろうとしています。まあ、なんとかなると思うので、やらなくてもいいんですが、ちょっと意地になっています。

Google App Engine で、アンケート集計をするようなサービスを作っています。アンケートへの回答が秒間5000 くらいです。一瞬だけであればさばけますが、これが長時間続くとキツイなぁというところです。memcache に置ければいいのですが、誰が回答したかをあとで取り出す必要があるので、揮発する memcache には置かない方針です。ちょっと意地になっています。

で、モヒカンたちに相談したところ...
  • 無理だろ m9
  • そういうことに使うなよ m9
  • GAE のキューは datastore なんだから遅ぇよ m9
  • いくらインフラ弱いっつっても、そろそろ潮時だろ m9
と...。だんだん、意地になってきました。

ボトルネックは backends のインスタンスが、 pull キューから lease_tasks()/delete_tasks() するとき、add() にすごく時間がかかることです。数秒かかります。ここをなんとか工夫したいなぁと思っていたのですが...



Python 不安定になっとるやんけ! というわけで、今日は寝る。

2012-10-19

プロジェクトマネージャが知るべき97のこと

「プロジェクト・マネジャーが知るべき97のこと」を読みました。この本は他の97のことシリーズと同じく、複数の寄稿(っていうのか?)を集めたものです。したがって、全体としてひとつの一貫した考え方にはなっていない箇所もあります。複数の視点があるというのは、そういうことです。

プロジェクト・マネジャーの仕事の範囲というのは、本来はどこかで決まってそうですが、実際には組織ごと、プロジェクトごとに、その守備範囲はことなっている印象があります。今の職場でのプロジェクト・マネジャーと、前の職場でのプロジェクト・マネジャーの具体的な仕事の内容は、けっこう違います。なので、そんなことも範疇に含まれるのか、と思うこともありつつ。

最近の個人的な課題は、変更をいかにコントロールするか、です。その視点から見た感想として気になった箇所をふたつ紹介します。

完全な知識という誤った考えは、ソフトウェアプロジェクトのための完全で矛盾のない要求が獲得できるという妄想のことです。現実には、ソフトウェアプロジェクトのライフサイクルのいかなる時点においても、要求が完全にわかることはありません。分析フェーズ、開発フェーズ、保守フェーズ、システムがレガシーであるときですらそうなのです。
(デイビッド・ウッド 完全な知識という誤った考え)

これはなかなかに凹む意見ですが、実際問題としてそういうことなのでしょう。矛盾だらけの要件リストで開発を始めたりしませんが、形式的な手法を用いたりしない限り、無矛盾な要件は得るのは至難の業だと思います。

とくに性能要件があるときです。性能は、多くの場合、インフラやアーキテクチャに大きく依存し、その実現可能性は使えるコストにも依存します。場合によっては、使えるコストというが性能に依存することもあります(100万ユーザを捌けるなら金を出す、など)。ってなわけで、依存関係がループします。

とういわけで、全ての条件が明確ではなくても、どこかから始めなければいけません。

経験豊富なプロジェクト・マネジャーであれば、「高、中、低」というフィーチャーの分類をしません。顧客は何もかも「高」にしてしまうおそれがあるためです。彼らはビジネス価値に基づいたフィーチャーの優先順位リストを好んで使います。すぐれたプロジェクト・マネジャーはビジネスオーナーに対して「遅れが発生した場合に、優先順位リストの下の方にあにあるフィーチャーは、このリリースでは実現できないかもしれません」とくぎを刺します。こうしたやり方は、ビジネスオーナー、特にフィーチャー同士の衝突について学んでこなかった人にとって、苛立ちの原因となるでしょう。ですが時間がたてば彼らもこうしたプロセスに慣れて、プロジェクトの現実として受け入れるようになります。
(ジョー・ゼネビッチ 約束以上にすべきか、約束以下にすべきか)

まあ間違いなく「高」ってつけられます。ABC を使っていると、いつのまにか A+ とか S とかついてます。何かを優先する、ということは、別の何かを優先しないということです。発注側が「全部大事」といって押し込んだとき、受注側は見えないところで手を抜かざるを得なくなります。それは多くの場合、品質やパフォーマンスだったりすることでしょう。

いまだにプロジェクトの中での優先順位付けを、ステークホルダ間で共有する方法というのは持っていません。ゼネビッチが書いているように、理解できない人にとっては苛立ちの原因になりますし、状況によっては単純に受け入れらません。がっかりな話ですが、まあ、そんなもんです。

絶対うまくいく法則というのはないのですが、できるだけ相手に分かる指標に変換するように心がけています。「これもやってほしい」「はい、できます。+1週間です。」というような会話です。+100万円でも、同時アクセスできるユーザ数20%減、でもなんでもいいんですけど。

この本を読んで、正解を見つけるのは大変・不可能かも知れませんが、飲み屋で先輩や同僚の話を聞く、の効率良い版だとコストパフォーマンスはよいと思います。

2012-10-11

git-flow/hg-flow と継続的デリバリの悩み

git-flow/hg-flow を使ったブランチ方針に、不満というか不都合というか、気になることが出てきたのでメモしておきます。結論めいたものはありません。

git-flow はブランチ方針を楽にするためのツールでしかないけれど、この方針のことを git-flow と書いています。

そもそも…

基本的に、git-flow/hg-flow のブランチ方針はよいものです。Jenkins を使って自動テストをやるようになって、ふと気になり始めたことがあるわけです。

develop ブランチに変更があったら、その先頭のリビジョンに対して、単体テスト → テスト環境にデプロイ → 機能テスト → pep8+pyflake の順に実行します。すべて通ったリビジョンはリリース候補です。急ぎのときは pep8+pyflake がしくじっててもデプロイして、後で修正します。

リリース手順は以下のとおり。

hg flow release start XXX    # step 1
hg flow release finish XXX   # step 2
hg up -C master              # step 3
make deploy                  # step 4

リリース頻度は、日によりますが、1日に数回リリースすることもあります。

release ブランチ要らなくね?

git-flow/hg-flow は、step 1 の後で、テストとかドキュメントの更新とかをして、問題がなければ step 2 へ進む、というのを想定していると思います。ですが develop の先頭をテストした後に、release/XXX ブランチを作るので、ここは当然テストが通っています。なので、step 1 と 2 の間で、特にやることはありません。機能を追加したときに changelog を書き換えるくらいです。そんなのあとで書き換えてもいいので、実は relase ブランチは不要だろうという気分になります。

master ブランチをテストしてない

次に、step 2 をしたら、master へマージしています。git-flow/hg-flow をきちんと使っていたら、 master が develop と同じコードになるのって保証されてるんだっけ?という、残念な疑問があります。develop はテストしているけど、 master はテストしてないだろう、と。

そんなわけで大抵、差分を確認するハメになります。

hg diff -r develop:master --stat
 .hgtags |  6 ++++++
 1 files changed, 6 insertions(+), 0 deletions(-)

.hgtags にだけ変更があれば、問題ないってことでリリース。簡単なスクリプトで、このチェックは自動化できる気はします。

考えるのが嫌になってくる

もういっそのこと、trunk (master, default なんでもいいけど) 1本だけで、ブランチきって変更してマージしていくか、という気分になります。なりますが、これもだるい。新機能を鋭意開発中のとき、リリース済のバージョンにバグがあったとき、default の先頭以外のリビジョンから hotfix ブランチをつくり、もとのリビジョンにマージする。そうすると、default の head がひとつ増えて混乱する。マージすればいいんだけど。

git-flow で一番気に入っているのは、事実上リリース履歴になっている master ブランチがあるのと、hotfix ブランチで不安定なコードの影響なく修正ができることなのです。けど、自動化をしようとすると hotfix の取り回しがややこしそう。hotfix の作業を自動化するのは、やめておくか、というところで悶々としています。自動化は手段であって、目的ではないですしね。