How it works
Prerequisites
Everything below is set up in Databricks, by a workspace admin, before you create the warehouse in Popsink.1
A workspace with Unity Catalog
A Databricks workspace (AWS, Azure or GCP) attached to a Unity Catalog metastore. The tables your SQL reads and writes are addressed as
catalog.schema.table.2
A SQL warehouse
In SQL Warehouses, create a SQL warehouse or pick an existing one. Serverless is recommended: it starts in seconds and stops when idle, so a task scheduled every few minutes only bills while it runs.An all-purpose cluster cannot be used: Popsink only accepts a SQL warehouse’s HTTP path.
3
A service principal
In Settings → Identity and access → Service principals, add a service principal to the workspace. Use a service principal rather than a person’s account, so the connection keeps working when someone leaves.On the service principal’s Configurations tab, enable the Databricks SQL access entitlement: without it, the service principal cannot run statements on a SQL warehouse.
4
An access token for the service principal
Popsink authenticates with a personal access token belonging to the service principal.
- In Settings → Advanced → Personal Access Tokens, make sure tokens are enabled, and under Permission Settings give the service principal Can Use.
-
Generate the token as the service principal, for example with the Databricks CLI authenticated with its OAuth secret:
5
Permission to use the SQL warehouse
In SQL Warehouses → your warehouse → Permissions, give the service principal Can use.
6
Unity Catalog privileges
Grant the service principal what your SQL needs, and nothing more. Typically, read access on the tables you transform and write access on the schema you build into:If you set a default catalog and schema on the warehouse in Popsink, the service principal also needs
USE CATALOG and USE SCHEMA on them: the connection test reads both.7
Network access
If the workspace restricts inbound traffic with IP access lists, add Popsink’s egress IP addresses for your region. On a self-hosted data plane, allow the addresses your cluster leaves from instead.
Create a warehouse
1
Open the wizard
In Popsink, go to Transform data → Warehouses and click New Warehouse. Select Databricks as the Type, give it a Warehouse Name and pick the Owner domain.
2
Fill in the settings
Catalog and schema names accept letters, digits, underscores and hyphens only.
3
Test and save
Click Test credentials, then Continue once the test passes. If any setting changes after a green test, the test has to run again.
What the connection test does
Three read-only metadata calls, in the order a failure is easiest to explain:
The test runs no SQL statement: a
SELECT 1 would start a stopped warehouse, bill you for compute and take minutes to answer. You can re-run it at any time with Test connection from the warehouses list to refresh a warehouse’s status.
SQL examples
The examples run on a Databricks SQL warehouse, as a task or from the SQL editor. They use two tables inmain.sales; replace the names with your own:
orders: one row per order (id,customer_id,status,amount,created_at);orders_history: one row per change made to an order, with the order’s columns plus:
Build a reporting table
The most common task: rebuild an aggregate on a schedule.Full history of a row
Every change made to order42, with the previous status beside the new one:
Table as it was at a point in time
The latest change of each order before a given instant. TheWHERE runs before the window, which is what makes this a point-in-time read; the delete filter sits outside so a deleted row is not replaced by its previous version:
Build a merge table when creates arrive after updates
Rebuilding current state from a history table usually applies a simple rule: the latest change wins. That is right as long as changes arrive in the order they happened. Some sources, and some reloads, break that assumption: a create (c) or a snapshot read (r) lands in the history after updates for the same key, with a later timestamp. With “latest change wins”, that late create overwrites the updates.
For example, orders_history holds three changes for order 42:
The rule used below: for a given key, an update or a delete always outranks a create or a snapshot read. Among changes of the same rank, the latest wins (
event_ts, then seq).
The merge table keeps op and event_ts, which the incremental step needs.
1
Build the merge table from the full history
2
Keep it current with an incremental MERGE
On a large history, apply only recent changes instead of rebuilding. The source elects one change per key with the same rule, over a look-back window:What each case does:
The MERGE is idempotent: re-applying the same window changes nothing, so overlapping runs are safe. Schedule it as a task, and size the look-back window to cover the task’s interval plus the latest a change can arrive. If your history table records when each row was written, filter the window on that column rather than on
event_ts, so a change that arrives late is still picked up.3
Check which keys the rule changed
The keys whose latest change is a create or a snapshot read that the rule ignored, beside the change that was kept: