How eddy works
From a changed record to a customer answer.
Define the reporting model and eddy keeps its results updated as source records change. Your app and agents can query the data, or eddy can send it to another system.
- 01 use the data
- 02 customer data
- 03 define the model
- 04 corrections
- 05 change safely
- 06 follow updates
- time and alignment
- provenance
- tracing
Example screens and data. Our recorded query benchmark includes its workload and test setup.
01 · Using the data
Your app and agents use the reporting data.
Query the models through Postgres, HTTP or MCP, or push results to other systems. The example shows a SQL client and an agent reading the same report. Your app checks who can see each customer's data.
-- Your product authorizes account 42 before this read. acme=> select location, refunded from eddy.refunds_by_location where account_id = 42 and month = '2026-03-01' order by refunded desc limit 3; location | refunded ---------------+--------- Burnaby | 8,400 Vancouver | 6,200 North Vancouver | 1,400 (3 rows) Reporting period: March Source changes through April 2, 10:00 UTC acme=> select location, revenue, refunded from eddy.aligned_location_reporting where account_id = 42 and month = '2026-03-01'; -- Same customer, period and aligned source position.
customer: account 42 · period: March
report: refunds_by_location
→ 12 locations · Burnaby has the highest refund amountcustomer: account 42 · period: March
as of: April 2, 10:00 UTC · source persistence time
→ revenue and refunds at the same point in time02 · Customer data
Keep track of each customer's data.
Shared source tables can feed separate reporting state for each customer. See how far each has updated and query their results. Your app decides who may access them.
| account_id | name | rows served | store | ticks / h | last hour |
|---|---|---|---|---|---|
| 131 | Meridian | 1.9M | 58 MB | 412 | |
| 322 | Ashgrove | 1.1M | 31 MB | 377 | |
| 88 | Glenmore | 842k | 22 MB | 301 | |
| 42 | Northwind | 610k | 17 MB | 288 | |
| 398 | Lumen | 402k | 11 MB | 240 | |
| 233 | Halden | 377k | 9.8 MB | 203 | |
| 164 | Brightline | 291k | 7.6 MB | 177 | |
| 412 | Orbis | 188k | 4.1 MB | 131 | |
| 57 | Contoso | 164k | 3.9 MB | 118 | |
| 287 | Corvid | 120k | 2.7 MB | 96 | |
| 201 | Kestrel | 96k | 2.2 MB | 71 | |
| 301 | Tallis | 61k | 1.4 MB | 52 | |
| 371 | Fernwood | 33k | 720 KB | 31 | |
| 103 | Inkwell | 19k | 440 KB | 18 | |
| 345 | Verity | 6k | 160 KB | 4 |
Northwind
| account_id | name | mrr | risk |
|---|---|---|---|
| 42 | Northwind | 14,420 | low |
- 1Customer list. See activity, rows and storage for each customer.
- 2Separate state. Each customer has a store and a log position, sharing one replication slot.
- 3One customer's results. Query that customer's store through the API.
03 · Define the model
Define the model in SQL or dbt.
A model defines the data behind the report. In the editor, see the SQL as steps and inspect each step's rows. Edits to the steps and SQL stay in sync. Preview support before building the model.
| account_id | name | used | seats | events_30d |
|---|---|---|---|---|
| 42 | Northwind | 39 | 40 | 1,904 |
| 57 | Contoso | 14 | 40 | 31 |
| 88 | Glenmore | 71 | 80 | 2,377 |
| when | events_30d < 50 and used·2 < seats | 'high' |
| when | events_30d < 200 | 'watch' |
| else | 'low' |
- 1Dependencies. See the tables and models this view uses, and which models use it.
- 2Query steps. Read the query top to bottom and inspect each step's rows.
- 3Step details. Open the join conditions, calculations and update costs.
- 4Editable SQL. Edits to a step change the corresponding SQL text.
04 · Corrections
A correction should reach the report.
A March refund is amended in April, from $1,200 to $3,600. Burnaby's March total rises to $8,400 and moves ahead of Vancouver. Follow the change into the model, report and agent answer.
1 · The source row
One March refund is amended.
- Refund
- #102 · account 42
- Location
- Burnaby
- Refund date
- March 11, 2026
- Amount
- $1,200 CAD
The report includes source changes through April 2 at 09:59 UTC. The amendment is not included yet.
- Refund
- #102 · account 42
- Location
- Burnaby
- Refund date
- March 11, 2026
- Amount
- $3,600 CAD
Amended in the source on April 2 at 10:00 UTC. The refund still belongs to March.
2 · The reporting model
Keep the definition in SQL.
select account_id, month, location,
sum(amount) as refunded
from refunds
group by account_id, month, locationThe source stores a reporting month for each refund. The model totals refunds by account, month and location.
3 · Your customer report
March refunds by location.
Account 42 · March 1–31, 2026
Source changes through April 2, 09:59 UTC
| Location | Refunded |
|---|---|
| Vancouver | $6,200 |
| Burnaby | $6,000 |
| North Vancouver | $1,400 |
Account 42 · March 1–31, 2026
Source changes through April 2, 10:00 UTC
| Location | Refunded |
|---|---|
| Burnaby | $8,400 |
| Vancouver | $6,200 |
| North Vancouver | $1,400 |
4 · Your customer agent
The same model answers the question.
Which of my locations had the most refunds in March?
“Vancouver had the most March refunds: $6,200 CAD. Account 42, March 1–31. Source changes included through April 2, 09:59 UTC.”
“Burnaby had the most March refunds: $8,400 CAD. Account 42, March 1–31. Source changes included through April 2, 10:00 UTC.”
See the query for this account
Your app checks access to account 42 first. The dashboard and agent query the same model for that account and month. Parameters: 42 and 2026-03-01.
select location, refunded
from refunds_by_location
where account_id = $1 and month = $2
order by refunded desc, locationThe report still covers March, but now includes the change saved in April. How reporting time works · Recorded SQL correctness checks.
05 · Change safely
See what a model change will do.
Compare the old and new definitions. See which rows change and which other results need rebuilding, then apply the replacement alongside the running system. The history records who asked for the change.
+ case when u.events_30d < 80
| w | account_id | name | events_30d | risk |
|---|---|---|---|---|
| −1 | 164 | Brightline | 66 | watch |
| +1 | 164 | Brightline | 66 | high |
| −1 | 201 | Kestrel | 72 | watch |
| +1 | 201 | Kestrel | 72 | high |
| −1 | 345 | Verity | 58 | watch |
| +1 | 345 | Verity | 58 | high |
- 1The edit. Change a condition in the model's SQL.
- 2The comparison. Compare the old and new results over the same source rows.
- 3Changed accounts. See whose answer changes before applying the model.
- 4Online replacement. The new model builds alongside the running system and swaps in between update batches.
06 · Following updates
See how data reaches the report.
The graph connects source tables, models and the results your product uses. Follow a change through the models it affects, or open a customer's state to check their progress.
where account_id=57
and id > 14;
| account_id | name | mrr | seats | risk |
|---|---|---|---|---|
| 42 | Northwind | 14,420 | 39 / 40 | low |
| 57 | Contoso | 8,900 | 14 / 40 | high |
| 88 | Glenmore | 22,150 | 71 / 80 | low |
| 103 | Inkwell | 3,600 | 9 / 20 | watch |
| 131 | Meridian | 41,300 | 188 / 200 | low |
| 164 | Brightline | 12,800 | 21 / 50 | watch |
| 201 | Kestrel | 6,250 | 12 / 25 | watch |
| 233 | Halden | 18,700 | 60 / 60 | low |
| 260 | Norrland | 2,900 | 4 / 10 | high |
| 287 | Corvid | 9,450 | 30 / 30 | low |
| 301 | Tallis | 5,100 | 11 / 15 | low |
| 322 | Ashgrove | 27,600 | 90 / 120 | low |
| 345 | Verity | 1,800 | 2 / 5 | watch |
| 371 | Fernwood | 4,400 | 14 / 15 | low |
| 398 | Lumen | 15,050 | 49 / 60 | low |
| 412 | Orbis | 7,700 | 21 / 40 | watch |
- 1Source updates. See Postgres commits and their positions in the replication log.
- 2Affected models. A change lights up the edges it crosses.
- 3Update delay. Compare the time from source commit to updated results with a plain Postgres view.
- 4Reporting results. See the rows available to your app and the engine's measurements.
More detail · Time and alignment
March's report, as it stood on April 2.
A correction saved on April 3 should be left out of a report queried as of April 2, even if the invoice is dated March 10. By default, time travel follows when changes were saved in the source system.
Business time
The business dates you filter, such as the invoice dates selected in a date picker.
Source persistence time
When the source system saved a change. The default timeline for time travel and alignment.
Arrival time
When eddy received the change. This may be later than when it was saved upstream.
Choose which inputs move forward together.
Each view or materialized view can align its inputs, with a database-wide default. An aligned view waits for all inputs to reach the same point in time. Newer changes wait until then. Unaligned inputs advance independently.
For example, query invoices and payments as of April 2 to compare them at the same point in time. The result tells you its as-of timestamp and timeline. You can configure which timeline to use.
For your pilot, confirm how much history is available and what happens if a query asks for a moment the data hasn't reached yet.
More detail · Contributing rows
See the rows behind a number.
For supported queries, follow a result back through joins and aggregates to its contributing rows. The lens shows the path and samples from current state.
materialized viewaccount_health
| account_id | name | used | events_30d | risk |
|---|---|---|---|---|
| 57 | Contoso | 14 | 31 | high |
aggregateusage_30d · group 57
| account_id | events_30d |
|---|---|
| 57 | 31 |
joinseat_utilization · key 57
| account_id | used | seats |
|---|---|---|
| 57 | 14 | 40 |
sourceseats · account 57
| id | account_id | role |
|---|---|---|
| 1 | 57 | viewer |
| 2 | 57 | viewer |
| 3 | 57 | editor |
sourceusage_events · account 57
| event | user | at |
|---|---|---|
| report.viewed | u_1182 | Sep 25 09:14 |
| export.run | u_0413 | Sep 24 16:40 |
- 1The selected result. Follow supported operators back to contributing rows.
- 2Its path. Highlight the parts of the graph that contributed.
- 3Sample rows. Inspect samples and counts from the rows currently stored at that step.
- 4Source records. See the current seat records contributing to this account's utilization.
More detail · Transaction tracing
Follow a source transaction through the models.
See the source commit and its changed rows, then follow the operators and results it affected. The trace helps explain how a correction reached the report.
delete from seats where account_id = 57 and id > 14;Remove seats · run from this page
| w | id | account_id | role |
|---|---|---|---|
| −1 | 15 | 57 | viewer |
| −1 | 16 | 57 | viewer |
| −1 | 17 | 57 | editor |
| … |
- 1The commit. See the Postgres transaction, log position and SQL.
- 2Changed rows. See changes by table; a delete has weight −1.
- 3Affected operators. Follow which operators ran and what they emitted.
- 4Served. See the views changed by the transaction after the update batch finishes.
Let's look at your report.
Book a demo and bring the report, its source data and the questions customers ask.

