{"id":9397,"date":"2026-09-21T09:33:24","date_gmt":"2026-09-21T09:33:24","guid":{"rendered":"https:\/\/prwatech.in\/blog\/?p=9397"},"modified":"2026-09-22T15:10:42","modified_gmt":"2026-09-22T15:10:42","slug":"pubsub-stream-processing-with-dataflow","status":"publish","type":"post","link":"https:\/\/prwatech.in\/blog\/google-cloud-platform\/dataflow\/pubsub-stream-processing-with-dataflow\/","title":{"rendered":"Pubsub stream processing with dataflow"},"content":{"rendered":"\r\n<h1>Streaming Pub\/Sub Messages to Storage With Dataflow<\/h1>\r\n<p class=\"wp-block-paragraph\">This builds a streaming pipeline, one that runs continuously rather than once on a fixed dataset, reading messages published to a Pub\/Sub topic and writing them into Cloud Storage in timed batches. It is a different shape of problem from the batch ETL pipeline covered in the ETL Processing With Dataflow and BigQuery guide in this series, which runs once against a file that already exists. Here, the pipeline keeps running and keeps writing new output as new messages arrive.<\/p>\r\n<p>This walks through publishing test messages on a schedule, running the streaming pipeline against them, watching it write output to a bucket, and shutting everything down again.<\/p>\r\n<h2>Step by Step Process of Streaming Pub\/Sub Messages<\/h2>\r\n<h2>Step 1: Activate Cloud Shell<\/h2>\r\n<p>If a permission error comes up later, grant the Owner role to the default compute service account before continuing.<\/p>\r\n<h2>Step 2: Set Your Variables<\/h2>\r\n<p>BUCKET_NAME=your-bucket-name<br \/>PROJECT_ID=$(gcloud config get-value project)<br \/>TOPIC_ID=your-topic-id<br \/>REGION=your-dataflow-region<\/p>\r\n<h3>Choose a Dataflow region close to wherever you are actually running these commands from.<\/h3>\r\n\r\n\r\n\r\n<figure class=\"wp-block-image size-large is-resized\"><img loading=\"lazy\" decoding=\"async\" class=\"wp-image-9398\" src=\"https:\/\/prwatech.in\/blog\/wp-content\/uploads\/2021\/05\/image-648.png\" alt=\"\" width=\"866\" height=\"88\" srcset=\"https:\/\/prwatech.in\/blog\/wp-content\/uploads\/2021\/05\/image-648.png 628w, https:\/\/prwatech.in\/blog\/wp-content\/uploads\/2021\/05\/image-648-300x31.png 300w\" sizes=\"auto, (max-width: 866px) 100vw, 866px\" \/><\/figure>\r\n\r\n\r\n\r\n<h3>Step 3: Create a Cloud Scheduler Job<\/h3>\r\n<p class=\"wp-block-paragraph\">This publishes a test message to your Pub\/Sub topic once a minute:<\/p>\r\n<p>gcloud scheduler jobs create pubsub publisher-job \\<br \/>&#8211;schedule=&#8221;* * * * *&#8221; \\<br \/>&#8211;topic=$TOPIC_ID &#8211;message-body=&#8221;Hello!&#8221;<\/p>\r\n\r\n\r\n\r\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"628\" height=\"259\" class=\"wp-image-9399\" src=\"https:\/\/prwatech.in\/blog\/wp-content\/uploads\/2021\/05\/image-649.png\" alt=\"\" srcset=\"https:\/\/prwatech.in\/blog\/wp-content\/uploads\/2021\/05\/image-649.png 628w, https:\/\/prwatech.in\/blog\/wp-content\/uploads\/2021\/05\/image-649-300x124.png 300w\" sizes=\"auto, (max-width: 628px) 100vw, 628px\" \/><\/figure>\r\n\r\n\r\n\r\n<h3>Step 3: Start the Job<\/h3>\r\n<p class=\"wp-block-paragraph\">gcloud scheduler jobs run publisher-job<\/p>\r\n\r\n\r\n\r\n<figure class=\"wp-block-image size-large is-resized\"><img loading=\"lazy\" decoding=\"async\" class=\"wp-image-9400\" src=\"https:\/\/prwatech.in\/blog\/wp-content\/uploads\/2021\/05\/3-3.jpg\" alt=\"\" width=\"1033\" height=\"31\" srcset=\"https:\/\/prwatech.in\/blog\/wp-content\/uploads\/2021\/05\/3-3.jpg 970w, https:\/\/prwatech.in\/blog\/wp-content\/uploads\/2021\/05\/3-3-300x9.jpg 300w, https:\/\/prwatech.in\/blog\/wp-content\/uploads\/2021\/05\/3-3-768x24.jpg 768w\" sizes=\"auto, (max-width: 1033px) 100vw, 1033px\" \/><\/figure>\r\n\r\n\r\n\r\n<h3>Step 5: Clone the Sample Repository<\/h3>\r\n<p class=\"wp-block-paragraph\">git clone https:\/\/github.com\/GoogleCloudPlatform\/python-docs-samples.git<\/p>\r\n\r\n\r\n\r\n<figure class=\"wp-block-image size-large is-resized\"><img loading=\"lazy\" decoding=\"async\" class=\"wp-image-9401\" src=\"https:\/\/prwatech.in\/blog\/wp-content\/uploads\/2021\/05\/image-650.png\" alt=\"\" width=\"798\" height=\"88\" srcset=\"https:\/\/prwatech.in\/blog\/wp-content\/uploads\/2021\/05\/image-650.png 628w, https:\/\/prwatech.in\/blog\/wp-content\/uploads\/2021\/05\/image-650-300x33.png 300w\" sizes=\"auto, (max-width: 798px) 100vw, 798px\" \/><\/figure>\r\n\r\n\r\n\r\n<h3>Step 6: Move Into the Sample Directory<\/h3>\r\n<p class=\"wp-block-paragraph\">cd python-docs-samples\/pubsub\/streaming-analytics<\/p>\r\n\r\n\r\n\r\n<figure class=\"wp-block-image size-large is-resized\"><img loading=\"lazy\" decoding=\"async\" class=\"wp-image-9402\" src=\"https:\/\/prwatech.in\/blog\/wp-content\/uploads\/2021\/05\/image-651.png\" alt=\"\" width=\"1114\" height=\"35\" srcset=\"https:\/\/prwatech.in\/blog\/wp-content\/uploads\/2021\/05\/image-651.png 628w, https:\/\/prwatech.in\/blog\/wp-content\/uploads\/2021\/05\/image-651-300x10.png 300w\" sizes=\"auto, (max-width: 1114px) 100vw, 1114px\" \/><\/figure>\r\n\r\n\r\n\r\n<h3>Step 7: Install the Requirements<\/h3>\r\n<p class=\"wp-block-paragraph\">pip3 install -r requirements.txt<\/p>\r\n\r\n\r\n\r\n<figure class=\"wp-block-image size-large is-resized\"><img loading=\"lazy\" decoding=\"async\" class=\"wp-image-9403\" src=\"https:\/\/prwatech.in\/blog\/wp-content\/uploads\/2021\/05\/image-652.png\" alt=\"\" width=\"816\" height=\"46\" srcset=\"https:\/\/prwatech.in\/blog\/wp-content\/uploads\/2021\/05\/image-652.png 628w, https:\/\/prwatech.in\/blog\/wp-content\/uploads\/2021\/05\/image-652-300x17.png 300w\" sizes=\"auto, (max-width: 816px) 100vw, 816px\" \/><\/figure>\r\n\r\n\r\n\r\n<p class=\"wp-block-paragraph\">If a required Apache Beam version cannot be found, install the current version instead of pinning an old one. Open requirements.txt with nano requirements.txt and either remove a hard version number entirely or update it to a current release, rather than setting it back to 2.24.0.<\/p>\r\n<h3>Step 8: Edit the Script<\/h3>\r\n<p>Open the file:<\/p>\r\n<p>nano PubSubToGCS.py<\/p>\r\n<p>Replace its contents with the following, which is the corrected version of the script the live page&#8217;s copy had lost its formatting from:<\/p>\r\n<p>import argparse<br \/>from datetime import datetime<br \/>import logging<br \/>import random<br \/><br \/>from apache_beam import DoFn, GroupByKey, io, ParDo, Pipeline, PTransform, WindowInto, WithKeys<br \/>from apache_beam.options.pipeline_options import PipelineOptions<br \/>from apache_beam.transforms.window import FixedWindows<br \/><br \/><br \/>class GroupMessagesByFixedWindows(PTransform):<br \/>\u00a0\u00a0\u00a0\u00a0def __init__(self, window_size, num_shards=5):<br \/>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0self.window_size = int(window_size * 60)<br \/>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0self.num_shards = num_shards<br \/><br \/>\u00a0\u00a0\u00a0\u00a0def expand(self, pcoll):<br \/>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0return (<br \/>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0pcoll<br \/>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0| &#8220;Window into fixed intervals&#8221; &gt;&gt; WindowInto(FixedWindows(self.window_size))<br \/>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0| &#8220;Add timestamp to windowed elements&#8221; &gt;&gt; ParDo(AddTimestamp())<br \/>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0| &#8220;Add key&#8221; &gt;&gt; WithKeys(lambda _: random.randint(0, self.num_shards &#8211; 1))<br \/>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0| &#8220;Group by key&#8221; &gt;&gt; GroupByKey()<br \/>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0)<br \/><br \/><br \/>class AddTimestamp(DoFn):<br \/>\u00a0\u00a0\u00a0\u00a0def process(self, element, publish_time=DoFn.TimestampParam):<br \/>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0yield (<br \/>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0element.decode(&#8220;utf-8&#8221;),<br \/>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0datetime.utcfromtimestamp(float(publish_time)).strftime(<br \/>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0&#8220;%Y-%m-%d %H:%M:%S.%f&#8221;<br \/>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0),<br \/>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0)<br \/><br \/><br \/>class WriteToGCS(DoFn):<br \/>\u00a0\u00a0\u00a0\u00a0def __init__(self, output_path):<br \/>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0self.output_path = output_path<br \/><br \/>\u00a0\u00a0\u00a0\u00a0def process(self, key_value, window=DoFn.WindowParam):<br \/>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0ts_format = &#8220;%H:%M&#8221;<br \/>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0window_start = window.start.to_utc_datetime().strftime(ts_format)<br \/>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0window_end = window.end.to_utc_datetime().strftime(ts_format)<br \/>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0shard_id, batch = key_value<br \/>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0filename = &#8220;-&#8220;.join([self.output_path, window_start, window_end, str(shard_id)])<br \/><br \/>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0with io.gcsio.GcsIO().open(filename=filename, mode=&#8221;w&#8221;) as f:<br \/>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0for message_body, publish_time in batch:<br \/>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0f.write(f&#8221;{message_body},{publish_time}&#8221;.encode(&#8220;utf-8&#8221;))<br \/><br \/><br \/>def run(input_topic, output_path, window_size=1.0, num_shards=5, pipeline_args=None):<br \/>\u00a0\u00a0\u00a0\u00a0pipeline_options = PipelineOptions(<br \/>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0pipeline_args, streaming=True, save_main_session=True<br \/>\u00a0\u00a0\u00a0\u00a0)<br \/><br \/>\u00a0\u00a0\u00a0\u00a0with Pipeline(options=pipeline_options) as pipeline:<br \/>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0(<br \/>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0pipeline<br \/>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0| &#8220;Read from Pub\/Sub&#8221; &gt;&gt; io.ReadFromPubSub(topic=input_topic)<br \/>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0| &#8220;Window into&#8221; &gt;&gt; GroupMessagesByFixedWindows(window_size, num_shards)<br \/>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0| &#8220;Write to GCS&#8221; &gt;&gt; ParDo(WriteToGCS(output_path))<br \/>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0)<br \/><br \/><br \/>if __name__ == &#8220;__main__&#8221;:<br \/>\u00a0\u00a0\u00a0\u00a0logging.getLogger().setLevel(logging.INFO)<br \/><br \/>\u00a0\u00a0\u00a0\u00a0parser = argparse.ArgumentParser()<br \/>\u00a0\u00a0\u00a0\u00a0parser.add_argument(<br \/>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0&#8220;&#8211;input_topic&#8221;,<br \/>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0help=&#8221;The Cloud Pub\/Sub topic to read from, in the format projects\/PROJECT_ID\/topics\/TOPIC_ID.&#8221;,<br \/>\u00a0\u00a0\u00a0\u00a0)<br \/>\u00a0\u00a0\u00a0\u00a0parser.add_argument(<br \/>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0&#8220;&#8211;window_size&#8221;,<br \/>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0type=float,<br \/>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0default=1.0,<br \/>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0help=&#8221;Output file&#8217;s window size in minutes.&#8221;,<br \/>\u00a0\u00a0\u00a0\u00a0)<br \/>\u00a0\u00a0\u00a0\u00a0parser.add_argument(<br \/>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0&#8220;&#8211;output_path&#8221;,<br \/>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0help=&#8221;Path of the output GCS file including the prefix.&#8221;,<br \/>\u00a0\u00a0\u00a0\u00a0)<br \/>\u00a0\u00a0\u00a0\u00a0parser.add_argument(<br \/>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0&#8220;&#8211;num_shards&#8221;,<br \/>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0type=int,<br \/>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0default=5,<br \/>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0help=&#8221;Number of shards to use when writing windowed elements to GCS.&#8221;,<br \/>\u00a0\u00a0\u00a0\u00a0)<br \/>\u00a0\u00a0\u00a0\u00a0known_args, pipeline_args = parser.parse_known_args()<br \/><br \/>\u00a0\u00a0\u00a0\u00a0run(<br \/>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0known_args.input_topic,<br \/>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0known_args.output_path,<br \/>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0known_args.window_size,<br \/>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0known_args.num_shards,<br \/>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0pipeline_args,<\/p>\r\n\r\n\r\n\r\n<figure class=\"wp-block-image size-large is-resized\"><img loading=\"lazy\" decoding=\"async\" class=\"wp-image-9405\" src=\"https:\/\/prwatech.in\/blog\/wp-content\/uploads\/2021\/05\/12-1.jpg\" alt=\"\" width=\"1160\" height=\"48\" srcset=\"https:\/\/prwatech.in\/blog\/wp-content\/uploads\/2021\/05\/12-1-300x13.jpg 300w, https:\/\/prwatech.in\/blog\/wp-content\/uploads\/2021\/05\/12-1-768x33.jpg 768w\" sizes=\"auto, (max-width: 1160px) 100vw, 1160px\" \/><\/figure>\r\n\r\n\r\n\r\n<div class=\"wp-block-group has-black-color has-cyan-bluish-gray-background-color has-text-color has-background\">\r\n<div class=\"wp-block-group__inner-container is-layout-flow wp-block-group-is-layout-flow\">\r\n<div class=\"wp-block-group\">\r\n<div class=\"wp-block-group__inner-container is-layout-flow wp-block-group-is-layout-flow\">\r\n<div class=\"wp-block-group\">\r\n<div class=\"wp-block-group__inner-container is-layout-flow wp-block-group-is-layout-flow\">\r\n<p class=\"wp-block-paragraph\">Save and exit with control x, then y, then enter.<\/p>\r\n<\/div>\r\n<\/div>\r\n<\/div>\r\n<\/div>\r\n<\/div>\r\n<\/div>\r\n\r\n\r\n\r\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"628\" height=\"322\" class=\"wp-image-9407\" src=\"https:\/\/prwatech.in\/blog\/wp-content\/uploads\/2021\/05\/image-654.png\" alt=\"\" srcset=\"https:\/\/prwatech.in\/blog\/wp-content\/uploads\/2021\/05\/image-654.png 628w, https:\/\/prwatech.in\/blog\/wp-content\/uploads\/2021\/05\/image-654-300x154.png 300w\" sizes=\"auto, (max-width: 628px) 100vw, 628px\" \/><\/figure>\r\n\r\n\r\n\r\n<h3>Step 9: Run the Streaming Pipeline<\/h3>\r\n<p class=\"wp-block-paragraph\">python3 PubSubToGCS.py \\<br \/>&#8211;project=$PROJECT_ID \\<br \/>&#8211;region=$REGION \\<br \/>&#8211;input_topic=projects\/$PROJECT_ID\/topics\/$TOPIC_ID \\<br \/>&#8211;output_path=gs:\/\/$BUCKET_NAME\/samples\/output \\<br \/>&#8211;runner=DataflowRunner \\<br \/>&#8211;window_size=2 \\<br \/>&#8211;num_shards=2 \\<br \/>&#8211;temp_location=gs:\/\/$BUCKET_NAME\/temp<\/p>\r\n\r\n\r\n\r\n<figure class=\"wp-block-image size-large is-resized\"><img loading=\"lazy\" decoding=\"async\" class=\"wp-image-9408\" src=\"https:\/\/prwatech.in\/blog\/wp-content\/uploads\/2021\/05\/image-655.png\" alt=\"\" width=\"846\" height=\"207\" srcset=\"https:\/\/prwatech.in\/blog\/wp-content\/uploads\/2021\/05\/image-655.png 628w, https:\/\/prwatech.in\/blog\/wp-content\/uploads\/2021\/05\/image-655-300x74.png 300w\" sizes=\"auto, (max-width: 846px) 100vw, 846px\" \/><\/figure>\r\n\r\n\r\n\r\n<h3>Step 10: Watch the Job in Dataflow<\/h3>\r\n<p class=\"wp-block-paragraph\">Open the menu, then Dataflow, to see the job&#8217;s progress.<\/p>\r\n\r\n\r\n\r\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"628\" height=\"571\" class=\"wp-image-9409\" src=\"https:\/\/prwatech.in\/blog\/wp-content\/uploads\/2021\/05\/image-656.png\" alt=\"\" srcset=\"https:\/\/prwatech.in\/blog\/wp-content\/uploads\/2021\/05\/image-656.png 628w, https:\/\/prwatech.in\/blog\/wp-content\/uploads\/2021\/05\/image-656-300x273.png 300w\" sizes=\"auto, (max-width: 628px) 100vw, 628px\" \/><\/figure>\r\n\r\n\r\n\r\n<h3>Step 11: Check the Output<\/h3>\r\n<p class=\"wp-block-paragraph\">After a short wait, check the bucket for a samples folder with output inside it:<\/p>\r\n<p>gsutil ls gs:\/\/${BUCKET_NAME}\/samples\/<\/p>\r\n\r\n\r\n\r\n<figure class=\"wp-block-image size-large is-resized\"><img loading=\"lazy\" decoding=\"async\" class=\"wp-image-9410\" src=\"https:\/\/prwatech.in\/blog\/wp-content\/uploads\/2021\/05\/image-657.png\" alt=\"\" width=\"731\" height=\"246\" srcset=\"https:\/\/prwatech.in\/blog\/wp-content\/uploads\/2021\/05\/image-657.png 628w, https:\/\/prwatech.in\/blog\/wp-content\/uploads\/2021\/05\/image-657-300x101.png 300w\" sizes=\"auto, (max-width: 731px) 100vw, 731px\" \/><\/figure>\r\n\r\n\r\n\r\n<h3>Step 12: Clean Up<\/h3>\r\n<p class=\"wp-block-paragraph\">Delete the scheduler job so it stops publishing test messages:<\/p>\r\n<p>gcloud scheduler jobs delete publisher-job<\/p>\r\n\r\n\r\n\r\n<figure class=\"wp-block-image size-large is-resized\"><img loading=\"lazy\" decoding=\"async\" class=\"wp-image-9411\" src=\"https:\/\/prwatech.in\/blog\/wp-content\/uploads\/2021\/05\/image-658.png\" alt=\"\" width=\"712\" height=\"97\" srcset=\"https:\/\/prwatech.in\/blog\/wp-content\/uploads\/2021\/05\/image-658.png 628w, https:\/\/prwatech.in\/blog\/wp-content\/uploads\/2021\/05\/image-658-300x41.png 300w\" sizes=\"auto, (max-width: 712px) 100vw, 712px\" \/><\/figure>\r\n<h2>Streaming Compared to Batch Pipelines<\/h2>\r\n<p>A batch pipeline, such as the one in the ETL Processing With Dataflow and BigQuery guide, runs once against data that already exists, finishes, and stops. A streaming pipeline, like this one, starts and keeps running indefinitely, processing new data as it arrives in fixed time windows, here every two minutes as set by window_size. Streaming suits ongoing sources such as a live event feed, while batch suits a fixed file or table you already have in hand.<\/p>\r\n<h2>Common Mistakes to Avoid<\/h2>\r\n<ul>\r\n<li>Downgrading Apache Beam to an old pinned version to work around an install error. Install a current version instead, the old pin creates more problems than it solves.<\/li>\r\n<li>Copying commands with en dash characters instead of real double hyphens. Retype the flags if a command fails immediately.<\/li>\r\n<li>Leaving the Cloud Scheduler job running after you are done. It keeps publishing a message every minute until you delete it, which keeps the pipeline doing work and incurring cost.<\/li>\r\n<li>Pasting Python code from a web page without checking its indentation. Whitespace is significant in Python, and a page that reflows text can silently break it.<\/li>\r\n<\/ul>\r\n<p>That covers streaming Pub\/Sub messages into Cloud Storage with Dataflow, corrected end to end after several parts of the original had stopped working. To go further, explore <a href=\"https:\/\/prwatech.in\/gcp-training-institutes-in-bangalore\/\"><strong><b>Prwatech&#8217;s Google Cloud training<\/b><\/strong>\u00a0program<\/a>, which includes placement assistance.<\/p>\r\n","protected":false},"excerpt":{"rendered":"<p>Streaming Pub\/Sub Messages to Storage With Dataflow This builds a streaming pipeline, one that runs continuously rather than once on a fixed dataset, reading messages published to a Pub\/Sub topic and writing them into Cloud Storage in timed batches. It is a different shape of problem from the batch ETL pipeline covered in the ETL [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[1636,1],"tags":[605,699,700,984,617,683,684,685,611,1400,692,1523,1524,1525,1531,1530],"class_list":["post-9397","post","type-post","status-publish","format-standard","hentry","category-dataflow","category-google-cloud-platform","tag-gcp","tag-gcp-certification","tag-gcp-cloud-console","tag-gcp-course","tag-google-cloud","tag-google-cloud-certification","tag-google-cloud-console","tag-google-cloud-courses","tag-google-cloud-platform","tag-google-cloud-platform-tutorial","tag-google-cloud-training","tag-pubsub","tag-pubsub-google-cloud","tag-pubsub-message","tag-pubsub-to-bucket","tag-pubsub-to-gcs"],"acf":[],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v25.7 - https:\/\/yoast.com\/wordpress\/plugins\/seo\/ -->\n<title>Streaming Pub\/Sub Messages to Storage With Dataflow - Prwatech<\/title>\n<meta name=\"description\" content=\"Stream Pub\/Sub messages into Cloud Storage with Dataflow, with the Python script and commands corrected after several were badly broken. step by step process\" \/>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/prwatech.in\/blog\/google-cloud-platform\/dataflow\/pubsub-stream-processing-with-dataflow\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Streaming Pub\/Sub Messages to Storage With Dataflow - Prwatech\" \/>\n<meta property=\"og:description\" content=\"Stream Pub\/Sub messages into Cloud Storage with Dataflow, with the Python script and commands corrected after several were badly broken. step by step process\" \/>\n<meta property=\"og:url\" content=\"https:\/\/prwatech.in\/blog\/google-cloud-platform\/dataflow\/pubsub-stream-processing-with-dataflow\/\" \/>\n<meta property=\"og:site_name\" content=\"Prwatech\" \/>\n<meta property=\"article:publisher\" content=\"https:\/\/www.facebook.com\/prwatech.in\/\" \/>\n<meta property=\"article:published_time\" content=\"2026-09-21T09:33:24+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2026-09-22T15:10:42+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/prwatech.in\/blog\/wp-content\/uploads\/2021\/05\/image-648.png\" \/>\n\t<meta property=\"og:image:width\" content=\"628\" \/>\n\t<meta property=\"og:image:height\" content=\"64\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/png\" \/>\n<meta name=\"author\" content=\"Prwatech\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:creator\" content=\"@Eduprwatech\" \/>\n<meta name=\"twitter:site\" content=\"@Eduprwatech\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"Prwatech\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"7 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"WebPage\",\"@id\":\"https:\/\/prwatech.in\/blog\/google-cloud-platform\/dataflow\/pubsub-stream-processing-with-dataflow\/\",\"url\":\"https:\/\/prwatech.in\/blog\/google-cloud-platform\/dataflow\/pubsub-stream-processing-with-dataflow\/\",\"name\":\"Streaming Pub\/Sub Messages to Storage With Dataflow - Prwatech\",\"isPartOf\":{\"@id\":\"https:\/\/prwatech.in\/blog\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\/\/prwatech.in\/blog\/google-cloud-platform\/dataflow\/pubsub-stream-processing-with-dataflow\/#primaryimage\"},\"image\":{\"@id\":\"https:\/\/prwatech.in\/blog\/google-cloud-platform\/dataflow\/pubsub-stream-processing-with-dataflow\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/prwatech.in\/blog\/wp-content\/uploads\/2021\/05\/image-648.png\",\"datePublished\":\"2026-09-21T09:33:24+00:00\",\"dateModified\":\"2026-09-22T15:10:42+00:00\",\"author\":{\"@id\":\"https:\/\/prwatech.in\/blog\/#\/schema\/person\/db90baff7744090b2288bbc98fea87f3\"},\"description\":\"Stream Pub\/Sub messages into Cloud Storage with Dataflow, with the Python script and commands corrected after several were badly broken. step by step process\",\"breadcrumb\":{\"@id\":\"https:\/\/prwatech.in\/blog\/google-cloud-platform\/dataflow\/pubsub-stream-processing-with-dataflow\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/prwatech.in\/blog\/google-cloud-platform\/dataflow\/pubsub-stream-processing-with-dataflow\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/prwatech.in\/blog\/google-cloud-platform\/dataflow\/pubsub-stream-processing-with-dataflow\/#primaryimage\",\"url\":\"https:\/\/prwatech.in\/blog\/wp-content\/uploads\/2021\/05\/image-648.png\",\"contentUrl\":\"https:\/\/prwatech.in\/blog\/wp-content\/uploads\/2021\/05\/image-648.png\",\"width\":628,\"height\":64},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/prwatech.in\/blog\/google-cloud-platform\/dataflow\/pubsub-stream-processing-with-dataflow\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/prwatech.in\/blog\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Pubsub stream processing with dataflow\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/prwatech.in\/blog\/#website\",\"url\":\"https:\/\/prwatech.in\/blog\/\",\"name\":\"Prwatech\",\"description\":\"Share Ideas, Start Something Good.\",\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/prwatech.in\/blog\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":\"Person\",\"@id\":\"https:\/\/prwatech.in\/blog\/#\/schema\/person\/db90baff7744090b2288bbc98fea87f3\",\"name\":\"Prwatech\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/prwatech.in\/blog\/#\/schema\/person\/image\/\",\"url\":\"https:\/\/secure.gravatar.com\/avatar\/c00bafc1b04045f31eda917de39891456c44fa47c092b9bb6be0f860a3a30a2f?s=96&d=mm&r=g\",\"contentUrl\":\"https:\/\/secure.gravatar.com\/avatar\/c00bafc1b04045f31eda917de39891456c44fa47c092b9bb6be0f860a3a30a2f?s=96&d=mm&r=g\",\"caption\":\"Prwatech\"},\"url\":\"https:\/\/prwatech.in\/blog\/author\/prwatech123\/\"}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"Streaming Pub\/Sub Messages to Storage With Dataflow - Prwatech","description":"Stream Pub\/Sub messages into Cloud Storage with Dataflow, with the Python script and commands corrected after several were badly broken. step by step process","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/prwatech.in\/blog\/google-cloud-platform\/dataflow\/pubsub-stream-processing-with-dataflow\/","og_locale":"en_US","og_type":"article","og_title":"Streaming Pub\/Sub Messages to Storage With Dataflow - Prwatech","og_description":"Stream Pub\/Sub messages into Cloud Storage with Dataflow, with the Python script and commands corrected after several were badly broken. step by step process","og_url":"https:\/\/prwatech.in\/blog\/google-cloud-platform\/dataflow\/pubsub-stream-processing-with-dataflow\/","og_site_name":"Prwatech","article_publisher":"https:\/\/www.facebook.com\/prwatech.in\/","article_published_time":"2026-09-21T09:33:24+00:00","article_modified_time":"2026-09-22T15:10:42+00:00","og_image":[{"width":628,"height":64,"url":"https:\/\/prwatech.in\/blog\/wp-content\/uploads\/2021\/05\/image-648.png","type":"image\/png"}],"author":"Prwatech","twitter_card":"summary_large_image","twitter_creator":"@Eduprwatech","twitter_site":"@Eduprwatech","twitter_misc":{"Written by":"Prwatech","Est. reading time":"7 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"WebPage","@id":"https:\/\/prwatech.in\/blog\/google-cloud-platform\/dataflow\/pubsub-stream-processing-with-dataflow\/","url":"https:\/\/prwatech.in\/blog\/google-cloud-platform\/dataflow\/pubsub-stream-processing-with-dataflow\/","name":"Streaming Pub\/Sub Messages to Storage With Dataflow - Prwatech","isPartOf":{"@id":"https:\/\/prwatech.in\/blog\/#website"},"primaryImageOfPage":{"@id":"https:\/\/prwatech.in\/blog\/google-cloud-platform\/dataflow\/pubsub-stream-processing-with-dataflow\/#primaryimage"},"image":{"@id":"https:\/\/prwatech.in\/blog\/google-cloud-platform\/dataflow\/pubsub-stream-processing-with-dataflow\/#primaryimage"},"thumbnailUrl":"https:\/\/prwatech.in\/blog\/wp-content\/uploads\/2021\/05\/image-648.png","datePublished":"2026-09-21T09:33:24+00:00","dateModified":"2026-09-22T15:10:42+00:00","author":{"@id":"https:\/\/prwatech.in\/blog\/#\/schema\/person\/db90baff7744090b2288bbc98fea87f3"},"description":"Stream Pub\/Sub messages into Cloud Storage with Dataflow, with the Python script and commands corrected after several were badly broken. step by step process","breadcrumb":{"@id":"https:\/\/prwatech.in\/blog\/google-cloud-platform\/dataflow\/pubsub-stream-processing-with-dataflow\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/prwatech.in\/blog\/google-cloud-platform\/dataflow\/pubsub-stream-processing-with-dataflow\/"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/prwatech.in\/blog\/google-cloud-platform\/dataflow\/pubsub-stream-processing-with-dataflow\/#primaryimage","url":"https:\/\/prwatech.in\/blog\/wp-content\/uploads\/2021\/05\/image-648.png","contentUrl":"https:\/\/prwatech.in\/blog\/wp-content\/uploads\/2021\/05\/image-648.png","width":628,"height":64},{"@type":"BreadcrumbList","@id":"https:\/\/prwatech.in\/blog\/google-cloud-platform\/dataflow\/pubsub-stream-processing-with-dataflow\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/prwatech.in\/blog\/"},{"@type":"ListItem","position":2,"name":"Pubsub stream processing with dataflow"}]},{"@type":"WebSite","@id":"https:\/\/prwatech.in\/blog\/#website","url":"https:\/\/prwatech.in\/blog\/","name":"Prwatech","description":"Share Ideas, Start Something Good.","potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/prwatech.in\/blog\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":"Person","@id":"https:\/\/prwatech.in\/blog\/#\/schema\/person\/db90baff7744090b2288bbc98fea87f3","name":"Prwatech","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/prwatech.in\/blog\/#\/schema\/person\/image\/","url":"https:\/\/secure.gravatar.com\/avatar\/c00bafc1b04045f31eda917de39891456c44fa47c092b9bb6be0f860a3a30a2f?s=96&d=mm&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/c00bafc1b04045f31eda917de39891456c44fa47c092b9bb6be0f860a3a30a2f?s=96&d=mm&r=g","caption":"Prwatech"},"url":"https:\/\/prwatech.in\/blog\/author\/prwatech123\/"}]}},"_links":{"self":[{"href":"https:\/\/prwatech.in\/blog\/wp-json\/wp\/v2\/posts\/9397","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/prwatech.in\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/prwatech.in\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/prwatech.in\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/prwatech.in\/blog\/wp-json\/wp\/v2\/comments?post=9397"}],"version-history":[{"count":6,"href":"https:\/\/prwatech.in\/blog\/wp-json\/wp\/v2\/posts\/9397\/revisions"}],"predecessor-version":[{"id":11835,"href":"https:\/\/prwatech.in\/blog\/wp-json\/wp\/v2\/posts\/9397\/revisions\/11835"}],"wp:attachment":[{"href":"https:\/\/prwatech.in\/blog\/wp-json\/wp\/v2\/media?parent=9397"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/prwatech.in\/blog\/wp-json\/wp\/v2\/categories?post=9397"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/prwatech.in\/blog\/wp-json\/wp\/v2\/tags?post=9397"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}