私が歌川です

@utgwkk が書いている

#pastak生誕祭 2022でDJした

DJをやっていました。

xn--9myu5gp8s.pastak.net

はじめてのCDJ

さすがに京都からDDJ-400を運搬するのは大変なので、会場にある機材を使わせてもらうことにしました。CDJという機材があって、なんかLANケーブルを使うとパソコンと接続できるらしいぞ?? ぐらいの知識で臨んだところ、初めて触るボタンやツマミが多すぎて何が起こるのか分からずかつてない感じでした。

前日に開催されたIMAP++ #8では本当に初めてCDJを触り、接続方法と音量調整の方法、どこに再生ボタンがあるのか、を音出しのときにレクチャーしてもらったあと、一番手だったのでなめらかに本番が始まってなかなか大変でした。次に流す曲をプレビューするにはどこをどう操作したらいいの? という状態*1でしたが音を途切れさせなかったのでよいということにします。機材に対する解像度が低いのでは??

pastak生誕祭では昨日触ったばっかり、ということでもうちょっとうまくプレイできていたと思います。ツマミを触るよりも身体を揺らしたり腕を振ったりするシーンが多かった印象です。とにかくCDJの操作は勘でやって、オーディエンスを煽るのに注力していました。

セトリ

テーマはいつも通りの「いいね欄」でお送りしました。今回はわりと緩急ある感じだったのではないかと思います。ちゃんとメタデータを整備したほうがいい気がするけど素材の味ということにします。

gyazo.com

  1. Kazmasa ft.yuzuki - Wake Me Up
  2. Stella Drive (FRST. Bootleg)
  3. Transparent
  4. 日常 ED - Zzz [GF - Remix]
  5. フロントメモリーRemix_恋は雨上がりのように 【鈴木瑛美子】
  6. やくしまるえつこ / アンノウンワールドマップ
  7. 相対性理論 - チャイナアドバイス (covered by Toccoyaki feat. somunia)
  8. 【ピューパ!!】 It's too late
  9. ファジータウン (KMNZ × ORESAMA)
  10. ライカ - 長瀬有花 (Official Video)
  11. Natsuzora No Hanabi (value Remix)
  12. 気ままな天使たち (KiraraMagic Remix)
  13. Magical Silkhat
  14. Internet Pajamas Party
  15. 動く、動く (you House Remix)
  16. ミカヅキBIGWAVE - Platinum Groove

ご様子

感想をピックアップしていきます。

また参加したい

にゃんこ酒場の公開収録に始まり、終始いい音を聞きながら身体を揺らして、ポストカードのコンプを狙うという謎の空間になっていておもしろかったです。京都でもやってほしいです(そのほうが近いため)。

blog.pastak.net

*1:ボタンを押したらヘッドホンから聞ける音が変わる

RubyKaigi 2022にオフライン参加した #rubykaigi

RubyKaigi 2022にオフライン参加しました。参加するのは2016年の会*1以来で、これで2回目です。

トーク

トークを聞いたときのメモはScrapboxに残していました。

scrapbox.io

2016年の会に参加したときは全然理解が伴っていなくて、なんだかすごい話が行われていることぐらいは分かるけど……みたいな感じだったのが、もうちょっと噛み砕けるようになっていたと思います。RubyKaigiは、他の参加したことがあるカンファレンスに比べると、言語処理系のディープな話が多いという印象です。最終日のYJITのキーノートはさすがに何度かついて行けなくなったのであとで見返したい……。

mameさんによるTRICKの解説は、どのコードも異常な機能と奇抜な見た目が両立されていて真似できなさすぎる!! と驚嘆するばかりでした。地球を回すコードがリメイクされていて、あのコードベースを改造できるの?? という驚きがありました。

Rubyコミッタが集まって話すトークでは、将来・夢・議論・it がいいのでは? といったさまざまな話が聞けておもしろかったです。

RubyがWASMで動く、というのが一番インパクトがあったかもしれません。Rubyコードに document.getElementById みたいなメソッド呼び出しが出てきておもしろい感じがするけどたしかに動いていてすごいです。

DJイベント

最終日の夜にはDJイベントがあり、大きな音を聞いて身体を揺らしたり、インターネットでしか見ていない人と話したりしました。あと、なぜか盆踊りを踊っていました。

pixiv.connpass.com

飲みまくっていたら最後の方の記憶がないけど、3000円払った記録がマネーフォワードに残っていておもしろかったです。帰巣本能が強い気がします。

ネットワーク機器の後片付け

最終日の夜に、人手があると助かる、ということでネットワーク機器の後片付けに参加していました。ちょっと雑用をやるぐらいかな? と思っていたけど、案外やることがあって、夕方までかかってなんとか箱詰めができました。ねじりっこの切り方を知ることができたのでよかったです。

当日の連絡用にSlackコネクトでチャンネルに招待してもらったのですが、変なdisplay name*2を使うと目立った感じになってしまうので気をつけてください。

gyazo.com

感想

自分は普段Rubyを書いていない*3のですが、Rubyコミュニティの知り合いが周りにいるとか、久しぶりのオフラインカンファレンスなので行きたい、ということで参加していました。改めて、Rubyという言語が愛されているということが強く伝わってきました。どのトークも興味深く、偉業ともいえる高速化を成し遂げていてすごかったです。

会場までのバスが用意されていてなめらかに来場できたり、お弁当があって昼食に困らなかったりして、大変よかったです。地方で開催されるイベントだと交通の便やレストランの様子などが読みづらいので、イベント側で用意してもらえるのは助かります。

楽しい企画や便利グッズなども用意されていて、久しぶりにオフラインカンファレンスを楽しめたような気がします。運営の皆様、スポンサー企業の皆様、トークをされた皆様、参加者の皆様ありがとうございました。来年は松本で開催されるようなのでそれも行きたいですね。

*1:京都で開催された https://rubykaigi.org/2016/

*2:KMC Slackのdisplay nameがこうなっている

*3:最近はTypeScriptを書くことが多い

ISUCON12 チーム :old_noto_innocent: で本選に出て7位だった #isucon

最終スコアは167,491点で、7位でした。あともう1手ぐらい打てれば上位入賞できたかもしれません。

本選の問題

今回の本選の問題は、育成型放置ゲーム「ISU CONQUEST」の高速化でした。ブラウザから動作確認するとUnity製のゲームが動いていておもしろかったです。

www.youtube.com

最終的なサーバー構成

今回の本選ではサーバーが5台与えられました。

  • 1
    • App (Go)
    • nginx
  • 2
    • DB (MySQL, users.id % 3 == 0 用)
  • 3
    • DB (MySQL, users.id % 3 == 1 用)
  • 4
    • DB (MySQL, users.id % 3 == 2 用)
  • 5
    • App (Go)

やったこと

リポジトリはこちらです。

github.com

分析グッズを導入する (10:45)

予選のときと同じ分析基盤を使うためにアプリケーションコードを直してまわりました。具体的には context.Context を引き回すなどです。ひと通り導入できるまで時間がかかってしまっているのは今後の課題ですね。

ここでプロファイラやトレーシングの様子を見ると、明らかにDBがボトルネックになっており、とくに POST /loginPOST /user が重たすぎる、ということが分かりました。まずはそれらのエンドポイントから改善していくことにしました。

プレゼント付与・ログインボーナスまわりのN+1クエリを解消する (11:26)

プレゼント付与処理 (Handler.obtainPresent 関数) やログインボーナス受け取り処理 (obtainLoginBonus 関数) でN+1クエリが発行されていたので解消しました。受け取り済かどうかの判定をテーブルからSELECTしてきてあとで使うぐらいなので、ここはサクッとできました。

プレゼント付与時にbulk insertする (11:53)

N+1を解きほぐしたあとはbulk insertするようにしました。INSERTするものがない (スライスの長さが0) ときに NamedExecContext を呼び出すとエラーになるので気をつけましょう (1敗)。

このあたりで10749点を記録しました。

user_present_all_received_history テーブルに複合インデックスを貼る (12:05)

プレゼント付与時の以下のクエリが重たく、EXPLAINしたらインデックスが足りなかったので足しました。

SELECT * FROM user_present_all_received_history WHERE user_id=? AND present_all_id IN (?)

このあたりで20264点を記録しました。インデックスを足すだけで1万点ぐらい上がってて景気がいいですね。

アイテム付与処理を解きほぐしながらクエリ発行回数を減らす (13:37)

アイテム付与処理は、付与対象のアイテムの item_type カラムの値で分岐しつつ、別々のテーブルにクエリを都度発行するという実装になっていました。ここを解きほぐさないと先に進めなさそうでしたが、見るからに手をつけたくない雰囲気をかもし出していました。まあ、手をつけないといけないんですが……。

以下のような作戦でロジックの解きほぐしとクエリの効率化を進めました。

  • アイテム付与処理を管理するstructを作る (ItemObtainer struct)
  • ループ内のアイテム付与関数の呼び出しを置き換える*1
    • 同じシグネチャで呼べるようにする
    • 付与するアイテムのリストを溜めておく
  • ループを抜けたときに一括でクエリを発行できるようにする*2

元々の関数と同じような返り値を保持するならスライスの順序を保持する必要があるのか? と思いましたが、よく見ると返り値を捨てていたので気にする必要はありませんでした。

このあたりで43862点を記録していました。

DSNに interpolateParams=true を足す (13:50)

SQLのプレースホルダ置換を有効にするアレです。スコアが10000点ぐらい上がってウケました。

dsas.blog.klab.org

このあたりで54594点を記録していました。プレースホルダ置換は、ISUCONにおいては常に有効にすることにして初手で入れてしまってもいいのかもしれません。

users.id ごとにDBを分割する (15:28)

MySQLのCPU使用率がずっと高いままだったので、ユーザーデータごとにDBを分割するようにしました。

このあたりで119577点を記録していました。

アプリケーションを分割する (15:44)

アプリケーションのCPU使用率も上がりつつあったので、残っていたサーバーにアプリケーションを分割するようにしました。採番ロジックをアプリケーション側に寄せていたので、サーバーの番号をIDに埋め込めるように書き換えました。

このあたりで134670点を記録していました。

セッションキーに users.id の値を含める (17:23)

確率的にベンチマークの最初の整合性チェックが落ちる、という現象に頭を悩まされていました。たまにベンチマークが通るので、最初は気にしていなかったのですが、どんどん顕在するようになって焦ります。採番ロジックのバグや、ベンチマーカーのバグを疑いましたが、どうもそれらは違いそうです。

実は、先述したDB分割のさいにバグを埋め込んでいました。セッションキーを取得するときに、セッションキーを発行した先のユーザー用のDBを見ることができておらず、ベンチマーカーが意図したレスポンスを返せずに整合性チェックに通らなくなっていました。原因になかなか気づけなかったのですが、id:nonylene さんがトレーシングの様子と見比べながら原因を突き止めてくれました。今回のMVPだと思います。

最初は users.id ではなくセッションキーの頭文字に応じてDBを振り分ければよいかと思いましたが、セッションキーを再発行する処理でうまくいかない (セッションキーを全て無効化できない) ため、発行するセッションキーに users.id の値を含めてなんとかしました。競技終了が見えてきたタイミングでしたがなんとか修正できたのでよかったです。しかしヒヤヒヤした……。

分析グッズを剥がし、ログ出力を止める (~競技終了)

これ以上破壊的変更を加える余裕はなさそう、ということで分析グッズを無効化したり、echoのアクセスログを止めたりしました。それと並行して、ガチャのマスターデータのキャッシュ実装が途中までできていたので、バグを直しつつ投入して何度かベンチマークを走らせてみましたが (「これ以上破壊的変更を加える余裕はなさそう」とは何だったのか??)、18万点ぐらい出たものの再起動試験に通らないことが分かったので導入を断念しました。

感想

ISUCONは2016年 (ISUCON 6のとき) から毎年参加していて、本選に出るのはこれで4回目でした。それなりに手は打てて、順位表の上から数えるほうが早いぐらいのスコアを残せるようになったのはよかったと思います。アプリケーションコードとトレーシングやプロファイラの出力を見ながら作戦を考えて (考えてもらい)、順に試す、というサイクルがうまく回っているのだと思います。今回は失格/failになったチームが多かったようで、我々もセッションキーの不具合に気づかないままだったら……と思うとヒヤヒヤします。

一方で、上位入賞を狙うにはもうちょっと手数や実装の精密さが足りていなかったとも思いました。とくに今回はDB分割まわりで競技終了ギリギリまで悩むことになるバグを埋め込んでしまったのが悔やまれます。以下はもうちょっと余裕があれば導入できたかもしれない作戦です。

  • プレゼント配布時にbulk updateする
  • DB接続まわりのパラメータチューニング
  • ガチャまわりのN+1クエリを解消する
  • マスターデータをキャッシュしまくる

キャッシュ以外は感想戦で試してみているけど、20万点は超えたものの30万点には届かないので更なるブレイクスルーが必要そう……。このあたりは上位入賞チームの作戦を聞いてみたいです。

今回も予選・本選ともにおもしろい題材の問題で、競技に取り組んでいてすごく楽しかったです。運営の皆様ありがとうございました。次こそは優勝したいですね。

代替テキストが指定されていないimg要素をハイライトするブックマークレット

……というものを書いたので、どうぞご利用ください。

let.hatelabo.jp

(() => {
  const highlight = (element) => {
    element.querySelectorAll("img").forEach((img) => {
      // alt属性が指定されていたら緑の線、指定されていないなら赤線で囲まれる
      img.style.outline = img.getAttribute('alt') !== null ? "1px solid green" : "2px solid red"
    })
  }
  highlight(document.body)
  const observer = new MutationObserver((ms) => {
    ms.forEach((m) => {
      m.addedNodes.forEach((node) => {
        if (!(node instanceof HTMLElement)) {
          return
        }
        highlight(node)
      })
    })
  })
  observer.observe(document.body, { childList: true, subtree: true })
})()

2022/8/24 11:25 追記: 普通のページでは実行した瞬間にハイライトされるほうが明らかに便利なので、そのように修正しました。

みどころ

MutationObserver を使っているので、いちどブックマークレットを実行すれば、同じページ内で要素が動的に増えてもハイライトされます。シングルページアプリケーションの動作確認にも使えます。

展望

こういう機能を持ったブラウザ拡張があったらいい気がする。というか既にあるのでは?? 知ってる方がいれば教えてください。

2022/8/24 11:45 追記: 他の手段

id:nanto_vi さんにCSSを使って検査する方法について教えてもらいました。「alt 属性が指定されていない」という条件であればCSSセレクタで表現できるので、こっちのほうがリーズナブルでよさそうですね。

img[alt] {
  border: 1px solid green;
}

img:not([alt]) {
  border: 2px solid red;
}

digiper.com

www.mitsue.co.jp

コミケの出展に備えて買ったもの

個人サークルで参加するのは初めてで、なにも持っていないのでひと通り買った。

ホワイトボードノート

値段や休憩しているかどうかなどを書くのに便利だと思う。それはそれとしてPOPも作ったほうがよさそう。

POPスタンド

ホワイトボードノートを置くのに使う。見本誌も展示したいので2つ買ってもよかったかもしれない。

養生テープ

見本にベタッと貼って目印を付ける用。まあ普段の生活にあっても困らないと思う。

金庫

お金をそのまま置くのは気が引けるので買った。持ち運びまくると金庫の中で小銭がグシャグシャになる。

名札

自分自身が顔と名前がなかなか一致しないタイプだし、とくに初対面の人が多くなると思うので、名前が分かったほうがいいと思って買った。買ったけどとくに使ってない。

かばん

引っ越しのときにエコバックを捨てたので、大きめのかばんが欲しくなっていた。ちょうどいいので普段づかいにもなりそうなものを探して買った。思ったよりは小さかったけど荷物はなんとか入った。もっと大きくてもよいかもしれない……。

布は東急ハンズで見つけられなかったので、あの布屋というところで買った。サイズ指定とか分からないので面倒だったけど、最初から目的が決まっている布なら話は早い。

コイントレー

感染対策。

TODOコメントを列挙して棚卸しする行い

表題のような活動をやった。今あるTODOコメントを全部抜き出して、Scrapboxのページに並べた上でコメントしていく。すでに解決しているもの・今のうちに対応を考えておくべきもの・まだ前提が揃っていないもの などいろいろあると思うけど、現状どうなっているのかを眺めて議論できるのがいいと思う。

gitを使っているなら、以下のようなコマンドで簡単にTODOコメントを列挙できる。ファイル名だけでなく行番号も入っているほうが見やすいと思ったので含めてみている。

$ git grep -niE '\btodo\b'

これぐらいのコマンドで眺められるので、ちょっと手があいたときにやってみるとよさそう。fixme xxx hack みたいな語でgrepしても面白いかも?

スペイン語には todo という単語があるので、引っかかった場合はうまく除外してください。ローカライズ文言に含まれていたので手でちまちま除外していた……。

自分の言葉で書き残しておくこと

過去にあった出来事を振り返ってみると、自分はなぜあのときこうしたのか、他にもっとよい方法がなかったのか、と思い悩むことがある。そのとき何を考えていたのか、他に選択肢があったのかどうか、思い出せない場合はそれ以上掘り下げるのが難しくなる。記録が残っていたら、それらをもとに状況を思い起こせるようになるはず。自分の手元に写真も残っていない時期に、自分が何を考えてどう行動したのか、はっきりと思い出せなくて苦しくなることがまれによくある。気持ちよく忘れるために書き残しているのではないだろうか。

仕事をするにあたっては、いろいろな解決策やトレードオフがあって、そのときそのときに応じた判断を迫られることがある。何が起こっていて、どういったことを調べたり考えたりして、最終的にこうやって実現した、ということはできるだけ書き残すようにしている。書き残しておけば、あとで見返したときに本当によかったのか、いまいちだったのか、もっといい方法があるのか振り返ることができるはず。後から評価可能であるかどうか、が自分の行動基準の1つになっているのかもしれない。これを生活に置き換えると、もっと日記を書いたらいいということになりそう。

今この瞬間はこういうことを考えて、キーボードを叩かずにはいられなかった、ということもここに記録されて気持ちよく忘れられることになる。