After upgrading from Pigeon.LegacyFCM to Pigeon.FCM, there is a massive spike in CPU usage when sending push notifications to 7000+ devices.
Here is the new Pigeon 2 code:
defp do_send_push_message(client_devices, message) when is_list(client_devices) do
tokens =
client_devices
|> Enum.map(fn x ->
x.firebase_token
end)
|> Util.compact()
chunked_tokens = Enum.chunk_every(tokens, 500)
Enum.each(chunked_tokens, fn chunk ->
Logger.info("chunk length #{inspect(%{attributes: length(chunk)})}")
# dbg(chunk)
data_map = %{
"deep_link_route" => message.deep_link_route,
# "short_url" => message.short_url,
"media_type" => message.media_type,
"org_id" => message.org_id,
"thumbnail_path" => message.thumbnail_path,
"thumbnail_url" => message.thumbnail_url
}
Enum.each(chunk, fn x ->
n =
Pigeon.FCM.Notification.new(
{:token, x},
%{"title" => message.title, "body" => message.message},
data_map
)
# dbg(n)
push = FaithfulWord.FCM.push(n)
# dbg(push)
end)
end)
end
Previously we would use the convenient handle_push()
update()
and remove()
found in LegacyFCM, but they are no longer there.
Any suggestions as to what I’m doing wrong here?
Here is the previous code that would not cause a cpu spike:
def send_push_message_media_music_now(
push_message: %PushMessage{} = message,
media_music_uuid: media_music_uuid,
org_id: org_id
) do
case Repo.get_by(MediaMusic, uuid: media_music_uuid) do
nil ->
Logger.info(
"could not find media music with id: #{inspect(%{attributes: media_music_uuid})}"
)
media_music ->
Logger.info("found media music with id: #{inspect(%{attributes: media_music_uuid})}")
case Ecto.Query.from(c in ClientDevice, where: c.org_id == ^org_id) |> Repo.all() do
nil ->
Logger.info(
"could not find client devices with org_id: #{inspect(%{attributes: org_id})}"
)
client_devices ->
# do_send_push_message(client_devices, message)
tokens =
Enum.map(client_devices, fn x ->
x.firebase_token
end)
chunked_tokens = Enum.chunk_every(tokens, 500)
Enum.each(chunked_tokens, fn chunk ->
Logger.info("chunk length #{inspect(%{attributes: length(chunk)})}")
chunk
|> Pigeon.LegacyFCM.Notification.new()
|> Pigeon.LegacyFCM.Notification.put_notification(%{
"title" => message.title,
"body" => message.message
})
|> Pigeon.LegacyFCM.Notification.put_data(%{
"push_message_uuid" => message.uuid,
"deep_link_route" => message.deep_link_route,
"short_url" => message.short_url,
"media_type" => message.media_type,
"media_format" => media_music.media_format,
"media_path" => message.media_path,
"media_url" => message.media_url,
"media_id" => message.media_id,
"thumbnail_path" => message.thumbnail_path,
"thumbnail_url" => message.thumbnail_url,
"mutable-content" => true,
"version" => "1.3"
})
|> Pigeon.LegacyFCM.Notification.put_mutable_content(true)
|> Pigeon.LegacyFCM.Notification.put_content_available(true)
|> Pigeon.LegacyFCM.push(on_response: &handle_push/1)
end)
end
end
end
def handle_push(%Pigeon.LegacyFCM.Notification{status: :success} = notif) do
to_update = Pigeon.LegacyFCM.Notification.update?(notif)
Logger.info("handle_push to_update #{inspect(%{attributes: to_update})}")
to_remove = Pigeon.LegacyFCM.Notification.remove?(notif)
Logger.info("handle_push to_remove #{inspect(%{attributes: to_remove})}")
Enum.each(to_remove, fn remove_token ->
case get_client_device_for_token(remove_token) do
nil ->
{:error, "invalid_client_device_token"}
remove_device ->
Repo.delete(remove_device)
Logger.info(fn ->
"Device #{remove_device.id} for token #{remove_device.firebase_token} has been removed"
end)
{:ok, remove_token}
end
end)
# do the reg ID update and deletes
end
def handle_push(%Pigeon.LegacyFCM.Notification{status: other_error}) do
# some other error happened
Logger.info("handle_push other_error #{inspect(%{attributes: other_error})}")
end