cube-js / cube-js/cube

`rollup_join` not matched when query includes a measure from the secondary (right-side) cube, despite that measure being explicitly listed in the rollup_join definition

Open
#11,124 3 comments 0 reactions 0 assignees View on GitHub
Dominant language
Rust
Stars
20.8k
Forks
2.1k
Avg merge
1d 2h
Merged PRs (30d)
181

Description

**Describe the bug**
A `rollup_join` pre-aggregation that explicitly includes measures from both the primary and secondary cube fails to be matched when the query contains a measure originating from the secondary cube. Cube.js throws a cross-data-source error as if no matching rollup_join exists, even though the measure is explicitly declared in the rollup_join's `measures` array.

```
Error: To join across data sources use rollupJoin with Cube Store.
If rollupJoin is defined, this error indicates it doesn't match the query.
Please use Rollup Designer to verify it's definition.
Found data sources: secondary, default
```

The error also surfaces via RefreshScheduler (not just at query time), despite the rollup_join being correctly defined to handle the cross-data-source case.

**To Reproduce**
Steps to reproduce the behavior:
1. Configure two data sources in `cube.js` — `default` (standard PostgreSQL) and `secondary` (a second PostgreSQL instance)
2. Define `PrimaryCube` on the `default` data source with a `main` rollup pre-aggregation that includes a sum measure and an ID dimension (for join) and a location dimension
3. Define `SecondaryCube` on the `secondary` data source with a base rollup pre-aggregation, and a `rollup_join` pre-aggregation that explicitly lists measures from **both** cubes and dimensions from **both** cubes
4. Start the Cube.js service and wait for pre-aggregations to build
5. Run **Query A** — measures only from `PrimaryCube`, dimension from `SecondaryCube` → **succeeds**
6. Add a measure from `SecondaryCube` to the same query (Query B) → **throws cross-data-source error**

**Expected behavior**
The Cube.js service should start up cleanly without throwing cross-data-source errors during the pre-aggregation refresh cycle.

Both Query A and Query B should be served by the `rollup_join` pre-aggregation. Query B is a strict subset of the rollup_join's declared `measures` and `dimensions` arrays — Cube.js should recognise it as a match and route accordingly, exactly as it does for Query A.

**Screenshots**
Error text:
```
Error: To join across data sources use rollupJoin with Cube Store.
If rollupJoin is defined, this error indicates it doesn't match the query.
Please use Rollup Designer to verify it's definition.
Found data sources: secondary, default
```

**Minimally reproducible Cube Schema**
Two data sources must be configured in `cube.js` — `default` and `secondary`.

```javascript
// SecondaryCube — uses 'secondary' data source
cube(`SecondaryCube`, {
data_source: `secondary`,
sql: `
select 1 as id, 'loc_1' as location_id, 500 as contract_value
UNION ALL
select 2 as id, 'loc_2' as location_id, 300 as contract_value
`,

dimensions: {
id: { sql: `id`, type: `string`, primary_key: true },
location_id: { sql: `location_id`, type: `string` }
},

measures: {
total_contract_value: {
sql: `contract_value`,
type: `sum`
}
},

preAggregations: {
main: {
type: `rollup`,
external: true,
measures: [CUBE.total_contract_value],
dimensions: [CUBE.id, CUBE.location_id],
granularity: `day`
}
}
});

// PrimaryCube — uses 'default' data source
cube(`PrimaryCube`, {

sql: `
select 1 as id, 1 as secondary_id, 'SUCCESS' as action_status
UNION ALL
select 2 as id, 2 as secondary_id, 'SUCCESS' as action_status
UNION ALL
select 3 as id, 1 as secondary_id, 'FAILURE' as action_status
`,

joins: {
SecondaryCube: {
sql: `${CUBE}.secondary_id = ${SecondaryCube}.id`,
relationship: `many_to_one`
}
},

dimensions: {
id: { sql: `id`, type: `string`, primary_key: true },
secondary_id: { sql: `secondary_id`, type: `string` },
action_status: { sql: `action_status`, type: `string` }
},

measures: {
total_count: {
type: `count`,
filters: [{ sql: `${CUBE}.action_status = 'SUCCESS'` }]
}
},

preAggregations: {
primary_rollup: {
type: `rollup`,
external: true,
measures: [CUBE.total_count],
dimensions: [CUBE.secondary_id, CUBE.action_status],
granularity: `day`
},

joined_rollup: {
type: `rollup_join`,
rollups: [PrimaryCube.primary_rollup, SecondaryCube.main],
measures: [
PrimaryCube.total_count,
SecondaryCube.total_contract_value // ← secondary measure, explicitly listed
],
dimensions: [
PrimaryCube.secondary_id,
SecondaryCube.location_id // ← secondary dimension, explicitly listed
],
granularity: `day`
}
}
});
```

**Query A — succeeds** (measure from primary only, dimension from secondary):
```json
{
"measures": ["PrimaryCube.total_count"],
"dimensions": ["SecondaryCube.location_id"]
}
```

**Query B — fails** (adds a measure from the secondary cube):
```json
{
"measures": ["PrimaryCube.total_count", "SecondaryCube.total_contract_value"],
"dimensions": ["SecondaryCube.location_id"]
}
```

**Version:**
1.3.5

Contributor guide

Open the contributing guide

Research direction

Start with the two-data-source Cube.js schema and run Query A and Query B from the reproduction to compare rollup_join matching. Trace the rollup_join matching path and RefreshScheduler handling for measures from the secondary cube; done means both queries match the declared rollup_join and startup refresh completes without a cross-data-source error.

Written by the indexing model from the issue text.

Assessment

Tech stack
javascript, postgresql
Domain
backend, databases
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Quiet
Clarity
Clearly specified
Newbie friendliness
68/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.