How PostgreSQL aggregation works and how it inspired our hyperfunctions’ design
blog.timescale.com
blog.timescale.com
Particularly the part of getting logically-consistent results via different routes of computation. David Kohn says this in the blog post: "But, you have to have in-depth (and sometimes rather arcane) knowledge about each aggregate’s internals to know which ones meet the above criteria – and therefore, which ones you can re-aggregate."
I think Giarrusso addresses the internal requirements in a precise mathematical manner (though I may be wrong). Giarrusso Section 2.1 is the specific area I'm thinking, particularly Definition 2.1 and then later the discussion of the abelian group where certain binary operations must be commutative and associative. Giarrusso Equation 1 in the introducton defines externally what it means to be logically consistent.
Also, Giarrusso talks about "Self-Maintainability" where updating the continuous result only needs to look at the changes to the input. What was obvious to me from reading Giarrusso was that something simple like "AVG" is not self-maintainable unless you keep the intermediate state (sum and count) seperately. Kohn's distinction of Transition Function, Combine Function, and Final Function - together with Giarrusso's abelian group and change structure - is kind of a big deal in making arbitrary functions "continuous"-capable while being "self-maintainable".
I can see designing a [data] structure that adheres to the specific algabraic laws defined by Giarrusso's abelian group along with the 3 functions from Kohn (missing from Giarrusso). You can then feed this structure to a function that spits out two new functions for you: a normal function that can be used to calculate a result, and it's "continuous" version which only needs change to the inputs. For example, "avg()" and "avg_continuous()" can be automatically derived from a data structure of algebraically well-behaved operations.
Plus this would have a really cool Category Theory diagram for it!
David: absolutely love the animated gifs and pictures. Very well written indeed.
[0] Giarrusso et. al. https://www.researchgate.net/publication/269126515_A_theory_...
This problem also reminds me of this: https://jon.thesquareplanet.com/papers/phd-thesis.pdf
Glad you liked the GIFs! Will have to take a look at this. Thanks for sharing!
I found this to be a very common need, so I created denorm [1] for doing this in vanilla PostgreSQL. (It also does incrementally updated joins as well.)
It's a real help to instant aggregation results for large data sets.
(I expect the performance of a native implementation like Timescale to be superior. Unfortunately, I use managed database services, like RDS.)
I saw it is written in Python. Does the Python code run only when creating the sql query? Am I supposed to add the generated sql code to git?
Add the SQL DDL to whatever migration control system you use.
This is really useful for this kind of metric (distinct count) that can't be trivially aggregated. It allows generating pre-aggregated tables containing the intermediate aggregation state. Now you can compute your DAUs, WAUs and MAUs from the same daily pre-aggregates.
I wish this design was generalized in SQL. For instance, getting the same family of function for variance computation would be super useful for efficient & correct computation of confidence intervals at different levels of aggregation.
A number of the things you're looking for we've done experimentally and we'll be stabilizing over the next few releases. So we'd love some feedback while we're still able to futz with the API without making breaking changes.
But the two you're asking about are, I think, going to be covered by hyperloglog (we just reimplemented the internals with HLL++) and stats_agg family of functions, which have both 1D (which will give you avg, stddev, variance, etc) and 2D (co-variance, slope, intercept, x-intercept etc as well as all the 1D functions).
Would also love issues if you think we're missing other stuff, going to be generalizing this and want to make it useful for folks.
(NB: Post author here.)
(NB: Post author here.)
Yes! You can use any of the hyperfunction aggregates to build a continuous agg. Including sketches like the percentile approximation stuff.
With hyperloglog, it's still experimental, so you have to jump through some hoops to get it into a continuous agg as it could be dropped on next release, but once that's been stabilized it'll be very easy to use in continuous aggs. Feel free to test now, just know that it may get dropped and you'd have to re-create the continuous agg after the next release when it gets stabilized.
I was wondering if there plans for supporting Continuous Aggregates on Distributed hypertables? Its currently listed as not supported under the limitations docs page.
I think the two would fit together perfectly as it will allow users to aggregate very large tables.
There are plans to do that, I'm not sure the exact timeline, I think it's in the design phase now.
We do already support partitionwise aggregation, and this meshes very well with that, so that you're sending much, much less data back over the network between the data node and access node when you're trying to do things like percentiles. But I agree that continuous aggregate support will be an even bigger win!
Initially version will support case where the raw data is a distributed hypertable, while the cagg table is a hypertable on the access node. Then follow-up with support where the cagg table can be distributed as well.
We are simultaneously working on support for compressed continuous aggregates as well, which we are targeting the same timeframe.
In terms of creating a Hypertable (TimescaleDB table) you create the normal table first and then transform it into a Hypertable.
Can you explain why you doing this? Is this application data vs. time-series?
Sync on the other hand won't return from the transaction until the change is replicated.
For time series, generally async is acceptable, and even for regular data most people might also be fine with async (that's why it is the default). Sync is used if you absolutely aren't allowing any data loss. But because it waits for all nodes to confirm they wrote data to the disk writing will always be slower.
The problem is that if you use physical replication it applies to all data in the database (the logical resource not the physical server), so my guess is that some of the non-time series data has higher durability requirements.
(NB: Post author here)
[1]: https://docs.timescale.com/api/latest/hyperfunctions/percent...