Files
once-campfire/test/lib/web_push/pool_test.rb
T
Marcello Costagliola d485db6038 Count unread rooms for push badges once per batch
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
2026-10-05 16:49:59 +02:00

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