Skip to main content

Welcome to a Grasp lesson

Create a free account to keep studying and create your own study path.

Lesson 45

Unbounded Tables in Structured Streaming

Recommended resources

Hello! Welcome to your next lesson on Structured Streaming.

In our last session, we established the fundamental difference between full batch reprocessing and incremental processing. We learned that incremental processing, where we only compute on new or changed data, is the key to building efficient and scalable data pipelines. We also identified Spark Structured Streaming as the engine that makes this possible in Databricks.

Today, we'll dive into the core conceptual model that allows Structured Streaming to work its magic. Our learning outcome is to describe how Structured Streaming treats incoming data as an unbounded table. This single, powerful abstraction is what allows you to write simple, batch-like queries against a never-ending flow of data.

1. The Core Abstraction: A Stream as a Table

The challenge with incremental processing has always been complexity. How do you keep track of what's new? How do you handle failures? How do you manage aggregations over time?

Structured Streaming's elegant solution is to hide this complexity behind a simple model. It asks you to stop thinking about the mechanics of a "stream" and instead visualize it as a simple database table that is constantly having new rows added to it.

This is the unbounded table model.

Let's start with a resource from Databricks that introduces this idea.

Structured Streaming - Apache Spark™ Tutorial

This article from a Databricks tutorial provides a clear, high-level overview of the Structured Streaming model. It's the best place to start.

Please read the short 'Structured Streaming Overview' section. Pay close attention to the sentence that begins, 'In Structured Streaming, a data stream is treated as a table that is being continuously appended.'

The key takeaway is that you can express your computation as a standard query on a static table, and Spark will automatically run it as an incremental query on this unbounded input table.

The diagram in the article perfectly visualizes this concept. Here it is again for reference:

Structured Streaming: Data Stream as Unbounded Table
This image illustrates the core concept of Structured Streaming. A continuous data stream is conceptually treated as an unbounded table, where each new data item arriving on the stream becomes a new row appended to the table.

2. The Programming Model Explained

Now that we have the basic idea, let's get a bit more formal. The official Apache Spark documentation provides a precise definition of this programming model. It breaks the model down into three key components:

  1. Input Table: The conceptual, unbounded table representing the data stream. As new data arrives, it's appended as new rows to this table.
  2. Result Table: The table that is generated by applying your query logic (your transformations and aggregations) to the Input Table.
  3. Output: The new or updated rows from the Result Table that are written to an external system (the "sink") each time the query is triggered.

Let's read the official documentation to solidify this understanding.

Structured Streaming Programming Guide - Spark 3.5.6

The official Spark Programming Guide offers the most authoritative explanation of the Structured Streaming model.

Please read the section titled 'Programming Model', stopping at 'Handling Event-time and Late Data'. This section clearly defines the Input Table, Result Table, and Output modes.

A crucial point made in this guide is that Structured Streaming does not materialize the entire table. This is a common point of confusion. The unbounded table is a logical construct, a model to simplify development. Internally, Spark reads only the latest available data, processes it incrementally, updates the result, and then discards the source data. It only maintains the minimal state required to compute the next incremental result.

With your physics background, you can think of this as a powerful abstraction. Just as the concept of a "field" in physics provides a model to describe forces acting at a distance without having to track every individual particle interaction, the "unbounded table" model allows you to describe transformations on a stream without having to manage every micro-batch and state update yourself.


3. The Philosophy from the Creator

It's one thing to read the documentation, but it's another to hear the philosophy from the person who designed the system. This helps to understand the "why" behind the design.

Designing Structured Streaming Pipelines—How to Architect Things Right - Tathagata Das Databricks

In this video, Tathagata Das, the creator of Structured Streaming, explains the fundamental philosophy of the framework.

Please watch from 00:57 to 03:40. In the first minute, he explains the core philosophy. In the second part, he gives a concrete example of reading from Kafka and how each record becomes a row in the 'conceptual table'.

As he emphasizes, the goal is to let you, the developer, focus on the "what" (your business logic, expressed as a query) while Spark handles the "how" (the complex mechanics of incremental, fault-tolerant execution). The unbounded table is the interface that makes this separation of concerns possible.


4. How the Concept Translates to Code

So, how does this abstract model appear in the code you will write? You will see it most directly in the command used to start reading a stream: spark.readStream.

When you use spark.read (for batch processing), you are telling Spark to read a static, finite dataset. When you use spark.readStream, you are telling Spark that the source is not static and should be treated as an unbounded table.

Let's watch a short clip that shows this in a PySpark context.

Spark Structured Streaming with PySpark | DP-700 Exam Prep

This video from Aleksi Partanen gives a practical PySpark example and explicitly connects the readStream function to the unbounded table concept.

Watch from 02:04 to 05:16. The speaker contrasts batch processing with streaming and clearly states that spark.readStream treats the source as an 'unbounded table,' meaning Spark understands the data can keep growing.

This clip provides a perfect bridge from the concept we've discussed to the practical application we will explore in the next lesson. By calling spark.readStream, you are creating a DataFrame that represents this unbounded table. From that point on, you can apply transformations to that DataFrame just as you would with a batch DataFrame.


Conclusion

In this lesson, we demystified the core magic of Structured Streaming. You now understand the fundamental programming model that makes incremental processing feel as simple as batch processing.

Key Takeaways

  • The core idea of Structured Streaming is to treat a live data stream as an unbounded table that is continuously being appended with new rows.
  • This is a logical model; Spark does not actually store the entire table in memory. It's an abstraction to simplify development.
  • The model consists of an Input Table (the stream), a Result Table (the output of your query), and the Output (the changes written to a sink).
  • The spark.readStream command is the entry point for creating a DataFrame that represents this conceptual unbounded table.

Next Steps

Now that you have a firm grasp of this conceptual model, you are ready to put it into practice. In our next lesson, Read a data stream from a source (e.g., Delta table, cloud files) using spark.readStream, we will write the code to define a streaming source and create our first streaming DataFrame, turning this abstract "unbounded table" into a concrete object you can work with.