mirror of
https://github.com/basecamp/once-campfire.git
synced 2026-10-08 07:40:08 +09:00
d485db6038
Each push notification carries the subscriber's unread room count as its badge. The pool built it per subscription, loading the user and counting their unread memberships: two queries for every subscriber, all in the job before the deliveries reach the threads. With 1,000 subscribed members the job spent ~180 ms and 2,000 queries there; with 5,000, a second. The pool now counts the unread rooms of a whole batch with one grouped query and hands each subscription its badge; nothing else in the notification needs the user. The queries still run before the work is posted to the threads, which run outside the Rails executor. Push::Subscription#notification still counts by itself when no badge is given, as for the test notification. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0142qgjggdJ2KDdGk7RF9Xm9
54 lines
2.0 KiB
Ruby
54 lines
2.0 KiB
Ruby
require "test_helper"
|
|
|
|
class WebPush::PoolTest < ActiveSupport::TestCase
|
|
setup do
|
|
stub_web_push_dns_resolution
|
|
@pool = Rails.configuration.x.web_push_pool
|
|
@payload = { title: "Designers", body: "Hello", path: Rails.application.routes.url_helpers.room_path(rooms(:designers)) }
|
|
end
|
|
|
|
test "every notification carries its subscriber's unread room count as the badge" do
|
|
memberships(:jason_pets).update! unread_at: Time.current
|
|
memberships(:kevin_hq).update! unread_at: Time.current
|
|
memberships(:kevin_david_and_kevin).update! unread_at: Time.current
|
|
subscriptions = Push::Subscription.where(user: users(:david, :jason, :kevin))
|
|
expected = subscriptions.to_h { |subscription| [ subscription.endpoint, subscription.user.memberships.unread.count ] }
|
|
assert_equal [ 0, 1, 2 ], expected.values.sort
|
|
|
|
badges = Concurrent::Hash.new
|
|
WebPush.stubs(:payload_send).with { |options| badges[options[:endpoint]] = JSON.parse(options[:message]).dig("options", "data", "badge") }
|
|
@pool.queue(@payload, subscriptions)
|
|
wait_for_deliveries(3)
|
|
|
|
assert_equal expected, badges
|
|
end
|
|
|
|
test "queueing counts unread rooms per batch, not per subscription" do
|
|
WebPush.stubs(:payload_send)
|
|
|
|
two = Push::Subscription.where(user: users(:david, :jason))
|
|
four = Push::Subscription.where(user: users(:david, :jason, :jz, :kevin))
|
|
assert_equal [ 2, 4 ], [ two.count, four.count ]
|
|
|
|
queries_for_two = count_queries { @pool.queue(@payload, two) }
|
|
queries_for_four = count_queries { @pool.queue(@payload, four) }
|
|
wait_for_deliveries(6)
|
|
|
|
assert_equal queries_for_two, queries_for_four
|
|
end
|
|
|
|
private
|
|
def wait_for_deliveries(count)
|
|
Timeout.timeout(2) { sleep 0.01 while @pool.delivery_pool.completed_task_count < count }
|
|
end
|
|
|
|
def count_queries(&block)
|
|
count = 0
|
|
counter = ->(*, payload) { count += 1 unless payload[:name] == "SCHEMA" || payload[:cached] }
|
|
ActiveRecord::Base.uncached do
|
|
ActiveSupport::Notifications.subscribed(counter, "sql.active_record", &block)
|
|
end
|
|
count
|
|
end
|
|
end
|