Broadway Documentation

repository·main·Indexed 25 days ago

https://github.com/elixir-broadway/broadway

A library for building concurrent and multi-stage data ingestion and processing pipelines in Elixir. Broadway provides built-in support for back-pressure, batching, and automatic acknowledgements. It supports integration with Amazon SQS via `broadway_sqs`, Apache Kafka via `broadway_kafka`, and custom GenStage producers using transformers to convert raw events into `%Broadway.Message{}` structs.

Tokens
10.6K
Snippets
25
Records
49
Agent score
82%

What's inside Broadway

  1. Overview of Broadway features

    main

    Broadway is a library for building concurrent and multi-stage data ingestion and data processing pipelines in Elixir. It leverages the Erlang VM to provide a robust pipeline architecture with the following built-in capabilities:

    • Back-pressure: Manages flow control to prevent overwhelming downstream stages.
    • Automatic acknowledgements: Handles message ACKs at the end of the pipeline.
    • Batching: Groups messages for efficient processing.
    • Fault tolerance: Ensures pipeline resilience.
    • Graceful shutdown: Allows the pipeline to finish processing before stopping.
    • Built-in testing: Provides tools for verifying pipeline logic.
    • Custom failure handling: Allows defining specific logic for failed messages.
    • Ordering and partitioning: Supports maintaining message order and distributing work.
    • Rate-limiting: Controls the speed of message processing.
    • Metrics: Provides visibility into pipeline performance.
  2. Understand the Broadway pipeline model

    main

    Broadway uses a stage-based architecture built on GenStage. Data flows through the pipeline in the following sequence:

    1. Producers: Pull data from external sources (e.g., SQS, RabbitMQ).
    2. Processors: Execute handle_message/3 and prepare_messages/2. This is where individual message transformations or calculations occur.
    3. Batchers: Group messages into batches based on a specific batcher key. Broadway creates one batcher for each unique key.
    4. Batch Processors: Execute handle_batch/4 to process the accumulated groups of messages.
  3. Implement Broadway callbacks for RabbitMQ

    main

    To process messages, implement the required handle_message/3 callback. If you need to group messages, implement the optional handle_batch/4 callback and configure batchers in start_link/1.

    Note: Batching is optional. If you do not need to group messages, you can remove the :batchers configuration and the handle_batch/4 callback. In RabbitMQ, messages are acknowledged individually and never as a batch.

    @impl true
    def handle_message(_, message, _) do
      message
      |> Message.update_data(fn data -> {data, String.to_integer(data) * 2} end)
    end
    
    @impl true
    def handle_batch(_, messages, _, _) do
      list = messages |> Enum.map(fn e -> e.data end)
      IO.inspect(list, label: "Got batch")
      messages
    end
  4. Tune Broadway pipeline performance

    main

    You can optimize your pipeline by adjusting concurrency and batching settings for each layer (producers, processors, and batchers). Key options include:

    • concurrency: Controls the number of processes in a specific layer.
    • max_demand: Limits the number of messages requested from the producer.
    • batch_size: The number of messages to collect before triggering a batch.
    • batch_timeout: The maximum time to wait before processing a partial batch.

    It is recommended to include at least a default batcher in SQS pipelines to control the frequency and size of acknowledgments to Amazon SQS, which improves cost and time efficiency.

    defmodule MyBroadway do
      use Broadway
    
      def start_link(_opts) do
        Broadway.start_link(__MODULE__,
          name: __MODULE__,
          producer: [
            # ... producer config ...
            concurrency: 10,
          ],
          processors: [
            default: [
              concurrency: 100,
              max_demand: 1,
            ]
          ],
          batchers: [
            default: [
              batch_size: 10,
              concurrency: 10,
            ]
          ]
        )
      end
    end
  5. Run a Broadway Kafka pipeline in a supervision tree

    main

    Add your Broadway module as a child to your application's supervision tree (usually in lib/my_app/application.ex). If your Broadway pipeline depends on other services (like a database), ensure it is listed after those dependencies in the children list.

    children = [
      {MyBroadway, []}
    ]
    
    Supervisor.start_link(children, strategy: :one_for_one)
  6. Configure a Broadway Cloud Pub/Sub pipeline

    main

    Define a Broadway pipeline by implementing start_link/1, handle_message/3, and handle_batch/4. Use BroadwayCloudPubSub.Producer to consume from a specific subscription.

    It is highly recommended to include a batcher in Cloud Pub/Sub pipelines to control the frequency and size of message acknowledgements, which improves cost and time efficiency.

    defmodule MyBroadway do
      use Broadway
    
      alias Broadway.Message
    
      def start_link(_opts) do
        Broadway.start_link(__MODULE__,
          name: __MODULE__,
          producer: [
            module: {
              BroadwayCloudPubSub.Producer,
              subscription: "projects/test-pubsub/subscriptions/test-subscription"
            }
          ],
          processors: [
            default: []
          ],
          batchers: [
            default: [
              batch_size: 10,
              batch_timeout: 2_000
            ]
          ]
        )
      end
    
      def handle_message(_, %Message{data: data} = message, _) do
        message
        |> Message.update_data(fn data -> String.upcase(data) end)
      end
    
      def handle_batch(_, messages, _, _) do
        list = messages |> Enum.map(fn e -> e.data end)
        IO.inspect(list, label: "Got batch of finished jobs from processors, sending ACKs to Pub/Sub as a batch.")
        messages
      end
    end
  7. Integrate Broadway with Apache Kafka

    main

    To use Broadway with Kafka, you must use the BroadwayKafka connector. This connector allows you to subscribe to one or more Kafka topics and process streams of records using Kafka's Consumer API. Each GenStage producer initialized by BroadwayKafka acts as a consumer within a registered consumer group.

    Key behavior: Pipelines built on BroadwayKafka are automatically partitioned. This ensures that all messages from the same topic/partition are always forwarded to the same processor and batch processor, preserving Kafka's ordering semantics.

  8. Set up a Google Cloud Pub/Sub project

    main

    To use Broadway with Cloud Pub/Sub, you must have a Google Cloud project, a topic, a subscription, and service account credentials. You can use the gcloud CLI to set this up.

    1. Install and authenticate gcloud:
      brew install --cask google-cloud-sdk
      gcloud auth login
    2. Create project, topic, and subscription:
      gcloud projects create test-pubsub
      gcloud pubsub topics create test-topic --project test-pubsub
      gcloud pubsub subscriptions create test-subscription --project test-pubsub --topic test-topic
    3. Create a service account and assign roles:
      gcloud iam service-accounts create test-account --project test-pubsub
      gcloud projects add-iam-policy-binding test-pubsub \
          --member serviceAccount:test-account@test-pubsub.iam.gserviceaccount.com \
          --role roles/editor
    4. Generate credentials file:
      gcloud iam service-accounts keys create credentials.json --iam-account=test-account@test-pubsub.iam.gserviceaccount.com
    5. Enable Pub/Sub API:
      gcloud services enable pubsub --project test-pubsub
  9. Run a Broadway pipeline in a supervision tree

    main

    To start your Broadway pipeline when your application boots, add the Broadway module as a child to your application's supervision tree (typically in lib/my_app/application.ex).

    Important: If your Broadway pipeline depends on other services (like a database), ensure the Broadway module is listed after those dependencies in the children list.

    children = [
      {MyBroadway, []}
    ]
    
    Supervisor.start_link(children, strategy: :one_for_one)