Build a lambda architecture in Tinybird

Use this alternative processing pattern when the typical Tinybird flow doesn't fit.

The typical flow is Data Source --> incremental transformation through Materialized Views --> API Endpoint publication. When Materialized Views don't fit your processing requirements, use Copy Pipes to create intermediate Data Sources that keep your Endpoints performant.

The ideal Tinybird flow

You ingest data (usually streamed in, but can also be in batch), transform it using SQL, and serve the results of the queries via parameterizable Endpoints. Tinybird provides freshness, low latency, and high concurrency: Your data is ready to be queried as soon as it arrives.

Data flow with Data Source and Endpoint
Data flow with Data Source and Endpoint

Sometimes, transforming the data at query time isn't ideal. Some operations - doing aggregations, or extracting fields from JSON - are better if done at ingest time, then you can query that prepared data. Materialized Views are perfect for this kind of situation. They're triggered at ingest time and create intermediate tables (data sources in Tinybird lingo) to keep your Endpoints performance super efficient.

Data flow with Data Source, MV, and Endpoint
Data flow with Data Source, MV, and Endpoint

The best practice for this approach is usually having a Materialized View (MV) per use case:

Materialized Views for different use cases
Materialized Views for different use cases

If your use case fits in these first two paragraphs, stop reading. No need to over-engineer it.

When the ideal flow isn't enough

There are some cases where you may need intermediate Data Sources (tables) and Materialized Views don't fit.

  • Most common: Things like Window Functions where you need to check the whole table to make calculations.
  • Fairly common: Needing an Aggregation MV over a deduplication table (ReplacingMergeTree).
  • Scenarios where Materialized Views fit but aren't super efficient (hey uniqState).
  • And lastly, one of the hardest problems in syncing OLTP and OLAP databases: Change data capture (CDC).

Want to know more about why Materialized Views don't work in these cases? Read the docs.

For example, consider the Aggregation Materialized Views over deduplication DS scenario.

Deduplication in Tinybird happens asynchronously, during merges, which you can't force in Tinybird. That's why you always have to add FINAL or the -Merge combinator when querying.

Materialized Views only see the block of data being processed. When materializing an aggregation, the Materialized View processes any new row, whether it has a new or duplicate ID. That's why this pattern fails.

Aggregating MV over deduplication DS doesn't work as expected

Solution: Use an alternative architecture with Copy Pipes

Tinybird has another kind of Pipe that helps here: Copy Pipes.

At a high level, they're a helpful INSERT INTO SELECT, and they can be set to execute following a cron expression. You write your query, and (either on a recurring basis or on demand), the Copy Pipe appends the result in a different table.

So, in this example, you can have a clean, deduplicated snapshot of your data, with the correct Sorting Keys, and can use it to materialize:

Copy Pipes to the rescue

Avoid loss of freshness

"But if you recreate the snapshot every hour, day, or another interval, aren't you losing freshness?" Yes. That's when the lambda architecture comes into play:

Lambda/Kappa Architecture

Combine the already-prepared data with the same operations over the fresh data being ingested at that moment. This provides higher performance despite complex logic over both fresh and old data.

Next steps

Updated