(+351) 21 24 10006  ·  info@bconcepts.pt
Carnaxide, Lisbon

How to calculate events per second rate in Real-Time Analytics

João Barros 22 de September de 2026 4 min read

This tutorial shows how to calculate the events-per-second (EPS) rate in Real-Time Analytics to monitor systems and alerts. Knowing the EPS helps detect spikes, bottlenecks and performance regressions immediately.

Prerequisites

  • Streaming event source (e.g.: events sent to a service such as Azure Event Hubs or Kafka).
  • Real-time storage/query compatible with KQL (e.g.: Azure Data Explorer / Kusto) or similar.
  • Basic knowledge of KQL and time windows (sliding, tumbling).

Step 1: Choose the time window and aggregation type

Decide the granularity: 1s/5s/1m windows are common. For fast spike detection use short windows (1s-5s); for trends use 1m or longer. The main aggregation is the count of events per window and, optionally, a moving average to smooth noise.

Step 2: Write the base query to count events by window (KQL example)

This query counts events in 5-second windows using bin(), and computes the rate per second by dividing by the window duration. Replace EventTime and MyTable with the actual column/table name.

MyTable
| where EventTime >= ago(10m)
| summarize count_events = count() by bin(EventTime, 5s)
| extend eps = todouble(count_events) / 5.0
| order by EventTime asc

Step 3: Calculate a moving average to reduce noise

A simple moving average (rolling average) makes the visualization steadier. Here we use the series_fir or simple moving_avg with prev and next for Kusto; if not available, use a self-join or make-series. Example with make-series for regular windows and moving_avg.

MyTable
| where EventTime >= ago(10m)
| make-series count_events = count() on EventTime in range(now(-10m), now(), 5s)
| mv-expand EventTime to typeof(datetime), count_events to typeof(long)
| extend eps = todouble(count_events) / 5.0
| serialize
| extend eps_ma = moving_avg(eps, 6)  // moving average of 6 points (~30s)

Step 4: Handle empty windows and normalize

It is common to have intervals with no events; ensure those points appear with zero value so they don't bias the average. In the example above, make-series guarantees zero points. If you use summarize+bin, add a continuous range and do a left-join.

// alternative using summarize+bin and zero fill
let times = range t from ago(10m) to now() step 5s;
let counts = MyTable
| where EventTime >= ago(10m)
| summarize c = count() by bin(EventTime, 5s);
times
| join kind=leftouter (counts) on $left.t == $right.EventTime
| extend count_events = coalesce(c, 0)
| extend eps = todouble(count_events) / 5.0

Step 5: Calculate thresholds and detect simple anomalies

Define static thresholds (e.g.: eps > 100) or dynamic ones using mean + k*stddev. Dynamic example with standard deviation to flag spikes.

// compute mean and stdev over the last 10m and flag spikes
let series = (
  MyTable
  | where EventTime >= ago(10m)
  | make-series count_events = count() on EventTime in range(now(-10m), now(), 5s)
  | mv-expand EventTime to typeof(datetime), count_events to typeof(long)
  | extend eps = todouble(count_events) / 5.0
);
let stats = series | summarize mu = avg(eps), sigma = stdev(eps);
series
| extend mu = toscalar(stats.mu), sigma = toscalar(stats.sigma)
| extend is_spike = eps > mu + 3.0 * sigma

Verify the result

Create a dashboard/visual with two series: eps and eps_ma (moving average). Verify that points with is_spike=true match visible spikes. Also confirm that periods without events show eps=0. Test with synthetic data: inject events at high rate to see detected spikes.

Conclusion

Calculating the events-per-second (EPS) rate in Real-Time Analytics enables monitoring performance and triggering alerts. Next steps: integrate this query into an alerting pipeline (e.g.: alert rules that fire when is_spike=true) or correlate EPS with infrastructure metrics. Tip: experiment with the window duration and moving average size to balance sensitivity and noise.