時計を壊せ

駆け出してからそこそこ経ったWebプログラマーの雑記

#yokohamapm #10 に参加しました

ビールおいしかったです。スポンサーのKAYACさん、準備・運営して頂いた皆さんありがとうございました。


こんな感じの内容を話させて頂きました。


要約すると、既存のライブラリ、罠が多くて理解と慣れを要求されるけど、
Time::Momentがだいぶ良い感じな雰囲気なので、使っていこうよ。という感じの内容です。
strptime相当の機能が無いので、strptimeだけの機能を提供するモジュールを別モジュールとして用意すると良いんじゃないかなと考えています。
epochだけ返せば良いかなと思いつつ、offsetの情報がformatから失われるといろんなoffsetを含むformatをparseするケースでつらそうなので、
offsetの情報も返したほうがいいかなー。と思いつつ、どうなのだろうか。


karupanerura/Time-Strptime · GitHub
karupanerura/Time-Strptime-libc · GitHub


Time::StrptimeはApache::LogFormat::Compilerの個人的にeval compile hackと呼んでる実装を参考にしたんだけど、
そのへんでpure perl codeの高速化に関する知見が得られたのでこれは別途どっかに記事を書いたりはなしたりしたい。


あと、JSON Schemaおもしろいなーと思ったり、IRKitすごいっておもったり、DBD::mysql奥がふかいっておもった。
JSON Schema活用すると、client sideとserver sideでvalidatorのロジック/ルールを共通化できたり、
仕様書にもそのまんま使えて、実装と仕様書の差異を気にしなくてよくなるなと思ってり夢がひろがった。
最近、いにしえの技術について学び直したりもっと基礎的な部分の知識を補強したりしていて、最新の技術にすこし疎くなりがちなので、ちゃんと追っていきたいなーとおもった。

クソコード、あるいは技術的負債

クソコードについてここ数日で考えたことを書いてみる。

技術的負債まわりのえらいひとたちの議論を眺めてて、技術的負債って言うとなんかプロっぽいけど、クソコードって言ったほうが示したいモノを素直に表してるし分かりやすいきがしてきた。

 

 

クソコードを書くなとは思わないけど、クソコードをいつまでも放置するのはやめようって思う。

クソコードは次なるクソコードを生み出すし、バグを隠蔽するし、メンテナンスコスト増大の悪循環のキッカケになるし、新人の教育上良くないので無くて済むならもちろんないほうがいい。

 

 

ただ、ギークな人たちを除いて、さらっと60点*1のコードなんて書けない。僕を含め大多数のエンジニアは自分自身が書いたクソコードをリファクタリングして60点以上のコードを目指すための時間が必要になる。

そのうえ、そういうコードを書いてもだいたい時間経過に伴って事情が変わって、60点のコードの挙動を壊さないように慎重に書き換えてビジネス上の目的を達成していったら、どんどんコードはクソになってくとか、ザラだと思う。

そういうわけで、クソコード書くなって言っちゃうと、生産性*2は落ちると思う。

それに、だいたいビジネス上の目的を達成するためにはアホみたいに短い締め切りを守る必要があって、さもなくばチャンスを逃してしまい予算が達成できない。あるいはプロジェクトの継続が困難になってしまう。あるいは経理上ヤバイ数字になってしまってどうなってんだと株主から怒られる。あるいはユーザーに呆れられて製品から人が離れていく。いろんな最悪のシナリオが考えられて辛い気持ちになる。

そんなときはどんなにクソなコードを量産しようとも、ビジネス上の目的を達成しないといけない。いま自分の給料を稼いでくれているシステムはクソコードも厭わないビジネス上の目的を達成するための努力の結果出来上がったものかもしれない。

もちろん、最初からクソコードなしでそういうビジネス上の目的が達成出来れば最高だと思う。そこを目指すこと自体に異議は無い。

ただ、クソコード書くなって言っちゃうとビジネス上の目的を達成する事が(ただでさえ困難なのに)、より難しくなってしまう。

だから、クソコード書くなって言うのはやめた方がいいと思う。間に受けてひたすらコードを綺麗にするために命を懸けるみたいな事をする人が生まれかねない。*3

ただし、新人の場合は「こういうコードを目指せば良いのか」と思ってクソコードを量産するように育っても、自分達も本人も困る事になるのでちゃんとレビューしてクソコードは理由を説明しつつ突っぱねて良いコードが書けるように教育していくべきだと思う。

要するに、僕が思ったのは、やむなくクソコードを書いても、それで目的が達成出来るならいいじゃないか。ということだ。

 

 

ただし、クソコードはクソコードのままメンテナンスし続ける事は出来ない。

なぜなら、メンテナンスコストが増大し、ビジネス上の目的を達成するための変更を加えるのにかかる時間が増えていってしまうからだ。*4

クソコードの度合いにもよるが、時間経過に対して正比例的にメンテナンスコストは増大すると思う。(このへんは技術的負債が云々でホッテントリしてた人の記事が詳しく書いてたので読むといいと思う。僕はその記事にすごく共感した。)

だから、クソコードは直していかないとそのシステムでお給料を貰うのはどんどん困難になっていくと思う。

クソコードをメンテナンスし続けても給与3倍から遠ざかって行くし夢がない。

なので、クソコードは少しづつ改善しながらメンテナンスし続けていくのがいいと思う。これは最初からクソコードを書かないって事より遥かに難しいけど、給与3倍になると思えば出来ると思う。疲れてきた。

 

 

そして、時間的制約の中でどれだけ良いコードが書けるかというのは非常に大事だと思う。

クソコードは止むを得ず書くもので、クソコード自体は良いものではないので、書かなくて済めば書かないほど良い。

だから、スケジュールに影響が出ない程度に良いコードを目指すのはとても良い事だと思う。クソコードをちょっと直して60点以上になればビジネス上の目的を達成した上でコードは綺麗に保てるし最高だ。誰もが幸せになれる。

ただ、ぼくたちはだいたい未熟だしいつだってどんな仕様だって60点以上に出来る実力はないから、良いコードを書けるようになるために常日頃から努力を重ねる必要があると思う。だけど、ビジネス上の目的を達成出来ないと給与3倍から遠ざかっていくし、バランスが大事だと思う。その上で良いバランスを目指すために良いコードをさらっと書けるよう努力していくとみんな幸せになれそうだ。

 

 

iPhoneでベットでぬくぬくしながらてきとーに書いてたらつかれた。今日は有給だ。

*1:人によって基準は違うし、だいたいクソコードの基準を決めるのはギークな人たちなので、ここでは便宜上60点が合格点であると定義する

*2:時間あたりでどれだけの成果物を生み出せるかという意味合いでの

*3:もちろん、それはそれで良い事なんだけどビジネス上のメリットは殆んど無いケースが多いと思う

*4:そんな変更を加える必要も無いくらい完璧にシステムが作られているなら別だが、そうそうないでしょう

redis-sentinel管理のredisを手動failoverさせる

やんごとなき理由でredis-sentinel管理のredisのmasterを再起動したくなって、
手動failoverのやりかた調べてたら公式のドキュメントにちゃんと書いてあった。
公式のドキュメントちゃんと読まないとダメだ。


適当なsentinelに入って、

redis 127.0.0.1:26379> SENTINEL failover <master name>

としたら良い感じにfailoverしてくれた。
redis-sentinelべんり。

configファイルのmasterのIPも新しいものに変えておかないと次回sentinelを再起動したタイミングで狂うので忘れずに換えて置かなければならないのだけ注意したい。
というか先にconfigのIP変えてから手動failoverしたほうがいいかもしれない。

参考: http://redis.io/topics/sentinel

2013年のKPT

2013年終わったのでKPTやってみる。

Keep

新しい分野に挑戦した

  • Android
  • フロントエンド開発
    • こっちはお手伝いとお遊び程度だけど

新しい言語に挑戦した

新しいモジュールを作った

  • Parallel::Async
  • SQL::Maker::Plugin::Case
  • Hash::Compact::Lite

ISUCON3出た

  • 予選通過
  • 本戦惨敗

作曲した


ぼくの干し椎茸 by karupanerura on SoundCloud - Hear the world’s sounds

帰り道(demo) by karupanerura on SoundCloud - Hear the world’s sounds

帰り道はこのデモをもとにThe Monzllisでバンドバージョンを作った。そっちは2番もある。

ライブやった

高円寺界隈の人と仲良くなれた

  • バンドつながりで知り合いがふえた

資格試験受けた

温泉発火村やった

  • 熱海行った
  • 旅館がすばらしかった

地域PMに参加した

  • Hachioji.pm
  • Chiba.pm

エンジニアのコミュニティにより深く関われた

  • kenjiskywalkerさんとかstudio3104さんとかuzullaさんとかxtetsujiさんpython_spameggsさんとかsongmuさんとさyusukebeさんとか、挙げてくとキリが無いくらいの人と知り合えた。

ITエンジニア平成会に参加できた

  • 意外とあの人と歳近かったのか!みたいなのあってよかった。
  • 新年会やりましょう

Problem

デスマった

  • 見積もりミスこわい

引っ越しの片付けが済んでいない

  • 言い訳のしようも無い

Yomiuri完成してない

  • Text::Markdown::Hoedownのlow-level APIを使って組んでたがAPI変更があったり

ISUCON3惨敗

  • インフラ周り本格的に勉強したいしノウハウ積みたい

情報セキュリティスペシャリスト試験落ちた

  • 午前Iでマークシート写し損ねがあって減点したのが痛かった
  • 自己採点だとたしか午前Iだけ落ちてたはず。かなしい

IT芸人になりつつある


自分が作った代表的なプロダクト的なものが無いまま名前だけそこそこ知られてるって状況になりつつあってエンジニアとしての危機を感じている。

Try

新しい言語を会得する

新しい分野に挑戦する

  • サーバー運用
  • 言語処理系

新しいモジュールをつくる

  • ORM
  • 痒い所に手が届く系

遠方の地域PM/参加した事の無い地域PMに参加する

  • Hokkaido.pm

Perl/MySQL以外の勉強会に参加する

  • rbとかgo

英語を勉強する

  • YAPCで英語でトークしたい

数学を勉強する

ソロでライブやる

  • 弾き語り
  • のために曲作る

ISUCON3でまたもや惨敗したはなし

ISUCON3への参加から日が経ってしまいましたが、来年の自分へのメッセージとして書きます。

だいたいの内容

坂パスタチームの仲間の面々が書いてくれているのでそちらをご参照下さい。
id:kfly8 http://kfly8.hatenablog.com/entry/2013/11/30/200331
id:masasuz http://masasuzu.hatenablog.jp/entry/2013/11/10/isucon

事前準備

生憎、仕事もプライベートも予定がキツキツになってしまって準備に割ける時間が直前でなくなってしまって全然動けなかった記憶があります。
もっとはやく準備してればよかったなーと思いました。足回り系とか、準備が主な仕事なので役割果たせてない。
id:kfly8 先生にげんこつされる夢を見ました。うそです。
でも、げんこつされても反論できないくらい、仕事できてなかったので来年がんばる。

ぼくがやったこと

  • 緊張する
  • gitとか整える(ここまで平常運転)
  • とりあえずconvertコマンドがボトルネックになってる事がわかったのでこいつを速くするぞ!まずは置き換えてみよう!というアウトな方針を立てる
  • convertコマンドを他のcpanモジュールで置換しようと延々と試行錯誤を行い時間を無駄に潰す
    • 画像のdiffが多くてコケる
  • 慌てる
  • ImageMagicのperl bindingなら大丈夫だろ
    • 画像のdiffが多くてコケる
  • 慌てる

以上です。アウトー!!

反省

事前準備に時間を割かなさすぎた

時間がない!って分かった時点で他の人に事前準備の役割を移譲するべきだったし、
そもそもぎりぎりになって準備をしようと考えている時点で甘かった。

方針決定に時間を割かなさすぎた

ほぼ思い付きの方針みたいな感じだったし、冷静に考えればまずはリソースを使いきれるようスケールアウトさせるのが先決だった。
チューニングの順序も、本来は「処理数を減らす」「処理を高速化する」の2通りあるはずだったが、
予選でmarkdownコマンドをHoedownに変更した事が功を奏した事などから短絡的に「処理を高速化する」方向に舵を切ってしまった。
方針変更を躊躇した原因もここにあり、なぜなら他のチューニング方針を考えていないので、
方針変更は新たな方針を立てる事から始めなければならず、方針変更のコストが高くなってしまっていた。
ましてや、普段触ってるアプリとは違う性質のアプリをチューニングする上で、
僕みたいな経験の浅い若者にベストな答えが一発で出せるわけがなく、
色々なチューニング方法を検討した上で、ダメだった場合のプランBも決めて、
一気に手を動かしたほうが確実に効率は良かった。

慌てた

慌てんな。

感想記事を書くのが遅すぎた

覚えている事が少なくて反省の記録として質が悪い。

感想

毎度、反省が多く、勉強になり素晴らしいイベントだなと思います。
今回も多くの学びがありました。
技術的な部分だけではなく、短期間で成果をあげるためのアンチパターンやら、
チームで仕事をする上でのアンチパターンみたいなものにもハマるので、
本当に、参加するだけで価値のあるイベントになっているとおもいます。
特に、新卒や学生の人こそ参加してみて、プロの仕事の品質を身を持って体感してみると良いと思います。
運営の方々、本当にありがとうございました。来年も楽しみにしております。

Amon2で非同期レスポンスを使う方法と、非同期WebAppのハマりどころ

この記事はPerl Advent Calendar 2013の15日目の記事です。

Amon2とは

@tokuhirom さんが開発しているPerl製のWAF*1です。
Plackを軽くwrapしたような軽量でシンプルなWAFです。
現在、Version 6.00がリリースされていますが、Version 3.50からwebsocketのサポートが入り、
その関係でPSGIの遅延レスポンス/ストリーミングレスポンスのインターフェースに対応しています。

Amon2で非同期レスポンスを使う

Amon2::Plugin::Web::Streamingを使う事により非同期でレスポンスを返す事が出来ます。
例えば、index.txを5秒後にrenderして返す場合は以下のようになります。

use strict;
use warnings;
use utf8;
use Amon2::Lite;
use AnyEvent;

get '/' => sub {
    my $c = shift;
    return $c->streaming(sub {
       my $responder = shift;

       my $w; $w = AnyEvent->timer(after => 5, cb => sub {
           my $res = $c->render('index.tt')->finalize;
           $responder->($res);
           undef $w;
       });
   });
};

# load plugins
__PACKAGE__->load_plugin('Web::CSRFDefender' => { post_only => 1 });
__PACKAGE__->load_plugin('Web::Streaming');
__PACKAGE__->enable_session();

__PACKAGE__->to_app(handle_static => 1);
__DATA__
@@ index.tt
<!doctype html>
<html>
<head>
    <meta charset="utf-8">
    <title>Streaming</title>
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
    <script type="text/javascript" src="[% uri_for('/static/js/main.js') %]"></script>
    <link href="//netdna.bootstrapcdn.com/twitter-bootstrap/2.3.1/css/bootstrap-combined.min.css" rel="stylesheet">
    <script src="//netdna.bootstrapcdn.com/twitter-bootstrap/2.3.1/js/bootstrap.min.js"></script>
    <link rel="stylesheet" href="[% uri_for('/static/css/main.css') %]">
</head>
<body>
    <div class="container">
        <header><h1>Streaming</h1></header>
        <section class="row">
            This is a Streaming
        </section>
        <footer>Powered by <a href="http://amon.64p.org/">Amon2::Lite</a></footer>
    </div>
</body>
</html>

@@ /static/js/main.js

@@ /static/css/main.css
footer {
    text-align: right;
}

このように$c->streamingにcallbackを渡すとそれがそのまんまPSGIの非同期レスポンスとして使えてあとは$responderをすきに料理すればいいというスンポーです。簡単ですね!

ハマりどころ

ブロックする処理を混ぜてはいけない

現実的に、現状は非同期レスポンスを返すアプリケーションを書く場合はAnyEventのイベントループの上で動くことになる為、イベントループを止めるような処理を書いてはいけません。
例えばネットワークIOがあるものなどはAnyEvent::Socketを使うライブラリを使い、sleepやwait(pid)?の代わりにAnyEvent->timer/AnyEvent->childを使うなど、イベントループを止めずに終了を通知させるAPIを使う必要があります。
イベントループを止めてしまうとその間他の処理が一切出来ないため、たとえばDBに1秒かかるクエリを投げた場合、1秒間リクエストの受付すら出来なくなってしまうという状態が起こりえます。

context毎に他のサーバーへのconnectionを保持するとリクエストの度にconnectionが増加して死に至る

HTTPの同時接続数と同じだけmysqlへの接続数が増えるという事になると、
非同期レスポンスが中心になるアプリケーションでは同時接続数が非常に膨らむ為、問題になります。

非同期レスポンス中ではAmon2->contextを使ってはいけない

Amon2->contextはAmon2における現在のcontextオブジェクトがどこからでも取れるという便利な代物ですが、
非同期レスポンスではこれはハマりどころになります。


なぜなら、Amon2の中でのリクエストの開始と終了というのはAmon2::Liteにおけるget '/'などに登録したcallbackが終わるまでの間*2だからです。
つまり、$c->streaming()の中ではcontextは基本的にundefになります。
また、同時に複数のリクエストを捌いているケースではタイミングによっては他のリクエストのcontextを参照してしまう為気付きにくいです。
シーケンスで表現すると以下のような感じです。*3



解決策

Object::Containerなどを使い、globalにリソースを持つ

globalにリソースを持つ事によって、それを複数リクエストで共有する事が出来ます。
もちろん、トランザクションなど、リソースに状態変化が起こる処理を行うと問題になりますが、
そのような処理を行わない場合は検討しても良いかもしれません。
例えば、トランザクションが必要な処理はGearmanなどのWorkerに任せてAnyEvent::Gearmanなどでタスクとして投げてその終了をcallbackで受けるなど、別プロセスで同期的に処理するのが良いかと思います。
もっとシンプルにやる方法もあるとベターかとは思いますが現状ではなかなか辛そうな印象です。
(あるはAnyEvent::DBIが同時に複数のトランザクションが走らないようなアーキテクチャになれば問題は解決するかもです)
他に良い方法があれば教えて下さい!

まとめ

非同期レスポンスむずい!!!!!!!!!!!
明日はid:mackee_wさんです!

*1:Web Application Framework

*2:もっと厳密に言えばAmon2::Web#handle_requestが終了するまでの間

*3:seqdiag.jsを使っています。walf443++

state変数は初期化のタイミングで例外が発生しても二度と初期化が行われる事は無い

タイトルの通り。
まあ、初期化は1度しか行われないので、あたりまえといえばあたりまえですがハマったのでメモ。


以下検証コード。undef,2,2となるかと思いきや初期化は1度しか行われないのでundef,undef,undefとなる。

static variable be undef if throw when initialize ...