Skip to content

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

@mazz

Description

@mazz

Environment

  • Elixir & Erlang/OTP versions (elixir --version): elixir:1.14.4-erlang-25.3-alpine-3.17.2
  • Operating system: ubuntu 18.04
  • Pigeon version: 2.0.0.rc2

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:

  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)})}")

              data_map = %{
                "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"
              }

              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
    end
  end

Previously we would use the convenient handle_push() update() remove() put_mutable_content() put_content_available() 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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions