ISUCON7予選に秒速5000兆クエリというチームで参加した

ISUCON7 開催&日程決定! #isucon : ISUCON公式Blog

ISUCON7のオンライン予選にid:astjid:tanishiking24の3人で秒速5000兆クエリというチームで出場した。最終スコアは9万ちょっとで、ベストスコアとほぼ同値だったので実力を出し切ったと思う。

ISUCONに競技者として参加するのは初めてで、エンジニアリングにスコアをつけて競い合うというのは聞いていた以上に楽しい。

ちなみに「競技者として初」というのは、昨年のISUCON6の参考実装の移植として関わっていたので、ISUCONと関わったという意味では初ではなかった。

準備

2日前くらいにcatatuy/private-isuで練習した。振り返るとISUCON7の予選問題も画像をいかに効率よく返すかというところが最初のボトルネックで、早速予習が活きてお得。

pt-query-digestを使うとか、nginxのアクセスログを解析してボトルネックを見つけるとか、見つけたボトルネックの解消の分担だとか、タイムキープだとか、一通り押さえておきたいところは練習できたので事前準備は万端と言ってよい。

当日

開始が遅れるとのことだったのでオフィスの近くにあるちょっといい肉料理のお店でランチをいただいた。

肉専科はふう 京都御所南の和牛ステーキを堪能できる洋食のお店

開始してからは、まず3人でレギュレーションの読み合わせをした。予選だけど複数台構成ということでおおっとなる。練習していた時のデプロイは雑にホストに入って `git pull` するだけだったけど、手間とかリスクを考えて早めにデプロイ整備をしようということになった。

ほか、ローカルとグローバルにそれぞれ帯域制限があることなどを確認しあった。この時点で参考実装は読んでいなかったけれどグローバル側が100Mbpsだったので、とにかくベンチマーカーから送られてくるリクエストを減らす = クライアントキャッシュを使うのが肝要というあたりをつけた。

読み合わせが終わってからはid:astjSSH鍵のセットアップなどをしつつ、自分が初期プロビジョニング (mackerel-agentを入れたり、redis, memcachedを入れるなど) やデプロイ準備を進め、id:tanishiking24がkataribeのセットアップをしつつコードリーディングを進める、という分担になった。

レギュレーションを読み合わせましょう、とか、3人がどういう分担になるといいか、とかはISUCON本戦出場経験のあるid:astjに音頭を取ってもらった。普段からオペレーションも得意だしあてにはしていたけど、実際かなり頼りになり助かった。
id:tanishiking24も「じゃあこれよろしく」と振って危なげなく進めてくれるので、チームの並列度はマックスで最高だった。

練習でやったとおりnginxのアクセスログMySQLのslow.logを見てボトルネックを1つずつ潰していく方針で進めた。

id:astjがやってくれたimage.nameにuniqueインデックスを張る変更でスコアが5万くらいに上がってテンションが上がってきたと思う。早いチームは早いうちからスコアを伸ばしていて内心やや焦っていたけど、同じ土俵に立てたことで安心しつつ、競い合えているなーと楽しくなってきた。

その後、N+1クエリを殺すなど分担してスコアをちまちま上げつつ、いよいよアイコンの配信をどうにかしないと帯域で当たってどうしようもない、という状態になって案を考えはじめた。

last-modifiedをつけてconditional GETさせよう、という方針になったのだけど、これはあとから考えると穴がある判断だったし、ハマる原因になってしまったな、と反省。

JSやCSSにはif-modified-sinceは来ているように見えたけどアイコンには来ていないので、last-modifiedヘッダの日付形式がおかしいのではないか、とか、304を返すロジックを間違えているのではないか、とか後から考えれば的外れな検討ばかりして時間が消えていった。たぶん3時間くらい費してしまったと思う。

最後のほうは本当にお通夜で、しかも大差ない変更をしたつもりだけどスコアが5万→3万→2万→3万とよくわからない上下をするので、一層お通夜が進んだ。

だんだん頭がおかしくなってきて、gzip圧縮かけているとstrong etagが毎回変わってしまいそれでキャッシュされていないのでは? ということになりgzipを切るなどしたが、効果はなし。

頭も朦朧としてきてヤケクソになって誰ともなくcache-control max-age=0とかつけてfailしなかったら儲けものじゃない? って話になり、ベンチマーカーを走らせるとなんと6万くらいになった。

その時のid:astjはこんなかんじだった:

                                 ,.へ
  ___                             ム  i
 「 ヒ_i〉                            ゝ 〈
 ト ノ                           iニ(()
 i  {              ____           |  ヽ
 i  i           /__,  , ‐-\           i   }
 |   i         /(●)   ( ● )\       {、  λ
 ト-┤.      /    (__人__)    \    ,ノ  ̄ ,!
 i   ゝ、_     |     ´ ̄`       | ,. '´ハ   ,!
. ヽ、    `` 、,__\              /" \  ヽ/
   \ノ ノ   ハ ̄r/:::r―--―/::7   ノ    /
       ヽ.      ヽ::〈; . '::. :' |::/   /   ,. "
        `ー 、    \ヽ::. ;:::|/     r'"
     / ̄二二二二二二二二二二二二二二二二ヽ
     | 答 |     コ ロ ン ビ ア       │|
     \_二二二二二二二二二二二二二二二二ノ

あとは初期データに含まれるアイコン画像を静的に書き出すのをやって9万 (ベスト値) が出る。……ものの、my.cnfを書き換えてベンチマークを走らせると5万くらいに落ちる→もう一度走らせる→スコア戻らず→変更を戻す……というようなよくわからないスコアの上下が相変わらず発生するので、最後の30分はベストスコアである9万を越えるまでひたすらエンキューする猿たちになっていた。結果、最後の10分くらいでベストスコア近いスコアが出たので、そこで打ち止めとなり終了。

実践できて、次回もやっていきたいこと

  • ボトルネックの解析・発見→修正という基本的なプロセスを回しつづけたこと
  • 30分ごとに現況確認をしあい、ハマるのを概ね防げたこと
  • (良くも悪くも) 普段の自分たちが切れる手札だけを切れたこと、本番一発勝負の必殺技みたいなものはなかった

反省点

  • ベンチマーカーのconditional GETの挙動が不可解だとなったときに、動かす手が止まってしまった
    • 「仕様的には我々は間違っていないはずなのに」となったときに、仕様に照らすのではなく、ベンチマーカーの動きを知るためとにかくあれこれやってみるべきだった
    • 他にボトルネックのあたりはついていたが、帯域で当たっているのでまずアイコンをどうにかしないといけない、けど手がない……ということで3人とも止まってしまった
  • 他の人の感想を見るとgzip_staticとか知らないディレクティブがいろいろあった

感想

普段やっていることに近いし社内の他のチームと感想戦をしあってみて、丁寧な配信最適化をやろうというかんじで、楽しめたし自分たちに足りていないところがあることを痛感させられ、たいへん楽しめました。

悔しくありつつも、初ISUCON参戦で楽しかったので、次は優勝したい。

余談

予選参加することを決めたあとで10月22日に『響け!ユーフォニアム』のコンサートと被っていることに気がついて、慌てて21日の参加で問題ないか確認した時が一番肝が冷えた。

週5日8時間労働は厳しい

しばらく体調が悪く、週末になるとへとへとになる状態が続いている。

土日に休むとリフレッシュできて月曜日には元気一杯になるので、老衰による疲れやすさがあるにせよ、体力の最大値がそんなに大きく目減りしていることでもなさそうなので、今週は午後からの勤務として労働時間をだいたい50%にしている。

そしていまのところ調子がいい。いきなり気温が下がって身体に負荷がかかっているにしては、わりといい調子が続いている。

あとフィジカルというよりメンタルの負荷が大きそうで、午後に出勤するまでのあいだ、家の掃除をしたり洗濯をしたり、あるいはピアノを弾いたり、そういう時間が増えたことも大きいように思う。

つまりそういった家事や趣味をするぶんのフィジカルリソースの消費は変わらずあるものの元気というわけなので、メンタルリソースの消費が激しいという推論。

生涯で得られる幸福の総量について考えると、収入を2倍にするより労働時間を半分にするほうが総量は大きくなりそうだし、健康も保てて寿命が延びそう。

スマートフォンからSlackを消した

休んでいる時にたまに見たりしてしまい無闇に疲れていたので消してしばらく経つ。

緊急の連絡はDMでやりましょうって合意できているし、DMはメールでも通知が来るようにしているので、ひとまず困らないだろうと思っているけど、実際どうかはわからない。

あとグループウェアにも気軽にアクセスできないようにしたいけど、それはちょっとめんどうそうで悩む。

休み中に疲れが溜まる要因を減らしていきたい。

『Scalaで自動作曲の練習』を社内勉強会で話した

speakerdeck.com

だいぶ大仰なタイトルでありますが掲題の内容を社内勉強会で話しました。

音楽を作る古典的な方法論 (機能和声) を取り上げ、その中でも和声 (harmony) をコアドメインと見据え、実際に和音進行の規則などを実装したという内容です。

和声と周辺の概念をScalaで表現したコードを交えながら、和音や調・音階をエンジニア向けにどういうパラメーターを受け取ってどのように導出できるか、といった観点で紹介しています。

端折ったりやや正確さに欠ける説明になっているところがあると認識していますが、プログラミングあるいは抽象的な構造とその生成規則を発見するという観点でおもしろさを感じてもらえるようなアレンジです。

紹介したコードは素朴なWebアプリケーションとして実装し、公開しています: GitHub - aereal/music-study

きっかけ

アカデミックな知識を師事したりきちんと学んだことはないのですが、ピアノを弾き始めたころからずっと学んできた分野で、プライベートワークもいくつか作っています。

一方、プログラミングを始めてからしばらくして、ものごとを抽象化して似た特徴や規則を発見するおもしろさを見出してから、たとえば和音の特徴付けは音程 (周波数比) で決まるよなとか、音楽や音楽を作り上げるルールたちをプログラミングや数学の語彙を使って述べ直すことができそうだなということはずっと頭の中にありました。

ちょうど社内勉強会の順番がやってきて、最近は業務に直結した話が多くマンネリを感じていたので、ハッタリを効かせる意味と自分に発破をかける意味で「自動作曲」というワードで取り組むことにしました。

実際にコードを書き始めたのは10日前くらいでいけるやろと思っていたけど後述する点にハマるなどし、動くものができたのはギリギリだったのでだいぶ焦りました。
なにせ発破をかけるために大仰なタイトルをつけたのに、蓋を開けたら動くものはなにもありませんでした、だと顔向けできないので……。

普段の業務範囲からだいぶ離れたドメインを扱うので退屈に感じないかなあという心配もしていたのですが、思ったより反響があったことには救われました。実際、こういうことが考えられるよねという実装を提示してワイワイ議論が起こったらいいなと目論んでいたので、そういった意味では成功したといえます。


難しかったところ

当初は12平均律を前提として、周波数を実装に使っていたのですが数値の精度が足りなくてテストが落ちはじめた時は、進まない進捗と共に絶望的な気持ちになりました。

Rationalに切り替えていくのも大変だったので、動くものを作ることを重視して、物理的な実装に寄せる作戦は諦めました。

また種々の概念は一見すると規則的で実装しやすく見えたのですが実際はそうでもなく、ある言葉が指す概念が非常に曖昧あるいは広すぎてユビキタス言語を発見するのに戸惑いました。

たとえば音程の完全1度、短2度、長3度、増1度みたいな名前は、一見すると「修飾子」+「N度」という構造をとれるのではないかと思うのですが「N度」という概念は五線上の距離を指す一方、修飾子は半音上げたり下げたりといった音高に作用するものなのでドメインが異なり表現が困難です。

音程は結局、半音の数で表すこととし、半音の数が6つの場合はDiminishedFifth, AugmentedFourth, あるいはTritoneという名前で参照できるということにしました。

12平均律が前提であればこれでよいのですが、音律に依存せずN度の表現ができると望ましいと感じているので今後改善していきたいところです。

今後の展望

Web Audioなどを用いて実際にこのソフトウェア実装から生成した和音進行を耳にできるようにして、そこから実装されていない規則を追加していきたいですね。

また、和声といいつつ和音進行しか実装できていないので、せめて声部配置くらいは実装したいし、できていて然るべきかなと思っています。


プレゼンテーション内で短三和音を誤って「長三和音」としていた点を修正しました。

リモート勤務メモ

社内グループに書いていたメモをせっかくなので放流します。

前提

  • 昼食はオフィスで食べられる
  • 基本はオフィス勤務だが、相談の上リモート勤務も可能
  • 自宅からオフィスには徒歩で20分、自転車で10分
  • 京都在住
  • 北海道の実家で2週間程度のリモート勤務を2回経験

リモート勤務の感想

  • お昼ごはんの用意をしていると昼休みがあっという間になくなる
    • 普段オフィスにいるとランチをいただくのに20〜30分くらい、残りは休憩スペースで同僚と話したり本を読んだしている
  • お昼休み・定時になるとチャイムが鳴るが、自宅だと鳴らないのでしょっちゅう時計を気にして疲れる
  • 途中までSlackで参加していた会話が口頭に変わると経過がわからなくなるので、あとはうまく話が進んでいることを祈るしかなくなる
    • いまのチームに慣れたからいいけど、入りたてとかだと不安だと思う
  • 人と関わりがなくて気が滅入ってくる
    • 普段、人と話すわけではないけど同じフロア・同じオフィスにたくさん人がいて、なんとなくワイワイしているのを感じるだけでけっこう満足していることがわかった
  • 椅子がしょぼいオフィスチェアで自宅勤務2日目で身体中が痛くなってきた
  • 外部ディスプレイなくて効率落ちる気がする
  • 顔を合わせるMTGの時、こっちはHangoutで1だけど、向こうは会議室で複数人いるという状況だと、1対多という状況に言い様のないストレスを感じる
  • 設計の相談とかするときにホワイトボードが使えないのけっこう不便
    • 手書きしたメモをSlackに上げたりとかしていた

結論

リモート勤務するなら本格的にディスプレイや椅子を揃えて環境を作らないと自分はやりづらいということがわかった。

あるいはMacBook1枚とインターネットさえあれば砂漠でも仕事できるという強靭な心身を手に入れるか。

あと通勤が苦ではなく、むしろデスクワークに終始しがちな毎日において貴重な身体を動かす機会と捉えていた節がある。

天候が悪かったり、どうしても自宅にいない用事が発生したら、柔軟に自宅で勤務できる会社なので、いまのところ自分はリモート勤務そのものに対するこだわりは無いかなあと思った。

むしろオフィス勤務だと福利厚生としてディスプレイとかいいオフィスチェアを用意してもらえるので、そういう点を考えると自宅勤務はけっこう非効率だなあと感じた。

追記

リモート勤務メモ - Sexually Knowing

リモートワークなのにオフィス時間割に合わせてる????

2017/08/29 17:28
b.hatena.ne.jp

労働形態の話と勤務地の話は別なので、コアタイムがある時は勤務地がどこであろうと合わせるべきと思います。

という形式的な話もありつつ、物理的に同じ空間にいないからこそ朝に挨拶するなどして不在かどうかを発信することも、同じ空間にいない時に他のメンバーに不安を与えないために重要だと思って時間は特に意識して守るようになりました。

#builderscon tokyo 2017で『Goで実装する軽量マークアップ言語パーサー』というトークをしてきた

Goで実装する軽量マークアップ言語パーサー - builderscon tokyo 2017

buildersconは初参加。ぼちぼちパーサーを書いていたのでいい機会だと思って応募し採択されてこの度初ビルコンで話してきた。

トークについて

60分枠でひとりで話すのは初めてで時間配分にとても悩んだ。なんとか20分×3みたいな構成にしようと思ったけど前半は抽象的な話が多かったので眠くなってしまったかもしれない。

仕事とは関係なく作り上げたものについて話をするのは、ずいぶん気が楽だし自信を持てることだったなあと思い出した。仕事は自分ひとりの成果といえるものはごく少ないし、自分は抽象的な話をするのは苦手だ。

聞いたトークについて

特におもしろかったトークについて。

まず、基本オフレコなので感想を書けないのがとても残念だけど、オフレコという縛りがあるのも納得の前夜祭のトークはどれも強烈だった。

DeepLearningによるアイドル顔識別を支える技術 - builderscon tokyo 2017

id:sugyanさんのアイドルの顔認識の話。

アイドルの顔の系統が似通っていて識別が大変という話がおもしろかった。たしかに年代が近くその時代でかわいいとされる顔立ちの人がなるものと考えると頷ける。

自撮りの角度が決まっているのでそれを学習してしまいたまに違う角度で写っていると判別できないことがあるという話を聞いて、いわゆるハナザーさん (花澤香菜) アングルがある話を思い出した。

小さく始めて育てるコンパイラ - builderscon tokyo 2017

id:rhysdさんのコンパイラを作っていく話。多相型の推論アルゴリズムがいくつかあるということも知らなかったのでまさに「知らなかった、を聞く」だった。

おもしろそうだったので自分もコンパイラを書いてみたい。

buildersconについて

コピー通りWeb系に囚われないトークがたくさんあって刺激的だった。あとスピーカー弁当はじめ供出いただいたフード類がおいしくて嬉しかったです。次もなにか話したい。

あとめっちゃいい杓文字いただけました。

https://www.instagram.com/p/BXU1ew_BrgP/
😹 #builderscon

Content-Security-Policyやらsubresource integrityなどに関するメモ

最近、セキュリティに特に気をかけなければいけないサービスの開発をしていて調べた知見のメモ。

subresource integrity

Subresource Integrity - Web security | MDN

いわゆるチェックサムの仕組み。 integrity 属性に ${hashalgorithm}-${hashdigest} 形式の値を書いておき、フェッチしたファイルのハッシュ値が一致していなければブラウザが読み込みをブロックする。

これは、たとえばCDNが攻撃されるなどしてスクリプトなどが改竄された場合に有効。

JSやCSSはプリ・ポストプロセッサで処理し出力することがほとんどだろうが、subresource integrityとどうやって統合するかというとgulp-hashsumを使ってコンパイルしたファイルのハッシュ値をdigest.jsonに保存し、HTMLのレンダリング時にそれを参照してscript/link要素に埋める、という方法が考えられる。
開発しているサービスは、使っているツールセットなどの兼ね合いからこの方法をとっている。

webpack-subresource-integrityのようなプラグインもある。

Content-Security-Policy

インタラクティブな要素が少ないので、Plackミドルウェアで一律 default-src 'none' style-src 'self'を指定した後にホワイトリスト形式で追加するという運用にしている。

これでいくつかハマった点があるので紹介。

Google Tag Managerとの兼ね合い

Google Tag ManagerはHTML断片やGoogle Analyticsなどのタグタイプを選べるが、これはつまるところ動的なスクリプトの実行・読み込みなので script-src などのポリシーが適用される。

上記のようなホワイトリスト形式のポリシーと相性が少し悪い。

動的なスクリプトの実行については、都度nonceを付与するということも考えられるけど、ホワイトリスト形式で運用したいページではそもそも使わないのが手間を考えると楽かと思う。

外部スクリプト読み込みは、諦めてタグタイプを追加する度にポリシーに追加することにした。といってもGoogle Analyticsくらいしか使わないだろうから、そんなに困っていない。

複数ポリシーを指定するとorではなくandで検証される

仕様に書いてある

Content-Security-Policy: default-src 'self' http://example.com http://example.net;
                         connect-src 'none';
Content-Security-Policy: connect-src http://example.com/;
                         script-src http://example.com/

Is a connection to example.com allowed or not? The short answer is that the connection is not allowed.

Content Security Policy Level 2

たとえばデフォルトは厳しいルールを指定しておくが、エンドポイントごとに緩めたい時はデフォルトのポリシーをレスポンスヘッダから取得しマージした値をレスポンスヘッダに再度書き出すということが必要。

広告に関する考え

※やまなし、いみなし、おちなし

Web広告は目にするとイラつく
街頭広告はそうでもない
テレビCMはイラつく

WEB広告は有限のリソースを浪費されているかんじがする
ページと関係ないと→ネットワークリソースや表示領域が浪費された
ページと関係あると→広告とそうでない情報を区別する労力が浪費された
なぜ広告を区別する必要があるか
広告は情報価値の期待値が低い
なぜ低いと感じるか
一般的な情報は外部の指標によりフィルタリングされる (Google, SNSの反応)
広告は入札の仕組み以外にフィルタリングの仕組みが存在しない
→ 一般的な情報を評価するのと広告が広告であるのか判断するのとで、労力が変わっているのか気になる

テレビCMがイラつくのも恐らく同根
時間が奪われる

街頭広告はなぜイラつかないのか
浪費されるリソースが無限に近い
壁面は無限 目を背けられる
歩いているときは情報を求めていない → 価値の低い情報を見ても区別したり評価する必要がない

最近Atomでコードを書く時間を増やしている

最近思うところあってAtomでコードを書く時間を増やしている。

といっても普段書くPerlはこれまで通りVimで、趣味でTypeScriptを書く時にAtomを使うようにした。

Vimの使い勝手に慣れ切ったつもりだけど、昨年一年間はIntelliJScalaを書く時間が多かった。それが思いの外に馴染んだので、もしかしたらVim以外でもなんとかなるのでは? と思い、まずは趣味の時間からちょくちょく使いはじめることにした。

Vim設定をGitで管理してけっこう設定に凝っている。道具を自分の手に馴染むように変えるスタイル。

対してAtomで書く時は、あまりカスタマイズせず、キーボードショートカットも覚えずにGUIでぽちぽちすることにしている。

いままで設定を練ったVimよりできることは減ったり手間がかかるようになったはずだけれども、耐え難い感じはしない。

むしろGUIがリッチなので便利に思う時が多い。

その後、ENSIMEを試してみたところVimより安定して使えたのでScalaを書く時もAtomにしようかな。

grpcを見たメモ

クライアント (ブラウザ) とサーバーがやりとりするためにgrpcを使えないかなと思ってサンプルコードを見たりちょっと触ったメモ。

結論からいうと今回の用途には合わないので見送ったけど、後のために。

ブラウザでは使えない 😭

Can I use it in the browser?

Not yet. This is an area that's being actively explored and we welcome feedback and contributions. We are collecting names of people interested in early access program here. There is node.js support for server side JavaScript.

grpc / FAQ

メモ

  • protoc(1) でサービス定義からクライアント実装と、サーバー実装の雛形を生成する
  • サーバーは定義されたRPCインターフェースを実装するのと、listenさせるところまでは最低限書く必要がある
  • G SuiteのAPIはgrpc (Protobuf?) で提供されている

Scalaで書いたWebアプリケーションのレスポンスにデプロイされているGitコミットのSHA1を含める

WebアプリケーションのレスポンスヘッダにGitのsha1を含めておくと、デプロイに失敗していて古いバージョンだった (そのせいでおかしかった) ということに気がつきやすくなって多少便利。

Perl (PSGI) だと、 `git rev-parse HEAD > VERSION` などしておいて Plack::Middleware でファイルの内容を読んで `Plack::Util::header_set` と `Plack::Util::response_cb` で用が済むけれど、Scalaコンパイル時にいろいろできそうな気がしたので調べた。

sbt-buildinfoでビルド情報をcase objectに出力する

sbt-buildinfoというsbtプラグインを使うとプロジェクト名やバージョンなどの情報を含むcase objectのコードを生成してくれる。

Usageより:

lazy val root = (project in file(".")).
  enablePlugins(BuildInfoPlugin).
  settings(
    buildInfoKeys := Seq[BuildInfoKey](name, version, scalaVersion, sbtVersion),
    buildInfoPackage := "hello"
  )
GitHub - sbt/sbt-buildinfo: I know this because build.sbt knows this.

`build.sbt` にこう書いておくと `hello.BuildInfo` に `name` や `version` が埋められたcase objectを定義するコードが出力される。

sbt-gitでGitのコミット情報をプロジェクトのバージョン文字列にする

sbt-gitというsbtプラグインを使うとプロジェクトのバージョン文字列をGitのコミット情報から生成することができる。

`git describe` を使うようにするとか、自分でsha1を加工する (e.g. 先頭8文字だけを使う) ことができるので使い勝手がよい。

タグは使っていないのでsha1の先頭8文字を埋めるようにしている。

lazy val root = (project in file(".")).
  enablePlugins(GitVersioning).
  settings(
    git.formattedShaVersion := git.gitHeadCommit.value map { sha =>
      sha.substring(0, 8)
    }
  )

Playのレスポンスを加工してバージョンを含める

Action.async { implicit request =>
  val result = ???
  result.withHeaders("x-version" -> app.BuildInfo.version)
}

こういうかんじ。

筋トレティータイム

僕はコーヒーが苦手で、飲むと必ずお腹を壊すので避けるようにしている。

その代わりではないけれど、気分転換したいときには紅茶を淹れて飲むことが多い。

コーヒーと違って蒸らし時間があるので待ち時間がどうしても発生する。ティーバッグを淹れてそれを処分する関係上、自席にも戻りづらい。

そこで腹筋ローラーの出番となる。

もともと学生時代と比べて筋肉の衰えが気になりはじめたので買ってオフィスに送ったのだけれども、持って帰るのが面倒で置きっぱなしにしていた。

あるとき、ふと思い立って紅茶を蒸らしているあいだに腹筋ローラーをしてみたらちょうどいい時間だったので、これ幸いとしばらく続けている。

紅茶の蒸らし時間はだいたい2分くらいで、慣れていないうちは10回腹筋ローラーするだけでもそれくらいの時間がかかる。

数回続けると慣れて素早くできるようになってくるので、成長と共に蒸らし時間あたりの回数が増えていって、ティータイムと共に成長の実感が得られて思わぬ喜びがあった。

腹筋ローラー、1オフィスに1台オススメです。

デフォルトでHTTPレスポンスがキャッシュされるのはどんな場合か

HTTP APIのキャッシュ戦略を考えながら、ふとcache-controlなどを指定しなかったとき、デフォルトのキャッシュ戦略はどのように定義されているのか気になったので調べた。

Storing Responses in Caches

RFC7234のStoring Responses in Cachesにレスポンスをキャッシュしてもよい条件が述べられている。

A cache MUST NOT store a response to any request, unless:

  • The request method is understood by the cache and defined as being cacheable, and the response status code is understood by the cache, and
  • the "no-store" cache directive (see Section 5.2) does not appear in request or response header fields, and
  • the "private" response directive (see Section 5.2.2.6) does not appear in the response, if the cache is shared, and
  • the Authorization header field (see Section 4.2 of RFC7235) does not appear in the request, if the cache is shared, unless the response explicitly allows it (see Section 3.2), and
  • the response either:
    • contains an Expires header field (see Section 5.3), or
    • contains a max-age response directive (see Section 5.2.2.8), or
    • contains a s-maxage response directive (see Section 5.2.2.9) and the cache is shared, or
    • contains a Cache Control Extension (see Section 5.2.3) that allows it to be cached, or
    • has a status code that is defined as cacheable by default (see Section 4.2.2), or
    • contains a public response directive (see Section 5.2.2.5).
RFC 7234 - Hypertext Transfer Protocol (HTTP/1.1): Caching

(マークアップは筆者による)

Cacheable Methods

まず1つ目でHTTPメソッドがcacheableであるかとある。特に言及はないけどRFC7231で定義されている。

this specification defines GET, HEAD, and POST as cacheable, although the overwhelming majority of cache implementations only support GET and HEAD.

RFC 7231 - Hypertext Transfer Protocol (HTTP/1.1): Semantics and Content

注意したいのはPOSTもcacheableである点。「ほとんどの実装は対応していないけどね」と但し書きされているが、明示しておくに越したことはなさそう。

a status code that is defined as cacheable by default

巡り巡ってRFC7231のSection 6.1で定義されている。

Responses with status codes that are defined as cacheable by default (e.g., 200, 203, 204, 206, 300, 301, 404, 405, 410, 414, and 501 in this specification

RFC 7231 - Hypertext Transfer Protocol (HTTP/1.1): Semantics and Content

501 Not Implementedはキャッシュ可能と定義されているので、5xxがキャッシュされないという認識でいると注意が必要かもしれない。

その他

range付きリクエストのレスポンスのキャッシュ戦略については別途述べられているが、必要としなかったので読んでいない。

結論

  • HTTPメソッドがGET, HEADもしくはPOSTの場合で、かつ特定のステータスコードの場合はキャッシュ可能と定義されている

まずどんなcache-controlが付けられそうか考えるだけでも、HTTP APIの特徴を掴めそうなのでこれからもやっていきたい。

nginx: 同じ configuration level で auth_basic off は指定できない

nginx の ngx_http_auth_basic_module を使って BASIC 認証をかけることができる。

ドキュメントを読むと明示的に `auth_basic off` を指定することで BASIC 認証を切ることができそうだったので試したけど、うまくいかないケースがある。

結論からいうと `auth_basic off` は上位のコンテキストから継承した設定を打ち消すためのもので、同じコンテキストで指定された `auth_basic` は打ち消せない。

実際のコード例:

# app.nginx.conf
server {
    server_name app.example.com;

    include "/path/to/config/auth.nginx.conf"; # 同じコンテキストで auth_basic が指定されている
    auth_basic off; # 祖先コンテキストではなく同じコンテキストなのでダメ
}

auth.nginx.conf:

auth_basic "Authentication Required";
auth_basic_user_file "/etc/nginx/conf/auth.htpasswd";

実際に config test を行うとエラーになる:

nginx: [emerg] "auth_basic" directive duplicate in app.nginx.conf:5
nginx: configuration file /etc/nginx/nginx.conf test failed

これはドキュメント中の以下の文章で示されている:

The special value off allows cancelling the effect of the auth_basic directive inherited from the previous configuration level.

http://nginx.org/en/docs/http/ngx_http_auth_basic_module.html#auth_basic

生活における夢を集めるTrelloのボードを作った

f:id:aereal:20170414214141p:plain

プライベートで締切のあるタスクや、忘れてはいけないタスクは To Do 管理ツールでリマインドしている。

それらのようなタスクは、誰かに依頼されるだとかして具体的なタスクとして直接インプットされる一方、日々の生活における夢はたとえば「石油を掘り当てたい」といった曖昧でふわっとしているし、なんなら実現可能性なんてものを考えていない。

ふとした瞬間に省みると、自分が細々としたルーチンを繰り返す生活の奴隷に思えることがあってげんなりする。

しかし自分はまだまだ生活に夢や希望を抱いていて、壮大とはいえぬものの少なくないそれらを支えにして生きているわけだから、目に見えるようにしておくと楽しいのではないかと思って夢を集める場所を作った。

それがタイトルにあるとおり Trello のボードでその名も「生活の夢」。

Trello にした深い理由はなく、たまたま楽しい気分で Trello を見ていたら思いついただけ。

ルールや運用なんてものは存在せず、ただ思いついたときに思いついたことを書いていくだけ。達成したら Done に移す。

達成したタスクを捨てたりせずただ Done と名付けられたリストへ移動するやり方は、カンバンに由来するもので、叶った・叶えた夢が集まってくるというところがとても楽しい。

最近は「テレビを買う」という夢を叶えた。「テレビを買う」ことがなぜ夢たりえたかというと、あると楽しいだろうけれど必要というわけではなく、そのわりに高いのでどうかと思っていたから。贅沢ともいう。

世の中においては理性的な振る舞いが求められるので、この Trello の前では自分の欲望に素直になろうと思います。