Skip to content


Released: February 25, 2022

Aggregate after clause

This release fixes and enhances support for the after clause in aggregates, which is used to restrict aggregated values to those that were written after a given timestamp.

Correct variable scope

In a cross-event aggregate with an after clause, e.g.:

event purchase
pswd_change := LatestTime<change_password>(by acct_id)
num_logins := Count<login>(by acct_id after pswd_change)

the Scowl compiler incorrecly looked for pswd_change in the scope of the writing event (i.e. login).

The compiler now correctly looks for pswd_change in the scope of the reading event (i.e. purchase).

Support in Latest

Previously, an after clause in a Latest aggregate, e.g.:

Latest<login>(ip after pswd_change last 10 minutes)

was not being honored.

Now, in the example above, the feature returns null if there was a login in the last 10 minutes, but it occured before the pswd_change timestamp.

Enforce no after in approximate aggregates

Approximate aggregates cannot support an after clause, because they do not maintain sufficient state to compute the correct value.

Now, attempting to use an after clause in an approximate aggregate results in a compile-time error.

DecayedAverage(rate after login_time last 10 days)
1:1-1:51: after clause is not supported by DecayedAverage

Null aggregate keys

This release changes the semantics of aggregate reads with null keys, e.g.:

zipcode := Int(null)
Average(rate by zipcode)

New Semantics

Previously, null was treated as a valid group-by key. In the example above, all events with a null zipcode would have their rates averaged.

Now, if the key is null, no value is written to the database, and at read-time the aggregate immediately returns null without querying the database.

Motivation and Impact

  • Reduce the incidence of performance degradation from inadvertently introducing null as a hot key
  • Eliminate the need to write where key is not null to achieve the more common default behavior

Achieving old behavior

If grouping by null is something you really want to do, you can always coalesce the value to some default value first like "<unknown>", e.g.

zipcode_key := IfNull(zipcode, -1)
Average(rate by zipcode_key)

Faster Scowl compilation time

The Scowl compiler now compiles multi-event topologies must faster.

The impact is:

  • Faster branch list load times in the "Scowl Editor" UI
  • A ~100ms latency reduction in Lambda cold-start times. (With steady traffic, cold starts are rare, anyway.)