HackerReposA FIELD GUIDE TO OPEN SOURCE
ProSponsor the shelf♧ 0
← Back to top all-time
TimelyDataflow
differential-dataflow
Rust2019–2026
TimelyDataflow / differential-dataflow

differential-dataflow

An implementation of differential dataflow using timely dataflow on Rust.

24Distinct people
37Mentions
0Explicit recommenders
3KGitHub stars
REPOSITORY AT A GLANCE

What to know before exploring

What it is

An implementation of differential dataflow using timely dataflow on Rust.

GitHub

Where it is used

No documented product use was verified in the last source check.

What people are saying

No recommendation phrase was found in the indexed comments.

No classified warning is attached to this page. Read the discussions before treating silence as approval.

Also in these discussions

Mentioned in the same Hacker News threads. That is not proof they are substitutes.

This week on Hacker News

Nov 7–Nov 14 UTC · Direct repository links and separately labeled related project stories. Indexed through 11/14/2026, 11:56:56 AM UTC.

No direct repository links were found in the indexed HN discussions over the past seven days. Older discussions are available below.

Mentions over time

HN mentions by year37 dated mentions

Observed mentions: 2019-03-11 00:45:13 UTC to 2026-07-14 04:51:30 UTC.

0369122019-01-01 00:00:00 UTC to 2020-01-01 00:00:00 UTC (exclusive): 4 mentions; 1 submissions, 3 comments20192020-01-01 00:00:00 UTC to 2021-01-01 00:00:00 UTC (exclusive): 12 mentions; 0 submissions, 12 comments2021-01-01 00:00:00 UTC to 2022-01-01 00:00:00 UTC (exclusive): 12 mentions; 0 submissions, 12 comments2022-01-01 00:00:00 UTC to 2023-01-01 00:00:00 UTC (exclusive): 3 mentions; 0 submissions, 3 comments20222023-01-01 00:00:00 UTC to 2024-01-01 00:00:00 UTC (exclusive): 3 mentions; 0 submissions, 3 comments2024-01-01 00:00:00 UTC to 2025-01-01 00:00:00 UTC (exclusive): 1 mentions; 0 submissions, 1 comments2025-01-01 00:00:00 UTC to 2026-01-01 00:00:00 UTC (exclusive): 0 mentions; 0 submissions, 0 comments2026-01-01 00:00:00 UTC to 2027-01-01 00:00:00 UTC (exclusive): 2 mentions; 1 submissions, 1 comments. Incomplete calendar period.2026*

* An incomplete calendar period is marked with an outlined bar. Its count is not directly comparable with a full period.

Counts come from the repository’s dated source evidence. Index cutoff: 2026-11-02 08:16:59 UTC.

View mention counts and date ranges
UTC ranges include their start and exclude their end. Select a period to filter the source evidence.
PeriodStart (UTC)End (exclusive, UTC)MentionsSubmissionsComments
20192019-01-01 00:00:00 UTC2020-01-01 00:00:00 UTC413
20202020-01-01 00:00:00 UTC2021-01-01 00:00:00 UTC12012
20212021-01-01 00:00:00 UTC2022-01-01 00:00:00 UTC12012
20222022-01-01 00:00:00 UTC2023-01-01 00:00:00 UTC303
20232023-01-01 00:00:00 UTC2024-01-01 00:00:00 UTC303
20242024-01-01 00:00:00 UTC2025-01-01 00:00:00 UTC101
20252025-01-01 00:00:00 UTC2026-01-01 00:00:00 UTC000
2026 (partial)2026-01-01 00:00:00 UTC2027-01-01 00:00:00 UTC211

From the project README

An excerpt from the repository, fetched Nov 13, 2026.

Read the project’s introduction
Differential Dataflow An implementation of differential dataflow over timely dataflow on Rust. Background Differential dataflow is a data-parallel programming framework designed to efficiently process large volumes of data and to quickly respond to arbitrary changes in input collections. You can read more in the differential dataflow mdbook and in the differential dataflow documentation. Differential dataflow programs are written as functional transformations of collections of data, using familiar operators like map, filter, join, and reduce. Differential dataflow also includes more exotic operators such as iterate, which repeatedly applies a differential dataflow fragment to a collection. The programs are compiled down to timely dataflow computations. For example, here is a differential dataflow fragment to compute the out-degree distribution of a directed graph (for each degree, the number of nodes with that many outgoing edges): let out_degr_dist = edges.map(|(src, _dst)| src) // extract source .count() // count occurrences of source .map(|(_src, deg)| deg) // extract degree .count(); // count occurrences of degree Alternately, here is a fragment that computes the set of nodes reachable from a set roots of starting nodes: let reachable = roots.iterate(|scope, reach| edges.enter(scope) .semijoin(reach) .map(|(src, dst)| dst) .concat(reach) .distinct() ) Once written, a differential dataflow responds to arbitrary changes to its initially empty input collections, reporting the corresponding changes to each of its output collections. Differential dataflow can react quickly because it only acts where changes in collections occur, and does no work elsewhere. In the examples above, we can add to and remove from edges, dynamically altering the graph, and get immediate feedback on how the results change: if the degree distribution shifts we'll see the changes, and if nodes are now (or no longer) reachable we'll hear about that too. We could also add to and remove from roots, more fundamentally altering the reachability query itself. Be sure to check out the differential dataflow documentation, which is continually improving. An example: counting degrees in a graph. Let's check out that out-degree distribution computation, to get a sense for how differential dataflow actually works. This example is examples/hello.rs in this repository, if you'd like to follow along. A graph is a collection of pairs (Node, Node), and one standard analysis is to determine the number of times each Node occurs in the first position, its "degree". The number of nodes with each degree is a helpful graph statistic. To determine the out-degree distribution, we create a new timely dataflow scope in which we describe our computation and how we plan to interact with it. // create a degree counting differential dataflow let (mut input, probe) = worker.dataflow(|scope| { // create edge input, count a few ways. let (input, edges) = scope.new_collection(); let out_degr_distr = edges.map(|(src, _dst)| src) // extract source .count() // count occurrences of source .map(|(_src, deg)| deg) // extract degree .count(); // count occurrences of degree // show us something about the collection, notice when done. let probe = out_degr_distr .inspect(|x| println!("observed: {:?}", x)) .probe(); (input, probe) }); The input and probe we return are how we get data into the dataflow, and how we notice when some amount of computation is complete. These are timely dataflow idioms, and we won't get in to them in more detail here (check out the timely dataflow repository). If we feed this computation with some random graph data, say fifty random edges among ten nodes, we get output like Echidnatron% cargo run --release --example hello -- 10 50 1 inspect Finished release [optimized + debuginfo] target(s) in 0.05s Running `target/release/examples/hello 10 50 1 inspect` observed: ((3, 1), 0, 1) observed: ((4, 2), 0, 1) observed: ((5, 4), 0, 1) observed: ((6, 2), 0, 1) observed: ((7, 1), 0, 1) round 0 finished after 772.464µs (loading) This shows us the records that passed the inspect operator, revealing the contents of the collection: there are five distinct degrees, three through seven. The records have the form ((degree, count), time, delta) where the time field says this is the first round of data, and the delta field tells us that each record is coming into existence. If the corresponding record were departing the collection, it would be a negative number. Let's update the input by removing one edge and adding a new random edge: observed: ((2, 1), 1, 1) observed: ((3, 1), 1, -1) observed: ((7, 1), 1, -1) observed: ((8, 1), 1, 1) round 1 finished after 149.701µs We see here some changes! Those degree three and seven nodes have been replaced by degree two and eight nodes; looks like one node lost an edge and gave it to the other! How about a few more changes? round 2 finished after 127.444µs round 3 finished after 100.628µs round 4 finished after 130.609µs observed: ((5, 3), 5, 1) observed: ((5, 4), 5, -1) observed: ((6, 2), 5, -1) observed: ((6, 3), 5, 1) observed: ((7, 1), 5, 1) observed: ((8, 1), 5, -1) round 5 finished after 161.82µs Well a few weird things happen here. First, rounds 2, 3, and 4 don't print anything. Seriously? It turns out that the random changes we made didn't affect any of the degree counts, we moved edges between nodes, preserving degrees. It can happen. The second weird thing is that in round 5, with only two edge changes we have six changes in the output! It turns out we can have up to eight. The degree eight gets turned back into a seven, and a five gets turned into a six. But: going from five to six changes the count for each, and each change requires two record differences. Eight and seven wer
Read the full README on GitHub

What Hacker News says

37 sources
I don't want to rain on discovering a genuinely cool bit of theory, but it's not "new" per se. The equivalence with Einstein summation was noted in this paper [0]. Sandia labs wrote an SQL database based around graphBLAS called TenSQL a few years back. And something similar to your idea of relational algebra as the basis for AI had a paper published earlier this year [1], naming it Tensor Logic. Are all of these common operations secretly relational, just with the wrong data model? Sadly not, but you can get a long way before you find the limits. Modern databases are very well optimized for their use cases, but there's many other possible points in the design space to explore. If you want to really make efficient use of hardware, there are fundamental scaling problems with natural joins because of the combinatorial explosion that giv
Read the rest
es us WCO though. If you accept an inability to express triangular queries, you can get something that's embarrassingly parallel and scales really flipping well. If you want to stick to the full relational model, you might be interested in Differential Dataflow [2]. [0] https://arxiv.org/html/2510.12269v3 [1] https://arxiv.org/html/2601.17188v1 [2] https://github.com/TimelyDataflow/differential-dataflow
In Show HN: I implemented a neural network in SQL
I'm looking for this but can't find it, how does this project compare to differential dataflow? As a sibling commenter mentioned, it's built on timely dataflow (which is lower-level), but that already has differential dataflow[0] built on top of it by the same authors. How do they differ? (btw. I think dataflow is very cool as a computing model (not just timely dataflow), to the point of building OctoSQL[1] around it, so I'm really curious about the details here) [0]: https://github.com/TimelyDataflow/differential-dataflow [1]: https://github.com/cube2222/octosql
In Hydroflow: Dataflow Runtime in Rust
Sure! So first, being in C/asm/.. is not incompatible with what i say: i'm saying that some of these are not hand-written, but are generated. So off the top of my head: - FFTW: an ocaml synthesizer for fft kernels from a fourier-transform DSL - basically all SQL engines, in particular the "query planner" part, which compiles an SQL query (functional spec based on the theory of relational algebra) into an imperative program (eg https://sqlite.org/opcode.html) - https://github.com/mit-plv/fiat-crypto, verified crypto routines - https://www.flopoco.org/, float cores for fpga - https://faust.grame.fr/, audio synthesis from a functional stream-based language - tensorflow and other "static graph" neural-whatever-matrix-multiplication-libs (https://ocaml.xyz/tutorial/cgraph.html) - https://github.com/timelydataflow/differential-dataflow, increme
Read the rest
ntal dataflow computations edit: i almost forgot all the openssl perl scripts, even if it can be seen more as macro-expansion than actual meta-programing/program synthesis: https://github.com/search?l=Perl&q=repo%3Aopenssl%2Fopenssl+... Basically all crypto libs use at least some form of compile-time execution to generate constants, offsets, tables, .. edit2: also svelte, a "virtual virtual" dom js thingy, never looked much into it, but afaik they do compile-time diffing, and thus generate imperative code (ie ugly old-school efficient spaghetti) from react-like vdom code.
In Program Synthesis is Possible (2018)
hi! sujay from convex here. I remember reading about your "reverse query engine" when we were getting started last year and really liking that framing of the broadcast problem here. as james mentions, we entirely re-run the javascript function whenever we detect any of its inputs change. incrementality at this layer would be very difficult, since we're dealing with a general purpose programming language. also, since we fully sandbox and determinize these javascript "queries," the majority of the cost is in accessing the database. eventually, I'd like to explore "reverse query execution" on the boundary between javascript and the underlying data using an approach like differential dataflow [1]. the materialize folks [2] have made a lot of progress applying it for OLAP and readyset [3] is using similar techniques for OLTP. [1] https://githu
Read the rest
In Convex vs. Firebase
I question the premise that MapReduce really ever went away. Many migrated away from Hadoop, but in frameworks that succeeded it, MapReduce was still a core pattern. And in some cases, moving away from Hadoop wasn't ideal because later frameworks still got some things wrong. Maybe we stopped talking about MapReduce because we were focused on new patterns and challenges -- how to support many complex jobs and pipelines, more interactive and exploratory analysis, etc. I'm curious about the difference between "continuous MapReduce" and I guess a subgraph in a "differential dataflow" (which I have read about but never really used). https://github.com/TimelyDataflow/differential-dataflow
In MapReduce is making a comeback
You can also inject pull queries at some intermediate location into the dataflow, either in the form of a Dirac-delta for a instant-in-time query, or persisting to receive push updates for the query results. [0] makes it fairly easy to handle, via, say, inserting the persistent queries into a native table of it (which don't typically get persisted, though that could have changed in the past few months), and the keep-alive messages from the viewer bump the expire-at field in the query entry in the table. [1] is an example of how exactly the internal dataflow of such "demand-driven push" works, though it omits the expiry handling you'd want, and doesn't detail the interaction with the outside needed for live feeds. What this doesn't emphasize is that due to the timestamping and consistent nature of the underlying dataflow engine, you can se
Read the rest
e when exactly the new query is in your output stream, and also e.g. bundle multiple queries together into one atomic transaction, to not get any tearing-style output/display artifacts. The underlying Rust framework Differential Dataflow[2] is even more powerful, but also far less easy to use, due to the lack of a query optimizer. It arguably makes up by already supporting recursive/iterative computation and multi-temporal timestamps. [0]: https://materialize.com/a-simple-and-efficient-real-time-app... [1]: https://materialize.com/lateral-joins-and-demand-driven-quer... [2]: https://github.com/TimelyDataflow/differential-dataflow
In Searching for a better persistent cache
> In the simplest case, I'm talking about regular SQL non-materialized views which are essentially inlined. I see that now -- makes sense! > Wish we had some better database primitives to assemble rather than building everything on Postgres - its not ideal for a lot of things. I'm curious to hear more about this! We agree that better primitives are required and that's why Materialize is written in Rust using using TimelyDataflow[1] and DifferentialDataflow[2] (both developed by Materialize co-founder Frank McSherry). The only relationship between Materialize and Postgres is that we are wire-compatible with Postgres and we don't share any code with Postgres nor do we have a dependence on it. [1] https://github.com/TimelyDataflow/timely-dataflow [2] https://github.com/TimelyDataflow/differential-dataflow
In Ask HN: Is there a way to efficiently subscribe to an SQL query for changes?
It's partially inspired by Linq, so the similarity you see is expected. It's not really arbitrary structures so much, though you're mostly free in what record type you use in a relation (structs and tagged enums are typical, though). The incremental part is that you can feed it changes to the input (additions/retractions of facts) and get changes to the outputs back with low latency (you can alternatively just use it to keep an index up-to-date, where you can quickly look up based on a key (like a materialized view in SQL)). This [0] section in the readme of the underlying incremental dataflow framework may help get the concept across, but feel free to follow up if you're still not seeing the incrementality. [0]: https://github.com/TimelyDataflow/differential-dataflow#an-e...
In Differential Datalog
What you're asking about is the magic at the heart of Materialize. We're built atop an open-source incremental compute framework called Differential Dataflow [0] that one of our co-founders has been working on for ten years or so. The basic insight is that for many computations, when an update arrives, the amount of incremental compute that must be performed is tiny. If you're computing `SELECT count(1) FROM relation`, a new row arriving just increments the count by one. If you're computing a `WHERE` clause, you just need to check whether the update satisfies the predicate or not. Of course, things get more complicated with operators like `JOIN`, and that's where Differential Dataflow's incremental join algorithms really shine. It's true that there are some computations that are very expensive to maintain incrementally. For example, maint
Read the rest
aining an ordered query like SELECT * FROM relation ORDER BY col would be quite expensive, because the arrival of a new value will change the ordering of all values that sort greater than the new value. Materialize can still be quite a useful tool here, though! You can use Materialize to incrementally-maintain the parts of your queries that are cheap to incrementally maintain, and execute the other parts of your query ad hoc. This is in fact how `ORDER BY` already works in Materialize. A materialized view never maintains ordering, but you can request a sort when you fetch the contents of that view by using an `ORDER BY` clause in your `SELECT` statement. For example: CREATE MATERIALIZED VIEW v AS SELECT complicated FROM t1, t2, ... -- incrementally maintained SELECT * FROM v ORDER BY col LIMIT 5 -- order and limit computed ad hoc, but still fast [0]: https://github.com/TimelyDataflow/differential-dataflow
In Introducing dbt + Materialize

Showing the 15 most recent of 37 sources. Enable JavaScript to search and browse every indexed mention.

MAKE YOURSELF AT HOME

A few shortcuts

/
Search the collection
?
Open this guide
Esc
Close dialogs and navigation

Your saved repositories and interests stay in this browser.