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:
- Configure two data sources in
cube.js — default (standard PostgreSQL) and secondary (a second PostgreSQL instance)
- 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
- 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
- Start the Cube.js service and wait for pre-aggregations to build
- Run Query A — measures only from
PrimaryCube, dimension from SecondaryCube → succeeds
- 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.
// 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):
{
"measures": ["PrimaryCube.total_count"],
"dimensions": ["SecondaryCube.location_id"]
}
Query B — fails (adds a measure from the secondary cube):
{
"measures": ["PrimaryCube.total_count", "SecondaryCube.total_contract_value"],
"dimensions": ["SecondaryCube.location_id"]
}
Version:
1.3.5
Describe the bug
A
rollup_joinpre-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'smeasuresarray.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:
cube.js—default(standard PostgreSQL) andsecondary(a second PostgreSQL instance)PrimaryCubeon thedefaultdata source with amainrollup pre-aggregation that includes a sum measure and an ID dimension (for join) and a location dimensionSecondaryCubeon thesecondarydata source with a base rollup pre-aggregation, and arollup_joinpre-aggregation that explicitly lists measures from both cubes and dimensions from both cubesPrimaryCube, dimension fromSecondaryCube→ succeedsSecondaryCubeto the same query (Query B) → throws cross-data-source errorExpected 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_joinpre-aggregation. Query B is a strict subset of the rollup_join's declaredmeasuresanddimensionsarrays — Cube.js should recognise it as a match and route accordingly, exactly as it does for Query A.Screenshots
Error text:
Minimally reproducible Cube Schema
Two data sources must be configured in
cube.js—defaultandsecondary.Query A — succeeds (measure from primary only, dimension from secondary):
{ "measures": ["PrimaryCube.total_count"], "dimensions": ["SecondaryCube.location_id"] }Query B — fails (adds a measure from the secondary cube):
{ "measures": ["PrimaryCube.total_count", "SecondaryCube.total_contract_value"], "dimensions": ["SecondaryCube.location_id"] }Version:
1.3.5