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
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions