Diary of a Perpetual Student

Perpetual Student: A person who remains at university far beyond the normal period

ダークモードをおまけ感覚で提供するのは厳しい

スマートフォンを先日発売された Google Pixel 6a に乗り換えました。 もともとは Pixel 3a を利用していたのですが、セキュリティアップデートの提供保証が切れたことが買い替えの主な理由です。

store.google.com

機種変更に伴って、ワンタイムパスワードを用いた2要素認証の設定をし直しました。

GitHub の Web 上で表示された2次元コードをスキャンしたのですが、なぜか読み込むことができませんでした。

2次元コードはダミーです

原因はダークモードなのに2次元コードの周囲に白い余白が十分用意されていなかったことでした。 試しに、開発者ツールからスタイルを変更して、以下のように余白を白くすると読み込むことができました。

.qr-code-img {
  background: white !important;
}

近年は、CSS でカスタムプロパティが使えるようになり、変数の値をまとめて変えるだけでフロントエンドのデザインテーマを切り替えられるようになりました。 しかし、ライトなテーマの延長として色を変えるだけで実装すると、このように操作性の悪い UI が生まれてしまうことがあります。

今回の件以外にも、プロパティの設定値によってコントラストの差が小さくなり、視認性が悪くなるという問題が起こることも考えられます。

実装コストとの兼ね合いを考えると気づきベースでなんとかしていくしかないような気もします。しかし、ダークモードが市民権を得た今、アクセシビリティテストを CI で動かすなどの手法(思いつきです、できるかどうかは分からない)により、ダークモードを安心して世に出せる仕組みが整備されたいと思いました。

株式会社はてなに入社しました(本物)

株式会社はてなに入社しました

株式会社はてなに入社しました - hitode909の日記


といういつものやつではなくて、これは本当の入社エントリ(にしては遅すぎないか?)です。

株式会社はてなにWebアプリケーションエンジニアとして入社して4か月が経とうとしています。 はじめての査定結果が通知され会計年度が終わろうとしているこのタイミングで振り返っていきます。

自己紹介

id:arthur-1 です。お理工だけじゃダメなんだ大学の学部に7年間通った後に、はてなに新卒入社しました。 3留なのでどこにも雇ってもらえないと思っていたけどなんとかなりました。

学生時代は学園祭実行委員会で参加団体向けの Web 申請システムやお客さん向けの優良企画投票システムを作ったり、ボードゲームに関する Web サービスを個人開発・運用したりしていました。

db.buratsuki.page

neo.buratsuki.page

また、専攻はコンピュータサイエンスだったのですが、研究では比較的得意だったプログラミング系から距離を置き、数理最適化×信号処理という分野をターゲットとしていました。

会社のこと

新卒研修

新卒エンジニア研修として、他の新卒エンジニアと一緒にとある社内向けサービスを開発しました。 この時の話だけで 1 エントリ書けてしまうので、今回は触れないでおきます。

技術グループのPodcast である Backyard Hatena でも少しだけ紹介されています。

developer.hatenastaff.com

所属チームのこと

5日間のエンジニア研修が終わったのちに、Mackerel という監視 SaaS の開発チームに配属されました。

ja.mackerel.io

Mackerel 開発チームへの onboarding

「Mackerel アプリケーションエンジニア入門」として、各領域に設定された実績を解除していきました。 チームとしてのタスク(スプリントバックログアイテムの消化)をメインとしてこなしつつ、余った時間で以下のような内容について学習しました。

幸いなことに、Mackerel で使っている技術スタックと自分が持っていたスキルがある程度合致していた(Scala・TypeScript+React)ので、業務に必要なプログラミングの習得には時間がかかりませんでした。 join して 3日目には最初の Pull Request をマージすることができました。 しかし、インフラ周り(特にクラウドサービス)の知識はほとんどなかったため、最初は話についていくのが大変でした。

メンターを始めとするチームメンバーの方々から絶妙なタイミングで新しいことにチャレンジする声掛けをしていただきました。 そのおかげで、毎日少しずつ背伸びをしながら、できる業務の幅を少しずつ広げていくことができました。

また、SRE と同じチームで一緒に仕事をしていることもあり、入社時と比べて格段にインフラ周りの知識が身についています。 これは、留学するとめちゃくちゃ英語できるようになる、みたいな話と近いかもしれません。

業務の上で心がけたこと

dogfooding

Mackerel を自分で使っているうちに見つけた要改善ポイントを、積極的にプロダクトバックログアイテムとして起票しています。 誰のどんな課題をどうやって解決するのかをきちんと言語化するのは大変ですが、時間をかけても良い大事な作業だと考えています。 これによってプロダクトオーナーを始めとするチームメンバーに、課題そのものや、課題の価値によってプロダクトの価値を上げられるということを共有することができます。 結果、優先度が高いと判断されたアイテムは、素早くスプリントに載せられ、チームとして取り組むことになります。

大規模なシステム開発だと、上から降ってきたタスクをただこなしているだけの人間になってしまいそうと思っていましたが、現実はそうではありませんでした。 Mackerel チームのスクラム開発では、 dogfooding を通じて明らかになった課題を、プロダクトの価値を高めるために解決していく仕組みが整っています。 自分がプロダクトに貢献できているという実感を持って取り組む仕事はとても楽しいです。

また、dogfooding から始めたこの取り組みによって、他のプロダクトバックログアイテムについても、利用者のことを考えた進め方を提案して進めることができています。

先ずテストより始めよ

チームに join してしばらくの間は、アプリケーションの仕様の理解に時間が掛かります。 先にテストを書いたり修正したりして、不安な場合にはこの状態で他のエンジニアに見てもらうことで、仕様への理解を深めました。 その上で、テストが通るようにアプリケーションコードを改修しました。

闇雲にタスクに取り組むのではなくテストコードから始めることにより、手戻りを減らしつつ、自信を持って Pull Request を作ることができました。

最後に

特に入社エントリとしてのまとめとしての話は用意していないのですが、このエントリで楽しく仕事をしていますという雰囲気が伝われば、エントリの目的は果たしていそうです。

これだけだと締まらないので、将来の話を少しだけします。

8月から始まる来半期は、自分の成長だけを考えていた状態からチーム・プロダクトの成長も合わせて考えられるようにシフトしていきたいと考えています。 チームとしてカバレッジが足りていない領域や、プロダクトが抱えている問題に合わせて、自分の成長の指針となる目標を設定しようと、メンターやディレクターと相談しています。

こんな形で今後も public な振り返りを定期的に書いていきたいですね。

ISUCON 12: はじめての ISUCON 体験記

ISUCON 12 予選にチーム「横浜の缶詰」*1として参戦し、最終スコア 9368 点を残して敗退しました。参考値ではありますが 104 位(661 チーム中?)ですね。

isucon.net

結果としては残念な感じですが、初めての ISUCON で今後の成長に繋がるやり方を貫き通せたところが良かったと思っています。次回以降も前向きにチャレンジしていきます。

さて、事前準備や当日の様子・学びについて振り返っていきます。

事前準備

まず、3人で作戦会議をしました。アプリケーション担当 2人 + インフラ担当 1人という形で分担することにしました。(自分はアプリ担当。)

また、事前演習として、チームで ISUCON 10 予選問題を解きました。計測すると明らかに「なぞって検索」が支配的に重いことが分かるのですが、ここの改善は MySQL の GEOMETRY 型(初見)が登場してとても難しいです。なんとか改修することができたのですが、演習時間のほとんどをこれ1本に注いで終わってしまいました。

演習後、チームで KPT 法による振り返りを行いました。以下はその抜粋です。

  • K: deploy 周りのスクリプト用意されていてよかった
  • K: log の grep テク大事
  • K: 計測して改善のムーブを大事にできた
  • P: 今何をしているか共有できなかった
  • P: 重いタスクを2つに分割したつもりだったけど完全に分割できていなかった。2人で同じところにハマってしまった
    • T: リソース効率よりもフロー効率を重視して、基本ペアプロで進めていく

加えて、チームでの事前演習とは別に社内の ISUCON 練習会に参加しました。朝まで飲んでいたら寝坊したので、本番前日はたとえ飲みに行っても終電で帰るという強い誓いを立てました。

予選当日

このセクションでは、アプリケーション担当の id:arthur-1 目線で競技中のタイムラインを記述する。(インフラ担当の頑張りはあまり記述していない。)

9:30 起床

起床チャレンジ成功。ゴミ捨てチャレンジには失敗した。

10:00 マニュアル・ドキュメント読み合わせ開始

インフラ担当が準備している間にアプリ担当2人でマニュアルを読んだりブラウザで動作確認したりしていた。

10:30 リポジトリ準備完了

インフラ担当がソースコードGitHubリポジトリとして共有した。ソースコードをまじまじと眺めて、テナントごと SQLite のファイルがあるのに気づき戦慄する。

動画で CSV によるスコア入稿が重いという話があったので、 func competitionScoreHandler を読んでいた。明らかに n+1 なので、bulk insert にしたり一括で select するようにしたい。

11:00 対戦環境が整う

Discord に alp や slow-query の結果を投稿してくれる君が動くようになった。alp の結果を正規化し、ベンチを回して解析結果を眺めようやくスタートラインに立つ。

GET /api/player/competition/:competition_id/ranking エンドポイントを 2人がかりで改善していくことに決定。

11:30 competitionRankingHandler をターゲットに

visit_history という謎テーブルはどうやら課金額の計算に使用しているらしいことが分かる。以下の修正方針を立てる。

  • player_score テーブルに index 張りたい
  • retrievePlayer を for文で回しているところが n+1 なので直したい

改善したいクエリって slow_query に載ってるのか?という痛い指摘を受けたけれど、残念ながら SQLite なので改善すべきクエリなのかはわからない。

11:50 player_score に index 張った

他のクエリに影響がでないよう player_score 関連のクエリを洗い出して index 設計。SQLite だと CREATE TABLE と同時に INDEX 作れないことを知る。

12:20 デプロイ成功 & index の効果をベンチで確認

自分達で build したバイナリのデプロイ成功。スコアは誤差レベルでほとんど変わっていない。

12:40 SQLite 脱却の決意

GET /api/player/competition/:competition_id/ranking の n+1 クエリ脱却に成功。デプロイしたが、スコアは向上せず、むしろ下がってしまった。pprof や alp の結果を見ると明らかに良くなっているのだけれど。

これは SQLite なのが悪いと断定した。また、以下の点から出題者が MySQL への移行を推奨しているのではないかと思った。

  • テナントごと DB が分かれているにもかかわらず、tenant_id カラムが用意されていること
  • SQLite の db ファイルを SQL のクエリ列に変換するスクリプトが用意されていること

SQLite のままだと3台フルに生かした複数台構成にしにくいこともあり、脱却を決意した。

13:50 アプリケーションコードの SQLite 脱却完了

昼休みを交互に取りながら、SQLite 脱却作業を行う。

tenantDB にアクセスしに行っているコードを adminDB に書き換える作業が完了した。残すは初期データの生成のみ。

14:10 tenantDB の初期データを MySQL に作り替え始める

tenantDB 初期データの mysql 向けの dump ファイルを作りたい。sqlite3-to-sql スクリプトの出力結果を MySQL に食わせているけど永遠に終わる気配がない。これはでかい釣り針に釣られてしまったのではないかと焦り始める。

15:15 bulk insert に変換する

sqlite3-to-sql スクリプトの出力結果を bulk insert に変換するスクリプトができたので、初期データを MySQL に移すのを試してみる。

max_allowed_packet に邪魔されて実行できない。

15:30 MySQL 移行諦める

事前練習でなぞって検索に全振りしてしまった反省から、 MySQL 移管を損切りすることに決める。

これができないと App 2+ DB 1 のような複数台構成を目指すのがかなり無理になってしまい、インフラ担当が今後できる改善ポイントが減ってしまうので、やり遂げる技術力がなくてただただ残念に思っていた。

作戦会議をして、全体としては GET /api/organizer/billing の改善に pivot することに決めた。

また、僕は動画で紹介されていた CSV 周りの処理が気になっていたので、 POST /api/organizer/competition/:competition_id/score の改善に取り組むことにした。

15:50 competitionScoreHandler の n+1 を直す

POST /api/organizer/competition/:competition_id/score の処理が n+1 になっていたので直した。元から NamedExecContext を使っていたので bulk insert に書き換えるのが簡単だった。優しさを感じる。

この頃インフラ担当が App + DB の 2台構成を試していたのでスコアの比較はできなかったが、少しは改善しているように見えた。

16:45 billingHandler の改善を進める

visit_history を記録しているけど、課金に必要なのは最初の visit の情報だけなので、 min_visited_at を記録するテーブルとして分割することにした。コードの改修が終わってベンチを回すも整合性チェックに fail する。

どうやら、初期化時にテーブルの生成ができていないようだ。webapp/sql/admin/10_schema.sql に書けば良いと思っていたけど、このファイルはアプリケーションでは使っていないことに気づく。init.sql に移し替える。

16:55 最高スコア記録

インフラ担当の頑張りによって、最高スコア 9777 点を記録する。確か player_id の謎の生成方法をやめて uuid に変えていた。

17:05 min_visited_at 初期データ作り

500 エラーは発生しなくなったが、引き続き整合性チェックに引っかかる。そもそも、初期から visit_history にレコードがあるので、ここから初期データの min_visited_at を作らなければならないことに気づく。

ここを直すと大幅にスコアが伸びると期待されていたので、焦りながらも黙々と進める。

17:30 初期データ作り失敗

min_visited_at の初期データを作るクエリを組み立てるのに試行錯誤していた。成功するも整合性チェックに通らず。どうやら課金額 50 円ほど少なく計算されてしまっているようだ。課金計算という言葉に拒否感を覚え始める。

インフラ担当が店じまい(デバッグログ出力の停止など)を始める。

18:00 競技終了

最終スコアは 9368 点。結局 GET /api/organizer/billing の改善を完遂することはできなかった。最後のデバッグログ停止などはあまりスコアに響かなかったようだ。

チームで振り返りをした後解散。夕食を食べて酒を買い出しに行き、社内のオンライン反省会に参加した。PHP 大好き〜〜と語っていた。

スコアの変遷

感想

個人的には、以下の点について反省しています。

仕様の理解をおろそかにしていて、改善アイデアが参照実装コードに縛られていた

コードがユーザにどんな機能を提供しているのかの理解をおろそかにしていました。コードを見れば、「あっ、ここは n+1 なので直せるな」といったことはすぐに分かります。しかし、このような一目でわかる小手先の改善だけでは戦っていけません。その関数が結果として何を返しているかをあまり考えずにいたので、「visit_history テーブルってそもそも必要なくない?」などの抜本的な改善のアイデアに辿りつくことができませんでした。

ブラウザ上でアプリケーションを動かすのをもっと丁寧にやると良いのかもしれません。

コーディング速度が遅かった

単純に、改善ポイントを見つけてから改善するまでの時間をもっと短くしたいと思いました。チームでは Go 言語を選択したのですが、僕は Go を読み書きし始めて日が浅く、流暢にプログラムを書くことができませんでした。

ISUCON に関する圧倒的な情報量・pprof などの使いやすい解析ツールの有無を考えると、Go 言語を選ばないとハンディキャップになってしまいます。仕事でも Go 言語を使う機会がかなりあるので、半年以内には素早く書ける言語にしていきたいです。

とは言ったものの、最初の練習から比べるとかなり書けるようになったので、その分は自分を褒めたいです。NamedExecContext を見て「おっ!」と反応するのは golang 初見ではできなかったでしょう。

最後に

ISUCON めちゃくちゃ楽しい!!!本当に isuports(esports) そのものだと思いました。ISUCON に取り組むことで得た技術力やマネジメント力はきっと業務に活かせると確信しています。来年も絶対出るぞ!!!!

*1:チーム名は ISUCON チーム名ジェネレータ で決めました。たまたま自分は横浜市民でした

最近聴いてる曲: Jul. 2022

社内でおすすめの新譜を載せていくチャンネルが生えていてめちゃくちゃ良いな〜と思った。

全然新譜ではないけれど最近聞いてる曲を淡々と載せていく。

我愛你 / Cody・Lee(李)

www.youtube.com

ドラを鳴らす曲を作りたかったので参考に。

ただ選択があった / フロクロ

www.youtube.com

IV△7→♭VII7 が最高。

24 night / matsudamiki

www.youtube.com

近所のラーメン屋さんでずっとこのアーティストの曲を流しているので聴いている。*1

負け犬 / RuLu

www.youtube.com

缶缶さんめちゃくちゃカッコ良い〜〜〜良い。アルバムとしても結構好きな曲が多いので良く聴いている。

唇の凍傷 / ワルキューレ

www.youtube.com

華やかな転調、そして転調。

君たちキウイ・パパイア・マンゴーだね。 / 古幡愛

www.youtube.com

名曲のカバー。みんな言ってるけど PV が良い。

まるで霧雨のような / はるふり

www.youtube.com

ギターの音色、シンセソロ、好きな要素しかない。

感想

仕事始めてから高校の軽音部に定期的に遊びに行くことがなくなってしまったので、若者のトレンドについて行けていない感じがする。

*1:と思ったけど、この前行った時には Just the Two of Us が流れていて、あれ?ってなった

悪しき日にレジリエンスを考える

この4月にブログを新しく作り直したのですが、前のブログの記事の移転がまだできていません。

それはそうと、今日は自分が昔書いたエントリを掘り返して、令和最新版にリファインメントしていこうと思います。

東日本大震災

急に思い出した話を書きます。

7年弱前*1に起きた東日本大震災。 (もうこんなに昔なんですね)

当時僕は中学2年生で、学校の体育館で3年生を送る会を行っていました。 突然の長い揺れ。ガラスの割れる音。

急いで教室に戻りテレビをつけると、大津波警報の文字が。 震源から数百km離れた沼津でも、保護者引き渡しとなりました。 家に帰るための国道は津波警報のため通行止めになりました。

中学の体育館は、新しいものができるまで立ち入り禁止になりました。 仙台の親戚の家は水たまりを残して跡形もなくなりました。 電池などの物資が不足し、大阪の友人が送ってくれました。

震災から1週間経ったある日、 国語の授業のはじめに先生がこんなことを言いました。

「大震災の報道を見て、 非日常に興奮してしまっている自分がいるんじゃないか。 みんなも自問自答してみてほしい。」

この発言は非常に衝撃的でした。 さして当事者でもない僕の気持ちを表現するに どれほど的を射た発言だったでしょうか。

多くの人が亡くなって、 生き残った人も過酷な避難生活を過ごして、 大規模な原発事故も起きて、

そんな状況に自分は興奮しているとするならば、 どれだけ残酷なことだろう、 と子どもながらショックを受けました。

こんなことをメディアで言ったら それこそ津波のような勢いで叩かれるでしょう。 しかしながら、それを的確な言葉で伝えた先生。 この立場だからこそできることだと思います。

不謹慎という言葉だけで片付けられるでしょうか。

ニュースや週刊誌に載っているのは他人の不幸ばかり。

これは人間の真理なのかもしれません。

最後の締めにそれっぽいことを言っていますが、結局何が言いたいのか分からない文章ですね。これは、当時の自分が「じゃあ人々はどうしたら良いのか」という問いに対する回答を明確に持ち合わせていなかったことによります。書いた当人(自分です)は満足しているかもしれませんが、これではただのポエムです。

4年半経った今、自分の思っていることを、解像度を上げて書いていきます。

客観視し、自分を受け入れる(抽象)

僕は医者や心理学者ではないので断定はできませんが、大きなインパクトのある事故や事件に対して、落ち着きのなさを感じるのは心理的反応としておおむね自然なものでしょう。無理して「いつも通りに生きよう」という気持ちにはなれない、もしなれたつもりでも本当にそうなのかは分からないと思います。

レジリエンスという言葉があります。心理学の分野では、自己に不利な状況に自身のライフタスクを対応させる能力を指すそうです。

ja.wikipedia.org

アメリカ心理学会が提唱する「レジリエンスを築く10の方法」のうちに、「変えられない状況を受容する」ことがあります。動揺し興奮した自分を否定せず、生理的現象・事実として許容することが、レジリエンスの面で有効ではないかと考えています。

仕事の話に例える(具体)

Web アプリケーションエンジニアをしていると、多かれ少なかれシステム障害に遭遇することがあります。対応中、当然多くのエンジニアは焦燥感を覚えるでしょう。焦った結果、ミスコミュニケーションが発生したり、判断ミスでより良くない結果をもたらしたり、ストレスを感じ engagement の低下に繋がったりすることになります。

しかし、「焦らないようにしよう」「落ち着いて対応しよう」という TRY が提示されても、それを実現するのは難しいと思います。もちろん、こういった問題への対応は、個人の努力だけに依存せず、仕組みで解決する方向に持っていきたいですね。ただ、理論的にはそうでも、個人の心情問題なので難しいところがあります。

1つの策として、事象を受け入れられない自分や他者を客観視し、受け入れるという方法があるのではないかと思います。これは個人的な経験に基づくものでしかないのですが、自分の感情を一歩外から引いて見つめ、「まあそういうこともあるよね」「人間として自然だね」と事実として受け止めることで、感情の保持者という意識が薄れ落ち着くことができます。「落ち着け」と言って押さえ込むのは、ポジティブフィードバック*2になる可能性があり、制御の観点上望ましくありません。外圧を受け入れしなやかに適応していこうとすることが、無理せず維持できる、あるべき心の持ちようなのではないでしょうか。

最後に

いつかこの話をしようとずっと思っていたのですが、自分の身の回りのあらゆる様子が乱れている今日この頃に、言葉としてまとめておきたかったのでした。そして、この行動自体もまた防衛機制の表れなのかもしれません。

*1:本エントリ執筆は2017年12月20日です

*2:前向きな内容を相手に伝えるという意味ではありません: ポジティブフィードバック - Wikipedia

LambdaでSlack Botを開発するTips

本エントリは、バックエンドにAWS Lambdaを用いたSlack Botを開発するときのTipsを雑多に垂れ流していくものです。普段の記事と異なり、エントリとしての主張のまとまりに関してはほとんど考慮していません。

Slackで使えるインタフェースを把握しよう

Slack Appを作るとき、我々はSlackの仕様に縛られることになります。Slack側が用意してくれているインタフェースを使ってユーザはAppとやりとりすることになります。

ただ、Slack Appでは思ったよりもリッチなアプリケーションを作れます。作りたいものが決まって余裕があるなら、Slackが提供しているユーザインタフェースのうちどれを使うと良い体験になるかを考えてみると良いでしょう。(そもそも、こんなUIが実現できるんだという存在を認識するところがスタートです。)

  • メッセージ
  • スラッシュコマンド
  • モーダル(アクション)
  • ホームタブ
  • ショートカット
  • ワークフロー

もちろん、UIがリッチになっていくほど実装は大変になります。作り始めから特定のUIに依存しない形でサービスロジックのコーディングができていると、他のUI経由にしたいときも実装を変えやすいです。

Slack Appの設定はコード化しておこう

複数人で開発すると、どうしてもSlack Appの設定(エンドポイントURLや必要な権限など)の情報を各人で共有することが難しくなります。開発環境と本番環境など、同じような設定を複数のSlack Appにしたいならば尚更です。

インフラ構成をIaC (Infrastructure as Code) で管理したくなるのと同様に、Slack Appの設定はmanifestファイルとして管理することができます。

api.slack.com

個人でお遊びのものを作る分には良いのですが、業務で使うようなAppの開発は引き継ぎできるかも大事なポイントの1つです。設定をコード化して属人化を防ぎましょう

レスポンスは3秒以内にSlackに届けよう

スラッシュコマンド実行時やModalでの送信時に、SlackからSlack Appに対してリクエストが送られます。API Gateway経由で起動したLambdaでそのリクエストを処理してレスポンスをSlack側に返してあげる必要があります。

ここで、リクエスト送信からレスポンス到着までの時間が3秒以上かかってしまうと、Slack上では「何らかのエラーが発生しました」と表示されてしまいます。ある程度時間がかかってもレスポンスさえ返せばSlack上での画面変化が正常に行われるのですが、エラーと表示されてしまうとユーザとしては不安ですよね。

アルゴリズムの改善や実行に必要なファイル群のサイズを減らすなどによって実行時間を減らすことができますが、これらの細かい工夫だけで解決ができるかは保証できません。何より、他のAPIにアクセスして結果をもらうという典型的な処理においては、そのアクセスのレイテンシも乗っかってくるので、自分たちがいくら努力しようともどうしようもないことがあるでしょう。

以下の記事で紹介されている通り、レスポンスを受け取ったらすぐに空の200を返してしまい、その後で黙々と処理をするのが良さそうです。

qiita.com

また、金銭的余裕があるなら、Lambdaのコールドスタートをできる限り回避するためにプロビジョニング済み同時実行を使うなどの工夫も考えられます。(それだとLambdaの良さ台無しでは?というところではありますが。)

なお、Slack Appレベルの規模の場合、ランタイムの選択によって3秒問題を解決できるほど大きく実行時間が変わるということはないと考えています。JavaScriptでもGoでも好きな言語を選ぶと良さそうです。(ただしJavaは微妙かも。)

acro-engineer.hatenablog.com

dev.classmethod.jp

Slackの再送による多重実行を回避しよう

「レスポンスは3秒以内にSlackに届けよう」にも関連するのですが、SlackがSlack Appのエンドポイントにリクエストを送った結果エラーとなった場合、Slackは自動でリクエストを再送します。これは、3秒以内にレスポンスが届かなかったとき(http_timeout)にも起こるので、上の問題と絡み合って不具合の原因特定と解消が困難になってしまいがちです。

Slackのお節介な仕様が必要でなければ、再送された場合のリクエストに対しては処理せず200を返すだけにしてしまいましょう。概ね、以下の記事の「パターン3, そもそも再送を無視する方法」に紹介されている通り、ヘッダを見て再送しているリクエストかどうかを判別するのが良さそうです。

dev.classmethod.jp

AppからSlackにメッセージを投稿する場合、エラーハンドリングの方針を考えよう

Slackを通じて何らかのアクションを行う場合、きっとSlackのメッセージで結果を返したくなるでしょう。もし、バックエンドのメインロジックの処理が成功したのに、その後でSlackのエラーが発生してレスポンスがエラーになると、一見処理は正しくできているのにSlack上では「エラーが発生しました」と表示されてしまいます。

で、Slackのエラー(もっと具体的に言うと、LambdaからSlackのAPIを叩くときのクライアントエラー)なんて起こるのかというと、意外と起こる可能性があります。例えば、SlackのAPIは短い時間でたくさん呼ぶとthrottlingエラーを返します。1回のアクションに対して複数回postするような場合には注意が必要です。

補助的にSlackのメッセージを送りたいのであれば、あえて、Slackの投稿に失敗してもLambdaの実行失敗としないようにエラーを隠してしまうのも一つの手だと思います。

逆にSlackにメッセージを送るのがMustな要件の場合は、キューサービス(AWS SQSなど)を挟んで投稿専用Lambdaを作るとよいでしょう。投稿用Lambdaは投稿に失敗したら異常終了するようにし、Queueに再送することでthrottlingなどによる投稿失敗を防ぐことができます。何回も失敗する場合には何かがおかしいので、Dead Letter Queueに入れて元のキューが詰まらないようにし、DLQのメッセージ数をモニタリングしてアラートを出すと良さそうです。

リンク集

標準エラー出力が見れなくなるpreztoユーザはバージョンを確認しよう

結論

  • 昔のpreztoを使っている人は、最新のリポジトリの状態を反映しよう

本文

僕のターミナルがおかしい

僕のターミナル環境はなにかがおかしい。何がおかしいかというと、標準エラー出力に書き込まれているものが見えなくなることがありました。

コマンドの実行に失敗しても結果が見れず何も表示されないため、「あれ?一瞬でコマンド終了したけど成功はしてないな?ステータスコードも0じゃないし……」と困惑していました。

経験的にCtrl+cを押した後に確率的に動かなくなるという感覚を持っていました。また、新しいタブを開くとそのタブでは問題が解消することも知っていました。

この現象が、OSを問わず、僕の使っているあらゆるPCで発生していて、とても気持ち悪いなと思っていました。

preztoが悪いのだろう

そんな生活を2年ほど続けていたのですが、さすがに嫌になってきたので、原因調査と問題解消に取り組むことにしました。

実は原因はzshの設定にあるのだろうということは大方分かっていました。Macのデフォルトシェルがzshになって、preztoを導入した頃からこの事象が発生し続けているからです。

pretzoとはzshフレームワークのようなもので、様々なプロンプトのテーマを選んだり、補完や情報表示ができるプラグインをかんたんに導入できたりするものです。好きな僕はprezto本家のリポジトリをフォークして自前のcustomを施したものを各PCでcloneして使っています。

github.com

こんなデザインのターミナルに見覚えがありませんか?

ただ、「prezto 標準エラー出力 見れない」などと検索してもそれっぽい情報にたどり着くことができず、甘んじて受け入れていたのです。

issue発見

今日は本気で調べようと思ったので、英語で「pretzo stderr」と検索したら、あっさりと同様の不具合を訴えるissueが見つかりました。

github.com

依存していた zsh-async で、stderrを/dev/nullにマップしたままにしてしまうバグがあったようで、現在は改修されているとのことです。

そういえば、最初にforkしてから一回も本家リポジトリの最新版をpullしていないなあということに気づきました。

問題解決

2, 3年分の変更をpullして、自前のカスタムとのコンフリクトを解消しつつpreztoを最新版に置き換えることができました。

$ cd ~/.zpretzo
$ git stash
$ git pull git@github.com:sorin-ionescu/prezto.git master
$ ## コンフリクト解消
$ git commit
$ git push origin master
$ git stash pop
$ ## コンフリクト解消
$ git restore --staged
$ git stash destroy

*1

何度かコマンド実行中にCtrl-cを押して確認していますが、標準エラー出力が見えなくなる事象は発生していないような気がします。

まとめ

一度整備した開発環境を弄るのは、壊れてしまう可能性もあるので躊躇われますよね。ただ、ソフトウェアが意味もなくアップデートすることはそれほど多くありません。今回はシェルの設定に問題がありましたが、開発環境を構成する様々な要素の更新履歴を定期的に確認し、暇な時間にアップデートする習慣をつけていきます。

*1:余談ですが、git checkoutがgit switchとgit restoreに分かれていたことを最近知りました。たしかにcheckoutでできることが広すぎましたね