Work in Progress: This page is under development. Use the feedback button on the bottom right to help us improve it.

Time & Windows

Time is at the heart of stream processing. This guide covers how Laminar handles time and how to use windows effectively. Examples use the sample data.

Why Windows Matter

In batch SQL, you can aggregate over an entire table because it's finite. In streaming, data never stops arriving. Without boundaries, aggregations would never complete.

-- This would never return in streaming
SELECT COUNT(*) FROM orders
 
-- Instead, use time windows:
SELECT
  TUMBLE(interval '1 minute') as window,
  COUNT(*) as order_count
FROM orders
GROUP BY window
window_startwindow_endorder_count
10:00:0010:01:002
10:01:0010:02:002
10:02:0010:03:002

Event Time vs Processing Time

Event time: When the event actually happened (embedded in the data)

Processing time: When Laminar receives the event

Laminar uses event time by default. This matters because events can arrive late or out of order, and results should be deterministic.


Watermarks

Watermarks answer: "Have all events for this time period arrived?"

When a watermark passes a window boundary, Laminar knows that window is complete and emits the result.

Configuring Late Data Tolerance

CREATE TABLE orders (
  order_id VARCHAR,
  amount DECIMAL(10,2),
  timestamp TIMESTAMP
) WITH (
  connector = 'kafka',
  ...
  watermark_delay = '30 seconds'
);

Tumbling Windows

Fixed-size, non-overlapping windows. Every event belongs to exactly one window.

SELECT
  region,
  TUMBLE(interval '1 minute') as window,
  COUNT(*) as orders,
  SUM(amount) as revenue
FROM orders
GROUP BY region, window
regionwindow_startwindow_endordersrevenue
us-east10:00:0010:01:002350.00
us-west10:00:0010:01:00175.50
us-east10:01:0010:02:001200.00
eu-west10:01:0010:02:00150.00

Tumbling with Multiple Dimensions

Group by additional dimensions within each window.

SELECT
  region,
  status,
  TUMBLE(interval '5 minutes') as window,
  COUNT(*) as order_count,
  SUM(amount) as total_amount
FROM orders
GROUP BY region, status, window
regionstatuswindow_startwindow_endorder_counttotal_amount
us-eastcompleted10:00:0010:05:003364.99
us-eastpending10:00:0010:05:001200.00
us-westcompleted10:00:0010:05:002375.50
eu-westcancelled10:00:0010:05:00150.00

Sliding (Hop) Windows

Overlapping windows that slide forward at a regular interval. Events can belong to multiple windows.

-- HOP(slide_interval, window_size)
-- 5-minute window, sliding every 1 minute
SELECT
  TUMBLE(interval '1 minute') as window,
  HOP(interval '1 minute', interval '5 minutes') as sliding_window,
  COUNT(*) as orders,
  AVG(amount) as avg_amount
FROM orders
GROUP BY window
window_startwindow_endordersavg_amount
09:56:0010:01:002112.75
09:57:0010:02:004118.88
09:58:0010:03:006133.42

Moving Averages

Sliding windows are ideal for moving averages.

-- 5-minute moving average of metric values, updated every minute
SELECT
  name,
  HOP(interval '1 minute', interval '5 minutes') as window,
  AVG(value) as moving_avg
FROM metrics
WHERE name = 'cpu_usage'
GROUP BY name, window
namewindow_startwindow_endmoving_avg
cpu_usage09:56:0010:01:0045.50
cpu_usage09:57:0010:02:0048.83
cpu_usage09:58:0010:03:0054.70

Session Windows

Dynamic windows based on activity gaps. A session ends after a period of inactivity.

SELECT
  user_id,
  SESSION(interval '30 minutes') as session,
  MIN(timestamp) as session_start,
  MAX(timestamp) as session_end,
  COUNT(*) as page_views,
  ARRAY_AGG(page ORDER BY timestamp) as pages_visited
FROM events
GROUP BY user_id, session
user_idsession_startsession_endpage_viewspages_visited
u110:00:0510:00:454[/home, /products, /products/1, /checkout]
u210:00:1810:01:153[/home, /about, /products]
u310:01:0010:01:001[/home]

Choosing the Right Window

Use CaseWindow TypeExample
Fixed reporting intervalsTumblingHourly sales reports
Moving averagesSliding5-min avg latency
User behavior analysisSessionEngagement per visit
Real-time dashboardsTumbling (small)10-second metrics
Trend detectionSlidingAnomaly detection

Window Fields

The window object contains useful fields.

SELECT
  window.start as window_start,
  window.end as window_end,
  COUNT(*) as event_count,
  SUM(amount) as total_amount
FROM orders
GROUP BY TUMBLE(interval '5 minutes') as window
window_startwindow_endevent_counttotal_amount
10:00:0010:05:0081440.49

Combining Windows with Filters

Filter before windowing for efficiency.

SELECT
  TUMBLE(interval '1 minute') as window,
  COUNT(*) as error_count
FROM logs
WHERE level = 'ERROR'
GROUP BY window
HAVING COUNT(*) > 1
window_startwindow_enderror_count
10:01:0010:02:002

The WHERE filters to errors first, the window aggregates, then HAVING filters to windows with more than 1 error.