Upgrading to Pigeon 2.0.0-rc.2 Spike in CPU usage

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

Sounds like you should open an issue on their bug tracker.

done!

1 Like