Describe the bug
I used AI to go through this bug.
Polling pre-aggregation build jobs via POST /cubejs-api/v1/pre-aggregations/jobs { "action": "get", "tokens": [...] } throws 500:
TypeError: Cannot read properties of undefined (reading 'getQueueDriver')
at PreAggregations.isPartitionExist (…/PreAggregations.ts:471:47)
at ApiGateway.getPreAggJobResultStatus (…/gateway.ts:1276:9)
at async Promise.all (index N)
at ApiGateway.preAggregationsJobsGET (…/gateway.ts:1138:45)
isPartitionExist uses this.queue[dataSource] without creating the queue. this.queue is an in-process map.
Cube Store can already hold the job. The replica that handles the poll may never have called getQueue() for that data source.
This shows up with the Orchestration API: POST jobs on one API process, then GET the tokens from another. Jobs stay posted until isPartitionExist runs. If they have already left this replica’s queue, the poll hits the missing this.queue[dataSource] and throws.
To Reproduce
Two Cube API processes, same model, shared Cube Store (not in-memory queue). Official image cubejs/cube:latest (1.7.24).
- Run Postgres + Cube Store + two Cube replicas (:4000 builder, :4001 poller) with CUBEJS_CACHE_AND_QUEUE_DRIVER=cubestore, a named data source, and CUBEJS_DEFAULT_API_SCOPES including jobs.
- Use the schema below (orders extends a parent that owns data_source; partitioned rollup so POST returns several tokens).
- Trigger builds on replica A:
curl -sS -X POST http://localhost:4000/cubejs-api/v1/pre-aggregations/jobs \
-H "Authorization: $CUBE_TOKEN" \
-H 'Content-Type: application/json' \
-d '{
"action": "post",
"selector": {
"contexts": [{"securityContext": {}}],
"timezones": ["UTC"],
"preAggregations": ["orders.main"]
}
}'
- Wait until partitions leave the queue (scheduled / processing is too early). Then poll replica B with all tokens:
curl -sS -X POST http://localhost:4001/cubejs-api/v1/pre-aggregations/jobs \
-H "Authorization: $CUBE_TOKEN" \
-H 'Content-Type: application/json' \
-d '{"action":"get","resType":"object","tokens":["paste", "tokens", "here"]}'
- Replica B returns 500 with getQueueDriver of undefined.
Same crash without HTTP, calling isPartitionExist(..., 'named_data_source', ...) on a PreAggregations instance that never called getQueue for that source.
Expected behavior
The poller replica should lazily create the local queue client (getQueue(dataSource)), read the job result from shared Cube Store, and return 200 with per-token statuses (done / missing_partition / failure), not 500.
Screenshots
Only response body:
{
"error": "TypeError: Cannot read properties of undefined (reading 'getQueueDriver')"
}
Minimally reproducible Cube Schema
cubes:
- name: orders_base
data_source: named_data_source
sql: |
SELECT 1 AS id, 100 AS amount, 'new' AS status, TIMESTAMP '2024-01-01' AS created_at
UNION ALL
SELECT 2, 200, 'new', TIMESTAMP '2024-01-02'
UNION ALL
SELECT 3, 300, 'processed', TIMESTAMP '2024-01-03'
UNION ALL
SELECT 4, 500, 'processed', TIMESTAMP '2024-01-04'
UNION ALL
SELECT 5, 600, 'shipped', TIMESTAMP '2024-01-05'
measures:
- name: count
type: count
- name: total_amount
sql: amount
type: sum
dimensions:
- name: id
sql: id
type: number
primary_key: true
- name: status
sql: status
type: string
- name: created_at
sql: created_at
type: time
- name: orders
extends: orders_base
pre_aggregations:
- name: main
measures: [count, total_amount]
dimensions: [status]
time_dimension: created_at
granularity: day
partition_granularity: day
Version:
1.7.24
Additional context
Related: #11313 (getPreAggJobQueueStatus inspected only the default data source queue, so named-source builds reported missing_partition while still running). Passing job.dataSource into the queue lookup does not cover this crash: after the job has left the queue, the poll still calls isPartitionExist, which indexes this.queue[dataSource] without getQueue().
Describe the bug
I used AI to go through this bug.
Polling pre-aggregation build jobs via POST /cubejs-api/v1/pre-aggregations/jobs { "action": "get", "tokens": [...] } throws 500:
isPartitionExistusesthis.queue[dataSource]without creating the queue.this.queueis an in-process map.Cube Store can already hold the job. The replica that handles the poll may never have called
getQueue()for that data source.This shows up with the Orchestration API:
POSTjobs on one API process, thenGETthe tokens from another. Jobs stay posted until isPartitionExist runs. If they have already left this replica’s queue, the poll hits the missingthis.queue[dataSource]and throws.To Reproduce
Two Cube API processes, same model, shared Cube Store (not in-memory queue). Official image cubejs/cube:latest (1.7.24).
Same crash without HTTP, calling
isPartitionExist(..., 'named_data_source', ...)on a PreAggregations instance that never called getQueue for that source.Expected behavior
The poller replica should lazily create the local queue client (getQueue(dataSource)), read the job result from shared Cube Store, and return 200 with per-token statuses (done / missing_partition / failure), not 500.
Screenshots
Only response body:
Minimally reproducible Cube Schema
Version:
1.7.24
Additional context
Related: #11313 (getPreAggJobQueueStatus inspected only the default data source queue, so named-source builds reported missing_partition while still running). Passing job.dataSource into the queue lookup does not cover this crash: after the job has left the queue, the poll still calls isPartitionExist, which indexes this.queue[dataSource] without getQueue().