Publish to and read from Cloud Pub/Sub#

Assumes the artifacts and credentials from the Quickstart index, and the imports an IDE resolves from the Java API reference.

Publish a stream to a topic#

StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
env.setRuntimeMode(RuntimeExecutionMode.STREAMING);
env.enableCheckpointing(60_000);

env.fromData("hello", "world")
        .sinkTo(
                PubSubSink.<String>builder()
                        .topic(TopicDestination.of("my-project", "orders"))
                        .serializer(
                                PubSubSerializationSchema.payload(new SimpleStringSchema()))
                        .build());

env.execute("pubsub-sink-quickstart");

Checkpointing is not decoration: the sink is at-least-once only with it, since the checkpoint is what makes Flink flush the messages the SDK publishers are still batching.

payload(...) wraps any Flink SerializationSchema for payload-only messages. Attributes and an ordering key layer onto it with withAttributes(...) and withOrderingKey(...); a schema that needs full control returns a PubsubMessage directly.

The topic is created if it does not exist. An auto-created topic has no subscriptions, so messages published before one is attached reach nobody — which is what topic auto-creation is about. Create the subscription first when trying this.

Read a stream from a subscription#

StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
env.setRuntimeMode(RuntimeExecutionMode.STREAMING);
// Also not optional here, and for a sharper reason: the source acknowledges on checkpoint
// completion, so without checkpointing nothing is ever acknowledged and it stalls once
// the client library's flow control fills. It fails the job itself after 10 minutes of
// that rather than hanging quietly.
env.enableCheckpointing(60_000);

Source<String, ?, ?> source =
        PubSubSource.<String>builder()
                .subscription(SubscriptionDestination.of("my-project", "orders-sub"))
                .deserializer(PubSubDeserializationSchema.payload(new SimpleStringSchema()))
                .build();

env.fromSource(source, WatermarkStrategy.noWatermarks(), "pubsub").print();

env.execute("pubsub-source-quickstart");

The subscription must already exist: passing it without creation settings is the statement that it does, and the enumerator checks before assigning a split. Pub/Sub’s publish time becomes each record’s event timestamp, so a WatermarkStrategy over it is what to use instead of noWatermarks() in an event-time job.

The same thing in SQL#

Pub/Sub, BigQuery and Bigtable have table connectors; Cloud Tasks’ is tracked on #99. Put flink-sql-connector-gcp-pubsub in Flink’s lib/, or add it in the SQL client — and flink-sql-connector-gcp-bigquery beside it if the job also writes to BigQuery, since the jars are built to share a classpath:

ADD JAR '/path/to/flink-sql-connector-gcp-pubsub-1.1.0.jar';

CREATE TABLE orders (
  order_id STRING,
  amount   INT
) WITH (
  'connector' = 'pubsub',
  'project'   = 'my-project',
  'topic'     = 'orders',
  'format'    = 'json'
);

INSERT INTO orders VALUES ('a-1', 10), ('a-2', 20);

Reading is the same table definition with subscription in place of topic, and the parts of a message that are not the payload — attributes, ordering key, message id, publish time — arrive as metadata columns:

CREATE TABLE incoming_orders (
  order_id     STRING,
  amount       INT,
  publish_time TIMESTAMP_LTZ(3) METADATA FROM 'publish-time' VIRTUAL,
  WATERMARK FOR publish_time AS publish_time - INTERVAL '5' SECOND
) WITH (
  'connector'    = 'pubsub',
  'project'      = 'my-project',
  'subscription' = 'orders-sub',
  'format'       = 'json'
);

SELECT * FROM incoming_orders;

Checkpointing is a cluster setting here rather than a line of code — set execution.checkpointing.interval in flink-conf.yaml or with SET in the SQL client. It matters for exactly the reasons the two jobs above give. The full option surface is on the Pub/Sub SQL connector page.

Next#

Continue with the Pub/Sub examples by direction: DataStream source, DataStream sink, Table source, Table sink, or local development.