技術書典2 で 邪悪なWebクローラー読本 を頒布しました

技術書典の存在を知ったのはちょうど1年前ごろで、当時の職場のSlackでこのイベントの情報が流れてきたのがきっかけだった。

techbookfest.connpass.com

自分自身の技術スタックは尖ったものが少なく、特に人様に向けてお披露目できるような知見を持ちあわせてはいなかった。――Webクローラーにまつわるものを除いては。

Webクローラーそのもの自体は広くありふれた存在であるが、これを業務上運用するとなると様々な課題が持ち上がり、現実クリーンな運用をしている組織は Google をはじめ指折り数える程度しかないであろう。また、ひとたびその性質に倫理的ではない観点から語ろうものであれば、各方面からの指摘がたちまち飛んでくることになる。

わかっている、わかっているのだ。それでも、現場はやるしかない。

数年前、プログラミング経験もろくにない駆け出しSEだった僕は、それをやった。実にうまいことやった。今まで経験したどんな電子ゲームよりも楽しかった。そして、やっていくうちにありとあらゆる知見が蓄積された。しかしその知見は、CSVパーサーや排他制御もまともに書けないような同僚に引き継がれることはなかった。いずれ僕は会社を辞めた。

しかし、この知見は誰かに共有したい、この世界に存在する同類と共有したいという気持ちがあり、いくつかの勉強会で資料非公開という条件の元に発表をしたことがあったが、形に残らないというのがなんとも残念であった。その発表形態のひとつとして、同人誌という可能性を見いだしたのが技術書典であった。


同人活動に携わっている知人は少なくなかったが、自分が同人活動に携わるのはこれが初めてだった。昔から作文は非常に苦手であり、一人で本を書くというのはどうにも不安であったため、共同執筆者を探していた。そんな時にたまたま知り合ったのが momonga 氏であった。

執筆の目的を伝え自身の知見を共有すると、ほぼ初対面であったが執筆を快諾してくれた。こうして、サークル「自動機械」が誕生することとなった。

技術書典 (1回目) の応募は残念ながら落選となってしまったものの、技術書典2 は当選した。

本を頒布できるというところまでは決まったが、次に課題となったのはこの本を求める読者に手にとってもらうこと、そして満足してもらうことであった。

本としてのクオリティを高めつつ、想定読者層にアプローチできそうな共著者を検討したところ、挿絵が描けてこの方面に知見があり独特な方面へのつながりを持つ似非原さんに声をかけていた。これも初対面であったが快諾して頂けた。また、同人誌製本に知見がある、まくるめ氏にも監修を依頼し製本方面で助言を頂くこととなった。

執筆編集と製本は GitHub & GitBook を用いて行った。余談だが GitBook で製本をしたのは失敗で理由は様々あるがとにかく使わない方がよい。


もともと100部を売るつもりでいたが、入稿直前に様子がおかしいことに気がついた。

注目度が思ったよりも高いらしく、100部では足りないのではという予感があったため、急遽頒布部数を200部に増やした。

(最終的に被チェック数は201になった)

知人のアドバイスもあり見よう見まねで体裁を整えた。

そして、会場から2時間少々で無事完売。

値段設定はやや強気であったが、釣り銭が不要であることや原価が高めのこと、内容が内容だけに今回限りで二度と刷られないことを考えると妥当ではあると思う。

会期中、ブースにやってきた知人や見知らぬ同好者と、他所では決して話せない内容を語らうのは非常に楽しかった。インターネットの時代は終わりだ。

終了後、執筆各位で打ち上げをして、またいつもの日常に戻っていった。帰りの満員電車内で、珍しく気分は晴れやかだった。数年間の心の中の重荷が落ちたような、懺悔の後のような、そのような心持ちであった。


エゴサーチはするタイプの人間なので、した。

評判はほぼ好評一辺倒で驚きだったが、これはちょうど1年前に見いだした同人誌という媒体の特性にこの題材がうまくマッチしたのだと今にして思った。

自分の行動によって他人に良い(かどうかはさておき)影響を与えられたのは非常に嬉しく、個人的に尊敬している各位も反応しているのを見てしばらく感無量であった。

今は、仕事でも趣味でもクローラーを書いていない。

【募集終了】2017年版 2F Gaming ゲーミングハウス募集要項

追記 2017-01-14

入居者が決定したため募集は終了いたしました。

趣旨

別にプロゲーマーを目指すとかそういう要素は一切なくて、ただのゲーム好きが集まっただけのシェアハウスです。

いわゆるモンハンとかポケモンとかではなく、ValveとかBethesdaとかBlizzardとかのゲームが好きな人の方が相性がいいと思います。

たびたび週末に首都圏周辺の知人たちを呼んで集まって、楽しくLAN Co-opとかをしたりしてます。

入居条件

  • 20歳以上の安定した収入のある人
  • 当方と共同生活を送る上で問題がないと判断した人
  • 2017年1月中に初期費用を用意できること
  • 最低2年間入居できる人

物件情報

東京都墨田区の総武線沿線のマンション

1人1部屋あります。家財家具類は一切ありません。

家賃その他費用について

  • 入居時初期費用: 15万円以上 (初月の家賃を含む)
  • 家賃: 5万円
  • その他水道光熱費や通信費などは折半とします。

他の入居者

  • ぷりんたい

twitter.com

  • kokken

twitter.com

2F Gaming って何

www.2fgaming.club

自称・マルチゲーミングクラブです。

過去にはいち早く Rocket League の国内大会を主催したり、

SuperColliderCup

富士山の山頂でLANパーティーをしたり、

ゲーミングハウスでゲーミングクリスマスツリーを作ったり

他にも他所のイベントに出向いて騒いだり面白いことをしたり、といったことをちまちまとしています。

ここまで読んで興味がある人は

ぷりんたいのツイッターアカウントまでリプライを飛ばして頂ければより詳細な情報をお伝えします。

ご連絡お待ちしております。

エンジニア立ち居振舞い: 他社サービスの不具合を見つけたとき

お題「エンジニア立ち居振舞い」

 

中の人の気持ちを考慮してセッションとかUUIDとかそういう用語を使わずに、発生した現象だけを報告する。また、余計な推測を行うことをしない。

 

f:id:Purintai:20161118180237p:plain

 

Rails ActiveSupport の String#blank? について

github.com

現在、ActiveSupportString#blank? は以下のような方式で判定を行っており、Stringに不正なエンコードの文字が含まれていると Regexp#match から ArgumentError が発生する。

class String
  def blank?
    empty? || /\A[[:space:]]*\z/.match?(self)
  end
end

"".blank?     # => true
" ".blank?    # => true
"hoge".blank? # => false
"\xAE".blank? # => ArgumentError: invalid byte sequence in UTF-8

それに対し、文字列の中に「不正な文字」が含まれているのであれば、それは空白で構成されている文字列ではないから false と判定してもいいのでは、という要旨で Pull-Request を送りつけたのが冒頭のリンクとなる。

# 差分はこんな感じ
-    empty? || /\A[[:space:]]*\z/.match?(self)
+    empty? || valid_encoding? && /\A[[:space:]]*\z/.match?(self)

この変更に対しての懸念がコアメンバー各位からコメントされ、

  1. 他の Stringインスタンスメソッド (例えば、 String#strip ) と挙動が変わってしまう
  2. 不正なエンコード文字が含まれた文字列 ( ill-formed ) の場合 String#blank? の挙動は未定義のため、現状から変更しないほうがいい

の大きく2つを指摘された。(英語が難しいので、他にもある場合は指摘してほしい)

これに対しては、

  1. String#empty? のように評価を行うメソッドと比較すべきで、実際にエラーを発生させずに評価を行うことは可能である
  2. String#blank? は対象オブジェクトのエンコードにおいての空白文字のみで文字列が構成されているかどうかを判定するメソッドなので、 ill-formed な文字列を特別に例外とするのはおかしい
  3. そもそも、Stringの各メソッドから ArgumentError が発生することはドキュメントに書かれていないので、将来的にこのような挙動になることを保証されていないししていない
  4. だいたい、 ArgumentErrorRegexp#match が送出するエラーでは

という反論を考えた。 ここで、各Stringの文字列とメソッドの挙動を整理してみると、

String #empty? #valid_encoding? #blank? #strip #scrub
"" true true true "" ""
" " false true true "" " "
"a" false true false "a" "a"
"\xAE" false false ArgumentError ArgumentError "�"

となり、この中では #blank? のみが ActiveSupport のメソッドであるが、基本的に素のStringは処理/評価を行えるものはそれを行うような方針であるように見える。

また、コメントにおける ill-formed な文字列を特別視するという方針を改めてしまえば、空白文字の集合の否定には ill-formed 文字が含まれるので、 #valid_encoding? はそのまま ill-formed な文字列の包有確認に使用することができると考える。(もちろん、 #valid_encoding?false なら自動的に #blank?false になる。)

というところまでが自身の考えの整理ですが、この Pull-Request についての応援や反論などがあれば、Pull-Request のコメント欄やエモート、または直接私に Twitter などで語りかけてもらえるとうれしいです。 (本音: 英語がしんどい)

#ISUCON 6 に参加して僅差で人権を失いました

結果について

予選敗退、惜しくも決勝進出ライン90214点を逃した。

参加の動機について

力試し

25歳。ただの事務職から4回の転職を経て、Webサービスをやっている会社で技術者のはしくれをやっていましたが、自分自身が持つ実力がどの程度なのか未だにわからなかった。

技術者として1年やっていくごとにどんどんわかるようになり、どんどんできる領域が増えているという感覚はあったけど、それらは定量的に計ることができるでもなく、自身に対する上司や同僚からの評価・査定やアドバイス、意見した事柄に対する受け止めなどが客観的にどれほど適切なのかがとにかくわからなかった。

そんな中、少なくともISUCONはWebサービス界隈の有力者がうごめいていて、かつ業務上慣れ親しんだルールの上で競うことができ、自身のベンチマークとしては最適だろう、ということで参加を決意した。

チーム編成について

最初、所属している会社の同僚と出場するという算段を踏んでいたが、同僚各位に外せない予定ができてしまったため解散した。

困っていた所、繋がりのあった supermomonga 氏に「絶対に優勝しよう」と声を掛けたところ、心よく応諾してもらえたのでここにチームを結成した。

あと一人について、僕はいわゆるアプリレイヤの実装や全体の設計するのが得意で、モモンガ氏はアプリもインフラもできるがデータベースは分からないと言っていたので、データベースができそうな人を探しましょう、ということになり、モモンガ氏が aomoriringo 氏に声を掛けてくれて、ここに3人が揃った。

言語は僕とモモンガ氏が共通で使えるRubyを書くことになり、リンゴ氏はRuby書いたことはなかったがデータベースやミドルウェア周りを見て頂くことにした。

チーム名ですが、全員の頭文字を取って SMAP (SuperMomonga, Aomoriringo, Purintai) となった。

当日までの環境的な準備について

まず、運営からの割と大切な連絡がメールでくることが分かっていたが、代表者1人にメールが来るなり共有することに各位不安があったためプライベートなGoogleグループ、要はメーリングリストを作成し、そのメールアドレスで登録した。これは情報共有コストを格段に落としたのでとても良かった。

次にSlackを立て、過去問や他者の参加記や便利そうなツール・ライブラリをひたすら共有して蓄積し議論した。この試みもとても良くて、その理由は後述する。

Azureは先述のメーリングリストのアドレスを用いて登録した。いざという時にメンバーの誰かがパスワードを再発行してマスターアカウントでログインできるようにした。これは心理的な負担を少しでも減らすための試みで、いつ人は消えるかわからないので職務上もそのようにしておくと安心です。

直前集中合宿について

とりあえず過去問をたくさんやろう、会ってやろう、という話が交わされていたが、9月第2週にわたしが RubyKaigi 2016 @京都 に参加しており、予習のタイミングが本番直前の土曜しかなかった。

ところが、リンゴ氏が金曜に有給休暇を取るということになり、私も有給休暇を取って急遽、金土で過去問をやることになった。

金土日と連続でモモンガ氏の家(とても広い)にお邪魔してそこで初顔合わせとなったが、いわゆる初対面のわだかまりはなく、それはSlackで言葉を交わしていたことがとても大きく、また技術選定や方針についても「あの大会参戦記のアレを参考にしよう」とか「あのライブラリ・方法を試そう」などスムーズにすり合わせができ、普段同じ現場やプロダクト開発をしている同僚などではない間柄でも事前に前提知識の共有並列化が可能であり効果的であることがよくわかった。

過去問は ISUCON 5 予選問題と、Pixiv社内 ISUCONの問題をそれぞれ1日づつ使って解いた。ISUCON 5 予選問題は決勝進出程度のスコアになり、Pixivの問題は1位の点数を上回ってだいたい満足した。

作業の分担については僕が主にアプリコードを書いたりRedisを活用、モモンガ氏がアプリ書きつつnginxチューニングやログ集計可視化、ベンチマーク系環境整備、リンゴ氏がテーブルやSQL文やアルゴリズムをどんどん良くしていく感じで、それぞれの得意分野は各位が引っ張っていく感じでやった。

僕はSQLがやや苦手で「ここをこんな風に高速で呼び出し繋ぎたいがどうすれば良いか」と聞くとリンゴ氏がサクっとSQLを書いてくれたりざっくり伝えると最適な形でALTERしてくれたりして最高で、モモンガ氏が色々便利ツールを拾ってきて快適な過去問練習環境の構築やalpやkataribeなどのログを綺麗に再集計したり事前キャッシュしたりする書き捨てスクリプトをどんどん書いてくれたりしてくれて、僕はアプリコードを良くすることに集中できて本当に助かり、最高のチーム編成だと思った。

メシは2日連続で近所の海鮮丼と油そばを食べ、夜はアマゾンプライムで NEW GAME! を見て過ごした。

当日について

NEW GAME! を見て気分を高めていた所、開始10分前に突然モ氏がクソコラを作りはじめてやばかった。

開始直後、これは増田(Isuda)とはてなスター(Isutar)ですね、後なんかスパム判定マイクロサービス(Isupam)があるという話になり、僕はソースコード読みから仕様の把握に努めてお二人はデータベースのバックアップを取ったりコンフィグを確認したりをした。

事前に始まったらやること一覧リストというのを作ってあって流れ作業で進行しており、僕はまずソースコードを読んで機能一覧を書きだし、それからアプリコードを書き出すあたりにはRedisやPhpMyAdminなどが動いていてとてもよかった。

まず二人がhtmlifyが明らかにやばいことが分かったがデータベース利用が絡むため一旦検討を二人に託して、単純にPOST系でスコア稼げそうな、はてなスターとログイン認証をさくっとRedisにのせかえて、Isutarは消滅しハッシュされたパスワードは平文で保存され平文検証となりすこし速くなった。

記事が投稿されると過去の記事にリンク張り替え直さないといけないことが明らかにやばく、キャッシュしてもPOST毎に検証&破棄しないといけないかもしれない、ということが分かって最初は Redis の Sorted Set を活用することが検討されたが、仮実装で明らかに無理があることが判明したのですぐに切り戻しが行われ、とりあえずキーワード長カラムと予めエスケープしたキーワードを保存するカラムを拡張しようとなった。

拡張や再実装の方針について二人で検討している間、とりあえずスコアの伸びが確認できない問題だろうと仮実装の Sorted Set などを活用してキャッシュする機構や、どうせベンチマーカーは GET / の20要素しかすぐには見ないから、先頭の要素だけすぐに再生成すれば良くないですか、後は非同期でやりましょう、ということになり急遽 Sidekiq を導入して、サクっとワーカーを書いたら84000点出た。

後は Sidekiq を活用していい感じに非同期でやるように頑張っていったり、モ氏が Etag 活用して変更なければ 304 Not Modified 返すなどを仕込んだりしたら終了23分前に11万点出て、全員が予選突破できるかも、という気持ちになった。

ただ、dstatを見るとメモリが余りまくっていて unicorn プロセスを増やせる余地があることに気づき、思い切って200プロセスとかにしたらメモリを使いきってしまって5万点とかが出て、慌てて設定を完全に戻したら93224点になり、焦りからもう一度キューを入れたら89923点(ぐらい)が出て終了した。

結果、15位のボーダーが90214点でもう数百点の違いで、しかもベンチ回し直さなければ決勝進出できていたのでかなりつらくなり、皆で街に出て慰労会をやった。

終わった後について

この結果はもはや運の領域であり、運も勝負の一部であることから我々は確かに負けたのであったが、どうにもつらい、やりきれない気持ちとなった。

結果発表後にベンチマーカー実装の不具合と思われる事象や、それによる採点と運営レギュレーションとの乖離について愚痴が発生したが、仮にベンチマーカーに不具合があるならばそれを突いたり活用するのは正攻法であるため、アプリを書く役割として割り切れなかったことについても反省がある。勝負は勝負でやり直しはないということを久々に噛み締めた。

参加の動機について、敗退が決まった後は無力感などが存在していたが考えなおしてみるとアプリを書く能力はまあ、あるのでは、となり自信と自意識が高まってよかった。

終わった後、男3人によるエモい話などがあり、プリパラ1~11話を見てこれはかわいいですねとひたすら言いながら眠りについた。

そして朝が来て皆起きて、あそこはやはりああするべきだった、この部分の実装はもっといい方法があった、など各位から色々出てきたので、来年があれば本当にまたこのチームで参加したいと思った。

最後に海鮮丼を食べて、豚汁を飲んで電車に乗り帰った。

ブログはじめました

ツイッターで適当に散文してるものをまとめる場としてブログ始めようと思います。