YAPC::Fukuoka 2025
#YAPC
HTTP Message Signatures ― HTTPクライアントの身の証を立てる
RFC 8941: Structured Field Values for HTTP
HTTPヘッダに構造化データ
ほぼネストはできない
内部リスト
パラメータ
RFC 9421: HTTP Message Signatures
ボットの識別
ActivityPubのフォローリクエスト
HTTPメッセージに電子署名
プロキシを通すとヘッダの内容は変わりうる
一部のヘッダだけを署名する
Signature-Input
何に対して署名したか
Signature
署名本体
派生要素
Content-Digest
content-bodyのダイジェスト値を表現するためのフィールド
keyid
詳細は規定されていない
ActivityPubの場合はURIを指定する
アクセスするとJSONが得られる
draftとRFCの互換性がない
Introducing RFC9111 / YAPC::Fukuoka 2025 - Speaker Deck
Web配信の技術
共有キャッシュ
RFC2616
RFC7234
RFC9111
参照RFC
プロキシサーバー
CDN
なぜ未だに7234の参照が多いのか
RFC9111は定義をはっきりさせるものとして出てきた印象がある
https://github.com/2manymws/rc
リクエストディレクティブに対する挙動は意図的に未実装
オリジンのためのキャッシュ機構として使用することを想定しているため
https://github.com/2manymws
特殊な用途のHTTP middlewareやその周辺のパッケージを提供している
GoのHTTPミドルウェアやその周辺パッケージを配布する新しいGitHub Orgをはじめました - Copy/Cut/Paste/Hatena
キャッシュの格納
キャッシュの利用
競合するディレクティブの明確化
より厳しいディレクティブが尊重されるべき
public, privateはキャッシュの格納のためのディレクティブ
Tests for HTTP Caches
https://github.com/http-tests/cache-tests
なぜインフラコードのモジュール化は難しいのか - アプリケーションコードとの本質的な違いから考える - Speaker Deck
まとめすぎ
例: terraform-aws-vpcモジュール
変数が多い
凝集度が低い
細分化しすぎ
例: terraform-google-networkモジュール
各サブモジュールが1〜2種類のリソースしか扱っていない
セットで管理するリソースが分離されていてかえって全体像が把握しづらい
結合度が高い
多段構成による問題の悪化
例: terraform-google-project-factoryモジュール
凝集度と結合度
コナーセンス
2つのモジュール間の変更に関する依存関係
オブジェクト指向プログラミングを念頭に置いている
統合強度
アプリケーションコードでは悪い設計として認識されているものが、インフラコードではまかり通っている
インフラコードとアプリケーションコードとの違い
状態の記述
処理の記述
関心ごと
抽象化のしやすさ
抽象化の目的
インフラの実装の詳細が隠蔽できない
構成のパターンを再利用するために抽象化する
ホワイトボックス的利用
設計への影響
階層化は慎重に
難しさとどう向き合うか
向き合わない
ディレクトリ分割
ファイル分割
論理的凝集ではなく機能的凝集を意識する
main.tf を避ける
ここまでやってからモジュール機能を活用する
機能的凝集を意識する
誤ったDRYに注意
レベル別の階層化
パブリックモジュールは原則として利用しない
汎用的すぎる
「データ無い! 腹立つ! 推論する!」から 「データ無い! 腹立つ! データを作る」へ チームでデータを作り、育てられるようにするまで / How can we create, use, and maintain data ourselves? - Speaker Deck
カード決済店舗名
素のコンピュータは名前を理解しない
コンピュータに推論させる
人間が見てわからないものは機械にもわからない
データがなくても困らない
必要になってからその重要性に気づく
可能性が狭まっている
研究による悪循環とは
散らばったデータには出口すらない
データが使える形で整理されていなければならない
推論させればよいのでは?
コストが高い
基盤モデルが知識として持っていない可能性が高い
推論せずに済むものは推論しないほうがいい
我々が決定論的に計算できるものをLLMに計算させるべきではない
何を目標として捉えるか
指針が定まらない
データソースを認識する
データソースを整理する
ドメインの知識が乏しいと良いデータを作るのは難しい
DuckDB
Garbage In, Garbage Out
後処理
SQLiteのダンプファイルにしておくと何かとポータビリティが高くて便利
ソフトウェア開発のベストプラクティスはデータ作りにも有効
LLM-assited "Tool Assisted"
データ作りをLLMに全部移譲すると微妙
人力を諦めない
孤独にデータを作るのをやめる
超人性が求められる
分割統治
データを作り切ってからシステムを作るのではなく併走する
「完璧」なデータセットを作りたくなる症候群
「網羅的なデータ」よりも「効くデータ」を目指す
大量に出現するデータ
マイルストーンを適切に設定する
継続的にデータを育てる
効くデータ
ユーザーにデータを評価してもらう
作ったデータは当初と違う使われかたをする可能性がある
用途の広がりを持たせられるデータが良いデータ
間違った用途に使われるとお互い不幸
勇気を持ってデータを作り分ける
チームをどう作るか
経験者がいるのが理想
小規模組織では難しいことが多い
ガイドラインをしっかり作ることで障壁を減らしていく
一貫性を保てるようにする
手順は途中で変えてもよいが判断基準は変えるとまずい
データを買ってくるという手
データは資産
データを作ることは手段であり目的ではない
なぜThrottleではなくDebounceだったのか? 700並列リクエストと戦うサーバーサイド実装のすべて
課題背景
テストの結果をがんがん収集して分析している
分散テスト実行
早くテストを終わらせるためには平均的な時間で終わるように揃える
ほぼ同時に終わる = ほぼ同時に結果が送られる
700並列でテストを実行しているお客さんが顧客になる
非同期にしたりジョブキューに入れたりしてもあんまり効果がない
Close処理はまとめられる
真のエンドとは何か
Close処理を間引く
Throttle
待ち時間後に1回だけ処理を実行する
5秒に1回
500msのdelayを持つthrottle
Debounce
待ち時間内に次のイベントが発生するとタイマーがリセットされて処理の実行が遅延する
例
GUIのWindowリサイズ
入力サジェスト
500msのdelayを持つdebounce
最後の入力から500ms何も入力されなかったので処理を実行
元ネタはハードウェアのボタンスイッチ
700並列リクエストの対応
秒間10以上のcloseが走る
重い処理が大量にある
どうするか
throttleよりもdebounceのほうが処理をまとめられる
タイミングに振れ幅がある
debounceは基本的にクライアントサイドの技術
1クライアントなら簡単
クライアントが多数
サーバーが複数台
Server-Side Debounce v1
Redisをロックオブジェクトにしたdebounce処理
値を書き込んでwaitしてから値が書き換えられていなかったら実行する
レースコンディションがあった
最後にDELしている
なぜ消すのか
消すことによって実行されたかどうかの判断ができる
対応
ロックを取る
削除時にkeyだけではなく値も一致しているときだけ消す
値の取得と削除をアトミックにやりたい
Luaスクリプト
Server-Side Debounce v2
もう少し間隔が長くてたくさん送ってくるテナントも発生してきた
10秒は超えるが1分以内に送られる
Debounceはdelay時間の調整がキモ
どうするか
Debounceで実行する処理の特性を利用する
相反する2個の実現したいことがある
重い処理なのでなるべくまとめて実行したい
ユーザーにはなるべく早く届けたい
Debounceは処理を複数回実行しても良い
処理を間引きたいのであって複数回実行してもいい
最後の呼び出しが実行されればいい
最初と最後の2回呼べばいいのでは
Redisに値が入っていなかったら即時実行する
アトミックに
SETのGETオプション
現実は厳しい
Server-Side Debounce v3
テストの量が大量なので2つに分割して送ってるテナントがいた
テスト結果が巨大
一気には送れないので2個に分割して送っている
クライアントも自分たちで作っている
ruby/ruby
最終結果がなかなか届かない
どうするか
初期割り当てよりも多くのデータを受信したときに配列を拡張するようなアプローチ
delay時間をフレキシブルにする
最初は短いdelayでdebounce
次のリクエストが来たら拡張する
間隔が長いときはv2に似た挙動になる
間隔が短いときはv1に似た挙動になる
実装
保存しておくべきデータができた
前回のリクエスト時間
前回使ったdelay
v1の実装を思い出す
UUIDの代わりにデータを入れておけばよい
サーバーをまたいだマルチスレッドプログラミング
シンプルなコードで複雑な問題に対応できると楽しい
v4を作るなら
「バイブス静的解析」でレガシーコードを分析・改善しよう - Speaker Deck
デッドコード
Perlの静的解析についておさらい
AIのコード書き換え、調査を信頼できない理由
Vibe codingの考えに則っている
正規表現を使ってソースコードの依存関係を調べて不要ファイルを列挙する
従来の静的解析との違い
静的解析だと誤動作が許容されない
プロジェクト内で使われている記法は限られている
偽陰性は許容、偽陽性は避ける
さまざまな依存の形を汎用ツール側で全て対応するのは難しい
確認や実行は人間の手にとどめておく
レポート生成機能をつけておくと便利
perldoc.jp のデッドコードを消す事例
コードを消す以外の事例
useしてないのにメソッド呼び出し
PPI版を正解データとして利用し、正規表現版を調整
実行時間を変えず精度アップ
失敗事例
ネストした構造は正規表現では正確に扱えない
PPRを使うとどうか
プロジェクト全体をPPRでパースするとPPIと同じくらい時間がかかった
コアモジュールではない
クレジットカードの不正を防止する技術 - Speaker Deck
決済電文の仕様
ISO8583
不正判定ロジック
バリデーションを順番に通して、どれか1つでも落ちたらNG
不正な決済カテゴリの判定
MCCを利用して判定
不正な加盟店の判定
MCCはアクワイアラが設定するためイシュアにコントロールできない
不正な加盟店のブロックリストを作成している
加盟店IDが時間経過で変わる
アクワイアラ・商材が変わったときに値が変わることがある
加盟店IDが重複する
複数のパラメータの組み合わせでブロックリストを構成
ルールベースの不正検知の仕組み
時間のかかる検知ロジックは非同期で実行する
不正検知ルールの運用
Redashで集計のダッシュボードを作って定期的にモニタリングしている
不正攻撃への対策
クレジットマスター攻撃
同一のBIN帯に対して決済取引を行う
特定の加盟店・アクワイアラが指定されることが多い
なりすまし攻撃
3Dセキュア
3Dセキュアの運用
100%にはできない
旧から新へ: 大規模ウェブクローラのPerlからGoへの置き換え
ラボサービスなので複雑にしない技術選定に寄せる
ページコンテンツをMySQLではなくS3に置いたのは?
凝ったクエリを書くことがまったくない
URLからコンテンツが特定できれば十分
容量が大きいわけではない
Redisのsorted setでキューを実現
スコアはクロール可能なepoch秒
page_id
クローラー
Perlではfork
Parallel::ForkManager
再キュー
あまりに時間がかかっているジョブは回収
クローラー特有の注意点
io.LimitReader
常に全体を読むことを避ける
Perlでは $ua->max_size()
x/net/html/charset.NewReader()
EUC-JPをコード中ではUTF-8として扱う
metaタグを読む
プロキシ
robots.txt
内部IPアドレスへのアクセス禁止
中間証明書のないサイト
旧実装と同じDBを触る
VARBINARYにEUC-JP & HTMLエスケープ済み文字列
移行
更新時刻を同じテーブルに書き出す
系統のコントロール
ページ単位で段階的に移行していく
切り戻しても、検出できたはずの更新が検出されないことを避けられるように
クローラの並列数を見積もる
単にSleepするだけにしてみる
canaryフラグ
共有実装の分離
クローラーにgRPCエンドポイントを追加
PerlからはOpenAPI::Clientで呼び出す
監視
OpenTelemetry
トレース