EventReporterのペイロード構築を購読者フィルタリング後に実行
ActiveSupport::EventReporter のイベント通知処理が最適化され、購読者の有無を確認してからペイロードを構築するようになりました。この変更により、イベント発行時の不要なオーバーヘッドが削減されます。
背景
#55900 でフレームワークのログ購読者が構造化イベントを消費するよう変更された際、イベントフィルタリングの仕組みも移植されました。しかし、この実装では購読者の有無にかかわらず常にペイロードを構築してからフィルタリングを行っていたため、購読者が存在しない場合でもコストが発生していました。
ActiveSupport::Notifications では、フィルタリングによってペイロード構築を回避できる設計になっていましたが、EventReporter ではこの最適化が機能していませんでした。イベントペイロードの構築は、特に複雑なオブジェクトを含む場合にコストが高くなります。
本PRは、フィルタリング機能を実用的なものにするため、処理順序を見直しています。
技術的な変更
activesupport/lib/active_support/event_reporter.rb の notify メソッドが書き換えられ、ペイロード構築前にイベント名のみでフィルタリングを行うようになりました。
変更前:
def notify(name_or_object, payload = nil, caller_depth: 1, **kwargs)
name = resolve_name(name_or_object)
payload = resolve_payload(name_or_object, payload, **kwargs)
event = {
name: name,
payload: payload,
# ...
}
@subscribers.each do |subscriber_entry|
subscriber = subscriber_entry[:subscriber]
filter = subscriber_entry[:filter]
next if filter && !filter.call(event) # フィルタリングはペイロード構築後
subscriber.emit(event)
end
end
変更後:
def notify(name_or_object, payload = nil, caller_depth: 1, **kwargs)
name = resolve_name(name_or_object)
event = { name: name } # イベント名のみの軽量オブジェクト
subscribers = @subscribers.filter_map do |subscriber_entry|
subscriber = subscriber_entry[:subscriber]
filter = subscriber_entry[:filter]
if !filter || filter.call(event) # イベント名のみでフィルタリング
subscriber
end
end
return if subscribers.empty? # 購読者がいなければペイロード構築をスキップ
payload = resolve_payload(name_or_object, payload, **kwargs)
event = {
name: name,
payload: payload,
# ...
}
subscribers.each do |subscriber|
subscriber.emit(event)
end
end
フィルタリングに使用される event オブジェクトは :name キーのみを含むようになり、ペイロード構築は購読者が存在する場合のみ実行されます。購読者が存在しない場合は早期リターンにより、resolve_payload の呼び出しとイベントオブジェクトの完全な構築が回避されます。
設計判断
フィルタ関数に渡す情報をイベント名のみに制限する という判断が下されました。
PR作成者は、ペイロードベースのフィルタリング機能(例: event[:payload].is_a?(AuditEvent))を削除することは技術的には破壊的変更と認めつつも、現状の実装では実質的なオーバーヘッド削減にならないため意味がないと判断しています。CHANGELOGにも「フィルタ中は :name キーのみが利用可能で、ペイロード全体は利用できない」と明記されています。
この制約により、フィルタリングは常に軽量な文字列比較で済むようになり、ActiveSupport::Notifications と同様の最適化パターンが実現します。ペイロード内容に基づくフィルタリングが必要な場合は、購読者側の emit メソッド内で判断することになります。
まとめ
本PRは、イベント通知の処理順序を「ペイロード構築→フィルタリング→配信」から「フィルタリング→ペイロード構築→配信」に変更することで、不要なペイロード構築コストを削減しています。フィルタリング機能の制約(イベント名のみ)は破壊的変更ですが、実用性のない機能を削除することで、ActiveSupport::Notifications と同等の効率性を実現しています。