diff --git a/CHANGELOG.md b/CHANGELOG.md
index 194650103f508..e8f1037491cc7 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -3,6 +3,18 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
+## [1.7.29](https://github.com/cube-js/cube/compare/v1.7.28...v1.7.29) (2026-08-27)
+
+### Bug Fixes
+
+- **api-gateway:** Make pre-agg build job status per-entry and data-source aware ([#11666](https://github.com/cube-js/cube/issues/11666)) ([694955f](https://github.com/cube-js/cube/commit/694955f864375515d84d463e9e7ee89a156a4a2b))
+- **query-orchestrator:** initialize missing data source queue in isPartitionExist, thanks [@dochernyshov](https://github.com/dochernyshov) ([#11617](https://github.com/cube-js/cube/issues/11617)) ([ab48039](https://github.com/cube-js/cube/commit/ab48039f1a3d46d890367b0fc898d9ebf1322786))
+- **server-core:** release orchestrators evicted from the LRU ([#11661](https://github.com/cube-js/cube/issues/11661)) ([b82c58a](https://github.com/cube-js/cube/commit/b82c58aa245919a36e3d20f79105b639d0c16863))
+
+### Features
+
+- **cubestore:** cap concurrent websocket connections per user ([#11667](https://github.com/cube-js/cube/issues/11667)) ([111eb3e](https://github.com/cube-js/cube/commit/111eb3ec049b9be1fa2f31186a7f83f58b5a0291))
+
## [1.7.28](https://github.com/cube-js/cube/compare/v1.7.27...v1.7.28) (2026-08-26)
### Bug Fixes
diff --git a/docs-mintlify/admin/connect-to-data/multiple-data-sources.mdx b/docs-mintlify/admin/connect-to-data/multiple-data-sources.mdx
index 89f66bc334857..42d56014832c1 100644
--- a/docs-mintlify/admin/connect-to-data/multiple-data-sources.mdx
+++ b/docs-mintlify/admin/connect-to-data/multiple-data-sources.mdx
@@ -64,6 +64,10 @@ Cubes that don't set `data_source` use the default source — there's no
need to write `data_source: default` explicitly, though you can if you
prefer to be explicit.
+A single query can also span sources: see [querying across data
+sources](/recipes/data-modeling/cross-data-source-queries) for appending rows
+from cubes in different databases into one result set.
+
For multitenancy scenarios where the data source is selected dynamically
per request, see [`driver_factory`](/reference/configuration/config#driver_factory)
and the [multitenancy guide][ref-config-multitenancy].
diff --git a/docs-mintlify/api-reference/api.yaml b/docs-mintlify/api-reference/api.yaml
index f7b62151099cc..ef1d9aa05230e 100644
--- a/docs-mintlify/api-reference/api.yaml
+++ b/docs-mintlify/api-reference/api.yaml
@@ -105,19 +105,14 @@ paths:
type: integer
minimum: 0
- in: query
- name: first
- required: false
+ name: after
schema:
- type: integer
- minimum: 1
- description: Page size for cursor-based pagination
+ type: string
- in: query
- name: after
- required: false
+ name: first
schema:
minimum: 1
- type: string
- description: Cursor for fetching the next page
+ type: integer
responses:
'200':
content:
@@ -272,6 +267,45 @@ paths:
tags:
- Deployments
/api/v1/deployments/{deploymentId}/dbt-sync:
+ get:
+ operationId: listDbtSyncs
+ parameters:
+ - in: path
+ name: deploymentId
+ required: true
+ schema:
+ type: integer
+ - in: query
+ name: status
+ schema:
+ oneOf:
+ - $ref: '#/components/schemas/DbtSyncRunsQueryStatus'
+ - type: 'null'
+ - in: query
+ name: trigger
+ schema:
+ oneOf:
+ - $ref: '#/components/schemas/DbtSyncRunsQueryTrigger'
+ - type: 'null'
+ - in: query
+ name: after
+ schema:
+ type: string
+ - in: query
+ name: first
+ schema:
+ minimum: 1
+ type: integer
+ responses:
+ '200':
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/DbtSyncRunListResponse'
+ description: ''
+ summary: List dbt syncs for a deployment
+ tags:
+ - dbt Sync
post:
operationId: startDbtSync
parameters:
@@ -344,6 +378,30 @@ paths:
summary: Get the status of a dbt sync
tags:
- dbt Sync
+ /api/v1/deployments/{deploymentId}/dbt-sync/{syncJobId}/logs:
+ get:
+ operationId: getDbtSyncLogs
+ parameters:
+ - in: path
+ name: deploymentId
+ required: true
+ schema:
+ type: integer
+ - in: path
+ name: syncJobId
+ required: true
+ schema:
+ type: string
+ responses:
+ '200':
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/DbtSyncLogEntriesListResponse'
+ description: ''
+ summary: Get the log of a dbt sync
+ tags:
+ - dbt Sync
/api/v1/deployments/{deploymentId}/dbt-sync/{syncJobId}/result:
get:
operationId: getDbtSyncResult
@@ -377,6 +435,19 @@ paths:
required: true
schema:
type: integer
+ - in: query
+ name: after
+ schema:
+ oneOf:
+ - type: string
+ - type: 'null'
+ - in: query
+ name: first
+ schema:
+ oneOf:
+ - minimum: 1
+ type: integer
+ - type: 'null'
responses:
'200':
content:
@@ -389,8 +460,9 @@ paths:
- Env Variables
x-mint:
content: >-
- Lists the deployment environment variables. Values of secret-named variables (containing
- PASS, SECRET, TOKEN, KEY, or CREDENTIAL) are returned as `[ENCRYPTED]`.
+ Lists the deployment environment variables, cursor-paginated via `first`/`after`. Values
+ of secret-named variables (containing PASS, SECRET, TOKEN, KEY, or CREDENTIAL) are
+ returned as `[ENCRYPTED]`.
put:
operationId: setEnvVariables
parameters:
@@ -464,6 +536,15 @@ paths:
- type: 'null'
type: integer
minimum: 0
+ - in: query
+ name: after
+ schema:
+ type: string
+ - in: query
+ name: first
+ schema:
+ minimum: 1
+ type: integer
responses:
'200':
content:
@@ -508,6 +589,15 @@ paths:
- type: 'null'
type: integer
minimum: 0
+ - in: query
+ name: after
+ schema:
+ type: string
+ - in: query
+ name: first
+ schema:
+ minimum: 1
+ type: integer
responses:
'200':
content:
@@ -822,6 +912,15 @@ paths:
oneOf:
- $ref: '#/components/schemas/DeploymentLogsQueryContainer'
- type: 'null'
+ - in: query
+ name: after
+ schema:
+ type: string
+ - in: query
+ name: first
+ schema:
+ minimum: 1
+ type: integer
responses:
'200':
content:
@@ -847,7 +946,6 @@ paths:
description: Numeric id of the deployment that owns the notification.
- in: query
name: dashboardId
- required: false
schema:
oneOf:
- minimum: 1
@@ -856,14 +954,8 @@ paths:
Optional filter: only return notifications for this numeric dashboard id. Provide
this OR dashboardPublicId, not both.
- type: 'null'
- type: integer
- minimum: 1
- description: >-
- Optional filter: only return notifications for this numeric dashboard id. Provide this
- OR `dashboardPublicId`, not both.
- in: query
name: dashboardPublicId
- required: false
schema:
oneOf:
- type: string
@@ -871,13 +963,8 @@ paths:
Optional filter: only return notifications for this dashboard public id. Provide
this OR dashboardId, not both.
- type: 'null'
- type: string
- description: >-
- Optional filter: only return notifications for this dashboard public id. Provide this OR
- `dashboardId`, not both.
- in: query
name: recipientUserId
- required: false
schema:
oneOf:
- minimum: 1
@@ -886,14 +973,8 @@ paths:
Optional filter: only return notifications received by this user, by user id.
Mutually exclusive with recipientEmail and the embed-user filter.
- type: 'null'
- type: integer
- minimum: 1
- description: >-
- Optional filter: only return notifications received by this user, by user id. Mutually
- exclusive with `recipientEmail` and the embed-user filter.
- in: query
name: recipientEmail
- required: false
schema:
oneOf:
- type: string
@@ -901,13 +982,8 @@ paths:
Optional filter: only return notifications received by this user, by email.
Mutually exclusive with recipientUserId and the embed-user filter.
- type: 'null'
- type: string
- description: >-
- Optional filter: only return notifications received by this user, by email. Mutually
- exclusive with `recipientUserId` and the embed-user filter.
- in: query
name: recipientEmbedTenantName
- required: false
schema:
oneOf:
- type: string
@@ -915,13 +991,8 @@ paths:
Optional filter: only return notifications received by this embed user. Required
together with recipientExternalId; mutually exclusive with the user filters.
- type: 'null'
- type: string
- description: >-
- Optional filter: only return notifications received by this embed user. Required
- together with `recipientExternalId`; mutually exclusive with the user filters.
- in: query
name: recipientExternalId
- required: false
schema:
oneOf:
- type: string
@@ -929,25 +1000,15 @@ paths:
Optional filter: only return notifications received by this embed user. Required
together with recipientEmbedTenantName; mutually exclusive with the user filters.
- type: 'null'
- type: string
- description: >-
- Optional filter: only return notifications received by this embed user. Required
- together with `recipientEmbedTenantName`; mutually exclusive with the user filters.
- in: query
- name: first
- required: false
+ name: after
schema:
- type: integer
- minimum: 1
- maximum: 200
- description: Page size for cursor pagination (default 100, max 200).
+ type: string
- in: query
- name: after
- required: false
+ name: first
schema:
minimum: 1
- type: string
- description: Opaque cursor for the next page; pass the previous response's `pageInfo.endCursor`.
+ type: integer
responses:
'200':
content:
@@ -1184,26 +1245,18 @@ paths:
type: integer
description: Numeric id of the notification (scheduled run).
- in: query
- name: first
- required: false
+ name: after
schema:
oneOf:
- type: string
- type: 'null'
- type: integer
- minimum: 1
- maximum: 200
- description: Page size for cursor pagination (default 100, max 200).
- in: query
- name: after
- required: false
+ name: first
schema:
oneOf:
- minimum: 1
type: integer
- type: 'null'
- type: string
- description: Opaque cursor for the next page; pass the previous response's `pageInfo.endCursor`.
responses:
'200':
content:
@@ -1283,6 +1336,19 @@ paths:
required: true
schema:
type: integer
+ - in: query
+ name: after
+ schema:
+ oneOf:
+ - type: string
+ - type: 'null'
+ - in: query
+ name: first
+ schema:
+ oneOf:
+ - minimum: 1
+ type: integer
+ - type: 'null'
responses:
'200':
content:
@@ -1597,6 +1663,64 @@ paths:
Requires **edit** access to the report.
/api/v1/deployments/{deploymentId}/reports/{reportId}/connect-workbook:
+ delete:
+ operationId: disconnectReportFromWorkbook
+ parameters:
+ - in: path
+ name: deploymentId
+ required: true
+ schema:
+ type: number
+ - in: path
+ name: reportId
+ required: true
+ schema:
+ type: number
+ requestBody:
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/DisconnectReportFromWorkbookInput'
+ description: DisconnectReportFromWorkbookInput
+ required: false
+ responses:
+ '200':
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/Report'
+ description: ''
+ summary: Disconnect a report from one spot in a spreadsheet
+ tags:
+ - Reports
+ x-mint:
+ content: |-
+ Remove a single placement of a report — one sheet + anchor in one
+ spreadsheet — leaving every other placement, in this workbook and in others, alone.
+
+ Address the placement by `placementId`, or (for placements recorded before ids
+ existed) by `externalWorkbookId` plus `sheetName` and `anchorCell` — or a
+ `resultLocation` naming both. Neither the sheet name nor the anchor is optional in
+ that form: matching is by sheet + anchor, and a target missing either addresses
+ nothing.
+
+ `sheetId` is an optional extra, not a substitute for `sheetName`: a placement
+ written before that field existed has only a name, and no id the server could
+ resolve to it, so an id-only address would match nothing while looking correct.
+
+ Send a `sheetId` only when you know it is the one the placement was stored with.
+ It NARROWS the target — a placement recorded under a different id is not matched,
+ even when the sheet name and anchor agree, because the stored name may belong to a
+ tab that has since been renamed and the id is the only stable identity. If your id
+ may have moved on (a tab deleted and recreated under the same name reports a new
+ one), address the placement by `placementId`, or by sheet name with no
+ `sheetId` at all.
+
+ A target that matches no placement succeeds and changes nothing, so retrying is
+ safe.
+
+ Like connecting, this records nothing about the report's definition and so requires
+ only read access: unplacing a report from your own sheet is not an edit to the report.
put:
operationId: connectReportToWorkbook
parameters:
@@ -1650,6 +1774,13 @@ paths:
required: true
schema:
type: number
+ requestBody:
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/RefreshReportInput'
+ description: RefreshReportInput
+ required: false
responses:
'200':
content:
@@ -1793,6 +1924,19 @@ paths:
required: true
schema:
type: integer
+ - in: query
+ name: after
+ schema:
+ oneOf:
+ - type: string
+ - type: 'null'
+ - in: query
+ name: first
+ schema:
+ oneOf:
+ - minimum: 1
+ type: integer
+ - type: 'null'
responses:
'200':
content:
@@ -3163,6 +3307,20 @@ paths:
/api/v1/github/installations:
get:
operationId: installations
+ parameters:
+ - in: query
+ name: after
+ schema:
+ oneOf:
+ - type: string
+ - type: 'null'
+ - in: query
+ name: first
+ schema:
+ oneOf:
+ - minimum: 1
+ type: integer
+ - type: 'null'
responses:
'200':
content:
@@ -3181,7 +3339,20 @@ paths:
name: installationId
required: true
schema:
- type: number
+ type: integer
+ - in: query
+ name: after
+ schema:
+ oneOf:
+ - type: string
+ - type: 'null'
+ - in: query
+ name: first
+ schema:
+ oneOf:
+ - minimum: 1
+ type: integer
+ - type: 'null'
responses:
'200':
content:
@@ -3196,11 +3367,11 @@ paths:
get:
operationId: branches
parameters:
- - in: query
- name: installationId
+ - in: path
+ name: owner
required: true
schema:
- type: integer
+ type: string
- in: path
name: repo
required: true
@@ -3208,9 +3379,22 @@ paths:
type: string
- in: query
name: installationId
- required: false
+ required: true
schema:
- type: number
+ type: integer
+ - in: query
+ name: after
+ schema:
+ oneOf:
+ - type: string
+ - type: 'null'
+ - in: query
+ name: first
+ schema:
+ oneOf:
+ - minimum: 1
+ type: integer
+ - type: 'null'
responses:
'200':
content:
@@ -3425,9 +3609,23 @@ paths:
/api/v1/regions:
get:
operationId: listRegions
- responses:
- '200':
- content:
+ parameters:
+ - in: query
+ name: after
+ schema:
+ oneOf:
+ - type: string
+ - type: 'null'
+ - in: query
+ name: first
+ schema:
+ oneOf:
+ - minimum: 1
+ type: integer
+ - type: 'null'
+ responses:
+ '200':
+ content:
application/json:
schema:
$ref: '#/components/schemas/RegionsListResponse'
@@ -3435,6 +3633,11 @@ paths:
summary: List regions
tags:
- Regions
+ x-mint:
+ content: >-
+ Lists the regions available to the account, cursor-paginated via `first`/`after`. The
+ legacy `data` field returns the full, unpaginated list and is deprecated — use `items` +
+ `pageInfo` instead.
/api/v1/spec:
get:
operationId: getSpec
@@ -3555,6 +3758,15 @@ paths:
oneOf:
- $ref: '#/components/schemas/GetUserAttributesQueryType'
- type: 'null'
+ - in: query
+ name: after
+ schema:
+ type: string
+ - in: query
+ name: first
+ schema:
+ minimum: 1
+ type: integer
responses:
'200':
content:
@@ -3565,6 +3777,11 @@ paths:
summary: Get user attributes
tags:
- User Attributes
+ x-mint:
+ content: >-
+ List user attributes, newest first, cursor-paginated via `first`/`after`. The legacy
+ `offset`/`limit` query params and the `data`/`count` response fields are deprecated — use
+ `items` + `pageInfo` instead.
post:
operationId: createUserAttribute
requestBody:
@@ -3829,6 +4046,53 @@ paths:
`github`, no starter project is scaffolded — connect a repository afterwards via POST
/build/api/v1/deployments/:deploymentId/github/connect.
/build/api/v1/deployments/{deploymentId}/branches:
+ delete:
+ operationId: removeBranch
+ parameters:
+ - in: path
+ name: deploymentId
+ required: true
+ schema:
+ type: integer
+ - in: query
+ name: branchName
+ required: true
+ schema:
+ type: string
+ - in: query
+ name: removeOnUpstream
+ required: false
+ schema:
+ type: boolean
+ default: false
+ responses:
+ '200':
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/DataModelWriteResponse'
+ description: ''
+ summary: Delete a branch
+ tags:
+ - Data Model
+ x-mint:
+ content: >-
+ Deletes the branch and its git ref — the API-side counterpart of pruning a branch in Cube
+ Cloud, for callers that create branches per run (a CI job syncing dbt on every pull
+ request, say) and would otherwise accumulate them forever. Child branches are re-parented
+ onto the deleted branch’s own parent, and anyone viewing the branch in dev mode is moved
+ back to the default branch, so a branch that is currently open is deleted rather than
+ refused. The deploy branch cannot be deleted (400) and an unknown branch is a 404.
+
+
+ `removeOnUpstream=true` additionally deletes the branch on the connected git provider
+ (GitHub/GitLab). It defaults to `false`, so by default the branch is removed from Cube
+ Cloud only and the ref on your own remote is left alone — a CI job cleaning up branches it
+ created itself should opt in explicitly.
+
+
+ `branchName` is a query parameter rather than a path segment because branch names contain
+ slashes (`dbt-sync/main-20260817120000-a1b2c3d4`).
get:
operationId: listBranches
parameters:
@@ -3837,6 +4101,19 @@ paths:
required: true
schema:
type: integer
+ - in: query
+ name: after
+ schema:
+ oneOf:
+ - type: string
+ - type: 'null'
+ - in: query
+ name: first
+ schema:
+ oneOf:
+ - minimum: 1
+ type: integer
+ - type: 'null'
responses:
'200':
content:
@@ -4019,6 +4296,19 @@ paths:
required: false
schema:
type: boolean
+ - in: query
+ name: after
+ schema:
+ oneOf:
+ - type: string
+ - type: 'null'
+ - in: query
+ name: first
+ schema:
+ oneOf:
+ - minimum: 1
+ type: integer
+ - type: 'null'
responses:
'200':
content:
@@ -4029,6 +4319,12 @@ paths:
summary: List the data-model source files for a deployment
tags:
- Data Model
+ x-mint:
+ content: >-
+ Returns the data-model file tree. `first`/`after` (from the shared cursor params) paginate
+ the **top-level** tree nodes only — a single node can carry a large subtree, so this
+ bounds the number of roots returned, not the total file count. Omit `first` to get the
+ whole tree.
put:
operationId: updateFiles
parameters:
@@ -4207,6 +4503,43 @@ paths:
summary: Start a data-model upload transaction
tags:
- Data Model Uploads
+ /build/api/v1/deployments/{deploymentId}/data-model/validate:
+ get:
+ operationId: validateDataModel
+ parameters:
+ - in: path
+ name: deploymentId
+ required: true
+ schema:
+ type: integer
+ - in: query
+ name: branchName
+ required: false
+ schema:
+ type: string
+ - in: query
+ name: devMode
+ required: false
+ schema:
+ type: boolean
+ responses:
+ '200':
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/DataModelValidationResponse'
+ description: ''
+ summary: Compile a deployment's data model and report compilation errors
+ tags:
+ - Data Model
+ x-mint:
+ content: >-
+ Asks the branch's own Cube runtime for `GET /cubejs-api/v1/meta` — the same call the
+ console makes in dev mode — so the verdict is the one that branch's API would give. With
+ neither parameter it validates the deploy branch (production). `branchName` validates a
+ named branch, and `devMode=true` validates the caller's active dev-mode branch; the two
+ are mutually exclusive. Answers 200 with `valid: false` and the compiler's errors when the
+ model doesn't compile — a broken model is a result, not a request error.
/build/api/v1/deployments/{deploymentId}/dev-mode:
delete:
operationId: stopDevMode
@@ -4491,10 +4824,21 @@ components:
BranchesListResponse:
properties:
data:
+ deprecated: true
+ description: 'Deprecated: use `items` instead. Kept for backward compatibility.'
+ items:
+ $ref: '#/components/schemas/BranchResponse'
+ type: array
+ items:
items:
$ref: '#/components/schemas/BranchResponse'
type: array
+ pageInfo:
+ oneOf:
+ - $ref: '#/components/schemas/PageInfo'
+ - type: 'null'
required:
+ - items
- data
type: object
BuildStatusResponse:
@@ -4715,18 +5059,47 @@ components:
type: object
ConnectReportToWorkbookInput:
properties:
+ anchorCell:
+ oneOf:
+ - type: string
+ - type: 'null'
endResultCell:
oneOf:
- type: string
- type: 'null'
externalWorkbookId:
type: string
+ host:
+ oneOf:
+ - $ref: '#/components/schemas/ConnectReportToWorkbookInputHost'
+ - type: 'null'
+ placementId:
+ oneOf:
+ - type: string
+ - type: 'null'
resultLocation:
type: string
+ sheetId:
+ oneOf:
+ - type: string
+ - type: 'null'
+ sheetName:
+ oneOf:
+ - type: string
+ - type: 'null'
+ workbookName:
+ oneOf:
+ - type: string
+ - type: 'null'
required:
- externalWorkbookId
- resultLocation
type: object
+ ConnectReportToWorkbookInputHost:
+ enum:
+ - GOOGLE_SHEETS
+ - EXCEL
+ type: string
ControlThemeBorderSectionInput:
properties:
color:
@@ -5076,6 +5449,10 @@ components:
oneOf:
- type: integer
- type: 'null'
+ host:
+ oneOf:
+ - $ref: '#/components/schemas/CreateReportInputHost'
+ - type: 'null'
jsonQuery:
oneOf:
- type: string
@@ -5104,6 +5481,10 @@ components:
oneOf:
- type: string
- type: 'null'
+ sheetId:
+ oneOf:
+ - type: string
+ - type: 'null'
source:
oneOf:
- $ref: '#/components/schemas/CreateReportInputSource'
@@ -5120,7 +5501,16 @@ components:
oneOf:
- type: integer
- type: 'null'
+ workbookName:
+ oneOf:
+ - type: string
+ - type: 'null'
type: object
+ CreateReportInputHost:
+ enum:
+ - GOOGLE_SHEETS
+ - EXCEL
+ type: string
CreateReportInputSource:
enum:
- GOOGLE_SHEETS
@@ -5928,6 +6318,7 @@ components:
- FILTER
- AI
- TIME_GRAIN
+ - PARENT
- SPACER
- DIVIDER
- CONTAINER
@@ -5967,6 +6358,7 @@ components:
- FILTER
- AI
- TIME_GRAIN
+ - PARENT
- SPACER
- DIVIDER
- CONTAINER
@@ -6030,6 +6422,47 @@ components:
- path
- newPath
type: object
+ DataModelValidationError:
+ properties:
+ fileName:
+ oneOf:
+ - type: string
+ - type: 'null'
+ message:
+ type: string
+ required:
+ - message
+ type: object
+ DataModelValidationResponse:
+ properties:
+ branchName:
+ type: string
+ cubesCount:
+ oneOf:
+ - type: integer
+ - type: 'null'
+ deploymentId:
+ type: integer
+ errors:
+ items:
+ $ref: '#/components/schemas/DataModelValidationError'
+ type: array
+ mode:
+ $ref: '#/components/schemas/DataModelValidationResponseMode'
+ valid:
+ type: boolean
+ required:
+ - deploymentId
+ - branchName
+ - mode
+ - valid
+ - errors
+ type: object
+ DataModelValidationResponseMode:
+ enum:
+ - production
+ - dev_mode
+ type: string
DataModelWriteResponse:
properties:
success:
@@ -6052,6 +6485,49 @@ components:
required:
- path
type: object
+ DbtSyncLogEntriesListResponse:
+ properties:
+ items:
+ items:
+ $ref: '#/components/schemas/DbtSyncLogEntry'
+ type: array
+ pageInfo:
+ $ref: '#/components/schemas/PageInfo'
+ required:
+ - items
+ - pageInfo
+ type: object
+ DbtSyncLogEntry:
+ properties:
+ durationMs:
+ oneOf:
+ - type: integer
+ description: For a phase line, how long the phase took, in milliseconds.
+ - type: 'null'
+ level:
+ description: info or error.
+ type: string
+ message:
+ type: string
+ phase:
+ oneOf:
+ - type: string
+ description: The phase this line belongs to, e.g. dbt-compile.
+ - type: 'null'
+ stream:
+ description: >-
+ Always "system" in this version. "stdout" and "stderr" join it once dbt’s own output is
+ captured, so treat this as an open set.
+ type: string
+ timestamp:
+ description: When the line was produced, as an ISO 8601 timestamp.
+ type: string
+ required:
+ - timestamp
+ - level
+ - stream
+ - message
+ type: object
DbtSyncManifestStats:
properties:
macros:
@@ -6125,6 +6601,189 @@ components:
- completedAt
- durationMs
type: object
+ DbtSyncRun:
+ properties:
+ branchName:
+ description: The Cube branch this sync wrote its generated cubes to.
+ type: string
+ completedAt:
+ oneOf:
+ - type: string
+ description: >-
+ When the sync finished, as an ISO 8601 timestamp, or null while it is still running.
+ This is stamped by the process that ran the sync whereas startedAt is stamped by the
+ process that launched it — two different clocks — so for a run that failed moments
+ after starting, completedAt can even precede startedAt. Use durationMs to show how
+ long a run took rather than subtracting these.
+ - type: 'null'
+ deploymentId:
+ type: integer
+ durationMs:
+ oneOf:
+ - type: integer
+ description: >-
+ How long the sync took, in milliseconds, and the value to use when showing a
+ duration. Measured from a single clock, so it is never negative — prefer it over
+ subtracting startedAt from completedAt, which can disagree with it by a small skew.
+ - type: 'null'
+ errorMessage:
+ oneOf:
+ - type: string
+ description: Why the sync stopped. Present only for a failed run.
+ - type: 'null'
+ failedPhase:
+ oneOf:
+ - type: string
+ description: The phase that failed, e.g. dbt-compile.
+ - type: 'null'
+ gitRef:
+ oneOf:
+ - type: string
+ description: The dbt-repository ref this sync was run against, when pinned.
+ - type: 'null'
+ lastStage:
+ oneOf:
+ - type: string
+ description: The pipeline stage the run reached, e.g. COMPILING_DBT.
+ - type: 'null'
+ phases:
+ oneOf:
+ - items:
+ $ref: '#/components/schemas/DbtSyncRunPhase'
+ type: array
+ description: Per-phase timings, in the order they ran. Absent while the sync is running.
+ - type: 'null'
+ startedAt:
+ description: >-
+ When the sync started, as an ISO 8601 timestamp. See completedAt before comparing the
+ two.
+ type: string
+ stats:
+ oneOf:
+ - $ref: '#/components/schemas/DbtSyncRunStats'
+ - type: 'null'
+ status:
+ description: >-
+ RUNNING, COMPLETED, FAILED, CANCELLED or UNKNOWN. UNKNOWN means the outcome could not be
+ established (the run’s record was lost and the workflow engine no longer knows the
+ workflow) and is deliberately distinct from FAILED. Treat an unrecognized value as
+ non-terminal.
+ type: string
+ syncJobId:
+ type: string
+ trigger:
+ description: >-
+ What started the sync: manual (the Cube Cloud UI), api (this REST API), webhook (a push
+ to the dbt repository), agent (Cube AI), or unknown.
+ type: string
+ triggerContext:
+ oneOf:
+ - $ref: '#/components/schemas/DbtSyncRunTriggerContext'
+ - type: 'null'
+ userId:
+ oneOf:
+ - type: integer
+ description: The Cube user who started the sync, when a user started it.
+ - type: 'null'
+ required:
+ - syncJobId
+ - deploymentId
+ - status
+ - trigger
+ - branchName
+ - startedAt
+ type: object
+ DbtSyncRunListResponse:
+ properties:
+ items:
+ items:
+ $ref: '#/components/schemas/DbtSyncRun'
+ type: array
+ pageInfo:
+ $ref: '#/components/schemas/PageInfo'
+ required:
+ - items
+ - pageInfo
+ type: object
+ DbtSyncRunPhase:
+ properties:
+ durationMs:
+ description: How long the phase took, in milliseconds.
+ type: integer
+ outcome:
+ description: ok or failed.
+ type: string
+ phase:
+ description: Phase token, e.g. repo-clone, dbt-deps, dbt-compile.
+ type: string
+ startedOffsetMs:
+ description: When the phase began, in milliseconds after the sync started.
+ type: integer
+ required:
+ - phase
+ - startedOffsetMs
+ - durationMs
+ - outcome
+ type: object
+ DbtSyncRunStats:
+ properties:
+ cubeCount:
+ oneOf:
+ - type: integer
+ description: How many cubes the sync generated.
+ - type: 'null'
+ generatedFileCount:
+ oneOf:
+ - type: integer
+ description: How many files the sync wrote to the branch it created.
+ - type: 'null'
+ macros:
+ oneOf:
+ - type: integer
+ description: dbt macros found in the manifest.
+ - type: 'null'
+ models:
+ oneOf:
+ - type: integer
+ description: dbt models found in the manifest.
+ - type: 'null'
+ sources:
+ oneOf:
+ - type: integer
+ description: dbt sources found in the manifest.
+ - type: 'null'
+ type: object
+ DbtSyncRunTriggerContext:
+ properties:
+ headSha:
+ oneOf:
+ - type: string
+ - type: 'null'
+ pushedBranch:
+ oneOf:
+ - type: string
+ - type: 'null'
+ targetBranch:
+ oneOf:
+ - type: string
+ - type: 'null'
+ type: object
+ DbtSyncRunsQueryStatus:
+ enum:
+ - RUNNING
+ - COMPLETED
+ - FAILED
+ - CANCELLED
+ - UNKNOWN
+ type: string
+ DbtSyncRunsQueryTrigger:
+ enum:
+ - manual
+ - api
+ - webhook
+ - agent
+ - unknown
+ type: string
DbtSyncStatusResponse:
properties:
error:
@@ -6258,14 +6917,28 @@ components:
DeploymentEnvironmentTokensListResponse:
properties:
data:
+ deprecated: true
+ description: 'Deprecated: use `items` instead. Kept for backward compatibility.'
items:
$ref: '#/components/schemas/DeploymentEnvironmentToken'
type: array
+ items:
+ items:
+ $ref: '#/components/schemas/DeploymentEnvironmentToken'
+ type: array
+ pageInfo:
+ oneOf:
+ - $ref: '#/components/schemas/PageInfo'
+ - type: 'null'
pagination:
- $ref: '#/components/schemas/DeploymentsPagination'
+ oneOf:
+ - $ref: '#/components/schemas/DeploymentsPagination'
+ deprecated: true
+ description: 'Deprecated: use `pageInfo` instead. Kept for backward compatibility.'
+ - type: 'null'
required:
+ - items
- data
- - pagination
type: object
DeploymentEnvironmentType:
enum:
@@ -6276,14 +6949,28 @@ components:
DeploymentEnvironmentsListResponse:
properties:
data:
+ deprecated: true
+ description: 'Deprecated: use `items` instead. Kept for backward compatibility.'
items:
$ref: '#/components/schemas/DeploymentEnvironment'
type: array
+ items:
+ items:
+ $ref: '#/components/schemas/DeploymentEnvironment'
+ type: array
+ pageInfo:
+ oneOf:
+ - $ref: '#/components/schemas/PageInfo'
+ - type: 'null'
pagination:
- $ref: '#/components/schemas/DeploymentsPagination'
+ oneOf:
+ - $ref: '#/components/schemas/DeploymentsPagination'
+ deprecated: true
+ description: 'Deprecated: use `pageInfo` instead. Kept for backward compatibility.'
+ - type: 'null'
required:
+ - items
- data
- - pagination
type: object
DeploymentLogLine:
properties:
@@ -6317,6 +7004,10 @@ components:
items:
$ref: '#/components/schemas/DeploymentLogLine'
type: array
+ pageInfo:
+ oneOf:
+ - $ref: '#/components/schemas/PageInfo'
+ - type: 'null'
required:
- items
type: object
@@ -6355,6 +7046,10 @@ components:
items:
$ref: '#/components/schemas/DeploymentPod'
type: array
+ pageInfo:
+ oneOf:
+ - $ref: '#/components/schemas/PageInfo'
+ - type: 'null'
required:
- items
type: object
@@ -6371,9 +7066,7 @@ components:
- $ref: '#/components/schemas/DeploymentSettingsCreationMethod'
- type: 'null'
creationStep:
- oneOf:
- - $ref: '#/components/schemas/CreationStep'
- - type: 'null'
+ $ref: '#/components/schemas/CreationStep'
cspsConfig:
oneOf:
- $ref: '#/components/schemas/CspsConfig'
@@ -6407,9 +7100,7 @@ components:
deployProjectRoot:
type: string
deploymentUrl:
- oneOf:
- - type: string
- - type: 'null'
+ type: string
id:
type: integer
isCold:
@@ -6466,6 +7157,8 @@ components:
- name
- template
- cloudProvider
+ - creationStep
+ - deploymentUrl
- releaseChannel
- deployBranchName
- deployBranchReadOnly
@@ -6541,12 +7234,16 @@ components:
type: string
DeploymentVersionsResponse:
properties:
- data:
+ items:
items:
$ref: '#/components/schemas/DeploymentVersion'
type: array
+ pageInfo:
+ oneOf:
+ - $ref: '#/components/schemas/PageInfo'
+ - type: 'null'
required:
- - data
+ - items
type: object
DeploymentsListResponse:
properties:
@@ -6554,8 +7251,14 @@ components:
oneOf:
- minimum: 0
type: integer
+ deprecated: true
+ description: >-
+ Deprecated: total number of accessible deployments, ignoring pagination. Kept for
+ backward compatibility.
- type: 'null'
data:
+ deprecated: true
+ description: 'Deprecated: use `items` instead. Kept for backward compatibility.'
items:
$ref: '#/components/schemas/Deployment'
type: array
@@ -6569,14 +7272,18 @@ components:
- type: 'null'
pagination:
$ref: '#/components/schemas/DeploymentsPagination'
+ deprecated: true
+ description: 'Deprecated: use `pageInfo` instead. Kept for backward compatibility.'
totalCount:
oneOf:
- minimum: 0
type: integer
+ deprecated: true
+ description: 'Deprecated: use `count`. Kept for backward compatibility.'
- type: 'null'
required:
- - data
- items
+ - data
- pagination
type: object
DeploymentsPagination:
@@ -6610,6 +7317,33 @@ components:
required:
- active
type: object
+ DisconnectReportFromWorkbookInput:
+ properties:
+ anchorCell:
+ oneOf:
+ - type: string
+ - type: 'null'
+ externalWorkbookId:
+ oneOf:
+ - type: string
+ - type: 'null'
+ placementId:
+ oneOf:
+ - type: string
+ - type: 'null'
+ resultLocation:
+ oneOf:
+ - type: string
+ - type: 'null'
+ sheetId:
+ oneOf:
+ - type: string
+ - type: 'null'
+ sheetName:
+ oneOf:
+ - type: string
+ - type: 'null'
+ type: object
DuplicateWorkbookInput:
properties:
shared:
@@ -6727,6 +7461,20 @@ components:
type: object
EmbedSessionSettings:
properties:
+ allowChatWorkspaceAuthoring:
+ oneOf:
+ - type: boolean
+ description: >-
+ Whether AI Chat authenticated with this session may create or modify persistent Cube
+ Workspace content. Set to `false` for headless chat integrations that render answers
+ in their own UI: ad-hoc data analysis and inline tables/charts remain available, but
+ the agent cannot save or update standalone explorations/reports, create or modify
+ workbooks, create or publish dashboards, or direct users to those Cube UI surfaces.
+ Omit or set to `true` to preserve the session user's role-derived authoring
+ capabilities; `true` never grants access the user does not already have. This
+ setting changes only AI Chat tools and instructions. It does not change user roles
+ or permissions for direct API calls.
+ - type: 'null'
showDashboardChat:
oneOf:
- type: boolean
@@ -6923,10 +7671,21 @@ components:
EnvVariablesListResponse:
properties:
data:
+ deprecated: true
+ description: 'Deprecated: use `items` instead. Kept for backward compatibility.'
+ items:
+ $ref: '#/components/schemas/EnvVariableItem'
+ type: array
+ items:
items:
$ref: '#/components/schemas/EnvVariableItem'
type: array
+ pageInfo:
+ oneOf:
+ - $ref: '#/components/schemas/PageInfo'
+ - type: 'null'
required:
+ - items
- data
type: object
FileHash:
@@ -7157,9 +7916,9 @@ components:
- $ref: '#/components/schemas/EmbedSessionSettings'
type: object
description: >-
- Per-session overrides for embed behavior. Each key is tri-state: omit it to inherit
- the account-wide setting, or set `true`/`false` to force the behavior for every
- embed viewed with this session, taking precedence over the account-wide setting.
+ Per-session overrides for embed behavior, stored in the signed embed token. Each
+ documented key applies only to this session; omitted keys preserve their existing
+ default behavior.
- type: 'null'
tenantGroupDefinitions:
oneOf:
@@ -7268,10 +8027,21 @@ components:
GitHubBranchesListResponse:
properties:
data:
+ deprecated: true
+ description: 'Deprecated: use `items` instead. Kept for backward compatibility.'
+ items:
+ $ref: '#/components/schemas/GitHubBranchResponse'
+ type: array
+ items:
items:
$ref: '#/components/schemas/GitHubBranchResponse'
type: array
+ pageInfo:
+ oneOf:
+ - $ref: '#/components/schemas/PageInfo'
+ - type: 'null'
required:
+ - items
- data
type: object
GitHubConnectStatusResponse:
@@ -7306,10 +8076,21 @@ components:
GitHubInstallationsListResponse:
properties:
data:
+ deprecated: true
+ description: 'Deprecated: use `items` instead. Kept for backward compatibility.'
+ items:
+ $ref: '#/components/schemas/GitHubInstallationResponse'
+ type: array
+ items:
items:
$ref: '#/components/schemas/GitHubInstallationResponse'
type: array
+ pageInfo:
+ oneOf:
+ - $ref: '#/components/schemas/PageInfo'
+ - type: 'null'
required:
+ - items
- data
type: object
GitHubRepoResponse:
@@ -7328,10 +8109,21 @@ components:
GitHubReposListResponse:
properties:
data:
+ deprecated: true
+ description: 'Deprecated: use `items` instead. Kept for backward compatibility.'
+ items:
+ $ref: '#/components/schemas/GitHubRepoResponse'
+ type: array
+ items:
items:
$ref: '#/components/schemas/GitHubRepoResponse'
type: array
+ pageInfo:
+ oneOf:
+ - $ref: '#/components/schemas/PageInfo'
+ - type: 'null'
required:
+ - items
- data
type: object
GroupDefinition:
@@ -7793,6 +8585,14 @@ components:
items:
type: string
type: array
+ measuresAxis:
+ oneOf:
+ - $ref: '#/components/schemas/PivotItemsMeasuresAxis'
+ - type: 'null'
+ measuresPosition:
+ oneOf:
+ - $ref: '#/components/schemas/PivotItemsMeasuresPosition'
+ - type: 'null'
rows:
items:
type: string
@@ -7817,6 +8617,14 @@ components:
items:
type: string
type: array
+ measuresAxis:
+ oneOf:
+ - $ref: '#/components/schemas/PivotItemsInputMeasuresAxis'
+ - type: 'null'
+ measuresPosition:
+ oneOf:
+ - $ref: '#/components/schemas/PivotItemsInputMeasuresPosition'
+ - type: 'null'
rows:
items:
type: string
@@ -7827,6 +8635,26 @@ components:
- rows
- filters
type: object
+ PivotItemsInputMeasuresAxis:
+ enum:
+ - columns
+ - rows
+ type: string
+ PivotItemsInputMeasuresPosition:
+ enum:
+ - before
+ - after
+ type: string
+ PivotItemsMeasuresAxis:
+ enum:
+ - columns
+ - rows
+ type: string
+ PivotItemsMeasuresPosition:
+ enum:
+ - before
+ - after
+ type: string
PostTokenBySessionIdInput:
properties:
sessionId:
@@ -7899,6 +8727,33 @@ components:
- stderr
- inputRowCount
type: object
+ RefreshReportInput:
+ properties:
+ anchorCell:
+ oneOf:
+ - type: string
+ - type: 'null'
+ externalWorkbookId:
+ oneOf:
+ - type: string
+ - type: 'null'
+ placementId:
+ oneOf:
+ - type: string
+ - type: 'null'
+ resultLocation:
+ oneOf:
+ - type: string
+ - type: 'null'
+ sheetId:
+ oneOf:
+ - type: string
+ - type: 'null'
+ sheetName:
+ oneOf:
+ - type: string
+ - type: 'null'
+ type: object
RegionResponse:
properties:
id:
@@ -7936,10 +8791,21 @@ components:
RegionsListResponse:
properties:
data:
+ deprecated: true
+ description: 'Deprecated: use `items` instead. Kept for backward compatibility.'
+ items:
+ $ref: '#/components/schemas/RegionResponse'
+ type: array
+ items:
items:
$ref: '#/components/schemas/RegionResponse'
type: array
+ pageInfo:
+ oneOf:
+ - $ref: '#/components/schemas/PageInfo'
+ - type: 'null'
required:
+ - items
- data
type: object
RemoveDataModelFilesInput:
@@ -8151,18 +9017,63 @@ components:
type: string
ReportPlacement:
properties:
+ anchorCell:
+ oneOf:
+ - type: string
+ - type: 'null'
endResultCell:
oneOf:
- type: string
- type: 'null'
+ host:
+ oneOf:
+ - $ref: '#/components/schemas/ReportPlacementHost'
+ - type: 'null'
+ id:
+ oneOf:
+ - type: string
+ - type: 'null'
+ placedAt:
+ oneOf:
+ - type: string
+ - type: 'null'
+ placedByUserId:
+ oneOf:
+ - type: integer
+ - type: 'null'
+ refreshedAt:
+ oneOf:
+ - type: string
+ - type: 'null'
+ refreshedByUserId:
+ oneOf:
+ - type: integer
+ - type: 'null'
resultLocation:
type: string
+ sheetId:
+ oneOf:
+ - type: string
+ - type: 'null'
+ sheetName:
+ oneOf:
+ - type: string
+ - type: 'null'
workbookId:
type: string
+ workbookName:
+ oneOf:
+ - type: string
+ - type: 'null'
required:
- workbookId
- resultLocation
type: object
+ ReportPlacementHost:
+ enum:
+ - GOOGLE_SHEETS
+ - EXCEL
+ type: string
ReportSnapshot:
properties:
description:
@@ -8202,6 +9113,10 @@ components:
oneOf:
- $ref: '#/components/schemas/ReportSnapshotPreferences'
- type: 'null'
+ pythonCode:
+ oneOf:
+ - type: string
+ - type: 'null'
reportId:
type: integer
spec:
@@ -8398,11 +9313,23 @@ components:
SourceTreeResponse:
properties:
data:
+ deprecated: true
+ description: 'Deprecated: use `items` instead. Kept for backward compatibility.'
+ items:
+ additionalProperties: true
+ type: object
+ type: array
+ items:
items:
additionalProperties: true
type: object
type: array
+ pageInfo:
+ oneOf:
+ - $ref: '#/components/schemas/PageInfo'
+ - type: 'null'
required:
+ - items
- data
type: object
StartDbtSyncInput:
@@ -8870,6 +9797,10 @@ components:
oneOf:
- type: integer
- type: 'null'
+ host:
+ oneOf:
+ - $ref: '#/components/schemas/UpdateReportInputHost'
+ - type: 'null'
jsonQuery:
oneOf:
- type: string
@@ -8887,6 +9818,10 @@ components:
oneOf:
- $ref: '#/components/schemas/PivotItems'
- type: 'null'
+ placementId:
+ oneOf:
+ - type: string
+ - type: 'null'
publicId:
oneOf:
- pattern: ^[0-9A-Za-z]+$
@@ -8898,6 +9833,10 @@ components:
oneOf:
- type: string
- type: 'null'
+ sheetId:
+ oneOf:
+ - type: string
+ - type: 'null'
sqlQuery:
oneOf:
- type: string
@@ -8910,7 +9849,16 @@ components:
oneOf:
- type: integer
- type: 'null'
+ workbookName:
+ oneOf:
+ - type: string
+ - type: 'null'
type: object
+ UpdateReportInputHost:
+ enum:
+ - GOOGLE_SHEETS
+ - EXCEL
+ type: string
UpdateWorkbookInput:
properties:
folderId:
@@ -9271,12 +10219,28 @@ components:
UserAttributesListResponse:
properties:
count:
+ deprecated: true
+ description: >-
+ Deprecated: total number of user attributes, ignoring pagination. Kept for backward
+ compatibility.
+ minimum: 0
type: integer
data:
+ deprecated: true
+ description: 'Deprecated: use `items` instead. Kept for backward compatibility.'
+ items:
+ $ref: '#/components/schemas/UserAttribute'
+ type: array
+ items:
items:
$ref: '#/components/schemas/UserAttribute'
type: array
+ pageInfo:
+ oneOf:
+ - $ref: '#/components/schemas/PageInfo'
+ - type: 'null'
required:
+ - items
- data
- count
type: object
@@ -9690,8 +10654,14 @@ components:
oneOf:
- minimum: 0
type: integer
+ deprecated: true
+ description: >-
+ Deprecated: total number of accessible workbooks, ignoring pagination. Kept for
+ backward compatibility.
- type: 'null'
data:
+ deprecated: true
+ description: 'Deprecated: use `items` instead. Kept for backward compatibility.'
items:
$ref: '#/components/schemas/Workbook'
type: array
diff --git a/docs-mintlify/api-reference/changelog.mdx b/docs-mintlify/api-reference/changelog.mdx
index d9815203b6e03..d99963b7378a0 100644
--- a/docs-mintlify/api-reference/changelog.mdx
+++ b/docs-mintlify/api-reference/changelog.mdx
@@ -7,6 +7,63 @@ rss: true
{/* GENERATED FILE — do not edit by hand. */}
{/* Run scripts/extract-changelog.js against the platform client CHANGELOG.md. */}
+
+ ### Added
+
+ #### dbt Sync — status, results and history
+
+ - `GET /api/v1/deployments/{deploymentId}/dbt-sync` (`DbtSyncPublicController.listDbtSyncs`) — sync run history, filterable by `status`/`trigger`, cursor-paginated. New schemas: `DbtSyncRun`, `DbtSyncRunListResponse`, `DbtSyncRunPhase`, `DbtSyncRunStats`, `DbtSyncRunTriggerContext`, `DbtSyncRunsQueryStatus`, `DbtSyncRunsQueryTrigger`.
+ - `GET /api/v1/deployments/{deploymentId}/dbt-sync/{syncJobId}` (`getDbtSyncStatus`) — poll a running sync. New schemas: `DbtSyncStatusResponse`, `DbtSyncProgress`.
+ - `GET .../dbt-sync/{syncJobId}/result` (`getDbtSyncResult`) and `GET .../dbt-sync/{syncJobId}/logs` (`getDbtSyncLogs`). New schemas: `DbtSyncResultResponse`, `DbtSyncGeneratedFile`, `DbtSyncManifestStats`, `DbtSyncLogEntry`, `DbtSyncLogEntriesListResponse`.
+ - `DELETE .../dbt-sync/{syncJobId}` (`cancelDbtSync`) — cancel a running sync. New schema: `DbtSyncCancelResponse`.
+
+ #### Data model — branch cleanup, staging environments and validation
+
+ - `DELETE /build/api/v1/deployments/{deploymentId}/branches` (`DataModelPublicController.removeBranch`, `branchName` as a query param since branch names contain slashes) — deletes a branch and its git ref, re-parenting any children onto the deleted branch's own parent; `removeOnUpstream` additionally deletes the ref on the connected GitHub/GitLab remote. New schema: `DataModelWriteResponse` (shared with the existing file-write endpoints).
+ - `PUT .../branches/staging-environment` (`setBranchStagingEnvironment`) — enable or disable a branch's always-on staging environment (`/dev-mode/{branchName}/cubejs-api/v1`). New schemas: `SetBranchStagingEnvironmentRequest`, `SetBranchStagingEnvironmentResponse`. `BranchResponse` (from `listBranches`) reads the flag back via a new `isStagingEnvironmentEnabled`.
+ - `GET .../data-model/validate` (`validateDataModel`) — compiles the deploy branch, a named branch, or the caller's dev-mode branch and reports compilation errors as a 200 response (`valid: false`), not a request error. New schemas: `DataModelValidationResponse`, `DataModelValidationError`, `DataModelValidationResponseMode`.
+
+ #### Deployment settings, versions and the spec endpoint
+
+ - `GET /api/v1/deployments/{deploymentId}/settings` (`DeploymentsPublicController.getDeploymentSettings`) — every deployment setting in one payload (secrets excluded); writes go through the existing `PUT /:deploymentId`. New schema: `DeploymentSettings` (+ its `cloudProvider`/`creationMethod`/`deployMode`/`releaseChannel`/`repoType`/`template` enums).
+ - `GET /api/v1/deployments/{deploymentId}/versions` (`listDeploymentVersions`) — the Cube versions this deployment can switch to (release-channel heads plus prior versions run before). New schemas: `DeploymentVersion`, `DeploymentVersionsResponse`, `DeploymentVersionReleaseChannel`.
+ - `PUT /api/v1/deployments/{deploymentId}` (`updateDeployment`) / `UpdateDeploymentInput` now accepts `cloudProvider`, `region`, `releaseChannel`, `releaseChannelVersion`, `releaseChannelVersionHold`, `template`, `templateVariables`, `defaultLaunchpadViewGroup`, and `launchpadTabs` — the same settings surface `GET .../settings` now reads back. `releaseChannelVersion` must be one of the versions `GET .../versions` lists for the target channel.
+ - `GET /api/v1/spec` (`OpenApiSpecPublicController.getSpec`) — the full OpenAPI 3.1 document served by the build handling the request, for runtime discovery by clients and agents.
+
+ #### Reports
+
+ - `DELETE /api/v1/deployments/{deploymentId}/reports/{reportId}/connect-workbook` (`ReportsPublicController.disconnectReportFromWorkbook`) — removes a single sheet+anchor placement of a report from a spreadsheet, leaving every other placement (in this workbook and others) alone. Addressed by `placementId`, or by `externalWorkbookId` + `sheetName` + `anchorCell`. New schema: `DisconnectReportFromWorkbookInput`.
+ - `PUT .../reports/{reportId}/refresh` now takes a body: new schema `RefreshReportInput` (`placementId`, or `externalWorkbookId` + `sheetName`/`sheetId`/`anchorCell`), addressing the same way as disconnect.
+ - Report placements are now individually addressable and trackable (new `ConnectReportToWorkbookInputHost` / `CreateReportInputHost` / `UpdateReportInputHost` enums, `"GOOGLE_SHEETS" | "EXCEL"`): `ConnectReportToWorkbookInput` gained `placementId`, `sheetId`, `sheetName`, `anchorCell`, `workbookName` and `host`; `CreateReportInput` gained `host`, `sheetId` and `workbookName`; `UpdateReportInput` gained `host`, `placementId`, `sheetId` and `workbookName`. `ReportPlacement` gained the matching `id`, `host`, `sheetId`, `sheetName`, `anchorCell`, `workbookName`, plus `placedAt`/`placedByUserId` and `refreshedAt`/`refreshedByUserId` (new `ReportPlacementHost` enum). `ReportSnapshot` gained `isSourceReport` and `pythonCode`.
+
+ #### Cursor pagination on previously offset-only or unpaginated lists
+
+ - `first`/`after` → `items` + `pageInfo` cursor pagination added to: deployment env variables (`GET .../env-vars`), GitHub branches/installations/repositories (`GitHubPublicController.*`), user attributes (previously `offset`/`limit`), `GET /api/v1/regions/`, report folders, data-model branches and files (`listBranches`/`listFiles`, `BranchesListResponse`/`SourceTreeResponse`), and deployment logs/pods (`DeploymentLogsResponse`/`DeploymentPodsResponse` gain `pageInfo`). Newly-paginated responses keep the legacy `data`/`count`/`pagination` fields, marked `@deprecated`.
+ - `UserGroupsPublicController.listGroups` and `UsersPublicController.getUsers` already had `first`/`after`; what's new on both is a `search` query param (case-insensitive substring match on group name / first name, username, email).
+ - `DashboardFilterOperator` / `DashboardFilterInputOperator` gained `is_empty` / `is_not_empty`.
+
+ #### Other new fields
+
+ - New `TimezoneSettings` schema, plus `timezoneSettings` on `AppConfigResponse` and `resolvedTimezone` / `deployBranchName` / `timezoneSettings` on `AIEngineerSettings`.
+ - New `CspsConfig` schema (customer-storage-provider config, mirrored on the existing `CspsConfigInput`).
+ - `scopeDeploymentId` added to the agent-space config schemas.
+ - `measuresAxis` / `measuresPosition` added to `PivotItems` / `PivotItemsInput` (new `PivotItemsMeasuresAxis` / `PivotItemsMeasuresPosition` / `PivotItemsInputMeasuresAxis` / `PivotItemsInputMeasuresPosition` enums, `"columns" | "rows"` and `"before" | "after"`).
+ - `name` / `userCount` added to `InheritedGroupPolicyDto` and `ResourceGroupPolicyDto`.
+ - `allowChatWorkspaceAuthoring` added to `EmbedSessionSettings`.
+ - `ref` added to `StartDbtSyncInput`.
+
+ ### Changed
+
+ - **BREAKING (TypeScript):** `DeploymentEnvironmentsListResponse.pagination` and `DeploymentEnvironmentTokensListResponse.pagination` moved from required to `pagination?: DeploymentsPagination | null` now that both responses also carry `items`/`pageInfo`. Code under `strictNullChecks` reading `res.pagination.currentPage` without a null check no longer compiles.
+ - **BREAKING (TypeScript):** `NotificationsPublicController.getRecipients`'s cursor params were narrowed — `first?: number | string | null` → `first?: number | null` and `after?: string | number | null` → `after?: string | null` — cleaning up a hand-declared `@OpenAPI({ parameters })` that had merged positionally with the real cursor-param types. A caller passing a numeric `after` or a stringified `first` (both accepted, if oddly typed, before) no longer compiles.
+ - `DashboardWidgetDtoType` / `DashboardWidgetInputType` no longer advertise `TABS_CONTAINER` in the type — replaced by `PARENT`, `SPACER`, `DIVIDER`, `CONTAINER` (a `CONTAINER` with `config.layout: 'tabs'` is the current shape for a tabbed container). Existing stored or sent `TABS_CONTAINER` values are still healed server-side for backward compatibility (CUB-3634), so this narrows the advertised type rather than breaking existing integrations at runtime — new code should target `CONTAINER` + `layout`.
+ - `PUT /api/v1/deployments/{deploymentId}` (`updateDeployment`) description clarified: writes are a partial merge (omitted fields are left alone, `templateVariables` is merged key-by-key), and `releaseChannelVersion` is validated against `GET .../versions`.
+
+ ### Removed
+
+ - **BREAKING:** `POST /api/v1/deployments/{deploymentId}/workbooks/{workbookId}/dashboard/ai-widget-thread` (`WorkbooksPublicController.updatePublishedDashboardAiWidgetThread`) and its `UpdatePublishedAiWidgetThreadInput` schema (added in `0.2.0`). It persisted an AI-analysis thread id + checksum into the published dashboard config — the mechanism that caused read-only/anonymous viewers to hit `WorkbookEdit`/403s and let one viewer's filter state clobber the shared config (CUB-3898). AI-analysis results are now cached server-side keyed by dashboard state, so no client-facing endpoint replaces it. This is a deliberate breaking removal; SDK consumers referencing the operation or schema should drop those references.
+
+
> Supersedes `0.2.1`, which never reached npm because its publish job failed.
> Upgrading from `0.2.0` picks up both releases, so the `0.2.1` entries are
diff --git a/docs-mintlify/api-reference/introduction.mdx b/docs-mintlify/api-reference/introduction.mdx
index d7f69f6cb67cb..33c7d132f9412 100644
--- a/docs-mintlify/api-reference/introduction.mdx
+++ b/docs-mintlify/api-reference/introduction.mdx
@@ -79,7 +79,7 @@ Resources by entity:
| [Data Model Uploads](/api-reference/data-model-uploads/content-hashes-of-the-current-data-model-files-for-upload-diffing) | `/build/api/v1/deployments/{deploymentId}/data-model` | v1 |
| [GitHub](/api-reference/github/list-repositories-accessible-to-a-github-app-installation) | `/api/v1/github` | v1 |
| [GitHub Connection](/api-reference/github-connection/connect-a-deployment-to-a-github-repository-and-trigger-a-build) | `/build/api/v1/deployments/{deploymentId}/github/connect` | v1 |
-| [dbt Sync](/api-reference/dbt-sync/start-a-dbt-sync-for-a-deployment) | `/api/v1/deployments/{deploymentId}/dbt-sync` | v1 |
+| [dbt Sync](/api-reference/dbt-sync/list-dbt-syncs-for-a-deployment) | `/api/v1/deployments/{deploymentId}/dbt-sync` | v1 |
| [Folders](/api-reference/folders/list-folders) | `/api/v1/deployments/{deploymentId}/folders` | v1 |
| [Reports](/api-reference/reports/list-reports) | `/api/v1/deployments/{deploymentId}/reports` | v1 |
| [Workbooks](/api-reference/workbooks/get-workbooks) | `/api/v1/deployments/{deploymentId}/workbooks` | v1 |
diff --git a/docs-mintlify/docs.json b/docs-mintlify/docs.json
index 90bed16172475..b86750bd9413f 100644
--- a/docs-mintlify/docs.json
+++ b/docs-mintlify/docs.json
@@ -71,6 +71,7 @@
"docs/explore-analyze/charts/chart-types/area",
"docs/explore-analyze/charts/chart-types/scatter",
"docs/explore-analyze/charts/chart-types/pie",
+ "docs/explore-analyze/charts/chart-types/sankey",
"docs/explore-analyze/charts/chart-types/heatmap",
"docs/explore-analyze/charts/chart-types/boxplot",
"docs/explore-analyze/charts/chart-types/table",
@@ -674,6 +675,7 @@
"recipes/data-modeling/dbt",
"recipes/data-modeling/using-dynamic-measures",
"recipes/data-modeling/dynamic-union-tables",
+ "recipes/data-modeling/cross-data-source-queries",
"recipes/data-modeling/polymorphic-cubes",
"recipes/data-modeling/custom-order"
]
@@ -808,12 +810,14 @@
"pages": [
"GET /build/api/v1/deployments/{deploymentId}/branches",
"POST /build/api/v1/deployments/{deploymentId}/branches",
+ "DELETE /build/api/v1/deployments/{deploymentId}/branches",
"PUT /build/api/v1/deployments/{deploymentId}/branches/staging-environment",
"POST /build/api/v1/deployments/{deploymentId}/commit",
"GET /build/api/v1/deployments/{deploymentId}/data-model/files",
"PUT /build/api/v1/deployments/{deploymentId}/data-model/files",
"DELETE /build/api/v1/deployments/{deploymentId}/data-model/files",
"POST /build/api/v1/deployments/{deploymentId}/data-model/files/rename",
+ "GET /build/api/v1/deployments/{deploymentId}/data-model/validate",
"POST /build/api/v1/deployments/{deploymentId}/dev-mode",
"DELETE /build/api/v1/deployments/{deploymentId}/dev-mode",
"POST /build/api/v1/deployments/{deploymentId}/merge",
@@ -852,9 +856,11 @@
"group": "dbt Sync",
"openapi": "/api-reference/api.yaml",
"pages": [
+ "GET /api/v1/deployments/{deploymentId}/dbt-sync",
"POST /api/v1/deployments/{deploymentId}/dbt-sync",
"GET /api/v1/deployments/{deploymentId}/dbt-sync/{syncJobId}",
"DELETE /api/v1/deployments/{deploymentId}/dbt-sync/{syncJobId}",
+ "GET /api/v1/deployments/{deploymentId}/dbt-sync/{syncJobId}/logs",
"GET /api/v1/deployments/{deploymentId}/dbt-sync/{syncJobId}/result"
]
},
@@ -880,6 +886,7 @@
"PUT /api/v1/deployments/{deploymentId}/reports/{reportId}",
"DELETE /api/v1/deployments/{deploymentId}/reports/{reportId}",
"PUT /api/v1/deployments/{deploymentId}/reports/{reportId}/connect-workbook",
+ "DELETE /api/v1/deployments/{deploymentId}/reports/{reportId}/connect-workbook",
"PUT /api/v1/deployments/{deploymentId}/reports/{reportId}/refresh"
]
},
diff --git a/docs-mintlify/docs/data-modeling/concepts/data-blending.mdx b/docs-mintlify/docs/data-modeling/concepts/data-blending.mdx
index 044535d218ea9..f7bf3e6c5c35b 100644
--- a/docs-mintlify/docs/data-modeling/concepts/data-blending.mdx
+++ b/docs-mintlify/docs/data-modeling/concepts/data-blending.mdx
@@ -18,6 +18,11 @@ efficient for large volumes of data.
The other situation in which data blending could be a better approach than joining
on date is when the two tables have mostly the same columns, such as in the example below.
+This pattern builds one cube over a union of the underlying cubes' SQL, so all of
+them must live in the same data source. To append rows from cubes in *different*
+databases, see [querying across data
+sources](/recipes/data-modeling/cross-data-source-queries).
+
For an example, consider an omnichannel store which has both online and offline sales. Let's
calculate summary metrics for revenue, customer count, etc. We have a
`retail_orders` cube for offline sales:
diff --git a/docs-mintlify/docs/explore-analyze/analytics-chat.mdx b/docs-mintlify/docs/explore-analyze/analytics-chat.mdx
index a5c4671f82d56..d92407675d616 100644
--- a/docs-mintlify/docs/explore-analyze/analytics-chat.mdx
+++ b/docs-mintlify/docs/explore-analyze/analytics-chat.mdx
@@ -13,10 +13,10 @@ The AI agent interprets your questions, generates queries against your semantic
- **Natural language queries** – Ask questions in plain language without knowing SQL
- **Multi-query reasoning** – The AI creates and synthesizes multiple queries for complex questions
-- **Python analysis** – For work SQL can't express — forecasting, cohort analysis, statistical tests — the agent can run [Python](/docs/explore-analyze/workbooks/python-analysis) and render the result inline, then save it to a workbook as a re-runnable report (in preview)
+- **Python analysis** – For work SQL can't express — forecasting, cohort analysis, statistical tests — the agent can run [Python](/docs/explore-analyze/workbooks/python-analysis) and render the result inline, then save it as a re-runnable report or exploration (in preview)
- **Semantic model integration** – All queries run against your semantic model with proper access control and security, honoring the active [security context](/docs/explore-analyze/workbooks/querying-data#applying-a-security-context)—including an override applied by a developer or admin
- **Queued messages** – Send follow-up messages while the agent is still processing
-- **Save to Workbook** – Export insights to [Workbooks](/docs/explore-analyze/workbooks) for further analysis
+- **Save your results** – Ask the agent to save a result as a [report](/docs/explore-analyze/workbooks) inside a workbook, or as a standalone [exploration](/docs/explore-analyze/explore#saving-explorations) when you don't want to create a workbook
## Discover available fields
diff --git a/docs-mintlify/docs/explore-analyze/charts/chart-types/index.mdx b/docs-mintlify/docs/explore-analyze/charts/chart-types/index.mdx
index 1783f2f1b7c42..caeb9d3fdbd4d 100644
--- a/docs-mintlify/docs/explore-analyze/charts/chart-types/index.mdx
+++ b/docs-mintlify/docs/explore-analyze/charts/chart-types/index.mdx
@@ -10,6 +10,7 @@ Cube includes a library of built-in chart types covering the most common visuali
- [Area](/docs/explore-analyze/charts/chart-types/area)
- [Scatter](/docs/explore-analyze/charts/chart-types/scatter)
- [Pie & donut](/docs/explore-analyze/charts/chart-types/pie)
+- [Sankey](/docs/explore-analyze/charts/chart-types/sankey)
- [Heatmap](/docs/explore-analyze/charts/chart-types/heatmap)
- [Boxplot](/docs/explore-analyze/charts/chart-types/boxplot)
- [Table](/docs/explore-analyze/charts/chart-types/table)
@@ -79,9 +80,9 @@ Otherwise Cube checks these in order and takes the first that matches:
The two [bar](/docs/explore-analyze/charts/chart-types/bar) rows are variants of the same chart
type, not separate ones.
-Pie, area, boxplot and HTML are never recommended — pick them yourself. Pie's shape, one measure
+Pie, sankey, area, boxplot and HTML are never recommended — pick them yourself. Pie's shape, one measure
across a few categories, is the one Bar already answers, and a bar chart compares those values more
-accurately. All four stay available in the picker like any other chart type.
+accurately. All five stay available in the picker like any other chart type.
### When nothing is recommended
diff --git a/docs-mintlify/docs/explore-analyze/charts/chart-types/sankey.mdx b/docs-mintlify/docs/explore-analyze/charts/chart-types/sankey.mdx
new file mode 100644
index 0000000000000..86b439cdf11fb
--- /dev/null
+++ b/docs-mintlify/docs/explore-analyze/charts/chart-types/sankey.mdx
@@ -0,0 +1,75 @@
+---
+title: Sankey
+description: Show flows between nodes, such as order status to year or channel to conversion.
+---
+
+Sankey diagrams draw one node per distinct endpoint and one ribbon per flow, with each ribbon's thickness proportional to its value. Best for showing how a quantity divides and moves between two sets of things: order statuses across years, traffic channels into outcomes, or one step of a user journey into the next.
+
+{/* Screenshot: sankey chart — order counts flowing from four statuses on the left to years on the right, with node labels on both sides. Place directly below this paragraph, full width. (hidden — replace this comment with
when image is ready) */}
+
+## Fields
+
+A sankey needs exactly three fields, picked on the Fields tab:
+
+- **Source** — a dimension (a time dimension works too, labelled by its grain). The node a flow leaves.
+- **Target** — a dimension. The node a flow enters.
+- **Value** — a measure. Each row's value becomes one ribbon's thickness.
+
+Each result row is read as one flow, so a query grouped by two dimensions with one measure draws directly. **Swap** exchanges the source and target, which reverses every ribbon's direction.
+
+{/* Screenshot: Fields tab with the Source, Target and Value selectors bound, and the Swap button below them. Place inline, 50% width, right-aligned. (hidden — replace this comment with
when image is ready) */}
+
+Stages come from shared node names rather than from a stage field: rows `A → B` and `B → C` draw as a three-stage diagram, because `B` appears as both a target and a source. Two dimensions that share no values always draw two stages.
+
+A row with a blank endpoint, or a value of zero or less, is not a flow and is left out — a sankey cannot draw a ribbon with no thickness.
+
+Cyclic data is rejected rather than drawn. A flow that returns to a node it already passed through has no left-to-right layout, so the chart names the loop and asks you to remove one hop.
+
+## Node grouping
+
+A wide breakdown can return more flows than a diagram can carry, and the small ones become unreadable first. Node grouping folds them into a single **Other** node per side, so the tail stays represented as an aggregate instead of vanishing. It is off until you pick a mode, in the Node grouping section of the Fields tab:
+
+- **Minimal share** — fold every node below a percentage of its side's total. A node sitting exactly on the threshold is kept.
+- **Top N nodes** — keep the N largest nodes per side. Tied nodes at the cutoff are kept together, so asking for the top 3 can keep more than 3 when third place is shared.
+
+The two sides fold independently, each against its own total, because a node that is small as a target may be large as a source. The bucket's name is yours to set; leave it blank for **Other**. When both sides fold at once, the two buckets are named apart — **Other (source)** and **Other (target)** — so one is not read as the other.
+
+{/* Screenshot: Node grouping section with Minimal share selected and a threshold entered. Place inline, 50% width, right-aligned. (hidden — replace this comment with
when image is ready) */}
+
+A single node is never folded on its own — replacing one named node with a same-sized **Other** would say less than leaving it alone. A node whose own value already matches the bucket's name is also left out of the bucket, so a real category called "Other" is never merged into it.
+
+The bucket stands for a set of members rather than one, so it has no query behind it: clicking a ribbon that touches it does not drill down.
+
+## Truncation
+
+Independently of grouping, a sankey draws at most 50 flows, keeping the largest. This is a backstop against a runaway result, not a legibility control — when it removes anything, a warning appears beside the Flows heading on the Fields tab saying how many of the total it is showing, pointing at node grouping, which is the real answer for a diagram too wide to read.
+
+## Data labels
+
+Each node carries its name and, optionally, its value. Both are independent buttons in the Data labels section of the Style tab: **Name** is on by default, **Value** is off. With both on, the value follows the name in parentheses; with only the value on, the node carries a bare number; with both off the diagram draws no labels.
+
+A node's value is the larger of its inflow and its outflow, and takes the value field's own number format.
+
+Labels sit to the right of their node, except in the final stage, where they flip to the left so they stay inside the frame.
+
+## Color
+
+Each node takes its own color from the palette selected in the **Palette** dropdown on the Style tab. The color follows the node's name rather than its position, so filtering or re-sorting the query does not repaint the diagram. See [Palettes](/docs/explore-analyze/charts/configuration/color-and-stacking#palettes) for the built-in palettes and how to define a custom one.
+
+The **Flows** control sets where a ribbon takes its color from:
+
+- **Source** — the color of the node the ribbon leaves. The default.
+- **Neutral** — one muted color for every ribbon, leaving the nodes to carry the palette.
+- **Target** — the color of the node the ribbon enters.
+
+{/* Screenshot: Style tab Color section with the Palette dropdown and the Flows buttons. Place inline, 50% width, right-aligned. (hidden — replace this comment with
when image is ready) */}
+
+## Tooltips
+
+Hovering a ribbon shows the result columns named in **Tooltip fields** on the Fields tab, which starts as every column in the query except the three the diagram already draws — source, target and value. Add them back like any other field if you want them repeated. Hovering a node shows its name and value.
+
+See [Tooltips](/docs/explore-analyze/charts/configuration/tooltips) for adding, removing and reordering fields.
+
+## Drill down
+
+Clicking a ribbon drills into the row behind that flow. Ribbons touching a grouped **Other** node are not clickable, since that node covers several members at once.
diff --git a/docs-mintlify/docs/explore-analyze/charts/configuration/series-mapping.mdx b/docs-mintlify/docs/explore-analyze/charts/configuration/series-mapping.mdx
index 4489b16d23a1a..33a5c2caea8dd 100644
--- a/docs-mintlify/docs/explore-analyze/charts/configuration/series-mapping.mdx
+++ b/docs-mintlify/docs/explore-analyze/charts/configuration/series-mapping.mdx
@@ -18,6 +18,8 @@ Series mapping controls which query columns are assigned to which chart channels
| **Split by** | Repeats the chart once per unique value, as a grid of panels — see [small multiples](/docs/explore-analyze/charts/configuration/small-multiples) | Bar, line, area, scatter |
| **Tooltip** | Fields shown on hover | All types |
| **Theta** (pie) | The measure that determines slice size | Pie |
+| **Source** / **Target** (sankey) | The dimensions holding the node each flow leaves and enters | Sankey |
+| **Value** (sankey) | The measure that determines ribbon thickness | Sankey |
## Assigning fields
diff --git a/docs-mintlify/docs/explore-analyze/charts/configuration/small-multiples.mdx b/docs-mintlify/docs/explore-analyze/charts/configuration/small-multiples.mdx
index 71b2a4ee07ada..14a1506a50607 100644
--- a/docs-mintlify/docs/explore-analyze/charts/configuration/small-multiples.mdx
+++ b/docs-mintlify/docs/explore-analyze/charts/configuration/small-multiples.mdx
@@ -55,6 +55,6 @@ A legend is shared across the grid rather than repeated per panel. Clicking a le
## Limitations
- **One split dimension.** One dimension fills the grid, panel by panel. Splitting by two dimensions at once — one down the rows and another across the columns — is not supported.
-- **Cartesian charts only.** Bar, line, area, and scatter. Pie, table, KPI, heatmap, boxplot, map, and HTML charts cannot be split.
+- **Cartesian charts only.** Bar, line, area, and scatter. Pie, sankey, table, KPI, heatmap, boxplot, map, and HTML charts cannot be split.
- **Panel labels are not configurable.** Each panel is labeled with its dimension value; the font, size, and color are fixed.
- **The split is enabled on a single-view chart.** A chart that already carries data labels, a reference line, or a second Y axis series cannot be split — turn the split on first. The order is the only constraint: once a chart is split, data labels and reference lines can be added freely and are drawn in every panel.
diff --git a/docs-mintlify/docs/explore-analyze/charts/configuration/tooltips.mdx b/docs-mintlify/docs/explore-analyze/charts/configuration/tooltips.mdx
index 1f4ec0b5956d6..33db7523c45b1 100644
--- a/docs-mintlify/docs/explore-analyze/charts/configuration/tooltips.mdx
+++ b/docs-mintlify/docs/explore-analyze/charts/configuration/tooltips.mdx
@@ -9,7 +9,9 @@ Tooltips appear when a user hovers over a data point in a chart. By default, Cub
## Default behavior
-When a chart is first created, all fields assigned to the chart (X, Y, color, size) are included in the tooltip automatically. The tooltip is enabled by default on all Vega-based chart types (bar, line, area, scatter, heatmap, boxplot) and on map charts.
+When a chart is first created, all fields assigned to the chart (X, Y, color, size) are included in the tooltip automatically. The tooltip is enabled by default on all Vega-based chart types (bar, line, area, scatter, heatmap, boxplot), on map charts, and on sankey charts.
+
+[Sankey](/docs/explore-analyze/charts/chart-types/sankey) is the exception to the first rule: it seeds the tooltip with every column *except* the source, target and value it already draws.
## Configuring tooltip fields
diff --git a/docs-mintlify/docs/explore-analyze/explore.mdx b/docs-mintlify/docs/explore-analyze/explore.mdx
index 44e29e7216cc6..44078c278f8b0 100644
--- a/docs-mintlify/docs/explore-analyze/explore.mdx
+++ b/docs-mintlify/docs/explore-analyze/explore.mdx
@@ -50,7 +50,11 @@ Explore results carry the same [freshness and pre-aggregation indicators](/docs/
## Saving explorations
You can save an exploration so you can return to it later without
-re-building your query.
+re-building your query. An exploration is the same kind of object as a
+workbook [report](/docs/explore-analyze/workbooks) — the only difference is
+whether it belongs to a workbook. [Analytics Chat](/docs/explore-analyze/analytics-chat)
+and the Cube MCP `createReport` tool save a standalone exploration the same
+way: by leaving the workbook unset.
- **Save** — For an unsaved exploration, click **Save** to open the
**Save Exploration** dialog. Give your exploration a name and click
diff --git a/docs-mintlify/docs/explore-analyze/workbooks/index.mdx b/docs-mintlify/docs/explore-analyze/workbooks/index.mdx
index 390915ad0af6b..e1f43bc5bc698 100644
--- a/docs-mintlify/docs/explore-analyze/workbooks/index.mdx
+++ b/docs-mintlify/docs/explore-analyze/workbooks/index.mdx
@@ -6,6 +6,9 @@ title: Workbooks
Workbooks allow you to build reports and explore data with AI agents, organize the results of your
analysis, and share trusted insights with your team.
+A report inside a workbook and a standalone [exploration](/docs/explore-analyze/explore#saving-explorations)
+are the same kind of object — the only difference is whether it's filed inside a workbook.
+
## Tabs
Workbooks can contain one or more tabs, each providing a different way to query and analyze your data. Tabs enable you to organize multiple analyses within a single workbook, making it easy to explore different aspects of your data or combine insights from different sources.
diff --git a/docs-mintlify/docs/explore-analyze/workbooks/python-analysis.mdx b/docs-mintlify/docs/explore-analyze/workbooks/python-analysis.mdx
index 74daa648ae53f..2d54566a87f78 100644
--- a/docs-mintlify/docs/explore-analyze/workbooks/python-analysis.mdx
+++ b/docs-mintlify/docs/explore-analyze/workbooks/python-analysis.mdx
@@ -32,9 +32,10 @@ Ask for the analysis in natural language — "forecast next quarter's revenue",
result inline in the [chat thread](/docs/explore-analyze/analytics-chat). This
result is ephemeral by default.
-Ask to **save it to a workbook** and Cube persists both the code and the run result
-onto a report, which then renders the saved output without re-running. Saving
-re-executes the analysis.
+Ask to **save it** — to a workbook, or as a standalone
+[exploration](/docs/explore-analyze/explore#saving-explorations) — and Cube persists
+both the code and the run result. Opening the saved copy renders that output without
+re-running; saving re-executes the analysis.
diff --git a/docs-mintlify/docs/explore-analyze/workbooks/querying-data.mdx b/docs-mintlify/docs/explore-analyze/workbooks/querying-data.mdx
index a49462a39596f..8fe236eca2726 100644
--- a/docs-mintlify/docs/explore-analyze/workbooks/querying-data.mdx
+++ b/docs-mintlify/docs/explore-analyze/workbooks/querying-data.mdx
@@ -105,7 +105,9 @@ above cannot express.
### Default filters
A data model author can define default workbook filters on a semantic view with
-the [`meta.default_ui_filters`][ref-default-ui-filters] parameter:
+the [`meta.default_ui_filters`][ref-default-ui-filters] parameter. The same
+defaults are also seeded when a query on that view is started in the Google
+Sheets / Excel add-in.
```yaml
views:
diff --git a/docs-mintlify/docs/integrations/google-sheets.mdx b/docs-mintlify/docs/integrations/google-sheets.mdx
index 54f03dae5493d..9ae74899c7501 100644
--- a/docs-mintlify/docs/integrations/google-sheets.mdx
+++ b/docs-mintlify/docs/integrations/google-sheets.mdx
@@ -73,10 +73,15 @@ looks and feels like [Playground][ref-playground].
-Google Cloud for Sheets works only with [views][ref-views], not cubes.
+Cube Cloud for Sheets works only with [views][ref-views], not cubes.
+If the view defines [`default_ui_filters`][ref-default-ui-filters], those
+filters are pre-populated as soon as you select the view — the same way they
+are in workbooks. They are a starting point, not enforcement: you can change
+their values, switch operators, or remove them.
+
Click on members to add them to **Rows** and **Measures**.
If needed, drag dimensions from **Rows** to **Columns**. Click on
the funnel buttons to add members to **Filters**. Click on **×** to
@@ -100,6 +105,31 @@ With every change to your query, Cube Cloud for Sheets will update the report on
the sheet after a slight delay. If you'd like to minimize it, consider
implementing [pre-aggregations][ref-pre-aggs].
+### Measure position and order
+
+By default, measures nest under each value of the dimension they're paired
+with — every measure for the first column value, then every measure for the
+next. Open the **Display** tab to change **Measure position** to **Before
+columns** to get the opposite layout: each measure spans every column value,
+with all values of one measure together before moving to the next.
+
+For example, with `Forecast Sales Units` and `Forecast Net Sales` on Measures
+and a `season` dimension on Columns:
+
+- **After columns** (default): `Q1: [Sales Units, Net Sales] | Q2: [Sales
+ Units, Net Sales]`
+- **Before columns**: `Sales Units: [Q1, Q2] | Net Sales: [Q1, Q2]`
+
+{/* TODO: screenshot — side-by-side of the two output layouts (After columns vs Before columns) */}
+
+The same choice applies when measures are placed on **Rows** instead (set
+with **Measures on**, in the same tab) — there it's labeled **After rows** /
+**Before rows**.
+
+To change the order measures appear in within their group, drag them within
+the **Measures** pane on the **Pivot** tab. Position and order are saved with
+the report and survive **Refresh**.
+
When your report is ready, you can optionally move it to [saved reports](#work-with-saved-reports)
by clicking **Save**.
@@ -111,8 +141,20 @@ exploration by name across your whole deployment, not just the current
folder — results are grouped by type and show each item's folder location,
and selecting one navigates you straight to it.
-Click **Refresh** to manually refresh the data in the report's location.
-Click **Edit** to change the query or the location.
+An exploration can be placed more than once — on different sheets or at
+different anchors in the same spreadsheet, and in more than one document at
+once (a Google Sheets spreadsheet and an Excel workbook simultaneously).
+Hovering a row in this list shows every placement it has in the **current**
+document under **Location** / **Locations**. Click **Refresh** to update all
+of that exploration's placements in the current document at once; click its
+title to open it and change the query, which applies to every placement.
+
+A placement survives renaming the sheet it's on or the spreadsheet it's in —
+it's tracked by the sheet's and spreadsheet's own stable ids, not by name. A
+placement's anchor is a fixed cell reference, though, so it does **not**
+survive rows or columns inserted above it: the visible data shifts down with
+the insert, but the stored anchor doesn't move with it, so the next refresh
+targets the wrong cell.
@@ -133,5 +175,6 @@ in Cube Cloud.
[ref-playground]: /docs/explore-analyze/playground
[ref-views]: /docs/data-modeling/views
[ref-pre-aggs]: /docs/pre-aggregations/using-pre-aggregations
+[ref-default-ui-filters]: /reference/data-modeling/view#default_ui_filters
[ref-sql-api-enabled]: /reference/core-data-apis/sql-api#cube-cloud
-[ref-saved-reports]: /docs/workspace/saved-reports
\ No newline at end of file
+[ref-saved-reports]: /docs/workspace/saved-reports
diff --git a/docs-mintlify/docs/integrations/mcp-server.mdx b/docs-mintlify/docs/integrations/mcp-server.mdx
index e69485ee866b4..8db7500f06543 100644
--- a/docs-mintlify/docs/integrations/mcp-server.mdx
+++ b/docs-mintlify/docs/integrations/mcp-server.mdx
@@ -239,11 +239,17 @@ work together.
| Tool | Description | Access |
| --- | --- | --- |
| `searchDataModel` | Searches the semantic model by similarity — views and their measures and dimensions — to discover what is queryable. Returns compact records (name, title, description, type) to reference in `runQuery`. | Read-only |
-| `runQuery` | Runs a Cube SQL query (PostgreSQL dialect) against the [SQL API][ref-sql-api]. Returns a schema, a page of rows, and `hasMore` / `totalRows` for pagination via `offset`. | Read-only |
+| `runQuery` | Runs a Cube SQL query (PostgreSQL dialect) against the [SQL API][ref-sql-api]. Returns a schema, a page of rows, and `hasMore` / `totalRows` for pagination via `offset`. Pass the dev `branchName` from `startDataModelEdit` to query that branch instead of the deployed model. | Read-only |
Call `searchDataModel` before `runQuery` to find exact view and member names rather than
guessing them.
+Omitting `branchName` queries the deployed model, exactly as before this parameter existed.
+Passing a `branchName` that isn't an active dev branch — for example the deploy branch's own
+name, or a feature branch with no dev worker running — fails with an error rather than
+silently answering from the deployed model, so a query aimed at the wrong branch never
+looks like a query that just returned no rows.
+
### Dashboard authoring
These tools build [workbooks][ref-workbooks] and [dashboards][ref-dashboards]
@@ -253,7 +259,7 @@ programmatically. Creating and editing workbooks requires the Explorer role or h
| --- | --- | --- |
| `readWorkbook` | Reads a workbook — its name and its current dashboard draft and published configs. | Read-only |
| `createWorkbook` | Creates a new empty workbook, the container that holds reports and a dashboard. | Write |
-| `createReport` | Saves a query plus its visualization as a report inside a workbook, and returns the `reportId` a chart widget references. | Write |
+| `createReport` | Saves a query plus its visualization as a report. Pass `workbookId` to file it inside a workbook and get the `reportId` a chart widget references; omit `workbookId` to save a standalone [exploration](/docs/explore-analyze/explore#saving-explorations) instead. | Write |
| `readReport` | Reads one saved report by id — its title, SQL, chart spec, placement, and a shareable URL. | Read-only |
| `updateReport` | Edits an existing report in place, keeping its `reportId`. Send only the fields you want to change. | Destructive — prompts |
| `deleteReport` | Deletes a report. | Destructive — prompts |
@@ -320,6 +326,9 @@ into the MCP server:
model-editing tools, `getBranchDiff`, `getDeploymentEnv`, and both pre-aggregation
tools — is offered only to users whose role allows editing the semantic model. A Viewer
never sees them at all.
+- **Verification happens on the branch, before a merge, not after.** Pass the dev
+ `branchName` to `runQuery` to check that an edit returns the right data — `valid: true`
+ from `writeDataModelFile` only means the model compiles, not that it's correct.
Review pending work with `getDataModelChanges` before you commit.
@@ -400,6 +409,8 @@ Call `startDataModelEdit` to enter development mode and get a dev `branchName`.
the current source with `listDataModelFiles` and `readDataModelFile`, then apply changes
with `writeDataModelFile` or `deleteDataModelFile`, passing that `branchName`. Each write
recompiles the model and reports validation errors, so you can iterate until it compiles.
+Before committing, verify the change returns the right data: call `runQuery` with the same
+`branchName` and check the numbers, since a compiling model can still return wrong results.
Review the result with `getDataModelChanges`, then commit the branch from the Cube UI to
publish it.
diff --git a/docs-mintlify/docs/integrations/microsoft-excel.mdx b/docs-mintlify/docs/integrations/microsoft-excel.mdx
index cb658fb1303b7..4024888e8402d 100644
--- a/docs-mintlify/docs/integrations/microsoft-excel.mdx
+++ b/docs-mintlify/docs/integrations/microsoft-excel.mdx
@@ -85,6 +85,11 @@ Cube Cloud for Excel works only with [views][ref-views], not cubes.
+If the view defines [`default_ui_filters`][ref-default-ui-filters], those
+filters are pre-populated as soon as you select the view — the same way they
+are in workbooks. They are a starting point, not enforcement: you can change
+their values, switch operators, or remove them.
+
Click on members to add them to **Rows** and **Measures**.
If needed, drag dimensions from **Rows** to **Columns**. Click on
the funnel buttons to add members to **Filters**. Click on **×** to
@@ -108,6 +113,31 @@ With every change to your query, Cube Cloud for Excel will update the report on
the sheet after a slight delay. If you'd like to minimize it, consider
implementing [pre-aggregations][ref-pre-aggs].
+### Measure position and order
+
+By default, measures nest under each value of the dimension they're paired
+with — every measure for the first column value, then every measure for the
+next. Open the **Display** tab to change **Measure position** to **Before
+columns** to get the opposite layout: each measure spans every column value,
+with all values of one measure together before moving to the next.
+
+For example, with `Forecast Sales Units` and `Forecast Net Sales` on Measures
+and a `season` dimension on Columns:
+
+- **After columns** (default): `Q1: [Sales Units, Net Sales] | Q2: [Sales
+ Units, Net Sales]`
+- **Before columns**: `Sales Units: [Q1, Q2] | Net Sales: [Q1, Q2]`
+
+{/* TODO: screenshot — side-by-side of the two output layouts (After columns vs Before columns) */}
+
+The same choice applies when measures are placed on **Rows** instead (set
+with **Measures on**, in the same tab) — there it's labeled **After rows** /
+**Before rows**.
+
+To change the order measures appear in within their group, drag them within
+the **Measures** pane on the **Pivot** tab. Position and order are saved with
+the report and survive **Refresh**.
+
When your report is ready, you can optionally move it to [saved reports](#work-with-saved-reports)
by clicking **Save**.
@@ -119,8 +149,20 @@ exploration by name across your whole deployment, not just the current
folder — results are grouped by type and show each item's folder location,
and selecting one navigates you straight to it.
-Click **Refresh** to manually refresh the data in the report's location.
-Click **Edit** to change the query or the location.
+An exploration can be placed more than once — on different sheets or at
+different anchors in the same workbook, and in more than one document at
+once (a Google Sheets spreadsheet and an Excel workbook simultaneously).
+Hovering a row in this list shows every placement it has in the **current**
+document under **Location** / **Locations**. Click **Refresh** to update all
+of that exploration's placements in the current document at once; click its
+title to open it and change the query, which applies to every placement.
+
+A placement survives renaming the sheet it's on or the workbook it's in —
+it's tracked by the sheet's and workbook's own stable ids, not by name. A
+placement's anchor is a fixed cell reference, though, so it does **not**
+survive rows or columns inserted above it: the visible data shifts down with
+the insert, but the stored anchor doesn't move with it, so the next refresh
+targets the wrong cell.
@@ -141,6 +183,7 @@ in Cube Cloud.
[ref-playground]: /docs/explore-analyze/playground
[ref-views]: /docs/data-modeling/views
[ref-pre-aggs]: /docs/pre-aggregations/using-pre-aggregations
+[ref-default-ui-filters]: /reference/data-modeling/view#default_ui_filters
[ref-sql-api-enabled]: /reference/core-data-apis/sql-api#cube-cloud
[link-excel-addins]: https://support.microsoft.com/en-us/office/add-or-remove-add-ins-in-excel-0af570c4-5cf3-4fa9-9b88-403625a0b460
[link-ms-appsource]: https://appsource.microsoft.com/en-us/product/office/WA200008486
diff --git a/docs-mintlify/embedding/iframe/creator-mode.mdx b/docs-mintlify/embedding/iframe/creator-mode.mdx
index d4d5f49736c96..07694a34c25c6 100644
--- a/docs-mintlify/embedding/iframe/creator-mode.mdx
+++ b/docs-mintlify/embedding/iframe/creator-mode.mdx
@@ -15,6 +15,13 @@ To enable it, pass `creatorMode: true` to the [Generate Session API][ref-generat
Creator Mode requires [Signed embedding](/embedding/iframe/auth/signed). Private embedding is not supported.
+## Dashboard header controls
+
+When a user opens a dashboard inside Creator Mode, its header renders the
+same as in a standalone dashboard embed. To hide any of its controls (title,
+back button, Edit, Duplicate) for your embed, see
+[Show or hide header controls](/embedding/iframe/dashboards#show-or-hide-header-controls).
+
## Embed tenant scoping
In Creator Mode, content (workbooks, dashboards) and the groups/user attributes referenced by the session are scoped to an **embed tenant**. Pass `embedTenantName` to isolate content per customer; omit it to use the current tenant.
diff --git a/docs-mintlify/embedding/iframe/dashboards.mdx b/docs-mintlify/embedding/iframe/dashboards.mdx
index 905fd42e34e74..955f950b23a79 100644
--- a/docs-mintlify/embedding/iframe/dashboards.mdx
+++ b/docs-mintlify/embedding/iframe/dashboards.mdx
@@ -77,20 +77,27 @@ This works on both regular and published (embedded) dashboards. The filter is
only applied if a matching filter widget for that dimension already exists on the
dashboard.
-## Allow CSV export
+## Allow chart export {#allow-csv-export}
By default, embedded dashboards do not expose a download action on individual
-widgets. To let viewers download a chart widget's data as a CSV file, add the
-`allowExport=true` query parameter to the embed URL:
+widgets. To let viewers download a chart widget as a **CSV**, **PNG**, or
+**PDF** file, add the `allowExport=true` query parameter to the embed URL:
```text
https://your-tenant.cubecloud.dev/embed/dashboard/YOUR_DASHBOARD_PUBLIC_ID?session=YOUR_SESSION_ID&allowExport=true
```
-When enabled, each chart widget's ⋮ menu shows a **Download as CSV** action. The
-CSV is generated client-side from the data already loaded into the widget, so no
-additional query is issued. The parameter is opt-in — omit it (the default) to
-keep the download action hidden.
+When enabled, each chart widget's ⋮ menu shows **Download as CSV**, **Download
+as PNG**, and **Download as PDF** actions. `allowExport` is the single switch
+for all three formats — there is no way to allow one format while blocking
+another. Only the exact literal string `true` opts in: `allowExport=1`,
+`allowExport=TRUE`, a bare `?allowExport`, and omitting the parameter (the
+default) all leave every download action hidden.
+
+The CSV is generated client-side from the data already loaded into the
+widget, so no additional query is issued. Inside an embed, `allowExport` —
+not the viewer's own Cube role — is the authority on whether the download
+actions appear.
## Show or hide the AI chat
@@ -122,6 +129,49 @@ This applies to embedded published dashboards; it does not affect the standalone
[Analytics Chat](/embedding/iframe/analytics-chat) surface, where embedding the
chat is itself the opt-in.
+## Show or hide header controls
+
+By default, an embedded dashboard's header shows its title, back button, and
+— when the viewer has permission — the Edit and Duplicate actions. Hide any
+of them individually with URL parameters on the embed iframe `src`:
+
+| Parameter | Hides |
+| --- | --- |
+| `showDashboardHeader=false` | The entire header bar |
+| `showDashboardBackButton=false` | The back button |
+| `showDashboardTitle=false` | The dashboard title |
+| `showDashboardEditButton=false` | The Edit action |
+| `showDashboardDuplicateButton=false` | The Duplicate action |
+
+```text
+https://your-tenant.cubecloud.dev/embed/dashboard/YOUR_DASHBOARD_PUBLIC_ID?session=YOUR_SESSION_ID&showDashboardBackButton=false&showDashboardTitle=false
+```
+
+Every one of these defaults to shown. Only the exact literal string `false`
+hides a control — `=0`, `=False`, `=FALSE`, and a bare `?showDashboardTitle`
+(no value) all leave it visible. Note this is the inverse of `allowExport`
+above, where only the literal string `true` opts in.
+
+
+These parameters are URL-only — they aren't accepted in the [Generate
+Session](/reference/embed-apis/generate-session#session-settings) `settings`
+object, unlike `showDashboardChat`, and they have no account-wide default
+under **Embed → Settings**.
+
+
+These parameters apply to the dashboard header only — the workbook header
+(which carries Publish and Share) is unaffected.
+
+Setting `showDashboardHeader=false` overrides the other four: the whole bar
+disappears regardless of their values. `showDashboardEditButton` can only
+hide the Edit action; it can never show one to a viewer who lacks edit
+permission.
+
+These parameters are read from the URL the host loaded the iframe with and
+stay pinned for the life of the embed, so they survive in-app navigation
+(for example, from the home-page dashboard card, or after publishing a
+draft).
+
## Set the language
Embedded dashboards render their UI in the account's default language, which you can
diff --git a/docs-mintlify/embedding/iframe/events.mdx b/docs-mintlify/embedding/iframe/events.mdx
index 066816c5c9964..25c78576972f9 100644
--- a/docs-mintlify/embedding/iframe/events.mdx
+++ b/docs-mintlify/embedding/iframe/events.mdx
@@ -186,9 +186,9 @@ painted" signal (distinct from `ready`, which fires at mount, before data loads)
#### `cube:event:download` {#cube-event-download}
-Emitted when a viewer exports something — a widget's data as CSV, or (in future)
-a dashboard image. Reports _that_ an export happened and its shape — never the
-exported rows themselves.
+Emitted when a viewer exports something — a widget's data as CSV, or a widget
+as a PNG or PDF image. Reports _that_ an export happened and its shape — never
+the exported rows themselves.
| Field | Type | Description |
| --- | --- | --- |
@@ -209,10 +209,10 @@ exported rows themselves.
```
- The CSV download action on a dashboard widget only appears when the embed URL
- includes `allowExport=true` (see [Dashboards → Allow CSV
+ A dashboard widget's download actions (CSV, PNG, PDF) only appear when the
+ embed URL includes `allowExport=true` (see [Dashboards → Allow chart
export](/embedding/iframe/dashboards#allow-csv-export)). The event fires when a
- viewer uses it.
+ viewer uses one of them.
#### `cube:event:drilldown` {#cube-event-drilldown}
diff --git a/docs-mintlify/recipes/data-modeling/cross-data-source-queries.mdx b/docs-mintlify/recipes/data-modeling/cross-data-source-queries.mdx
new file mode 100644
index 0000000000000..52016aa151268
--- /dev/null
+++ b/docs-mintlify/recipes/data-modeling/cross-data-source-queries.mdx
@@ -0,0 +1,368 @@
+---
+title: Querying across data sources
+description: Append rows from cubes that live in different databases into one result set with a UNION ALL query through the SQL API.
+---
+
+## Use case
+
+Some datasets are spread across more than one database. Product events, for
+example, are often split by age: the last few months stay in a fast analytical
+database that serves live dashboards, while everything older is moved to
+cheaper storage. Both tables describe the same events and carry the same
+columns.
+
+The goal is to report on them together — one result set with the rows of both
+databases appended, plus a dimension that says which database each row came
+from.
+
+This is a *union*: it adds rows. It is a different problem from a
+[`rollup_join`](/reference/data-modeling/pre-aggregations#rollup_join), which
+adds columns by relating entities that live in different databases. It is also
+different from [data blending](/docs/data-modeling/concepts/data-blending),
+which unions cubes inside a single database.
+
+The [SQL API](/reference/core-data-apis/sql-api) can do this at query time,
+against live data and without pre-aggregations. Each cube is queried on its own
+data source, and Cube appends the results.
+
+## Configuration
+
+Define the two connections as [multiple data
+sources](/admin/connect-to-data/multiple-data-sources). The default source needs
+no name; every other source gets one, and the full list goes in
+`CUBEJS_DATASOURCES`:
+
+```dotenv
+CUBEJS_DATASOURCES=default,recent
+
+CUBEJS_DB_TYPE=postgres
+CUBEJS_DB_HOST=archive.example.com
+CUBEJS_DB_NAME=analytics
+# ...
+
+CUBEJS_DS_RECENT_DB_TYPE=clickhouse
+CUBEJS_DS_RECENT_DB_HOST=clickhouse.example.com
+CUBEJS_DS_RECENT_DB_NAME=analytics
+# ...
+```
+
+## Data modeling
+
+Model each table as its own cube, and point one of them at the named data source
+with [`data_source`](/reference/data-modeling/cube#data_source). The cube without
+a `data_source` uses the default one.
+
+Give both cubes a matching set of members, and add a constant dimension that
+identifies the origin of each row. That dimension is what makes the two halves
+of the union distinguishable once they sit in the same result set:
+
+
+
+```yaml title="YAML"
+cubes:
+ - name: archived_events
+ sql_table: events
+
+ dimensions:
+ - name: id
+ sql: id
+ type: number
+ primary_key: true
+
+ - name: created_at
+ sql: created_at
+ type: time
+
+ - name: tier
+ sql: tier
+ type: string
+
+ - name: storage
+ sql: "'archive'"
+ type: string
+
+ measures:
+ - name: event_count
+ type: count
+
+ - name: recent_events
+ sql_table: events
+ data_source: recent
+
+ dimensions:
+ - name: id
+ sql: id
+ type: number
+ primary_key: true
+
+ - name: created_at
+ sql: created_at
+ type: time
+
+ - name: tier
+ sql: tier
+ type: string
+
+ - name: storage
+ sql: "'recent'"
+ type: string
+
+ measures:
+ - name: event_count
+ type: count
+```
+
+```javascript title="JavaScript"
+cube(`archived_events`, {
+ sql_table: `events`,
+
+ dimensions: {
+ id: {
+ sql: `id`,
+ type: `number`,
+ primary_key: true
+ },
+
+ created_at: {
+ sql: `created_at`,
+ type: `time`
+ },
+
+ tier: {
+ sql: `tier`,
+ type: `string`
+ },
+
+ storage: {
+ sql: `'archive'`,
+ type: `string`
+ }
+ },
+
+ measures: {
+ event_count: {
+ type: `count`
+ }
+ }
+})
+
+cube(`recent_events`, {
+ sql_table: `events`,
+ data_source: `recent`,
+
+ dimensions: {
+ id: {
+ sql: `id`,
+ type: `number`,
+ primary_key: true
+ },
+
+ created_at: {
+ sql: `created_at`,
+ type: `time`
+ },
+
+ tier: {
+ sql: `tier`,
+ type: `string`
+ },
+
+ storage: {
+ sql: `'recent'`,
+ type: `string`
+ }
+ },
+
+ measures: {
+ event_count: {
+ type: `count`
+ }
+ }
+})
+```
+
+
+
+## Querying
+
+Connect to the [SQL API](/reference/core-data-apis/sql-api) and append the two
+cubes with `UNION ALL`:
+
+```sql
+SELECT storage, tier, MEASURE(event_count) AS events
+FROM archived_events
+GROUP BY 1, 2
+UNION ALL
+SELECT storage, tier, MEASURE(event_count) AS events
+FROM recent_events
+GROUP BY 1, 2
+```
+
+```
+ storage | tier | events
+---------+------------+--------
+ archive | free | 412508
+ archive | enterprise | 95012
+ recent | free | 38471
+ recent | enterprise | 9930
+```
+
+Each half of the union is evaluated against its own database, in that database's
+own dialect, and Cube appends the two results. Filters reach the databases
+rather than being applied afterwards, so a `WHERE` clause on either side limits
+what that database scans:
+
+```sql
+SELECT storage, tier, MEASURE(event_count) AS events
+FROM archived_events
+WHERE tier = 'enterprise'
+GROUP BY 1, 2
+UNION ALL
+SELECT storage, tier, MEASURE(event_count) AS events
+FROM recent_events
+WHERE tier = 'enterprise'
+GROUP BY 1, 2
+```
+
+Add a time dimension to both halves to line the databases up on a common grain:
+
+```sql
+SELECT storage, tier, DATE_TRUNC('day', created_at) AS date, MEASURE(event_count) AS events
+FROM archived_events
+GROUP BY 1, 2, 3
+UNION ALL
+SELECT storage, tier, DATE_TRUNC('day', created_at) AS date, MEASURE(event_count) AS events
+FROM recent_events
+GROUP BY 1, 2, 3
+ORDER BY 3, 1
+```
+
+
+
+A cube whose name starts with `pg_` cannot be referenced by that name alone. The
+SQL API routes such a name to `pg_catalog`, where no cube is ever found, and the
+query fails with `Table or CTE with name 'pg_...' not found`. Qualify it with the
+schema that holds cubes, as in `FROM public.pg_costs`, or avoid the prefix.
+
+
+
+### Aggregating across the union
+
+Wrap the union in a CTE to aggregate over both databases at once. Here the
+per-tier totals combine events from both databases:
+
+```sql
+WITH blended AS (
+ SELECT storage, tier, MEASURE(event_count) AS events
+ FROM archived_events
+ GROUP BY 1, 2
+ UNION ALL
+ SELECT storage, tier, MEASURE(event_count) AS events
+ FROM recent_events
+ GROUP BY 1, 2
+)
+SELECT tier, SUM(events) AS total, COUNT(DISTINCT storage) AS databases
+FROM blended
+GROUP BY 1
+ORDER BY 2 DESC
+```
+
+```
+ tier | total | databases
+------------+--------+-----------
+ free | 450979 | 2
+ enterprise | 104942 | 2
+```
+
+`UNION` also works where duplicate rows should collapse, as do an outer
+`ORDER BY` and `LIMIT` over the union.
+
+
+
+Aggregating over the union like this is only correct for additive measures such
+as `count` and `sum`. Non-additive measures — `count_distinct`, `avg`,
+percentiles — cannot be combined from per-source results: summing distinct
+counts double-counts anything present in both databases, and averaging averages
+ignores how many rows each database contributed. No error is raised, so report
+these measures per data source instead.
+
+
+
+### Inspecting how a query is split
+
+Run `EXPLAIN` on any of these queries to see the plan. It puts a `Union` over one
+`CubeScan` per cube, and each scan carries its own filter — the `WHERE` clause is
+part of the request sent for that cube, not a step applied after the results are
+appended:
+
+```sql
+EXPLAIN SELECT storage, tier, MEASURE(event_count) AS events
+FROM archived_events
+WHERE tier = 'enterprise'
+GROUP BY 1, 2
+UNION ALL
+SELECT storage, tier, MEASURE(event_count) AS events
+FROM recent_events
+WHERE tier = 'enterprise'
+GROUP BY 1, 2
+```
+
+```
+Union
+ CubeScan: request={
+ "measures": [
+ "archived_events.event_count"
+ ],
+ "dimensions": [
+ "archived_events.storage",
+ "archived_events.tier"
+ ],
+ "segments": [],
+ "order": [],
+ "filters": [
+ {
+ "member": "archived_events.tier",
+ "operator": "equals",
+ "values": [
+ "enterprise"
+ ]
+ }
+ ]
+}
+ CubeScan: request={
+ "measures": [
+ "recent_events.event_count"
+ ],
+ "dimensions": [
+ "recent_events.storage",
+ "recent_events.tier"
+ ],
+ "segments": [],
+ "order": [],
+ "filters": [
+ {
+ "member": "recent_events.tier",
+ "operator": "equals",
+ "values": [
+ "enterprise"
+ ]
+ }
+ ]
+}
+```
+
+The plan identifies each scan by cube, not by data source, so read it together
+with the `data_source` of each cube to see which database serves which half.
+
+## Limitations
+
+Unions combine rows across data sources; joins do not. A query that joins two
+cubes on different data sources is rejected, and relating entities across
+databases needs a
+[`rollup_join`](/reference/data-modeling/pre-aggregations#rollup_join) instead.
+
+Each half of the union is subject to the [maximum row
+limit](/docs/data-modeling/configuration#maximum-row-limit) on its own, and the
+cap applies before the results are appended. Aggregate inside each half of the
+union, as in the examples above, rather than unioning raw rows and aggregating
+afterwards.
diff --git a/docs-mintlify/recipes/index.mdx b/docs-mintlify/recipes/index.mdx
index 469eb137d4509..4462abde37e77 100644
--- a/docs-mintlify/recipes/index.mdx
+++ b/docs-mintlify/recipes/index.mdx
@@ -4,7 +4,7 @@ description: Step-by-step tutorials and best practices for getting the most out
mode: wide
---
-Explore **39 recipes** across data modeling, calculations, analytics patterns,
+Explore **41 recipes** across data modeling, calculations, analytics patterns,
pre-aggregations, configuration, APIs, and AI.
## Data Modeling
@@ -28,6 +28,9 @@ pre-aggregations, configuration, APIs, and AI.
Combine multiple database tables that relate to the same entity into a single cube.
+
+ Append rows from cubes in different databases into one result set with a UNION ALL query.
+
Define a custom sort order for categorical values like pipeline stages that don't sort alphabetically.
diff --git a/docs-mintlify/reference/data-modeling/view.mdx b/docs-mintlify/reference/data-modeling/view.mdx
index 29920e8e454a7..f5a3731f04bb9 100644
--- a/docs-mintlify/reference/data-modeling/view.mdx
+++ b/docs-mintlify/reference/data-modeling/view.mdx
@@ -287,10 +287,11 @@ over the view's default. When `auto_run` is not set, auto-run is on.
#### `default_ui_filters`
In the Cube cloud platform, you can use the `default_ui_filters` key in a
-view's `meta` to define default filters for [workbooks][ref-workbook-filtering]
-and views embedded via the React Embed SDK. When the view is selected, these
-filters are pre-populated as regular, fully editable filters — data consumers
-can change their values, switch operators, or remove them entirely.
+view's `meta` to define default filters for [workbooks][ref-workbook-filtering],
+views embedded via the React Embed SDK, and the Google Sheets / Excel add-in.
+When the view is selected, these filters are pre-populated as regular, fully
+editable filters — data consumers can change their values, switch operators,
+or remove them entirely.
Each entry has a `member` (a dimension or measure exposed by the view), an
`operator`, and a list of `values` — or a single `value`, which is natural for
@@ -418,11 +419,11 @@ bounds, drops that filter entry with a browser-console warning — the remaining
entries still apply, and view selection never breaks.
Unlike [`default_filters`](#default_filters), which are enforced on every query
-against the view, `default_ui_filters` only provide a starting point for the
-workbook UI: they don't apply to queries made through APIs or other
-visualization tools, and workbook users can freely modify or remove them.
-They also only apply to new queries — defining or changing them in the data
-model does not add them to existing queries.
+against the view, `default_ui_filters` only provide a starting point: they
+don't apply to queries made through APIs or other visualization tools, and
+data consumers can freely modify or remove them. They also only apply to new
+queries — defining or changing them in the data model does not add them to
+existing queries or saved explorations.
### `cubes`
diff --git a/docs-mintlify/reference/embed-apis/generate-session.mdx b/docs-mintlify/reference/embed-apis/generate-session.mdx
index e0f1bc0c73782..bb7454defa8e4 100644
--- a/docs-mintlify/reference/embed-apis/generate-session.mdx
+++ b/docs-mintlify/reference/embed-apis/generate-session.mdx
@@ -79,6 +79,10 @@ over the account-wide setting.
|----------|------|----------|-------|
| `showDashboardChat` | boolean | No | Show or hide the AI chat (agent panel and launcher bubble) on embedded dashboards for this session. Omit to inherit the account-wide **Show dashboard chat** toggle (**Embed → Settings**; shown by default); `false` hides it even if enabled account-wide, `true` shows it even if disabled. Affects embedded **published dashboards** only — not the standalone [embedded chat][ref-analytics-chat] surface. See [Hiding the AI chat][ref-signed-embedding]. |
+
+The dashboard header's title, back button, Edit, and Duplicate controls are hidden with URL parameters on the embed iframe instead — they aren't part of this `settings` object. See [Show or hide header controls](/embedding/iframe/dashboards#show-or-hide-header-controls).
+
+
## User profile
`userProfile` lets you attach a human-readable display name and avatar to an external user so they render with a recognizable identity inside embedded surfaces (workbook owners, dashboard headers, etc.) instead of the raw `externalId`.
diff --git a/docs-mintlify/scripts/extract-api.mjs b/docs-mintlify/scripts/extract-api.mjs
index e6929d74b470c..028419e20a03f 100644
--- a/docs-mintlify/scripts/extract-api.mjs
+++ b/docs-mintlify/scripts/extract-api.mjs
@@ -168,12 +168,27 @@ const EXCLUDE_OPERATIONS = new Set([
'PUT /api/v1/resource-policies/group',
'PUT /api/v1/resource-policies/user',
'GET /api/v1/app-theme',
- 'GET /api/v1/ai-engineer/active-region',
'GET /api/v1/ai-engineer/settings',
// Report folders listing — not part of the public docs surface.
'GET /api/v1/deployments/{deploymentId}/report-folders',
]);
+// Cube-staff-only operations (provisioning real cloud infrastructure — Regions,
+// PrivateLinks, and anything else gated the same way) are excluded automatically
+// rather than via a hand-maintained EXCLUDE_OPERATIONS list: console-server's
+// `superAdminOnlyDescription()` (packages/console-server/src/api/middlewares/
+// validate-admin-access.ts in cubejs-enterprise) prefixes every such operation's
+// OpenAPI `description` with this exact marker text before ANY other processing
+// touches it, so matching on it here catches staff-only operations that didn't
+// exist yet when this list was last updated — not just the ones someone
+// remembered to add. No reader of these docs can call them (tenant admins are
+// rejected server-side), so nothing customer-facing is lost.
+//
+// A short, stable anchor rather than the full marker sentence — less brittle against
+// upstream rewrapping/rewording, and the residual scan below (which matches even more
+// loosely) is the real safety net if this ever stops matching.
+const SUPER_ADMIN_ONLY_MARKER = 'Cube super admin only';
+
// Explicit display names for tags whose auto-cleaned form would be unclear or
// collide. Everything else is cleaned by cleanTag() below.
const TAG_MAP = {
@@ -267,6 +282,8 @@ const src = yaml.load(fs.readFileSync(SRC, 'utf8'));
// trailing slash — a trailing slash breaks Mintlify dev), and clean tags +
// operationIds.
const paths = {};
+const matchedExcludes = new Set();
+let autoExcludedCount = 0;
for (const [key, val] of Object.entries(src.paths)) {
if (!INCLUDE_PREFIXES.some((prefix) => key.startsWith(prefix))) continue;
let newKey = key.length > 1 ? key.replace(/\/$/, '') : key; // drop trailing slash
@@ -274,7 +291,15 @@ for (const [key, val] of Object.entries(src.paths)) {
for (const m of METHODS) {
if (!val[m]) continue;
// Drop explicitly hidden operations before they reach the spec or nav.
- if (EXCLUDE_OPERATIONS.has(`${m.toUpperCase()} ${newKey}`)) {
+ const excludeKey = `${m.toUpperCase()} ${newKey}`;
+ if (EXCLUDE_OPERATIONS.has(excludeKey)) {
+ matchedExcludes.add(excludeKey);
+ delete val[m];
+ continue;
+ }
+ // Drop Cube-staff-only operations automatically — see SUPER_ADMIN_ONLY_MARKER above.
+ if (typeof val[m].description === 'string' && val[m].description.includes(SUPER_ADMIN_ONLY_MARKER)) {
+ autoExcludedCount++;
delete val[m];
continue;
}
@@ -310,6 +335,59 @@ if (!Object.keys(paths).length) {
process.exit(1);
}
+// An EXCLUDE_OPERATIONS entry that matches nothing was likely renamed or moved
+// upstream — silently letting it through would re-publish whatever it was meant
+// to hide (some entries there exist specifically to keep staff-only operations
+// out of the public docs), and `--check` can't catch this: both the committed
+// and freshly generated output would contain the leak.
+const unmatchedExcludes = [...EXCLUDE_OPERATIONS].filter((op) => !matchedExcludes.has(op));
+if (unmatchedExcludes.length) {
+ console.error(
+ 'Aborting: EXCLUDE_OPERATIONS entries matched nothing (renamed upstream?):\n ' +
+ unmatchedExcludes.join('\n ')
+ );
+ process.exit(1);
+}
+
+// The SUPER_ADMIN_ONLY_MARKER auto-detection above is an exact-substring match against
+// text authored in a different repo — it fails OPEN, not closed, if that text drifts
+// (rewording, a dropped 🔒, a punctuation change). Two guards restore a fail-closed
+// default without depending on the marker staying exact:
+//
+// Floor: the marker matched nothing at all. Today's spec always has staff-only
+// operations, so zero is almost certainly "the marker stopped matching," not "there
+// are none" — and if that ever becomes a real, deliberate zero, dropping this guard
+// is a one-line edit made by a human looking at exactly this message.
+if (!autoExcludedCount) {
+ console.error(
+ 'Aborting: SUPER_ADMIN_ONLY_MARKER matched no operation. Did the marker text change upstream ' +
+ '(packages/console-server/src/api/middlewares/validate-admin-access.ts in cubejs-enterprise)?'
+ );
+ process.exit(1);
+}
+// Residual scan: catch staff-only prose that slipped past the exact marker match —
+// worded more loosely than the marker itself (just "super admin"/"super-admin", not
+// the full sentence) so it still fires even when SUPER_ADMIN_ONLY_MARKER no longer
+// matches. Covers a *partial* drift too: if only a newly-added operation is reworded
+// while existing ones keep today's wording, autoExcludedCount stays non-zero and the
+// floor guard above can't catch it — this scan is what does.
+// NB: "super admin"/"super-admin" specifically, not the bare 🔒 — the same lock emoji
+// also opens the unrelated (and legitimately public) ADMIN_ONLY_DOC_MARKER ("🔒 Admin only.").
+const leakedStaffOnly = [];
+for (const [p, ops] of Object.entries(paths)) {
+ for (const m of METHODS) {
+ const text = ops[m]?.['x-mint']?.content ?? ops[m]?.description ?? '';
+ if (/super[-\s]?admins?\b/i.test(text)) leakedStaffOnly.push(`${m.toUpperCase()} ${p}`);
+ }
+}
+if (leakedStaffOnly.length) {
+ console.error(
+ 'Aborting: staff-only prose survived exclusion (SUPER_ADMIN_ONLY_MARKER stopped matching?):\n ' +
+ leakedStaffOnly.join('\n ')
+ );
+ process.exit(1);
+}
+
// 2. Transitive $ref schema closure.
function collectRefs(node, acc) {
if (Array.isArray(node)) { node.forEach((n) => collectRefs(n, acc)); return; }
@@ -403,6 +481,9 @@ const out = {
writeOrCheck(OUT, yaml.dump(out, { lineWidth: 100, noRefs: true }));
console.log('paths:', Object.keys(paths).length, '| schemas:', Object.keys(schemas).length, '| tags:', orderedTags.length);
+if (autoExcludedCount) {
+ console.log(`(${autoExcludedCount} Cube-staff-only operation(s) auto-excluded via SUPER_ADMIN_ONLY_MARKER)`);
+}
// 5. Group operations by tag (pages in source order within a tag) and capture,
// per tag, its paths + the first operation's summary — used to build both the
diff --git a/lerna.json b/lerna.json
index 6dd0bd7ff55f7..187c51197e30f 100644
--- a/lerna.json
+++ b/lerna.json
@@ -1,5 +1,5 @@
{
- "version": "1.7.28",
+ "version": "1.7.29",
"npmClient": "yarn",
"command": {
"bootstrap": {
diff --git a/packages/cubejs-api-gateway/CHANGELOG.md b/packages/cubejs-api-gateway/CHANGELOG.md
index e48a266fb4417..53a4870c28c5d 100644
--- a/packages/cubejs-api-gateway/CHANGELOG.md
+++ b/packages/cubejs-api-gateway/CHANGELOG.md
@@ -3,6 +3,12 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
+## [1.7.29](https://github.com/cube-js/cube/compare/v1.7.28...v1.7.29) (2026-08-27)
+
+### Bug Fixes
+
+- **api-gateway:** Make pre-agg build job status per-entry and data-source aware ([#11666](https://github.com/cube-js/cube/issues/11666)) ([694955f](https://github.com/cube-js/cube/commit/694955f864375515d84d463e9e7ee89a156a4a2b))
+
## [1.7.28](https://github.com/cube-js/cube/compare/v1.7.27...v1.7.28) (2026-08-26)
**Note:** Version bump only for package @cubejs-backend/api-gateway
diff --git a/packages/cubejs-api-gateway/package.json b/packages/cubejs-api-gateway/package.json
index 5b3553858ac10..9e78837e07933 100644
--- a/packages/cubejs-api-gateway/package.json
+++ b/packages/cubejs-api-gateway/package.json
@@ -2,7 +2,7 @@
"name": "@cubejs-backend/api-gateway",
"description": "Cube API Gateway",
"author": "Cube Dev, Inc.",
- "version": "1.7.28",
+ "version": "1.7.29",
"repository": {
"type": "git",
"url": "https://github.com/cube-js/cube.git",
@@ -27,9 +27,9 @@
"dist/src/*"
],
"dependencies": {
- "@cubejs-backend/native": "1.7.28",
- "@cubejs-backend/query-orchestrator": "1.7.28",
- "@cubejs-backend/shared": "1.7.28",
+ "@cubejs-backend/native": "1.7.29",
+ "@cubejs-backend/query-orchestrator": "1.7.29",
+ "@cubejs-backend/shared": "1.7.29",
"@ungap/structured-clone": "^0.3.4",
"assert-never": "^1.4.0",
"body-parser": "^1.19.0",
@@ -53,7 +53,7 @@
"zod": "^4.1.13"
},
"devDependencies": {
- "@cubejs-backend/linter": "1.7.28",
+ "@cubejs-backend/linter": "1.7.29",
"@types/express": "^4.17.21",
"@types/jest": "^29",
"@types/jsonwebtoken": "^9.0.2",
diff --git a/packages/cubejs-athena-driver/CHANGELOG.md b/packages/cubejs-athena-driver/CHANGELOG.md
index cc8ac76fe2c1d..1abbe31e6416e 100644
--- a/packages/cubejs-athena-driver/CHANGELOG.md
+++ b/packages/cubejs-athena-driver/CHANGELOG.md
@@ -3,6 +3,10 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
+## [1.7.29](https://github.com/cube-js/cube/compare/v1.7.28...v1.7.29) (2026-08-27)
+
+**Note:** Version bump only for package @cubejs-backend/athena-driver
+
## [1.7.28](https://github.com/cube-js/cube/compare/v1.7.27...v1.7.28) (2026-08-26)
**Note:** Version bump only for package @cubejs-backend/athena-driver
diff --git a/packages/cubejs-athena-driver/package.json b/packages/cubejs-athena-driver/package.json
index 1c49026cf01fb..41cc1a9f39a2d 100644
--- a/packages/cubejs-athena-driver/package.json
+++ b/packages/cubejs-athena-driver/package.json
@@ -2,7 +2,7 @@
"name": "@cubejs-backend/athena-driver",
"description": "Cube.js Athena database driver",
"author": "Cube Dev, Inc.",
- "version": "1.7.28",
+ "version": "1.7.29",
"repository": {
"type": "git",
"url": "https://github.com/cube-js/cube.git",
@@ -31,12 +31,12 @@
"dependencies": {
"@aws-sdk/client-athena": "^3.22.0",
"@aws-sdk/credential-providers": "^3.22.0",
- "@cubejs-backend/base-driver": "1.7.28",
- "@cubejs-backend/shared": "1.7.28"
+ "@cubejs-backend/base-driver": "1.7.29",
+ "@cubejs-backend/shared": "1.7.29"
},
"devDependencies": {
- "@cubejs-backend/linter": "1.7.28",
- "@cubejs-backend/testing-shared": "1.7.28",
+ "@cubejs-backend/linter": "1.7.29",
+ "@cubejs-backend/testing-shared": "1.7.29",
"@types/ramda": "^0.27.40",
"typescript": "~5.2.2"
},
diff --git a/packages/cubejs-backend-cloud/CHANGELOG.md b/packages/cubejs-backend-cloud/CHANGELOG.md
index 54bd3a3d49f02..c8ad1358b8b31 100644
--- a/packages/cubejs-backend-cloud/CHANGELOG.md
+++ b/packages/cubejs-backend-cloud/CHANGELOG.md
@@ -3,6 +3,10 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
+## [1.7.29](https://github.com/cube-js/cube/compare/v1.7.28...v1.7.29) (2026-08-27)
+
+**Note:** Version bump only for package @cubejs-backend/cloud
+
## [1.7.28](https://github.com/cube-js/cube/compare/v1.7.27...v1.7.28) (2026-08-26)
**Note:** Version bump only for package @cubejs-backend/cloud
diff --git a/packages/cubejs-backend-cloud/package.json b/packages/cubejs-backend-cloud/package.json
index d6dca569fa3cb..ed6f4baca3cdc 100644
--- a/packages/cubejs-backend-cloud/package.json
+++ b/packages/cubejs-backend-cloud/package.json
@@ -1,6 +1,6 @@
{
"name": "@cubejs-backend/cloud",
- "version": "1.7.28",
+ "version": "1.7.29",
"description": "Cube Cloud package",
"main": "dist/src/index.js",
"typings": "dist/src/index.d.ts",
@@ -30,7 +30,7 @@
"devDependencies": {
"@babel/core": "^7.24.5",
"@babel/preset-env": "^7.24.5",
- "@cubejs-backend/linter": "1.7.28",
+ "@cubejs-backend/linter": "1.7.29",
"@types/fs-extra": "^9.0.8",
"@types/jest": "^29",
"jest": "^29",
@@ -38,7 +38,7 @@
},
"dependencies": {
"@cubejs-backend/dotenv": "^9.0.2",
- "@cubejs-backend/shared": "1.7.28",
+ "@cubejs-backend/shared": "1.7.29",
"chokidar": "^3.5.1",
"env-var": "^6.3.0",
"form-data": "^4.0.0",
diff --git a/packages/cubejs-backend-maven/CHANGELOG.md b/packages/cubejs-backend-maven/CHANGELOG.md
index 83480ad76b2a3..09c764af28175 100644
--- a/packages/cubejs-backend-maven/CHANGELOG.md
+++ b/packages/cubejs-backend-maven/CHANGELOG.md
@@ -3,6 +3,10 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
+## [1.7.29](https://github.com/cube-js/cube/compare/v1.7.28...v1.7.29) (2026-08-27)
+
+**Note:** Version bump only for package @cubejs-backend/maven
+
## [1.7.28](https://github.com/cube-js/cube/compare/v1.7.27...v1.7.28) (2026-08-26)
**Note:** Version bump only for package @cubejs-backend/maven
diff --git a/packages/cubejs-backend-maven/package.json b/packages/cubejs-backend-maven/package.json
index af7564ecae986..4e6091b29e754 100644
--- a/packages/cubejs-backend-maven/package.json
+++ b/packages/cubejs-backend-maven/package.json
@@ -2,7 +2,7 @@
"name": "@cubejs-backend/maven",
"description": "Cube.js Maven Wrapper for java dependencies downloading",
"author": "Cube Dev, Inc.",
- "version": "1.7.28",
+ "version": "1.7.29",
"license": "Apache-2.0",
"repository": {
"type": "git",
@@ -31,12 +31,12 @@
"dist/src/*"
],
"dependencies": {
- "@cubejs-backend/shared": "1.7.28",
+ "@cubejs-backend/shared": "1.7.29",
"source-map-support": "^0.5.19",
"xmlbuilder2": "^2.4.0"
},
"devDependencies": {
- "@cubejs-backend/linter": "1.7.28",
+ "@cubejs-backend/linter": "1.7.29",
"@types/jest": "^29",
"@types/node": "^22",
"jest": "^29",
diff --git a/packages/cubejs-backend-native/CHANGELOG.md b/packages/cubejs-backend-native/CHANGELOG.md
index ec097487f0957..006a786d3ec99 100644
--- a/packages/cubejs-backend-native/CHANGELOG.md
+++ b/packages/cubejs-backend-native/CHANGELOG.md
@@ -3,6 +3,10 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
+## [1.7.29](https://github.com/cube-js/cube/compare/v1.7.28...v1.7.29) (2026-08-27)
+
+**Note:** Version bump only for package @cubejs-backend/native
+
## [1.7.28](https://github.com/cube-js/cube/compare/v1.7.27...v1.7.28) (2026-08-26)
**Note:** Version bump only for package @cubejs-backend/native
diff --git a/packages/cubejs-backend-native/package.json b/packages/cubejs-backend-native/package.json
index c190847902b5e..d7ecbf0f007b2 100644
--- a/packages/cubejs-backend-native/package.json
+++ b/packages/cubejs-backend-native/package.json
@@ -1,6 +1,6 @@
{
"name": "@cubejs-backend/native",
- "version": "1.7.28",
+ "version": "1.7.29",
"author": "Cube Dev, Inc.",
"description": "Native module for Cube.js (binding to Rust codebase)",
"main": "dist/js/index.js",
@@ -39,7 +39,7 @@
"dist/js"
],
"devDependencies": {
- "@cubejs-backend/linter": "1.7.28",
+ "@cubejs-backend/linter": "1.7.29",
"@types/jest": "^29",
"@types/node": "^22",
"cargo-cp-artifact": "^0.1.9",
@@ -50,8 +50,8 @@
"uuid": "^11.1.1"
},
"dependencies": {
- "@cubejs-backend/cubesql": "1.7.28",
- "@cubejs-backend/shared": "1.7.28",
+ "@cubejs-backend/cubesql": "1.7.29",
+ "@cubejs-backend/shared": "1.7.29",
"@cubejs-infra/post-installer": "^0.1.2"
},
"resources": {
diff --git a/packages/cubejs-backend-shared/CHANGELOG.md b/packages/cubejs-backend-shared/CHANGELOG.md
index aafab09388ab8..602e4f96aa936 100644
--- a/packages/cubejs-backend-shared/CHANGELOG.md
+++ b/packages/cubejs-backend-shared/CHANGELOG.md
@@ -3,6 +3,10 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
+## [1.7.29](https://github.com/cube-js/cube/compare/v1.7.28...v1.7.29) (2026-08-27)
+
+**Note:** Version bump only for package @cubejs-backend/shared
+
## [1.7.28](https://github.com/cube-js/cube/compare/v1.7.27...v1.7.28) (2026-08-26)
### Bug Fixes
diff --git a/packages/cubejs-backend-shared/package.json b/packages/cubejs-backend-shared/package.json
index 9bc2af1be01cf..88722abbea5c5 100644
--- a/packages/cubejs-backend-shared/package.json
+++ b/packages/cubejs-backend-shared/package.json
@@ -1,6 +1,6 @@
{
"name": "@cubejs-backend/shared",
- "version": "1.7.28",
+ "version": "1.7.29",
"description": "Shared code for Cube.js backend packages",
"main": "dist/src/index.js",
"typings": "dist/src/index.d.ts",
@@ -27,7 +27,7 @@
},
"license": "Apache-2.0",
"devDependencies": {
- "@cubejs-backend/linter": "1.7.28",
+ "@cubejs-backend/linter": "1.7.29",
"@types/bytes": "^3.1.5",
"@types/cli-progress": "^3.9.1",
"@types/jest": "^29",
diff --git a/packages/cubejs-base-driver/CHANGELOG.md b/packages/cubejs-base-driver/CHANGELOG.md
index 351d937c8f07f..13df2b18a20fb 100644
--- a/packages/cubejs-base-driver/CHANGELOG.md
+++ b/packages/cubejs-base-driver/CHANGELOG.md
@@ -3,6 +3,10 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
+## [1.7.29](https://github.com/cube-js/cube/compare/v1.7.28...v1.7.29) (2026-08-27)
+
+**Note:** Version bump only for package @cubejs-backend/base-driver
+
## [1.7.28](https://github.com/cube-js/cube/compare/v1.7.27...v1.7.28) (2026-08-26)
**Note:** Version bump only for package @cubejs-backend/base-driver
diff --git a/packages/cubejs-base-driver/package.json b/packages/cubejs-base-driver/package.json
index f9eae0553b85a..45ecd5cd90988 100644
--- a/packages/cubejs-base-driver/package.json
+++ b/packages/cubejs-base-driver/package.json
@@ -2,7 +2,7 @@
"name": "@cubejs-backend/base-driver",
"description": "Cube.js Base Driver",
"author": "Cube Dev, Inc.",
- "version": "1.7.28",
+ "version": "1.7.29",
"repository": {
"type": "git",
"url": "https://github.com/cube-js/cube.git",
@@ -33,11 +33,11 @@
"@aws-sdk/s3-request-presigner": "^3.49.0",
"@azure/identity": "^4.4.1",
"@azure/storage-blob": "^12.9.0",
- "@cubejs-backend/shared": "1.7.28",
+ "@cubejs-backend/shared": "1.7.29",
"@google-cloud/storage": "^7.13.0"
},
"devDependencies": {
- "@cubejs-backend/linter": "1.7.28",
+ "@cubejs-backend/linter": "1.7.29",
"@types/jest": "^29",
"@types/node": "^22",
"jest": "^29",
diff --git a/packages/cubejs-bigquery-driver/CHANGELOG.md b/packages/cubejs-bigquery-driver/CHANGELOG.md
index 7babf1af08460..9e05c45b9cd73 100644
--- a/packages/cubejs-bigquery-driver/CHANGELOG.md
+++ b/packages/cubejs-bigquery-driver/CHANGELOG.md
@@ -3,6 +3,10 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
+## [1.7.29](https://github.com/cube-js/cube/compare/v1.7.28...v1.7.29) (2026-08-27)
+
+**Note:** Version bump only for package @cubejs-backend/bigquery-driver
+
## [1.7.28](https://github.com/cube-js/cube/compare/v1.7.27...v1.7.28) (2026-08-26)
**Note:** Version bump only for package @cubejs-backend/bigquery-driver
diff --git a/packages/cubejs-bigquery-driver/package.json b/packages/cubejs-bigquery-driver/package.json
index 65a5e9f97cc50..756f3a93aa793 100644
--- a/packages/cubejs-bigquery-driver/package.json
+++ b/packages/cubejs-bigquery-driver/package.json
@@ -2,7 +2,7 @@
"name": "@cubejs-backend/bigquery-driver",
"description": "Cube.js BigQuery database driver",
"author": "Cube Dev, Inc.",
- "version": "1.7.28",
+ "version": "1.7.29",
"repository": {
"type": "git",
"url": "https://github.com/cube-js/cube.git",
@@ -29,15 +29,15 @@
"main": "index.js",
"types": "dist/src/index.d.ts",
"dependencies": {
- "@cubejs-backend/base-driver": "1.7.28",
+ "@cubejs-backend/base-driver": "1.7.29",
"@cubejs-backend/dotenv": "^9.0.2",
- "@cubejs-backend/shared": "1.7.28",
+ "@cubejs-backend/shared": "1.7.29",
"@google-cloud/bigquery": "^7.7.0",
"@google-cloud/storage": "^7.13.0",
"ramda": "^0.27.2"
},
"devDependencies": {
- "@cubejs-backend/testing-shared": "1.7.28",
+ "@cubejs-backend/testing-shared": "1.7.29",
"@types/big.js": "^6.2.2",
"@types/dedent": "^0.7.0",
"@types/jest": "^29",
diff --git a/packages/cubejs-cli/CHANGELOG.md b/packages/cubejs-cli/CHANGELOG.md
index 2e1a5e567a580..acd7b7a5be87e 100644
--- a/packages/cubejs-cli/CHANGELOG.md
+++ b/packages/cubejs-cli/CHANGELOG.md
@@ -3,6 +3,10 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
+## [1.7.29](https://github.com/cube-js/cube/compare/v1.7.28...v1.7.29) (2026-08-27)
+
+**Note:** Version bump only for package cubejs-cli
+
## [1.7.28](https://github.com/cube-js/cube/compare/v1.7.27...v1.7.28) (2026-08-26)
**Note:** Version bump only for package cubejs-cli
diff --git a/packages/cubejs-cli/package.json b/packages/cubejs-cli/package.json
index d27f6576158c6..8d77be17508bd 100644
--- a/packages/cubejs-cli/package.json
+++ b/packages/cubejs-cli/package.json
@@ -2,7 +2,7 @@
"name": "cubejs-cli",
"description": "Cube.js Command Line Interface",
"author": "Cube Dev, Inc.",
- "version": "1.7.28",
+ "version": "1.7.29",
"repository": {
"type": "git",
"url": "https://github.com/cube-js/cube.git",
@@ -30,10 +30,10 @@
"LICENSE"
],
"dependencies": {
- "@cubejs-backend/cloud": "1.7.28",
+ "@cubejs-backend/cloud": "1.7.29",
"@cubejs-backend/dotenv": "^9.0.2",
- "@cubejs-backend/schema-compiler": "1.7.28",
- "@cubejs-backend/shared": "1.7.28",
+ "@cubejs-backend/schema-compiler": "1.7.29",
+ "@cubejs-backend/shared": "1.7.29",
"chalk": "^2.4.2",
"cli-progress": "^3.10",
"commander": "^2.19.0",
@@ -50,8 +50,8 @@
"colors": "1.4.0"
},
"devDependencies": {
- "@cubejs-backend/linter": "1.7.28",
- "@cubejs-backend/server": "1.7.28",
+ "@cubejs-backend/linter": "1.7.29",
+ "@cubejs-backend/server": "1.7.29",
"@oclif/command": "^1.8.0",
"@types/cli-progress": "^3.8.0",
"@types/cross-spawn": "^6.0.2",
diff --git a/packages/cubejs-clickhouse-driver/CHANGELOG.md b/packages/cubejs-clickhouse-driver/CHANGELOG.md
index fb1aa319a78f2..6a740d925fee0 100644
--- a/packages/cubejs-clickhouse-driver/CHANGELOG.md
+++ b/packages/cubejs-clickhouse-driver/CHANGELOG.md
@@ -3,6 +3,10 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
+## [1.7.29](https://github.com/cube-js/cube/compare/v1.7.28...v1.7.29) (2026-08-27)
+
+**Note:** Version bump only for package @cubejs-backend/clickhouse-driver
+
## [1.7.28](https://github.com/cube-js/cube/compare/v1.7.27...v1.7.28) (2026-08-26)
**Note:** Version bump only for package @cubejs-backend/clickhouse-driver
diff --git a/packages/cubejs-clickhouse-driver/package.json b/packages/cubejs-clickhouse-driver/package.json
index ab4f5da8dd6ae..dd1f05b71589f 100644
--- a/packages/cubejs-clickhouse-driver/package.json
+++ b/packages/cubejs-clickhouse-driver/package.json
@@ -2,7 +2,7 @@
"name": "@cubejs-backend/clickhouse-driver",
"description": "Cube.js ClickHouse database driver",
"author": "Cube Dev, Inc.",
- "version": "1.7.28",
+ "version": "1.7.29",
"repository": {
"type": "git",
"url": "https://github.com/cube-js/cube.git",
@@ -29,16 +29,16 @@
},
"dependencies": {
"@clickhouse/client": "^1.12.0",
- "@cubejs-backend/base-driver": "1.7.28",
- "@cubejs-backend/shared": "1.7.28",
+ "@cubejs-backend/base-driver": "1.7.29",
+ "@cubejs-backend/shared": "1.7.29",
"moment": "^2.24.0",
"sqlstring": "^2.3.1",
"uuid": "^11.1.1"
},
"license": "Apache-2.0",
"devDependencies": {
- "@cubejs-backend/linter": "1.7.28",
- "@cubejs-backend/testing-shared": "1.7.28",
+ "@cubejs-backend/linter": "1.7.29",
+ "@cubejs-backend/testing-shared": "1.7.29",
"@types/jest": "^29",
"jest": "^29",
"typescript": "~5.2.2"
diff --git a/packages/cubejs-client-core/CHANGELOG.md b/packages/cubejs-client-core/CHANGELOG.md
index 328b69dd3fb2f..9cbd8695e28aa 100644
--- a/packages/cubejs-client-core/CHANGELOG.md
+++ b/packages/cubejs-client-core/CHANGELOG.md
@@ -3,6 +3,10 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
+## [1.7.29](https://github.com/cube-js/cube/compare/v1.7.28...v1.7.29) (2026-08-27)
+
+**Note:** Version bump only for package @cubejs-client/core
+
## [1.7.28](https://github.com/cube-js/cube/compare/v1.7.27...v1.7.28) (2026-08-26)
**Note:** Version bump only for package @cubejs-client/core
diff --git a/packages/cubejs-client-core/package.json b/packages/cubejs-client-core/package.json
index 1865c8a7dc5d2..60d127c4f3a72 100644
--- a/packages/cubejs-client-core/package.json
+++ b/packages/cubejs-client-core/package.json
@@ -1,6 +1,6 @@
{
"name": "@cubejs-client/core",
- "version": "1.7.28",
+ "version": "1.7.29",
"engines": {},
"type": "module",
"repository": {
@@ -58,7 +58,7 @@
],
"license": "MIT",
"devDependencies": {
- "@cubejs-backend/linter": "1.7.28",
+ "@cubejs-backend/linter": "1.7.29",
"@types/d3-format": "^3",
"@types/d3-time-format": "^4",
"@types/moment-range": "^4.0.0",
diff --git a/packages/cubejs-client-dx/CHANGELOG.md b/packages/cubejs-client-dx/CHANGELOG.md
index 1067446ed856f..5d99dca457a55 100644
--- a/packages/cubejs-client-dx/CHANGELOG.md
+++ b/packages/cubejs-client-dx/CHANGELOG.md
@@ -3,6 +3,10 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
+## [1.7.29](https://github.com/cube-js/cube/compare/v1.7.28...v1.7.29) (2026-08-27)
+
+**Note:** Version bump only for package @cubejs-client/dx
+
## [1.7.28](https://github.com/cube-js/cube/compare/v1.7.27...v1.7.28) (2026-08-26)
**Note:** Version bump only for package @cubejs-client/dx
diff --git a/packages/cubejs-client-dx/package.json b/packages/cubejs-client-dx/package.json
index 4a9017e66120e..dc8ee50644e45 100644
--- a/packages/cubejs-client-dx/package.json
+++ b/packages/cubejs-client-dx/package.json
@@ -1,6 +1,6 @@
{
"name": "@cubejs-client/dx",
- "version": "1.7.28",
+ "version": "1.7.29",
"engines": {},
"repository": {
"type": "git",
diff --git a/packages/cubejs-client-ngx/CHANGELOG.md b/packages/cubejs-client-ngx/CHANGELOG.md
index 3c8f41be681f7..a953a5363ef19 100644
--- a/packages/cubejs-client-ngx/CHANGELOG.md
+++ b/packages/cubejs-client-ngx/CHANGELOG.md
@@ -3,6 +3,10 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
+## [1.7.29](https://github.com/cube-js/cube/compare/v1.7.28...v1.7.29) (2026-08-27)
+
+**Note:** Version bump only for package @cubejs-client/ngx
+
## [1.7.28](https://github.com/cube-js/cube/compare/v1.7.27...v1.7.28) (2026-08-26)
**Note:** Version bump only for package @cubejs-client/ngx
diff --git a/packages/cubejs-client-ngx/package.json b/packages/cubejs-client-ngx/package.json
index 21ac9a14d43c7..bebfbd5f60c59 100644
--- a/packages/cubejs-client-ngx/package.json
+++ b/packages/cubejs-client-ngx/package.json
@@ -1,6 +1,6 @@
{
"name": "@cubejs-client/ngx",
- "version": "1.7.28",
+ "version": "1.7.29",
"author": "Cube Dev, Inc.",
"engines": {},
"repository": {
diff --git a/packages/cubejs-client-react/CHANGELOG.md b/packages/cubejs-client-react/CHANGELOG.md
index 665061d2f0f91..5358adc9d3404 100644
--- a/packages/cubejs-client-react/CHANGELOG.md
+++ b/packages/cubejs-client-react/CHANGELOG.md
@@ -3,6 +3,10 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
+## [1.7.29](https://github.com/cube-js/cube/compare/v1.7.28...v1.7.29) (2026-08-27)
+
+**Note:** Version bump only for package @cubejs-client/react
+
## [1.7.28](https://github.com/cube-js/cube/compare/v1.7.27...v1.7.28) (2026-08-26)
**Note:** Version bump only for package @cubejs-client/react
diff --git a/packages/cubejs-client-react/package.json b/packages/cubejs-client-react/package.json
index f266d1ae12ffe..8d371c8bb0fd3 100644
--- a/packages/cubejs-client-react/package.json
+++ b/packages/cubejs-client-react/package.json
@@ -1,6 +1,6 @@
{
"name": "@cubejs-client/react",
- "version": "1.7.28",
+ "version": "1.7.29",
"author": "Cube Dev, Inc.",
"license": "MIT",
"engines": {},
@@ -26,7 +26,7 @@
],
"dependencies": {
"@babel/runtime": "^7.1.2",
- "@cubejs-client/core": "1.7.28",
+ "@cubejs-client/core": "1.7.29",
"core-js": "^3.6.5",
"ramda": "^0.27.2"
},
diff --git a/packages/cubejs-client-vue3/CHANGELOG.md b/packages/cubejs-client-vue3/CHANGELOG.md
index 36375c32cf04f..cd7817677efd6 100644
--- a/packages/cubejs-client-vue3/CHANGELOG.md
+++ b/packages/cubejs-client-vue3/CHANGELOG.md
@@ -3,6 +3,10 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
+## [1.7.29](https://github.com/cube-js/cube/compare/v1.7.28...v1.7.29) (2026-08-27)
+
+**Note:** Version bump only for package @cubejs-client/vue3
+
## [1.7.28](https://github.com/cube-js/cube/compare/v1.7.27...v1.7.28) (2026-08-26)
**Note:** Version bump only for package @cubejs-client/vue3
diff --git a/packages/cubejs-client-vue3/package.json b/packages/cubejs-client-vue3/package.json
index bd4bc226b7454..b980de9651a36 100644
--- a/packages/cubejs-client-vue3/package.json
+++ b/packages/cubejs-client-vue3/package.json
@@ -1,6 +1,6 @@
{
"name": "@cubejs-client/vue3",
- "version": "1.7.28",
+ "version": "1.7.29",
"engines": {},
"repository": {
"type": "git",
@@ -27,7 +27,7 @@
"src"
],
"dependencies": {
- "@cubejs-client/core": "1.7.28",
+ "@cubejs-client/core": "1.7.29",
"ramda": "^0.27.0"
},
"devDependencies": {
diff --git a/packages/cubejs-client-ws-transport/CHANGELOG.md b/packages/cubejs-client-ws-transport/CHANGELOG.md
index 2f971fcb9096c..711b52b2ab5f5 100644
--- a/packages/cubejs-client-ws-transport/CHANGELOG.md
+++ b/packages/cubejs-client-ws-transport/CHANGELOG.md
@@ -3,6 +3,10 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
+## [1.7.29](https://github.com/cube-js/cube/compare/v1.7.28...v1.7.29) (2026-08-27)
+
+**Note:** Version bump only for package @cubejs-client/ws-transport
+
## [1.7.28](https://github.com/cube-js/cube/compare/v1.7.27...v1.7.28) (2026-08-26)
**Note:** Version bump only for package @cubejs-client/ws-transport
diff --git a/packages/cubejs-client-ws-transport/package.json b/packages/cubejs-client-ws-transport/package.json
index c9494f0ea742d..0d97dd5ae88b7 100644
--- a/packages/cubejs-client-ws-transport/package.json
+++ b/packages/cubejs-client-ws-transport/package.json
@@ -1,6 +1,6 @@
{
"name": "@cubejs-client/ws-transport",
- "version": "1.7.28",
+ "version": "1.7.29",
"engines": {},
"repository": {
"type": "git",
@@ -20,7 +20,7 @@
},
"dependencies": {
"@babel/runtime": "^7.1.2",
- "@cubejs-client/core": "1.7.28",
+ "@cubejs-client/core": "1.7.29",
"core-js": "^3.6.5",
"isomorphic-ws": "^4.0.1",
"ws": "^7.3.1"
@@ -33,7 +33,7 @@
"@babel/core": "^7.3.3",
"@babel/preset-env": "^7.3.1",
"@babel/preset-typescript": "^7.12.1",
- "@cubejs-backend/linter": "1.7.28",
+ "@cubejs-backend/linter": "1.7.29",
"@types/ws": "^7.2.9",
"typescript": "~5.2.2"
},
diff --git a/packages/cubejs-crate-driver/CHANGELOG.md b/packages/cubejs-crate-driver/CHANGELOG.md
index 5e11febd61d87..a345375c60d97 100644
--- a/packages/cubejs-crate-driver/CHANGELOG.md
+++ b/packages/cubejs-crate-driver/CHANGELOG.md
@@ -3,6 +3,10 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
+## [1.7.29](https://github.com/cube-js/cube/compare/v1.7.28...v1.7.29) (2026-08-27)
+
+**Note:** Version bump only for package @cubejs-backend/crate-driver
+
## [1.7.28](https://github.com/cube-js/cube/compare/v1.7.27...v1.7.28) (2026-08-26)
**Note:** Version bump only for package @cubejs-backend/crate-driver
diff --git a/packages/cubejs-crate-driver/package.json b/packages/cubejs-crate-driver/package.json
index 1daaeb392c711..c57761ab2335d 100644
--- a/packages/cubejs-crate-driver/package.json
+++ b/packages/cubejs-crate-driver/package.json
@@ -2,7 +2,7 @@
"name": "@cubejs-backend/crate-driver",
"description": "Cube.js Crate database driver",
"author": "Cube Dev, Inc.",
- "version": "1.7.28",
+ "version": "1.7.29",
"repository": {
"type": "git",
"url": "https://github.com/cube-js/cube.git",
@@ -29,13 +29,13 @@
"lint:fix": "eslint --fix src/* --ext .ts"
},
"dependencies": {
- "@cubejs-backend/postgres-driver": "1.7.28",
- "@cubejs-backend/shared": "1.7.28"
+ "@cubejs-backend/postgres-driver": "1.7.29",
+ "@cubejs-backend/shared": "1.7.29"
},
"license": "Apache-2.0",
"devDependencies": {
- "@cubejs-backend/linter": "1.7.28",
- "@cubejs-backend/testing-shared": "1.7.28",
+ "@cubejs-backend/linter": "1.7.29",
+ "@cubejs-backend/testing-shared": "1.7.29",
"testcontainers": "^10.28.0",
"typescript": "~5.2.2"
},
diff --git a/packages/cubejs-cubestore-driver/CHANGELOG.md b/packages/cubejs-cubestore-driver/CHANGELOG.md
index e0dab773a26e3..6f96fd950a826 100644
--- a/packages/cubejs-cubestore-driver/CHANGELOG.md
+++ b/packages/cubejs-cubestore-driver/CHANGELOG.md
@@ -3,6 +3,12 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
+## [1.7.29](https://github.com/cube-js/cube/compare/v1.7.28...v1.7.29) (2026-08-27)
+
+### Bug Fixes
+
+- **server-core:** release orchestrators evicted from the LRU ([#11661](https://github.com/cube-js/cube/issues/11661)) ([b82c58a](https://github.com/cube-js/cube/commit/b82c58aa245919a36e3d20f79105b639d0c16863))
+
## [1.7.28](https://github.com/cube-js/cube/compare/v1.7.27...v1.7.28) (2026-08-26)
### Bug Fixes
diff --git a/packages/cubejs-cubestore-driver/package.json b/packages/cubejs-cubestore-driver/package.json
index 6c06ae3a9cbb5..8d5cc4ce1ae86 100644
--- a/packages/cubejs-cubestore-driver/package.json
+++ b/packages/cubejs-cubestore-driver/package.json
@@ -2,7 +2,7 @@
"name": "@cubejs-backend/cubestore-driver",
"description": "Cube Store driver",
"author": "Cube Dev, Inc.",
- "version": "1.7.28",
+ "version": "1.7.29",
"repository": {
"type": "git",
"url": "https://github.com/cube-js/cube.git",
@@ -27,10 +27,10 @@
"unit": "jest --coverage"
},
"dependencies": {
- "@cubejs-backend/base-driver": "1.7.28",
- "@cubejs-backend/cubestore": "1.7.28",
- "@cubejs-backend/native": "1.7.28",
- "@cubejs-backend/shared": "1.7.28",
+ "@cubejs-backend/base-driver": "1.7.29",
+ "@cubejs-backend/cubestore": "1.7.29",
+ "@cubejs-backend/native": "1.7.29",
+ "@cubejs-backend/shared": "1.7.29",
"csv-write-stream": "^2.0.0",
"flatbuffers": "25.9.23",
"fs-extra": "^9.1.0",
@@ -41,7 +41,7 @@
"ws": "^7.4.3"
},
"devDependencies": {
- "@cubejs-backend/linter": "1.7.28",
+ "@cubejs-backend/linter": "1.7.29",
"@types/csv-write-stream": "^2.0.0",
"@types/jest": "^29",
"@types/node": "^22",
diff --git a/packages/cubejs-cubestore-driver/src/WebSocketConnection.ts b/packages/cubejs-cubestore-driver/src/WebSocketConnection.ts
index 362fc11cf6ff8..f20ef98cfaeac 100644
--- a/packages/cubejs-cubestore-driver/src/WebSocketConnection.ts
+++ b/packages/cubejs-cubestore-driver/src/WebSocketConnection.ts
@@ -67,6 +67,9 @@ interface CubeStoreWebSocket extends WebSocket {
// A failure that killed this socket and that re-sending can't fix, so the
// message that caused it is rejected instead of being re-sent.
fatalError: Error | null;
+ // Stops the heartbeat interval, which otherwise keeps a reference to this socket
+ // (and the socket itself alive) even when nothing else points to the connection.
+ teardown: () => void;
}
export class WebSocketConnection {
@@ -88,6 +91,17 @@ export class WebSocketConnection {
private cubeStoreVersion: string | null = null;
+ // Set by close(), and never unset: a closed connection stays open only for the
+ // messages already in flight and never establishes another socket. Without it
+ // close() does not actually close anything -- the 'close' handler re-sends
+ // whatever was pending over a fresh connection, and the heartbeat on that one
+ // keeps it (and the orchestrator holding it) alive for the life of the process.
+ private closed: boolean = false;
+
+ // When close() was asked for, so the heartbeat can bound the wait for the
+ // messages that were still in flight at that point.
+ private closedAt: Date | null = null;
+
public constructor(url: string) {
this.url = url;
this.messageCounter = 1;
@@ -99,6 +113,13 @@ export class WebSocketConnection {
}
protected async initWebSocket(): Promise {
+ if (this.closed) {
+ // Refusing here is what makes close() terminal: it covers a query issued
+ // after the release as well as the re-send path, whose `catch` then
+ // rejects the messages that were in flight instead of re-opening for them.
+ throw new ConnectionError('Cube Store connection is closed');
+ }
+
if (!this.webSocket) {
const headers: Record = {};
headers['x-process-id'] = getProcessUid();
@@ -115,11 +136,32 @@ export class WebSocketConnection {
webSocket.ping();
}
+ if (this.closed) {
+ // A closed connection is kept only for the messages already in
+ // flight, and this is where that wait is both re-checked and
+ // bounded. It has to be bounded here rather than by the heartbeat
+ // check below: Cube Store keeps answering the pings of a connection
+ // whose query is simply never completed, so that check never fires
+ // and a single wedged message would keep this socket -- and this
+ // interval, which is what makes it reachable -- for the life of the
+ // process. Pinging continues meanwhile, so a connection that is
+ // draining legitimately is not dropped for inactivity.
+ if (this.closedAt && new Date().getTime() - this.closedAt.getTime() > this.noHeartBeatTimeout * 1000) {
+ this.abandonClose(webSocket);
+ } else {
+ this.closeIfDrained(webSocket);
+ }
+
+ return;
+ }
+
if (new Date().getTime() - webSocket.lastHeartBeat.getTime() > this.noHeartBeatTimeout * 1000) {
webSocket.close();
}
}, 5000);
+ webSocket.teardown = () => clearInterval(pingInterval);
+
webSocket.sendAsync = async (message: Uint8Array) => new Promise((resolveSend) => {
// If socket is closing this message should be resent
if (webSocket.readyState !== WebSocket.OPEN) {
@@ -164,11 +206,48 @@ export class WebSocketConnection {
return;
}
+ // The socket is done either way, so stop its heartbeat now rather than
+ // relying on a 'close' that may not follow: the interval is what keeps
+ // the socket, and everything reachable from it, alive.
+ webSocket.teardown();
+
+ if (this.closed) {
+ // Nothing to retry towards: `initWebSocket()` refuses once closed.
+ // Whatever was written to the socket is rejected by the 'close'
+ // handler, which `ws` emits after an 'error' -- the drain bound
+ // cannot catch it any more, since `teardown()` above has already
+ // stopped the interval that evaluates it.
+ if (webSocket === this.webSocket) {
+ this.webSocket = null;
+ }
+
+ // A no-op once this socket has opened, and the only thing left that
+ // can settle `readyPromise` if it has not: `teardown()` has stopped
+ // the interval, the 'close' handler does not touch it, and the retry
+ // that used to settle it by adoption is skipped just above. A socket
+ // closed while still CONNECTING would otherwise leave the
+ // `sendMessage` awaiting it pending for good -- a request hung on
+ // nothing, which is worse than the failure it replaced.
+ reject(new ConnectionError('Cube Store connection is closed'));
+
+ return;
+ }
+
+ // Counted here rather than above the branch: the closed path does not
+ // attempt a connection, and this counter both paces the retries and
+ // spends the `maxConnectRetries` budget a genuinely cold connect needs.
this.currentConnectionTry += 1;
if (this.currentConnectionTry < this.maxConnectRetries) {
setTimeout(async () => {
- resolve(this.initWebSocket());
+ // `.then(resolve, reject)` rather than `resolve(promise)`: once
+ // this socket's `readyPromise` has settled, `resolve` returns
+ // without adopting -- so a rejection handed to it is never
+ // observed, and an unhandled rejection is fatal under Node's
+ // default. The retry above cannot reject for `closed` any more,
+ // but a close landing between this timer being armed and firing
+ // still can, as can anything else `initWebSocket()` throws.
+ this.initWebSocket().then(resolve, reject);
}, this.retryWaitTime());
} else {
reject(new ConnectionError(
@@ -188,7 +267,7 @@ export class WebSocketConnection {
webSocket.lastHeartBeat = new Date();
});
webSocket.on('close', (code: number, reason: Buffer) => {
- clearInterval(pingInterval);
+ webSocket.teardown();
const pending = Object.keys(webSocket.sentMessages);
@@ -259,6 +338,13 @@ export class WebSocketConnection {
setTimeout(async () => {
try {
+ // Everything this re-send was scheduled for has been settled in
+ // the meantime, so there is nothing left to carry over and a new
+ // connection would just stay open on its heartbeat.
+ if (!Object.keys(webSocket.sentMessages).length) {
+ return;
+ }
+
const nextWebSocket = await this.initWebSocket();
const resent = Object.keys(webSocket.sentMessages);
@@ -302,6 +388,7 @@ export class WebSocketConnection {
if (httpMessage.commandType() === HttpCommand.HttpError) {
resolver.reject(new QueryError(`${httpMessage.command(new HttpError())?.error()}`));
+ this.closeIfDrained(webSocket);
return;
}
@@ -311,6 +398,8 @@ export class WebSocketConnection {
} catch (e) {
resolver.reject(e);
}
+
+ this.closeIfDrained(webSocket);
});
});
@@ -530,9 +619,78 @@ export class WebSocketConnection {
return this.cubeStoreVersion ?? '0.0.0';
}
+ /**
+ * Closes the connection for good. Messages already in flight are given the
+ * chance to be answered -- an eviction can happen mid-query, and failing one
+ * that Cube Store is already working on would fail the request -- so the
+ * socket goes away once the last of them settles, or right away if there are
+ * none.
+ */
public close() {
- if (this.webSocket) {
- this.webSocket.close();
+ if (!this.closed) {
+ this.closed = true;
+ // First asked for, not most recently: `release()` closes the data-source
+ // and external drivers separately, which is the same connection twice when
+ // one `CubeStoreDriver` backs both, and re-stamping would restart the bound
+ // each time.
+ this.closedAt = new Date();
+ }
+
+ // Straight away when there is nothing in flight; otherwise the heartbeat
+ // takes it from here, both to notice the drain finishing and to bound it.
+ this.closeIfDrained(this.webSocket);
+ }
+
+ /**
+ * Gives up on the messages a closed connection was waiting for, so that one
+ * Cube Store never answers costs a query rather than a permanently open
+ * socket.
+ */
+ private abandonClose(socket: CubeStoreWebSocket) {
+ const unanswered = Object.keys(socket.sentMessages);
+
+ socket.teardown();
+
+ if (socket === this.webSocket) {
+ this.webSocket = null;
}
+
+ const error = new ConnectionError(
+ `Cube Store connection was closed with ${unanswered.length} message(s) still unanswered`
+ );
+
+ // eslint-disable-next-line no-restricted-syntax
+ for (const key of unanswered) {
+ const message = socket.sentMessages[key];
+ delete socket.sentMessages[key];
+ message.reject(error);
+ }
+
+ socket.terminate();
+ }
+
+ /**
+ * Closes `socket` once nothing is in flight on it. A no-op until close() has
+ * been called, so an ordinary idle connection is left alone.
+ *
+ * Takes the socket rather than reading `this.webSocket`: a late answer can
+ * arrive on a socket the re-send path has already superseded, and that must
+ * not be read as the current connection having drained.
+ */
+ private closeIfDrained(socket: CubeStoreWebSocket | null) {
+ if (!this.closed || !socket || socket !== this.webSocket) {
+ return;
+ }
+
+ if (Object.keys(socket.sentMessages).length) {
+ return;
+ }
+
+ // Stop the heartbeat before closing rather than leaving it to the 'close'
+ // handler: it is the timer, not the socket, that keeps this whole graph
+ // reachable, so it should not outlive the decision to close by even a tick.
+ socket.teardown();
+ this.webSocket = null;
+ socket.close();
}
}
diff --git a/packages/cubejs-cubestore-driver/test/websocket-connection.test.ts b/packages/cubejs-cubestore-driver/test/websocket-connection.test.ts
index 33c51c6867249..ddff629837074 100644
--- a/packages/cubejs-cubestore-driver/test/websocket-connection.test.ts
+++ b/packages/cubejs-cubestore-driver/test/websocket-connection.test.ts
@@ -1,7 +1,8 @@
import { Socket } from 'net';
+import WebSocket from 'ws';
import { WebSocketConnection } from '../src/WebSocketConnection';
-import { MessageTooLargeError, QueryError } from '../src/errors';
+import { ConnectionError, MessageTooLargeError, QueryError } from '../src/errors';
import { QueryResultFormat } from '../codegen';
import {
answeredBy,
@@ -48,6 +49,22 @@ const waitForBufferedWrite = async (socket: Socket) => {
}
};
+/**
+ * Polls until `condition` holds, for assertions about a socket that is torn down
+ * a few ticks after the call that asked for it.
+ */
+const waitUntil = async (condition: () => boolean, description: string) => {
+ const deadline = Date.now() + 5000;
+
+ while (!condition()) {
+ if (Date.now() > deadline) {
+ throw new Error(`Timed out waiting for ${description}`);
+ }
+
+ await new Promise((resolve) => { setTimeout(resolve, 25); });
+ }
+};
+
describe('WebSocketConnection', () => {
let server: MockCubeStoreServer;
let connection: WebSocketConnection | null = null;
@@ -168,6 +185,154 @@ describe('WebSocketConnection', () => {
await expectAnsweredBy(query('SELECT 1'), 1);
}, JEST_TIMEOUT);
+ describe('close', () => {
+ /** Whether Cube Store has seen the connection go away. */
+ const closedOnTheServer = (index: number) => server.connection(index).ws.readyState === WebSocket.CLOSED;
+
+ it('closes the socket when nothing is in flight', async () => {
+ connection = new WebSocketConnection(server.url);
+
+ await expectAnsweredBy(query('SELECT 1'), 0);
+
+ connection.close();
+
+ await waitUntil(() => closedOnTheServer(0), 'Cube Store to see the connection close');
+ expect((connection as any).webSocket).toBeNull();
+ }, JEST_TIMEOUT);
+
+ it('keeps the connection open until an in-flight query is answered', async () => {
+ connection = new WebSocketConnection(server.url);
+
+ let answer: () => void = () => undefined;
+ server.handler = (message, mockConnection) => {
+ answer = () => mockConnection.ws.send(
+ buildErrorMessage(message.messageId, answeredBy(mockConnection.index))
+ );
+ };
+
+ const promise = query('SELECT 1');
+ await server.waitForMessages(1);
+
+ // An eviction can land mid-query, and Cube Store is already working on the
+ // answer, so closing must not drop it: the socket stays OPEN rather than
+ // going into CLOSING out from under the query.
+ connection.close();
+ expect((connection as any).webSocket.readyState).toBe(WebSocket.OPEN);
+
+ answer();
+ await expectAnsweredBy(promise, 0);
+
+ await waitUntil(() => closedOnTheServer(0), 'the connection to close once drained');
+ expect(server.connections.length).toBe(1);
+ }, JEST_TIMEOUT);
+
+ it('does not establish a new connection for a query issued after close', async () => {
+ connection = new WebSocketConnection(server.url);
+
+ await expectAnsweredBy(query('SELECT 1'), 0);
+ connection.close();
+
+ // The released driver is still reachable from whoever held it, and this is
+ // the call that used to re-open the socket the release had just closed.
+ await expect(query('SELECT 2')).rejects.toThrow(ConnectionError);
+ expect(server.connections.length).toBe(1);
+ }, JEST_TIMEOUT);
+
+ it('gives up on a message Cube Store never answers instead of holding the socket', async () => {
+ // The bound the close waits out. Cube Store keeps answering the pings of a
+ // connection whose query never completes, so without it nothing would ever
+ // close this socket again.
+ process.env.CUBEJS_CUBESTORE_NO_HEART_BEAT_TIMEOUT = '1';
+
+ try {
+ connection = new WebSocketConnection(server.url);
+
+ server.handler = () => undefined;
+
+ const promise = query('SELECT 1');
+ await server.waitForMessages(1);
+
+ connection.close();
+
+ await expect(promise).rejects.toThrow(ConnectionError);
+ await expect(promise).rejects.toThrow('1 message(s) still unanswered');
+
+ await waitUntil(() => closedOnTheServer(0), 'the connection to be closed anyway');
+ expect((connection as any).webSocket).toBeNull();
+ } finally {
+ delete process.env.CUBEJS_CUBESTORE_NO_HEART_BEAT_TIMEOUT;
+ }
+ }, JEST_TIMEOUT);
+
+ it('rejects a query that is still connecting when the connection is closed', async () => {
+ connection = new WebSocketConnection(server.url);
+
+ // `initWebSocket()` creates the socket synchronously, so this close lands
+ // while it is still CONNECTING -- an eviction while the very first query
+ // of an orchestrator is connecting. `ws` reports closing a CONNECTING
+ // socket as an 'error', and nothing else left would settle the promise
+ // the query is awaiting.
+ const promise = query('SELECT 1');
+ connection.close();
+
+ await expect(promise).rejects.toThrow(ConnectionError);
+ await expect(promise).rejects.toThrow('Cube Store connection is closed');
+ }, JEST_TIMEOUT);
+
+ it('does not leave an unhandled rejection when a closed connection errors', async () => {
+ const unhandled: unknown[] = [];
+ const onUnhandled = (reason: unknown) => { unhandled.push(reason); };
+ process.on('unhandledRejection', onUnhandled);
+
+ try {
+ connection = new WebSocketConnection(server.url);
+
+ // Establish it, so this socket's `readyPromise` is already settled.
+ await expectAnsweredBy(query('SELECT 1'), 0);
+
+ const socket = (connection as any).webSocket;
+ connection.close();
+
+ // A post-open 'error' on a closed connection. Emitted rather than
+ // provoked because `ws` reports a dying socket as 'close' here, while
+ // the errors that do reach this handler in the wild -- a protocol error,
+ // or a connect retry still pending from before the close -- are not
+ // reproducible against the mock. The handler's contract is the subject:
+ // it schedules a retry, `initWebSocket()` refuses once closed, and
+ // handing that rejection to an already-settled `resolve` leaves nobody
+ // to observe it, which is fatal under Node's default
+ // --unhandled-rejections=throw.
+ socket.emit('error', Object.assign(new Error('read ECONNRESET'), { code: 'ECONNRESET' }));
+
+ // Past the retry the handler schedules.
+ await new Promise((resolve) => { setTimeout(resolve, 2500); });
+
+ expect(unhandled).toEqual([]);
+ } finally {
+ process.off('unhandledRejection', onUnhandled);
+ }
+ }, JEST_TIMEOUT);
+
+ it('does not re-open when the socket dies while draining', async () => {
+ connection = new WebSocketConnection(server.url);
+
+ // Cube Store never answers, so the query is still in flight at close.
+ server.handler = () => undefined;
+
+ const promise = query('SELECT 1');
+ await server.waitForMessages(1);
+
+ connection.close();
+ clientSocket().destroy(epipe());
+
+ // The re-send path would ordinarily raise a fresh connection here, whose
+ // heartbeat is exactly what kept an evicted orchestrator alive. Failing
+ // the query is the honest outcome once the connection is closed.
+ await expect(promise).rejects.toThrow(ConnectionError);
+ expect(server.connections.length).toBe(1);
+ }, JEST_TIMEOUT);
+ });
+
describe('message size limit', () => {
const MAX_MESSAGE_SIZE = 1024 * 1024;
diff --git a/packages/cubejs-databricks-jdbc-driver/CHANGELOG.md b/packages/cubejs-databricks-jdbc-driver/CHANGELOG.md
index 49e46739ddcc1..911f91903c4b5 100644
--- a/packages/cubejs-databricks-jdbc-driver/CHANGELOG.md
+++ b/packages/cubejs-databricks-jdbc-driver/CHANGELOG.md
@@ -3,6 +3,10 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
+## [1.7.29](https://github.com/cube-js/cube/compare/v1.7.28...v1.7.29) (2026-08-27)
+
+**Note:** Version bump only for package @cubejs-backend/databricks-jdbc-driver
+
## [1.7.28](https://github.com/cube-js/cube/compare/v1.7.27...v1.7.28) (2026-08-26)
**Note:** Version bump only for package @cubejs-backend/databricks-jdbc-driver
diff --git a/packages/cubejs-databricks-jdbc-driver/package.json b/packages/cubejs-databricks-jdbc-driver/package.json
index 2922707950ae2..6931578c0871e 100644
--- a/packages/cubejs-databricks-jdbc-driver/package.json
+++ b/packages/cubejs-databricks-jdbc-driver/package.json
@@ -2,7 +2,7 @@
"name": "@cubejs-backend/databricks-jdbc-driver",
"description": "Cube.js Databricks database driver",
"author": "Cube Dev, Inc.",
- "version": "1.7.28",
+ "version": "1.7.29",
"license": "Apache-2.0",
"repository": {
"type": "git",
@@ -30,17 +30,17 @@
"bin"
],
"dependencies": {
- "@cubejs-backend/base-driver": "1.7.28",
- "@cubejs-backend/jdbc-driver": "1.7.28",
- "@cubejs-backend/schema-compiler": "1.7.28",
- "@cubejs-backend/shared": "1.7.28",
+ "@cubejs-backend/base-driver": "1.7.29",
+ "@cubejs-backend/jdbc-driver": "1.7.29",
+ "@cubejs-backend/schema-compiler": "1.7.29",
+ "@cubejs-backend/shared": "1.7.29",
"node-fetch": "^2.6.1",
"ramda": "^0.27.2",
"source-map-support": "^0.5.19",
"uuid": "^11.1.1"
},
"devDependencies": {
- "@cubejs-backend/linter": "1.7.28",
+ "@cubejs-backend/linter": "1.7.29",
"@types/jest": "^29",
"@types/node": "^22",
"@types/ramda": "^0.27.34",
diff --git a/packages/cubejs-dbt-schema-extension/CHANGELOG.md b/packages/cubejs-dbt-schema-extension/CHANGELOG.md
index 6498bcdaf56e4..21f26c87a1bc5 100644
--- a/packages/cubejs-dbt-schema-extension/CHANGELOG.md
+++ b/packages/cubejs-dbt-schema-extension/CHANGELOG.md
@@ -3,6 +3,10 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
+## [1.7.29](https://github.com/cube-js/cube/compare/v1.7.28...v1.7.29) (2026-08-27)
+
+**Note:** Version bump only for package @cubejs-backend/dbt-schema-extension
+
## [1.7.28](https://github.com/cube-js/cube/compare/v1.7.27...v1.7.28) (2026-08-26)
**Note:** Version bump only for package @cubejs-backend/dbt-schema-extension
diff --git a/packages/cubejs-dbt-schema-extension/package.json b/packages/cubejs-dbt-schema-extension/package.json
index c7b5570c335d5..040f701ed5ec2 100644
--- a/packages/cubejs-dbt-schema-extension/package.json
+++ b/packages/cubejs-dbt-schema-extension/package.json
@@ -2,7 +2,7 @@
"name": "@cubejs-backend/dbt-schema-extension",
"description": "Cube.js dbt Schema Extension",
"author": "Cube Dev, Inc.",
- "version": "1.7.28",
+ "version": "1.7.29",
"repository": {
"type": "git",
"url": "https://github.com/cube-js/cube.git",
@@ -25,14 +25,14 @@
"lint:fix": "eslint --fix src/* --ext .ts,.js"
},
"dependencies": {
- "@cubejs-backend/schema-compiler": "1.7.28",
+ "@cubejs-backend/schema-compiler": "1.7.29",
"fs-extra": "^9.1.0",
"inflection": "^1.12.0",
"node-fetch": "^2.6.1"
},
"devDependencies": {
- "@cubejs-backend/linter": "1.7.28",
- "@cubejs-backend/testing": "1.7.28",
+ "@cubejs-backend/linter": "1.7.29",
+ "@cubejs-backend/testing": "1.7.29",
"@types/jest": "^29",
"jest": "^29",
"stream-to-array": "^2.3.0",
diff --git a/packages/cubejs-docker/CHANGELOG.md b/packages/cubejs-docker/CHANGELOG.md
index dd686784a926c..f0c1c8b58ad1a 100644
--- a/packages/cubejs-docker/CHANGELOG.md
+++ b/packages/cubejs-docker/CHANGELOG.md
@@ -3,6 +3,10 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
+## [1.7.29](https://github.com/cube-js/cube/compare/v1.7.28...v1.7.29) (2026-08-27)
+
+**Note:** Version bump only for package @cubejs-backend/docker
+
## [1.7.28](https://github.com/cube-js/cube/compare/v1.7.27...v1.7.28) (2026-08-26)
**Note:** Version bump only for package @cubejs-backend/docker
diff --git a/packages/cubejs-docker/package.json b/packages/cubejs-docker/package.json
index f91df7753bfb6..9c2fa30da73d7 100644
--- a/packages/cubejs-docker/package.json
+++ b/packages/cubejs-docker/package.json
@@ -1,6 +1,6 @@
{
"name": "@cubejs-backend/docker",
- "version": "1.7.28",
+ "version": "1.7.29",
"description": "Cube.js In Docker (virtual package)",
"author": "Cube Dev, Inc.",
"license": "Apache-2.0",
@@ -9,34 +9,34 @@
"node": ">=20.0.0"
},
"dependencies": {
- "@cubejs-backend/athena-driver": "1.7.28",
- "@cubejs-backend/bigquery-driver": "1.7.28",
- "@cubejs-backend/clickhouse-driver": "1.7.28",
- "@cubejs-backend/crate-driver": "1.7.28",
- "@cubejs-backend/databricks-jdbc-driver": "1.7.28",
- "@cubejs-backend/dbt-schema-extension": "1.7.28",
- "@cubejs-backend/dremio-driver": "1.7.28",
- "@cubejs-backend/druid-driver": "1.7.28",
- "@cubejs-backend/duckdb-driver": "1.7.28",
- "@cubejs-backend/firebolt-driver": "1.7.28",
- "@cubejs-backend/hive-driver": "1.7.28",
- "@cubejs-backend/ksql-driver": "1.7.28",
- "@cubejs-backend/materialize-driver": "1.7.28",
- "@cubejs-backend/mongobi-driver": "1.7.28",
- "@cubejs-backend/mssql-driver": "1.7.28",
- "@cubejs-backend/mysql-driver": "1.7.28",
- "@cubejs-backend/oracle-driver": "1.7.28",
- "@cubejs-backend/pinot-driver": "1.7.28",
- "@cubejs-backend/postgres-driver": "1.7.28",
- "@cubejs-backend/prestodb-driver": "1.7.28",
- "@cubejs-backend/questdb-driver": "1.7.28",
- "@cubejs-backend/redshift-driver": "1.7.28",
- "@cubejs-backend/server": "1.7.28",
- "@cubejs-backend/snowflake-driver": "1.7.28",
- "@cubejs-backend/sqlite-driver": "1.7.28",
- "@cubejs-backend/trino-driver": "1.7.28",
- "@cubejs-backend/vertica-driver": "1.7.28",
- "cubejs-cli": "1.7.28",
+ "@cubejs-backend/athena-driver": "1.7.29",
+ "@cubejs-backend/bigquery-driver": "1.7.29",
+ "@cubejs-backend/clickhouse-driver": "1.7.29",
+ "@cubejs-backend/crate-driver": "1.7.29",
+ "@cubejs-backend/databricks-jdbc-driver": "1.7.29",
+ "@cubejs-backend/dbt-schema-extension": "1.7.29",
+ "@cubejs-backend/dremio-driver": "1.7.29",
+ "@cubejs-backend/druid-driver": "1.7.29",
+ "@cubejs-backend/duckdb-driver": "1.7.29",
+ "@cubejs-backend/firebolt-driver": "1.7.29",
+ "@cubejs-backend/hive-driver": "1.7.29",
+ "@cubejs-backend/ksql-driver": "1.7.29",
+ "@cubejs-backend/materialize-driver": "1.7.29",
+ "@cubejs-backend/mongobi-driver": "1.7.29",
+ "@cubejs-backend/mssql-driver": "1.7.29",
+ "@cubejs-backend/mysql-driver": "1.7.29",
+ "@cubejs-backend/oracle-driver": "1.7.29",
+ "@cubejs-backend/pinot-driver": "1.7.29",
+ "@cubejs-backend/postgres-driver": "1.7.29",
+ "@cubejs-backend/prestodb-driver": "1.7.29",
+ "@cubejs-backend/questdb-driver": "1.7.29",
+ "@cubejs-backend/redshift-driver": "1.7.29",
+ "@cubejs-backend/server": "1.7.29",
+ "@cubejs-backend/snowflake-driver": "1.7.29",
+ "@cubejs-backend/sqlite-driver": "1.7.29",
+ "@cubejs-backend/trino-driver": "1.7.29",
+ "@cubejs-backend/vertica-driver": "1.7.29",
+ "cubejs-cli": "1.7.29",
"typescript": "~5.2.2"
},
"resolutions": {
diff --git a/packages/cubejs-dremio-driver/CHANGELOG.md b/packages/cubejs-dremio-driver/CHANGELOG.md
index a059d13f61e03..4c2d50f3a98e8 100644
--- a/packages/cubejs-dremio-driver/CHANGELOG.md
+++ b/packages/cubejs-dremio-driver/CHANGELOG.md
@@ -3,6 +3,10 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
+## [1.7.29](https://github.com/cube-js/cube/compare/v1.7.28...v1.7.29) (2026-08-27)
+
+**Note:** Version bump only for package @cubejs-backend/dremio-driver
+
## [1.7.28](https://github.com/cube-js/cube/compare/v1.7.27...v1.7.28) (2026-08-26)
**Note:** Version bump only for package @cubejs-backend/dremio-driver
diff --git a/packages/cubejs-dremio-driver/package.json b/packages/cubejs-dremio-driver/package.json
index ee512d1cf0323..6b29078c87ff8 100644
--- a/packages/cubejs-dremio-driver/package.json
+++ b/packages/cubejs-dremio-driver/package.json
@@ -2,7 +2,7 @@
"name": "@cubejs-backend/dremio-driver",
"description": "Cube.js Dremio driver",
"author": "Cube Dev, Inc.",
- "version": "1.7.28",
+ "version": "1.7.29",
"repository": {
"type": "git",
"url": "https://github.com/cube-js/cube.git",
@@ -23,14 +23,14 @@
"lint:fix": "eslint driver/*.js"
},
"dependencies": {
- "@cubejs-backend/base-driver": "1.7.28",
- "@cubejs-backend/schema-compiler": "1.7.28",
- "@cubejs-backend/shared": "1.7.28",
+ "@cubejs-backend/base-driver": "1.7.29",
+ "@cubejs-backend/schema-compiler": "1.7.29",
+ "@cubejs-backend/shared": "1.7.29",
"axios": "^1.8.3"
},
"devDependencies": {
- "@cubejs-backend/linter": "1.7.28",
- "@cubejs-backend/testing-shared": "1.7.28",
+ "@cubejs-backend/linter": "1.7.29",
+ "@cubejs-backend/testing-shared": "1.7.29",
"jest": "^29"
},
"license": "Apache-2.0",
diff --git a/packages/cubejs-druid-driver/CHANGELOG.md b/packages/cubejs-druid-driver/CHANGELOG.md
index 6801d2383e144..67c75300f4805 100644
--- a/packages/cubejs-druid-driver/CHANGELOG.md
+++ b/packages/cubejs-druid-driver/CHANGELOG.md
@@ -3,6 +3,10 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
+## [1.7.29](https://github.com/cube-js/cube/compare/v1.7.28...v1.7.29) (2026-08-27)
+
+**Note:** Version bump only for package @cubejs-backend/druid-driver
+
## [1.7.28](https://github.com/cube-js/cube/compare/v1.7.27...v1.7.28) (2026-08-26)
### Features
diff --git a/packages/cubejs-druid-driver/package.json b/packages/cubejs-druid-driver/package.json
index 9117e25e3847a..d03a18a18b7e6 100644
--- a/packages/cubejs-druid-driver/package.json
+++ b/packages/cubejs-druid-driver/package.json
@@ -2,7 +2,7 @@
"name": "@cubejs-backend/druid-driver",
"description": "Cube.js Druid database driver",
"author": "Cube Dev, Inc.",
- "version": "1.7.28",
+ "version": "1.7.29",
"license": "Apache-2.0",
"repository": {
"type": "git",
@@ -28,13 +28,13 @@
"dist/src/*"
],
"dependencies": {
- "@cubejs-backend/base-driver": "1.7.28",
- "@cubejs-backend/schema-compiler": "1.7.28",
- "@cubejs-backend/shared": "1.7.28",
+ "@cubejs-backend/base-driver": "1.7.29",
+ "@cubejs-backend/schema-compiler": "1.7.29",
+ "@cubejs-backend/shared": "1.7.29",
"axios": "^1.8.3"
},
"devDependencies": {
- "@cubejs-backend/linter": "1.7.28",
+ "@cubejs-backend/linter": "1.7.29",
"@types/jest": "^29",
"@types/node": "^22",
"jest": "^29",
diff --git a/packages/cubejs-duckdb-driver/CHANGELOG.md b/packages/cubejs-duckdb-driver/CHANGELOG.md
index 21e7f6bcb754a..1546c9bb0980c 100644
--- a/packages/cubejs-duckdb-driver/CHANGELOG.md
+++ b/packages/cubejs-duckdb-driver/CHANGELOG.md
@@ -3,6 +3,10 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
+## [1.7.29](https://github.com/cube-js/cube/compare/v1.7.28...v1.7.29) (2026-08-27)
+
+**Note:** Version bump only for package @cubejs-backend/duckdb-driver
+
## [1.7.28](https://github.com/cube-js/cube/compare/v1.7.27...v1.7.28) (2026-08-26)
**Note:** Version bump only for package @cubejs-backend/duckdb-driver
diff --git a/packages/cubejs-duckdb-driver/package.json b/packages/cubejs-duckdb-driver/package.json
index 023c6fca31241..1c71360f9b773 100644
--- a/packages/cubejs-duckdb-driver/package.json
+++ b/packages/cubejs-duckdb-driver/package.json
@@ -2,7 +2,7 @@
"name": "@cubejs-backend/duckdb-driver",
"description": "Cube DuckDB database driver",
"author": "Cube Dev, Inc.",
- "version": "1.7.28",
+ "version": "1.7.29",
"repository": {
"type": "git",
"url": "https://github.com/cube-js/cube.git",
@@ -28,15 +28,15 @@
"lint:fix": "eslint --fix src/* --ext .ts"
},
"dependencies": {
- "@cubejs-backend/base-driver": "1.7.28",
- "@cubejs-backend/schema-compiler": "1.7.28",
- "@cubejs-backend/shared": "1.7.28",
+ "@cubejs-backend/base-driver": "1.7.29",
+ "@cubejs-backend/schema-compiler": "1.7.29",
+ "@cubejs-backend/shared": "1.7.29",
"duckdb": "^1.4.1"
},
"license": "Apache-2.0",
"devDependencies": {
- "@cubejs-backend/linter": "1.7.28",
- "@cubejs-backend/testing-shared": "1.7.28",
+ "@cubejs-backend/linter": "1.7.29",
+ "@cubejs-backend/testing-shared": "1.7.29",
"@types/jest": "^29",
"@types/node": "^22",
"jest": "^29",
diff --git a/packages/cubejs-firebolt-driver/CHANGELOG.md b/packages/cubejs-firebolt-driver/CHANGELOG.md
index 6d5e5bcca144a..f5ca7b1f210ac 100644
--- a/packages/cubejs-firebolt-driver/CHANGELOG.md
+++ b/packages/cubejs-firebolt-driver/CHANGELOG.md
@@ -3,6 +3,10 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
+## [1.7.29](https://github.com/cube-js/cube/compare/v1.7.28...v1.7.29) (2026-08-27)
+
+**Note:** Version bump only for package @cubejs-backend/firebolt-driver
+
## [1.7.28](https://github.com/cube-js/cube/compare/v1.7.27...v1.7.28) (2026-08-26)
**Note:** Version bump only for package @cubejs-backend/firebolt-driver
diff --git a/packages/cubejs-firebolt-driver/package.json b/packages/cubejs-firebolt-driver/package.json
index 3edb279d6ce72..9b6d95577747c 100644
--- a/packages/cubejs-firebolt-driver/package.json
+++ b/packages/cubejs-firebolt-driver/package.json
@@ -2,7 +2,7 @@
"name": "@cubejs-backend/firebolt-driver",
"description": "Cube.js Firebolt database driver",
"author": "Cube Dev, Inc.",
- "version": "1.7.28",
+ "version": "1.7.29",
"repository": {
"type": "git",
"url": "https://github.com/cube-js/cube.git",
@@ -28,15 +28,15 @@
"lint:fix": "eslint --fix src/* --ext .ts"
},
"dependencies": {
- "@cubejs-backend/base-driver": "1.7.28",
- "@cubejs-backend/schema-compiler": "1.7.28",
- "@cubejs-backend/shared": "1.7.28",
+ "@cubejs-backend/base-driver": "1.7.29",
+ "@cubejs-backend/schema-compiler": "1.7.29",
+ "@cubejs-backend/shared": "1.7.29",
"firebolt-sdk": "1.10.0"
},
"license": "Apache-2.0",
"devDependencies": {
- "@cubejs-backend/linter": "1.7.28",
- "@cubejs-backend/testing-shared": "1.7.28",
+ "@cubejs-backend/linter": "1.7.29",
+ "@cubejs-backend/testing-shared": "1.7.29",
"typescript": "~5.2.2"
},
"publishConfig": {
diff --git a/packages/cubejs-hive-driver/CHANGELOG.md b/packages/cubejs-hive-driver/CHANGELOG.md
index 113b054d3599c..ebb2eb808632b 100644
--- a/packages/cubejs-hive-driver/CHANGELOG.md
+++ b/packages/cubejs-hive-driver/CHANGELOG.md
@@ -3,6 +3,10 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
+## [1.7.29](https://github.com/cube-js/cube/compare/v1.7.28...v1.7.29) (2026-08-27)
+
+**Note:** Version bump only for package @cubejs-backend/hive-driver
+
## [1.7.28](https://github.com/cube-js/cube/compare/v1.7.27...v1.7.28) (2026-08-26)
**Note:** Version bump only for package @cubejs-backend/hive-driver
diff --git a/packages/cubejs-hive-driver/package.json b/packages/cubejs-hive-driver/package.json
index fd22801d8d870..bd8526a025c80 100644
--- a/packages/cubejs-hive-driver/package.json
+++ b/packages/cubejs-hive-driver/package.json
@@ -2,7 +2,7 @@
"name": "@cubejs-backend/hive-driver",
"description": "Cube.js Hive database driver",
"author": "Cube Dev, Inc.",
- "version": "1.7.28",
+ "version": "1.7.29",
"repository": {
"type": "git",
"url": "https://github.com/cube-js/cube.git",
@@ -17,8 +17,8 @@
"lint:fix": "eslint --fix src/* --ext .ts"
},
"dependencies": {
- "@cubejs-backend/base-driver": "1.7.28",
- "@cubejs-backend/shared": "1.7.28",
+ "@cubejs-backend/base-driver": "1.7.29",
+ "@cubejs-backend/shared": "1.7.29",
"jshs2": "^0.4.4",
"sasl-plain": "^0.1.0",
"saslmechanisms": "^0.1.1",
@@ -27,7 +27,7 @@
},
"license": "Apache-2.0",
"devDependencies": {
- "@cubejs-backend/linter": "1.7.28"
+ "@cubejs-backend/linter": "1.7.29"
},
"publishConfig": {
"access": "public"
diff --git a/packages/cubejs-jdbc-driver/CHANGELOG.md b/packages/cubejs-jdbc-driver/CHANGELOG.md
index 2ac4c9f5ff3dd..96e8537b2b83e 100644
--- a/packages/cubejs-jdbc-driver/CHANGELOG.md
+++ b/packages/cubejs-jdbc-driver/CHANGELOG.md
@@ -3,6 +3,10 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
+## [1.7.29](https://github.com/cube-js/cube/compare/v1.7.28...v1.7.29) (2026-08-27)
+
+**Note:** Version bump only for package @cubejs-backend/jdbc-driver
+
## [1.7.28](https://github.com/cube-js/cube/compare/v1.7.27...v1.7.28) (2026-08-26)
**Note:** Version bump only for package @cubejs-backend/jdbc-driver
diff --git a/packages/cubejs-jdbc-driver/package.json b/packages/cubejs-jdbc-driver/package.json
index 1e65bbe900350..50e99318b7e66 100644
--- a/packages/cubejs-jdbc-driver/package.json
+++ b/packages/cubejs-jdbc-driver/package.json
@@ -2,7 +2,7 @@
"name": "@cubejs-backend/jdbc-driver",
"description": "Cube.js JDBC database driver",
"author": "Cube Dev, Inc.",
- "version": "1.7.28",
+ "version": "1.7.29",
"repository": {
"type": "git",
"url": "https://github.com/cube-js/cube.git",
@@ -26,9 +26,9 @@
"index.js"
],
"dependencies": {
- "@cubejs-backend/base-driver": "1.7.28",
+ "@cubejs-backend/base-driver": "1.7.29",
"@cubejs-backend/node-java-maven": "^0.1.3",
- "@cubejs-backend/shared": "1.7.28"
+ "@cubejs-backend/shared": "1.7.29"
},
"optionalDependencies": {
"@cubejs-backend/jdbc": "^0.9.0",
@@ -42,7 +42,7 @@
"testEnvironment": "node"
},
"devDependencies": {
- "@cubejs-backend/linter": "1.7.28",
+ "@cubejs-backend/linter": "1.7.29",
"@types/node": "^22",
"typescript": "~5.2.2"
}
diff --git a/packages/cubejs-ksql-driver/CHANGELOG.md b/packages/cubejs-ksql-driver/CHANGELOG.md
index eb0a5314cd698..d33341218ca76 100644
--- a/packages/cubejs-ksql-driver/CHANGELOG.md
+++ b/packages/cubejs-ksql-driver/CHANGELOG.md
@@ -3,6 +3,10 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
+## [1.7.29](https://github.com/cube-js/cube/compare/v1.7.28...v1.7.29) (2026-08-27)
+
+**Note:** Version bump only for package @cubejs-backend/ksql-driver
+
## [1.7.28](https://github.com/cube-js/cube/compare/v1.7.27...v1.7.28) (2026-08-26)
### Features
diff --git a/packages/cubejs-ksql-driver/package.json b/packages/cubejs-ksql-driver/package.json
index 9808f03761b60..cd6bce6c1b8f9 100644
--- a/packages/cubejs-ksql-driver/package.json
+++ b/packages/cubejs-ksql-driver/package.json
@@ -2,7 +2,7 @@
"name": "@cubejs-backend/ksql-driver",
"description": "Cube.js ksql database driver",
"author": "Cube Dev, Inc.",
- "version": "1.7.28",
+ "version": "1.7.29",
"repository": {
"type": "git",
"url": "https://github.com/cube-js/cube.git",
@@ -26,9 +26,9 @@
"lint:fix": "eslint --fix src/* --ext .ts"
},
"dependencies": {
- "@cubejs-backend/base-driver": "1.7.28",
- "@cubejs-backend/schema-compiler": "1.7.28",
- "@cubejs-backend/shared": "1.7.28",
+ "@cubejs-backend/base-driver": "1.7.29",
+ "@cubejs-backend/schema-compiler": "1.7.29",
+ "@cubejs-backend/shared": "1.7.29",
"async-mutex": "0.3.2",
"axios": "^1.8.3",
"kafkajs": "^2.2.3"
@@ -41,7 +41,7 @@
"extends": "../cubejs-linter"
},
"devDependencies": {
- "@cubejs-backend/linter": "1.7.28",
+ "@cubejs-backend/linter": "1.7.29",
"typescript": "~5.2.2"
}
}
diff --git a/packages/cubejs-linter/CHANGELOG.md b/packages/cubejs-linter/CHANGELOG.md
index 49d676961bfd7..c0f0eca4c0094 100644
--- a/packages/cubejs-linter/CHANGELOG.md
+++ b/packages/cubejs-linter/CHANGELOG.md
@@ -3,6 +3,10 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
+## [1.7.29](https://github.com/cube-js/cube/compare/v1.7.28...v1.7.29) (2026-08-27)
+
+**Note:** Version bump only for package @cubejs-backend/linter
+
## [1.7.28](https://github.com/cube-js/cube/compare/v1.7.27...v1.7.28) (2026-08-26)
**Note:** Version bump only for package @cubejs-backend/linter
diff --git a/packages/cubejs-linter/package.json b/packages/cubejs-linter/package.json
index 6325b24d02c89..d5547783f4c36 100644
--- a/packages/cubejs-linter/package.json
+++ b/packages/cubejs-linter/package.json
@@ -2,7 +2,7 @@
"name": "@cubejs-backend/linter",
"description": "Cube.js ESLint (virtual package) for linting code",
"author": "Cube Dev, Inc.",
- "version": "1.7.28",
+ "version": "1.7.29",
"repository": {
"type": "git",
"url": "https://github.com/cube-js/cube.git",
diff --git a/packages/cubejs-materialize-driver/CHANGELOG.md b/packages/cubejs-materialize-driver/CHANGELOG.md
index 09dfa15906788..4859b15f48624 100644
--- a/packages/cubejs-materialize-driver/CHANGELOG.md
+++ b/packages/cubejs-materialize-driver/CHANGELOG.md
@@ -3,6 +3,10 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
+## [1.7.29](https://github.com/cube-js/cube/compare/v1.7.28...v1.7.29) (2026-08-27)
+
+**Note:** Version bump only for package @cubejs-backend/materialize-driver
+
## [1.7.28](https://github.com/cube-js/cube/compare/v1.7.27...v1.7.28) (2026-08-26)
**Note:** Version bump only for package @cubejs-backend/materialize-driver
diff --git a/packages/cubejs-materialize-driver/package.json b/packages/cubejs-materialize-driver/package.json
index a35bfd95fd763..46ebc306c9cf5 100644
--- a/packages/cubejs-materialize-driver/package.json
+++ b/packages/cubejs-materialize-driver/package.json
@@ -2,7 +2,7 @@
"name": "@cubejs-backend/materialize-driver",
"description": "Cube.js Materialize database driver",
"author": "Cube Dev, Inc.",
- "version": "1.7.28",
+ "version": "1.7.29",
"repository": {
"type": "git",
"url": "https://github.com/cube-js/cube.git",
@@ -27,15 +27,15 @@
"lint:fix": "eslint --fix src/* --ext .ts"
},
"dependencies": {
- "@cubejs-backend/base-driver": "1.7.28",
- "@cubejs-backend/postgres-driver": "1.7.28",
- "@cubejs-backend/shared": "1.7.28",
+ "@cubejs-backend/base-driver": "1.7.29",
+ "@cubejs-backend/postgres-driver": "1.7.29",
+ "@cubejs-backend/shared": "1.7.29",
"semver": "^7.6.3"
},
"license": "Apache-2.0",
"devDependencies": {
- "@cubejs-backend/linter": "1.7.28",
- "@cubejs-backend/testing": "1.7.28",
+ "@cubejs-backend/linter": "1.7.29",
+ "@cubejs-backend/testing": "1.7.29",
"typescript": "~5.2.2"
},
"publishConfig": {
diff --git a/packages/cubejs-mongobi-driver/CHANGELOG.md b/packages/cubejs-mongobi-driver/CHANGELOG.md
index 36ea9dbef5570..65012a88cf6bb 100644
--- a/packages/cubejs-mongobi-driver/CHANGELOG.md
+++ b/packages/cubejs-mongobi-driver/CHANGELOG.md
@@ -3,6 +3,10 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
+## [1.7.29](https://github.com/cube-js/cube/compare/v1.7.28...v1.7.29) (2026-08-27)
+
+**Note:** Version bump only for package @cubejs-backend/mongobi-driver
+
## [1.7.28](https://github.com/cube-js/cube/compare/v1.7.27...v1.7.28) (2026-08-26)
**Note:** Version bump only for package @cubejs-backend/mongobi-driver
diff --git a/packages/cubejs-mongobi-driver/package.json b/packages/cubejs-mongobi-driver/package.json
index deec376e37ebd..099409fc5e8c4 100644
--- a/packages/cubejs-mongobi-driver/package.json
+++ b/packages/cubejs-mongobi-driver/package.json
@@ -2,7 +2,7 @@
"name": "@cubejs-backend/mongobi-driver",
"description": "Cube.js MongoBI driver",
"author": "krunalsabnis@gmail.com",
- "version": "1.7.28",
+ "version": "1.7.29",
"repository": {
"type": "git",
"url": "https://github.com/cube-js/cube.git",
@@ -27,8 +27,8 @@
"integration:mongobi": "jest dist/test"
},
"dependencies": {
- "@cubejs-backend/base-driver": "1.7.28",
- "@cubejs-backend/shared": "1.7.28",
+ "@cubejs-backend/base-driver": "1.7.29",
+ "@cubejs-backend/shared": "1.7.29",
"@types/node": "^22",
"moment": "^2.29.1",
"mysql2": "^3.11.5"
@@ -38,7 +38,7 @@
"access": "public"
},
"devDependencies": {
- "@cubejs-backend/linter": "1.7.28",
+ "@cubejs-backend/linter": "1.7.29",
"testcontainers": "^10.28.0",
"typescript": "~5.2.2"
},
diff --git a/packages/cubejs-mssql-driver/CHANGELOG.md b/packages/cubejs-mssql-driver/CHANGELOG.md
index d13c7056c13a2..0f9cdfc6ed851 100644
--- a/packages/cubejs-mssql-driver/CHANGELOG.md
+++ b/packages/cubejs-mssql-driver/CHANGELOG.md
@@ -3,6 +3,10 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
+## [1.7.29](https://github.com/cube-js/cube/compare/v1.7.28...v1.7.29) (2026-08-27)
+
+**Note:** Version bump only for package @cubejs-backend/mssql-driver
+
## [1.7.28](https://github.com/cube-js/cube/compare/v1.7.27...v1.7.28) (2026-08-26)
**Note:** Version bump only for package @cubejs-backend/mssql-driver
diff --git a/packages/cubejs-mssql-driver/package.json b/packages/cubejs-mssql-driver/package.json
index ac18b8d7c6a5e..6d171a5f101b5 100644
--- a/packages/cubejs-mssql-driver/package.json
+++ b/packages/cubejs-mssql-driver/package.json
@@ -2,7 +2,7 @@
"name": "@cubejs-backend/mssql-driver",
"description": "Cube.js MS SQL database driver",
"author": "Cube Dev, Inc.",
- "version": "1.7.28",
+ "version": "1.7.29",
"repository": {
"type": "git",
"url": "https://github.com/cube-js/cube.git",
@@ -25,8 +25,8 @@
"lint:fix": "eslint --fix src/* --ext .ts,.js"
},
"dependencies": {
- "@cubejs-backend/base-driver": "1.7.28",
- "@cubejs-backend/shared": "1.7.28",
+ "@cubejs-backend/base-driver": "1.7.29",
+ "@cubejs-backend/shared": "1.7.29",
"mssql": "^11.0.1"
},
"devDependencies": {
diff --git a/packages/cubejs-mysql-aurora-serverless-driver/CHANGELOG.md b/packages/cubejs-mysql-aurora-serverless-driver/CHANGELOG.md
index bac945f8ce0b3..500c5fba057e9 100644
--- a/packages/cubejs-mysql-aurora-serverless-driver/CHANGELOG.md
+++ b/packages/cubejs-mysql-aurora-serverless-driver/CHANGELOG.md
@@ -3,6 +3,10 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
+## [1.7.29](https://github.com/cube-js/cube/compare/v1.7.28...v1.7.29) (2026-08-27)
+
+**Note:** Version bump only for package @cubejs-backend/mysql-aurora-serverless-driver
+
## [1.7.28](https://github.com/cube-js/cube/compare/v1.7.27...v1.7.28) (2026-08-26)
**Note:** Version bump only for package @cubejs-backend/mysql-aurora-serverless-driver
diff --git a/packages/cubejs-mysql-aurora-serverless-driver/package.json b/packages/cubejs-mysql-aurora-serverless-driver/package.json
index ad32f02783745..39902c25528dd 100644
--- a/packages/cubejs-mysql-aurora-serverless-driver/package.json
+++ b/packages/cubejs-mysql-aurora-serverless-driver/package.json
@@ -2,7 +2,7 @@
"name": "@cubejs-backend/mysql-aurora-serverless-driver",
"description": "Cube.js Aurora Serverless Mysql database driver",
"author": "Cube Dev, Inc.",
- "version": "1.7.28",
+ "version": "1.7.29",
"repository": {
"type": "git",
"url": "https://github.com/cube-js/cube.git",
@@ -21,14 +21,14 @@
"lint": "eslint driver/*.js test/*.js"
},
"dependencies": {
- "@cubejs-backend/base-driver": "1.7.28",
- "@cubejs-backend/shared": "1.7.28",
+ "@cubejs-backend/base-driver": "1.7.29",
+ "@cubejs-backend/shared": "1.7.29",
"@types/mysql": "^2.15.15",
"aws-sdk": "^2.787.0",
"data-api-client": "^1.1.0"
},
"devDependencies": {
- "@cubejs-backend/linter": "1.7.28",
+ "@cubejs-backend/linter": "1.7.29",
"@types/data-api-client": "^1.2.1",
"@types/jest": "^29",
"jest": "^29",
diff --git a/packages/cubejs-mysql-driver/CHANGELOG.md b/packages/cubejs-mysql-driver/CHANGELOG.md
index 07b42b5b95027..6e4d408bdef6b 100644
--- a/packages/cubejs-mysql-driver/CHANGELOG.md
+++ b/packages/cubejs-mysql-driver/CHANGELOG.md
@@ -3,6 +3,10 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
+## [1.7.29](https://github.com/cube-js/cube/compare/v1.7.28...v1.7.29) (2026-08-27)
+
+**Note:** Version bump only for package @cubejs-backend/mysql-driver
+
## [1.7.28](https://github.com/cube-js/cube/compare/v1.7.27...v1.7.28) (2026-08-26)
**Note:** Version bump only for package @cubejs-backend/mysql-driver
diff --git a/packages/cubejs-mysql-driver/package.json b/packages/cubejs-mysql-driver/package.json
index 6c0712b1710c4..43380cb9c2fe2 100644
--- a/packages/cubejs-mysql-driver/package.json
+++ b/packages/cubejs-mysql-driver/package.json
@@ -2,7 +2,7 @@
"name": "@cubejs-backend/mysql-driver",
"description": "Cube.js Mysql database driver",
"author": "Cube Dev, Inc.",
- "version": "1.7.28",
+ "version": "1.7.29",
"repository": {
"type": "git",
"url": "https://github.com/cube-js/cube.git",
@@ -27,13 +27,13 @@
"lint:fix": "eslint --fix src/* test/* --ext .ts,.js"
},
"dependencies": {
- "@cubejs-backend/base-driver": "1.7.28",
- "@cubejs-backend/shared": "1.7.28",
+ "@cubejs-backend/base-driver": "1.7.29",
+ "@cubejs-backend/shared": "1.7.29",
"mysql2": "^3.16.1"
},
"devDependencies": {
- "@cubejs-backend/linter": "1.7.28",
- "@cubejs-backend/testing-shared": "1.7.28",
+ "@cubejs-backend/linter": "1.7.29",
+ "@cubejs-backend/testing-shared": "1.7.29",
"@types/jest": "^29",
"jest": "^29",
"stream-to-array": "^2.3.0",
diff --git a/packages/cubejs-oracle-driver/CHANGELOG.md b/packages/cubejs-oracle-driver/CHANGELOG.md
index 1e600ebc94a1b..e501d633f729e 100644
--- a/packages/cubejs-oracle-driver/CHANGELOG.md
+++ b/packages/cubejs-oracle-driver/CHANGELOG.md
@@ -3,6 +3,10 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
+## [1.7.29](https://github.com/cube-js/cube/compare/v1.7.28...v1.7.29) (2026-08-27)
+
+**Note:** Version bump only for package @cubejs-backend/oracle-driver
+
## [1.7.28](https://github.com/cube-js/cube/compare/v1.7.27...v1.7.28) (2026-08-26)
**Note:** Version bump only for package @cubejs-backend/oracle-driver
diff --git a/packages/cubejs-oracle-driver/package.json b/packages/cubejs-oracle-driver/package.json
index 680ac47c6de65..810a0419f388c 100644
--- a/packages/cubejs-oracle-driver/package.json
+++ b/packages/cubejs-oracle-driver/package.json
@@ -2,7 +2,7 @@
"name": "@cubejs-backend/oracle-driver",
"description": "Cube.js oracle database driver",
"author": "Cube Dev, Inc.",
- "version": "1.7.28",
+ "version": "1.7.29",
"repository": {
"type": "git",
"url": "https://github.com/cube-js/cube.git",
@@ -13,8 +13,8 @@
},
"main": "driver/OracleDriver.js",
"dependencies": {
- "@cubejs-backend/base-driver": "1.7.28",
- "@cubejs-backend/shared": "1.7.28",
+ "@cubejs-backend/base-driver": "1.7.29",
+ "@cubejs-backend/shared": "1.7.29",
"ramda": "^0.27.0"
},
"devDependencies": {
diff --git a/packages/cubejs-pinot-driver/CHANGELOG.md b/packages/cubejs-pinot-driver/CHANGELOG.md
index fdc893e539d8d..69cc30ee8382c 100644
--- a/packages/cubejs-pinot-driver/CHANGELOG.md
+++ b/packages/cubejs-pinot-driver/CHANGELOG.md
@@ -3,6 +3,10 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
+## [1.7.29](https://github.com/cube-js/cube/compare/v1.7.28...v1.7.29) (2026-08-27)
+
+**Note:** Version bump only for package @cubejs-backend/pinot-driver
+
## [1.7.28](https://github.com/cube-js/cube/compare/v1.7.27...v1.7.28) (2026-08-26)
**Note:** Version bump only for package @cubejs-backend/pinot-driver
diff --git a/packages/cubejs-pinot-driver/package.json b/packages/cubejs-pinot-driver/package.json
index 98e8deaa4b33d..ee8796faf305b 100644
--- a/packages/cubejs-pinot-driver/package.json
+++ b/packages/cubejs-pinot-driver/package.json
@@ -2,7 +2,7 @@
"name": "@cubejs-backend/pinot-driver",
"description": "Cube.js Pinot database driver",
"author": "Julian Ronsse, InTheMemory, Cube Dev, Inc.",
- "version": "1.7.28",
+ "version": "1.7.29",
"repository": {
"type": "git",
"url": "https://github.com/cube-js/cube.git",
@@ -28,9 +28,9 @@
"lint:fix": "eslint --fix src/* --ext .ts"
},
"dependencies": {
- "@cubejs-backend/base-driver": "1.7.28",
- "@cubejs-backend/schema-compiler": "1.7.28",
- "@cubejs-backend/shared": "1.7.28",
+ "@cubejs-backend/base-driver": "1.7.29",
+ "@cubejs-backend/schema-compiler": "1.7.29",
+ "@cubejs-backend/shared": "1.7.29",
"node-fetch": "^2.6.1",
"ramda": "^0.27.2"
},
@@ -39,7 +39,7 @@
"access": "public"
},
"devDependencies": {
- "@cubejs-backend/linter": "1.7.28",
+ "@cubejs-backend/linter": "1.7.29",
"@types/jest": "^29",
"jest": "^29",
"should": "^13.2.3",
diff --git a/packages/cubejs-playground/CHANGELOG.md b/packages/cubejs-playground/CHANGELOG.md
index 7116424ca53f6..af1d2e9337020 100644
--- a/packages/cubejs-playground/CHANGELOG.md
+++ b/packages/cubejs-playground/CHANGELOG.md
@@ -3,6 +3,10 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
+## [1.7.29](https://github.com/cube-js/cube/compare/v1.7.28...v1.7.29) (2026-08-27)
+
+**Note:** Version bump only for package @cubejs-client/playground
+
## [1.7.28](https://github.com/cube-js/cube/compare/v1.7.27...v1.7.28) (2026-08-26)
**Note:** Version bump only for package @cubejs-client/playground
diff --git a/packages/cubejs-playground/package.json b/packages/cubejs-playground/package.json
index 9c87f3853a47b..0c2cdbd104cc0 100644
--- a/packages/cubejs-playground/package.json
+++ b/packages/cubejs-playground/package.json
@@ -1,7 +1,7 @@
{
"name": "@cubejs-client/playground",
"author": "Cube Dev, Inc.",
- "version": "1.7.28",
+ "version": "1.7.29",
"engines": {},
"repository": {
"type": "git",
@@ -68,8 +68,8 @@
"@ant-design/compatible": "^1.0.1",
"@ant-design/icons": "^5.3.5",
"@cube-dev/ui-kit": "0.52.3",
- "@cubejs-client/core": "1.7.28",
- "@cubejs-client/react": "1.7.28",
+ "@cubejs-client/core": "1.7.29",
+ "@cubejs-client/react": "1.7.29",
"@types/flexsearch": "^0.7.3",
"@types/node": "^22",
"@types/react": "^18.3.4",
diff --git a/packages/cubejs-postgres-driver/CHANGELOG.md b/packages/cubejs-postgres-driver/CHANGELOG.md
index 196a07cb318ce..c26014a063d87 100644
--- a/packages/cubejs-postgres-driver/CHANGELOG.md
+++ b/packages/cubejs-postgres-driver/CHANGELOG.md
@@ -3,6 +3,10 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
+## [1.7.29](https://github.com/cube-js/cube/compare/v1.7.28...v1.7.29) (2026-08-27)
+
+**Note:** Version bump only for package @cubejs-backend/postgres-driver
+
## [1.7.28](https://github.com/cube-js/cube/compare/v1.7.27...v1.7.28) (2026-08-26)
**Note:** Version bump only for package @cubejs-backend/postgres-driver
diff --git a/packages/cubejs-postgres-driver/package.json b/packages/cubejs-postgres-driver/package.json
index 57d341c6b6f21..f9e595998d8c4 100644
--- a/packages/cubejs-postgres-driver/package.json
+++ b/packages/cubejs-postgres-driver/package.json
@@ -2,7 +2,7 @@
"name": "@cubejs-backend/postgres-driver",
"description": "Cube.js Postgres database driver",
"author": "Cube Dev, Inc.",
- "version": "1.7.28",
+ "version": "1.7.29",
"repository": {
"type": "git",
"url": "https://github.com/cube-js/cube.git",
@@ -27,8 +27,8 @@
"lint:fix": "eslint --fix src/* --ext .ts"
},
"dependencies": {
- "@cubejs-backend/base-driver": "1.7.28",
- "@cubejs-backend/shared": "1.7.28",
+ "@cubejs-backend/base-driver": "1.7.29",
+ "@cubejs-backend/shared": "1.7.29",
"@types/pg": "^8.16.0",
"@types/pg-query-stream": "^1.0.3",
"pg": "^8.18.0",
@@ -36,8 +36,8 @@
},
"license": "Apache-2.0",
"devDependencies": {
- "@cubejs-backend/linter": "1.7.28",
- "@cubejs-backend/testing-shared": "1.7.28",
+ "@cubejs-backend/linter": "1.7.29",
+ "@cubejs-backend/testing-shared": "1.7.29",
"testcontainers": "^10.28.0",
"typescript": "~5.2.2"
},
diff --git a/packages/cubejs-prestodb-driver/CHANGELOG.md b/packages/cubejs-prestodb-driver/CHANGELOG.md
index 3e288fad3ad90..e97f38e12cf7f 100644
--- a/packages/cubejs-prestodb-driver/CHANGELOG.md
+++ b/packages/cubejs-prestodb-driver/CHANGELOG.md
@@ -3,6 +3,10 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
+## [1.7.29](https://github.com/cube-js/cube/compare/v1.7.28...v1.7.29) (2026-08-27)
+
+**Note:** Version bump only for package @cubejs-backend/prestodb-driver
+
## [1.7.28](https://github.com/cube-js/cube/compare/v1.7.27...v1.7.28) (2026-08-26)
**Note:** Version bump only for package @cubejs-backend/prestodb-driver
diff --git a/packages/cubejs-prestodb-driver/package.json b/packages/cubejs-prestodb-driver/package.json
index e59b168c37d20..838bac240b0c6 100644
--- a/packages/cubejs-prestodb-driver/package.json
+++ b/packages/cubejs-prestodb-driver/package.json
@@ -2,7 +2,7 @@
"name": "@cubejs-backend/prestodb-driver",
"description": "Cube.js Presto database driver",
"author": "Cube Dev, Inc.",
- "version": "1.7.28",
+ "version": "1.7.29",
"repository": {
"type": "git",
"url": "https://github.com/cube-js/cube.git",
@@ -28,8 +28,8 @@
"lint:fix": "eslint --fix src/* --ext .ts"
},
"dependencies": {
- "@cubejs-backend/base-driver": "1.7.28",
- "@cubejs-backend/shared": "1.7.28",
+ "@cubejs-backend/base-driver": "1.7.29",
+ "@cubejs-backend/shared": "1.7.29",
"presto-client": "1.2.0",
"ramda": "^0.27.0"
},
@@ -38,7 +38,7 @@
"access": "public"
},
"devDependencies": {
- "@cubejs-backend/linter": "1.7.28",
+ "@cubejs-backend/linter": "1.7.29",
"@types/jest": "^29",
"jest": "^29",
"should": "^13.2.3",
diff --git a/packages/cubejs-query-orchestrator/CHANGELOG.md b/packages/cubejs-query-orchestrator/CHANGELOG.md
index d1a8c51c62e2a..08906729b2ace 100644
--- a/packages/cubejs-query-orchestrator/CHANGELOG.md
+++ b/packages/cubejs-query-orchestrator/CHANGELOG.md
@@ -3,6 +3,13 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
+## [1.7.29](https://github.com/cube-js/cube/compare/v1.7.28...v1.7.29) (2026-08-27)
+
+### Bug Fixes
+
+- **api-gateway:** Make pre-agg build job status per-entry and data-source aware ([#11666](https://github.com/cube-js/cube/issues/11666)) ([694955f](https://github.com/cube-js/cube/commit/694955f864375515d84d463e9e7ee89a156a4a2b))
+- **query-orchestrator:** initialize missing data source queue in isPartitionExist, thanks [@dochernyshov](https://github.com/dochernyshov) ([#11617](https://github.com/cube-js/cube/issues/11617)) ([ab48039](https://github.com/cube-js/cube/commit/ab48039f1a3d46d890367b0fc898d9ebf1322786))
+
## [1.7.28](https://github.com/cube-js/cube/compare/v1.7.27...v1.7.28) (2026-08-26)
**Note:** Version bump only for package @cubejs-backend/query-orchestrator
diff --git a/packages/cubejs-query-orchestrator/package.json b/packages/cubejs-query-orchestrator/package.json
index 9d1c663d82f18..de328f49fa74b 100644
--- a/packages/cubejs-query-orchestrator/package.json
+++ b/packages/cubejs-query-orchestrator/package.json
@@ -2,7 +2,7 @@
"name": "@cubejs-backend/query-orchestrator",
"description": "Cube.js Query Orchestrator and Cache",
"author": "Cube Dev, Inc.",
- "version": "1.7.28",
+ "version": "1.7.29",
"repository": {
"type": "git",
"url": "https://github.com/cube-js/cube.git",
@@ -30,15 +30,15 @@
"dist/src/*"
],
"dependencies": {
- "@cubejs-backend/base-driver": "1.7.28",
- "@cubejs-backend/cubestore-driver": "1.7.28",
- "@cubejs-backend/shared": "1.7.28",
+ "@cubejs-backend/base-driver": "1.7.29",
+ "@cubejs-backend/cubestore-driver": "1.7.29",
+ "@cubejs-backend/shared": "1.7.29",
"csv-write-stream": "^2.0.0",
"lru-cache": "^11.1.0",
"ramda": "^0.27.2"
},
"devDependencies": {
- "@cubejs-backend/linter": "1.7.28",
+ "@cubejs-backend/linter": "1.7.29",
"@types/jest": "^29",
"@types/node": "^22",
"@types/ramda": "^0.27.32",
diff --git a/packages/cubejs-query-orchestrator/src/orchestrator/QueryCache.ts b/packages/cubejs-query-orchestrator/src/orchestrator/QueryCache.ts
index 70d2c35f86958..c500df4681d8f 100644
--- a/packages/cubejs-query-orchestrator/src/orchestrator/QueryCache.ts
+++ b/packages/cubejs-query-orchestrator/src/orchestrator/QueryCache.ts
@@ -121,13 +121,30 @@ export type CacheKey =
[CacheKeyItem, CacheKeyItem, CacheKeyItem] |
[CacheKeyItem, CacheKeyItem, CacheKeyItem, CacheKeyItem];
-type CacheEntry = {
+export type CacheEntry = {
time: number;
result: any;
- renewalKey: string;
+ renewalKey?: string;
requestId?: string;
};
+export enum CacheAction {
+ ServeCached = 'serve-cached',
+ RefreshSameRequest = 'refresh-same-request',
+ RefreshBackground = 'refresh-background',
+ WaitForRenew = 'wait-for-renew',
+}
+
+type CacheOperationContext = {
+ cacheKey: CacheKey;
+ redisKey: string;
+ renewalKey?: string;
+ expiration: number;
+ spanId: string;
+ options: CacheQueryResultOptions;
+ log: (message: string, extra?: Record) => void;
+};
+
export interface QueryCacheOptions {
refreshKeyRenewalThreshold?: number;
externalQueueOptions?: any;
@@ -156,6 +173,8 @@ export class QueryCache {
protected memoryCache: LRUCache;
+ protected static readonly IN_MEMORY_CACHE_DISABLE_PERIOD = 5 * 60 * 1000;
+
public constructor(
protected readonly cachePrefix: string,
protected readonly driverFactory: DriverFactoryByDataSource,
@@ -888,174 +907,244 @@ export class QueryCache {
callback: () => MaybeCancelablePromise,
) => this.cacheDriver.withLock(`lock:${key}`, callback, ttl, true);
- public async cacheQueryResult(
- query: string | QueryWithParams,
- values: string[],
+ protected static decideCacheAction(
+ entry: CacheEntry,
+ renewedAgo: number,
+ options: CacheQueryResultOptions,
+ renewalKey?: string,
+ ): CacheAction {
+ const { renewalThreshold } = options;
+ const isExpired = !renewalThreshold || !entry.time || renewedAgo > renewalThreshold * 1000;
+ const isKeyMismatch = !!renewalKey && entry.renewalKey !== renewalKey;
+
+ if (!isExpired && !isKeyMismatch) {
+ return CacheAction.ServeCached;
+ }
+
+ const isSameRequest = options.requestId && entry.requestId &&
+ QueryCache.extractRequestUUID(entry.requestId) === QueryCache.extractRequestUUID(options.requestId);
+
+ // A client polling through continue-wait re-enters with the same requestId, so rejecting
+ // the result it just wrote would restart the fetch on every poll and never converge while
+ // the refreshKey keeps moving. Background renew opts out: fresh data is all it exists for.
+ if (isSameRequest && !options.renewCycle) {
+ return CacheAction.RefreshSameRequest;
+ }
+
+ // Without a refreshKey there is nothing to refresh against, so an elapsed threshold alone never triggers a fetch.
+ if (!renewalKey) {
+ return CacheAction.ServeCached;
+ }
+
+ return options.waitForRenew ? CacheAction.WaitForRenew : CacheAction.RefreshBackground;
+ }
+
+ protected static isMemoryEntryUsable(
+ entry: CacheEntry,
+ renewedAgo: number,
+ expiration: number,
+ renewalThreshold?: number,
+ renewalKey?: string,
+ ): boolean {
+ if (renewedAgo > expiration * 1000 || renewedAgo > QueryCache.IN_MEMORY_CACHE_DISABLE_PERIOD) {
+ return false;
+ }
+
+ if (!renewalKey) {
+ return true;
+ }
+
+ // Near expiry an in-memory entry races with refreshes carrying a different refreshKey value.
+ return !!renewalThreshold &&
+ !!entry.time &&
+ renewedAgo + QueryCache.IN_MEMORY_CACHE_DISABLE_PERIOD <= renewalThreshold * 1000 &&
+ entry.renewalKey === renewalKey;
+ }
+
+ protected cacheOperationContext(
cacheKey: CacheKey,
expiration: number,
options: CacheQueryResultOptions,
- ) {
+ ): CacheOperationContext {
const spanId = crypto.randomBytes(16).toString('hex');
- options = options || { dataSource: 'default' };
- const { renewalThreshold, primaryQuery, renewCycle } = options;
- const renewalKey = options.renewalKey && this.queryRedisKey(options.renewalKey);
- const redisKey = this.queryRedisKey(cacheKey);
- const fetchNew = () => (
- this.queryWithRetryAndRelease(query, values, {
- cacheKey,
- priority: options.priority,
- external: options.external,
+ const logContext = {
+ cacheKey,
+ requestId: options.requestId,
+ spanId,
+ primaryQuery: options.primaryQuery,
+ renewCycle: options.renewCycle,
+ };
+
+ return {
+ cacheKey,
+ redisKey: this.queryRedisKey(cacheKey),
+ renewalKey: options.renewalKey && this.queryRedisKey(options.renewalKey),
+ expiration,
+ spanId,
+ options,
+ log: (message, extra) => this.logger(message, extra ? { ...logContext, ...extra } : logContext),
+ };
+ }
+
+ protected fetchAndCacheQuery(
+ query: string | QueryWithParams,
+ values: string[],
+ ctx: CacheOperationContext,
+ ) {
+ const { cacheKey, redisKey, renewalKey, expiration, spanId, options } = ctx;
+
+ return this.queryWithRetryAndRelease(query, values, {
+ cacheKey,
+ priority: options.priority,
+ external: options.external,
+ requestId: options.requestId,
+ spanId,
+ persistent: options.persistent,
+ dataSource: options.dataSource,
+ useCsvQuery: options.useCsvQuery,
+ lambdaTypes: options.lambdaTypes,
+ }).then(res => {
+ const entry = {
+ time: (new Date()).getTime(),
+ result: res,
+ renewalKey,
requestId: options.requestId,
- spanId,
- persistent: options.persistent,
- dataSource: options.dataSource,
- useCsvQuery: options.useCsvQuery,
- lambdaTypes: options.lambdaTypes,
- }).then(res => {
- const result = {
- time: (new Date()).getTime(),
- result: res,
- renewalKey,
- requestId: options.requestId,
- };
- return this
- .cacheDriver
- .set(redisKey, result, expiration)
- .then(({ bytes }) => {
- this.logger('Renewed', { cacheKey, requestId: options.requestId, spanId, primaryQuery, renewCycle });
- this.logger('Outgoing network usage', {
- service: 'cache',
- requestId: options.requestId,
- spanId,
- bytes,
- cacheKey,
- });
- return res;
- });
- }).catch(e => {
- if (!(e instanceof ContinueWaitError)) {
- this.logger('Dropping Cache', {
- cacheKey,
- error: e.stack || e,
+ };
+
+ return this
+ .cacheDriver
+ .set(redisKey, entry, expiration)
+ .then(({ bytes }) => {
+ ctx.log('Renewed');
+ this.logger('Outgoing network usage', {
+ service: 'cache',
requestId: options.requestId,
spanId,
- primaryQuery,
- renewCycle
+ bytes,
+ cacheKey,
});
- this.cacheDriver.remove(redisKey)
- .catch(err => this.logger('Error removing key', {
- cacheKey,
- spanId,
- error: err.stack || err,
- requestId: options.requestId
- }));
- }
- throw e;
- })
- );
+ return res;
+ });
+ }).catch(e => {
+ if (!(e instanceof ContinueWaitError)) {
+ ctx.log('Dropping Cache', { error: e.stack || e });
+ this.cacheDriver.remove(redisKey)
+ .catch(err => this.logger('Error removing key', {
+ cacheKey,
+ spanId,
+ error: err.stack || err,
+ requestId: options.requestId
+ }));
+ }
+ throw e;
+ });
+ }
+
+ protected fetchAndCacheQueryInBackground(
+ query: string | QueryWithParams,
+ values: string[],
+ ctx: CacheOperationContext,
+ ): void {
+ this.fetchAndCacheQuery(query, values, ctx).catch(e => {
+ if (!(e instanceof ContinueWaitError)) {
+ ctx.log('Error renewing', { error: e.stack || e });
+ }
+ });
+ }
+
+ protected getFromMemoryCache(ctx: CacheOperationContext): CacheEntry | null {
+ const { redisKey, renewalKey, expiration, options } = ctx;
+ const entry = this.memoryCache.get(redisKey);
+
+ if (!entry) {
+ return null;
+ }
+
+ const renewedAgo = (new Date()).getTime() - entry.time;
+
+ if (!QueryCache.isMemoryEntryUsable(entry, renewedAgo, expiration, options.renewalThreshold, renewalKey)) {
+ this.memoryCache.delete(redisKey);
+ return null;
+ }
+
+ ctx.log('Found in memory cache entry', {
+ time: entry.time,
+ renewedAgo,
+ renewalKey: entry.renewalKey,
+ newRenewalKey: renewalKey,
+ renewalThreshold: options.renewalThreshold,
+ });
+
+ return entry;
+ }
+
+ protected storeInMemoryCache(entry: CacheEntry, renewedAgo: number, ctx: CacheOperationContext): void {
+ const { useInMemory, renewalThreshold } = ctx.options;
+
+ if (useInMemory && !!renewalThreshold &&
+ renewedAgo + QueryCache.IN_MEMORY_CACHE_DISABLE_PERIOD <= renewalThreshold * 1000) {
+ this.memoryCache.set(ctx.redisKey, entry);
+ }
+ }
+
+ public async cacheQueryResult(
+ query: string | QueryWithParams,
+ values: string[],
+ cacheKey: CacheKey,
+ expiration: number,
+ options: CacheQueryResultOptions,
+ ) {
+ options = options || { dataSource: 'default' };
+
+ const ctx = this.cacheOperationContext(cacheKey, expiration, options);
+ const { renewalThreshold } = options;
if (options.forceNoCache) {
- this.logger('Force no cache for', { cacheKey, requestId: options.requestId, spanId, primaryQuery, renewCycle });
- return fetchNew();
+ ctx.log('Force no cache for');
+ return this.fetchAndCacheQuery(query, values, ctx);
}
- let res;
-
- const inMemoryCacheDisablePeriod = 5 * 60 * 1000;
-
- if (options.useInMemory) {
- const inMemoryValue = this.memoryCache.get(redisKey);
- if (inMemoryValue) {
- const renewedAgo = (new Date()).getTime() - inMemoryValue.time;
-
- if (
- renewalKey && (
- !renewalThreshold ||
- !inMemoryValue.time ||
- // Do not cache in memory in last 5 minutes of expiry.
- // Most likely it'll cause race condition of refreshing data with different refreshKey values.
- renewedAgo + inMemoryCacheDisablePeriod > renewalThreshold * 1000 ||
- inMemoryValue.renewalKey !== renewalKey
- ) || renewedAgo > expiration * 1000 || renewedAgo > inMemoryCacheDisablePeriod
- ) {
- this.memoryCache.delete(redisKey);
- } else {
- this.logger('Found in memory cache entry', {
- cacheKey,
- time: inMemoryValue.time,
- renewedAgo,
- renewalKey: inMemoryValue.renewalKey,
- newRenewalKey: renewalKey,
- renewalThreshold,
- requestId: options.requestId,
- spanId,
- primaryQuery,
- renewCycle
- });
- res = inMemoryValue;
- }
- }
+ let entry: CacheEntry | null = options.useInMemory ? this.getFromMemoryCache(ctx) : null;
+
+ if (!entry) {
+ entry = await this.cacheDriver.get(ctx.redisKey);
}
- if (!res) {
- res = await this.cacheDriver.get(redisKey);
+ if (!entry) {
+ ctx.log('Missing cache for');
+ return this.fetchAndCacheQuery(query, values, ctx);
}
- if (res) {
- const parsedResult = res;
- const renewedAgo = (new Date()).getTime() - parsedResult.time;
- this.logger('Found cache entry', {
- cacheKey,
- time: parsedResult.time,
- renewedAgo,
- renewalKey: parsedResult.renewalKey,
- newRenewalKey: renewalKey,
- renewalThreshold,
- requestId: options.requestId,
- spanId,
- primaryQuery,
- renewCycle
- });
-
- const isExpired = !renewalThreshold || !parsedResult.time || renewedAgo > renewalThreshold * 1000;
- const isKeyMismatch = renewalKey && parsedResult.renewalKey !== renewalKey;
- const isSameRequest = options.requestId && parsedResult.requestId &&
- QueryCache.extractRequestUUID(parsedResult.requestId) === QueryCache.extractRequestUUID(options.requestId);
-
- // Continue-wait cycle: result was produced by our request,
- // refreshKey changed during execution — return cached, refresh in background.
- // Skip for renewCycle — it must always fetch fresh data to keep cache up-to-date.
- if (isSameRequest && !renewCycle && (isExpired || isKeyMismatch)) {
- this.logger('Same request cache hit (background refresh)', { cacheKey, renewalThreshold, requestId: options.requestId, spanId, primaryQuery, renewCycle });
- fetchNew().catch(e => {
- if (!(e instanceof ContinueWaitError)) {
- this.logger('Error renewing', { cacheKey, error: e.stack || e, requestId: options.requestId, spanId, primaryQuery, renewCycle });
- }
- });
- } else if (renewalKey && (isExpired || isKeyMismatch)) {
- // Cache expired or refreshKey changed — need to refresh
- if (options.waitForRenew) {
- this.logger('Waiting for renew', { cacheKey, renewalThreshold, requestId: options.requestId, spanId, primaryQuery, renewCycle });
- return fetchNew();
- } else {
- this.logger('Renewing existing key', { cacheKey, renewalThreshold, requestId: options.requestId, spanId, primaryQuery, renewCycle });
- fetchNew().catch(e => {
- if (!(e instanceof ContinueWaitError)) {
- this.logger('Error renewing', { cacheKey, error: e.stack || e, requestId: options.requestId, spanId, primaryQuery, renewCycle });
- }
- });
- }
- }
+ const renewedAgo = (new Date()).getTime() - entry.time;
- this.logger('Using cache for', { cacheKey, requestId: options.requestId, spanId, primaryQuery, renewCycle });
- if (options.useInMemory && renewedAgo + inMemoryCacheDisablePeriod <= renewalThreshold * 1000) {
- this.memoryCache.set(redisKey, parsedResult);
- }
- return parsedResult.result;
- } else {
- this.logger('Missing cache for', { cacheKey, requestId: options.requestId, spanId, primaryQuery, renewCycle });
- return fetchNew();
+ ctx.log('Found cache entry', {
+ time: entry.time,
+ renewedAgo,
+ renewalKey: entry.renewalKey,
+ newRenewalKey: ctx.renewalKey,
+ renewalThreshold,
+ });
+
+ switch (QueryCache.decideCacheAction(entry, renewedAgo, options, ctx.renewalKey)) {
+ case CacheAction.WaitForRenew:
+ ctx.log('Waiting for renew', { renewalThreshold });
+ return this.fetchAndCacheQuery(query, values, ctx);
+ case CacheAction.RefreshSameRequest:
+ ctx.log('Same request cache hit (background refresh)', { renewalThreshold });
+ this.fetchAndCacheQueryInBackground(query, values, ctx);
+ break;
+ case CacheAction.RefreshBackground:
+ ctx.log('Renewing existing key', { renewalThreshold });
+ this.fetchAndCacheQueryInBackground(query, values, ctx);
+ break;
+ default:
+ break;
}
+
+ ctx.log('Using cache for');
+ this.storeInMemoryCache(entry, renewedAgo, ctx);
+
+ return entry.result;
}
protected async lastRefreshTime(cacheKey) {
diff --git a/packages/cubejs-query-orchestrator/test/unit/QueryCache.test.ts b/packages/cubejs-query-orchestrator/test/unit/QueryCache.test.ts
index 6cdbdb684da2d..578ebc3d89de0 100644
--- a/packages/cubejs-query-orchestrator/test/unit/QueryCache.test.ts
+++ b/packages/cubejs-query-orchestrator/test/unit/QueryCache.test.ts
@@ -1,5 +1,290 @@
+import { CacheAction, CacheEntry, CacheQueryResultOptions, QueryCache } from '../../src';
import { QueryCacheTest } from './QueryCache.abstract';
QueryCacheTest('Local', {
cacheAndQueueDriver: 'memory',
});
+
+class QueryCacheOpened extends QueryCache {
+ public static get disablePeriod(): number {
+ return this.IN_MEMORY_CACHE_DISABLE_PERIOD;
+ }
+
+ public static decideCacheAction(
+ entry: Partial,
+ renewedAgo: number,
+ options: Partial,
+ renewalKey?: string,
+ ): CacheAction {
+ return super.decideCacheAction(
+ entry as CacheEntry,
+ renewedAgo,
+ options as CacheQueryResultOptions,
+ renewalKey,
+ );
+ }
+
+ public static isMemoryEntryUsable(
+ entry: Partial,
+ renewedAgo: number,
+ expiration: number,
+ renewalThreshold?: number,
+ renewalKey?: string,
+ ): boolean {
+ return super.isMemoryEntryUsable(
+ entry as CacheEntry,
+ renewedAgo,
+ expiration,
+ renewalThreshold,
+ renewalKey,
+ );
+ }
+}
+
+describe('QueryCache.decideCacheAction', () => {
+ const THRESHOLD = 600;
+ const NOT_EXPIRED = 100 * 1000;
+ const EXPIRED = 700 * 1000;
+
+ type Case = {
+ name: string,
+ entry: Partial,
+ renewedAgo: number,
+ options: Partial,
+ renewalKey?: string,
+ expected: CacheAction,
+ };
+
+ const cases: Case[] = [
+ {
+ name: 'not expired and renewal key matches: serves cache even for the same request',
+ entry: { time: 1, renewalKey: 'rk', requestId: 'req-1-span-1' },
+ renewedAgo: NOT_EXPIRED,
+ options: { renewalThreshold: THRESHOLD, requestId: 'req-1-span-2', waitForRenew: true },
+ renewalKey: 'rk',
+ expected: CacheAction.ServeCached,
+ },
+ {
+ name: 'expired with renewal key and waitForRenew: blocks on fetch',
+ entry: { time: 1, renewalKey: 'rk', requestId: 'req-1' },
+ renewedAgo: EXPIRED,
+ options: { renewalThreshold: THRESHOLD, requestId: 'req-2', waitForRenew: true },
+ renewalKey: 'rk',
+ expected: CacheAction.WaitForRenew,
+ },
+ {
+ name: 'expired with renewal key without waitForRenew: refreshes in background',
+ entry: { time: 1, renewalKey: 'rk', requestId: 'req-1' },
+ renewedAgo: EXPIRED,
+ options: { renewalThreshold: THRESHOLD, requestId: 'req-2', waitForRenew: false },
+ renewalKey: 'rk',
+ expected: CacheAction.RefreshBackground,
+ },
+ {
+ name: 'renewal key mismatch while not expired and waitForRenew: blocks on fetch',
+ entry: { time: 1, renewalKey: 'old', requestId: 'req-1' },
+ renewedAgo: NOT_EXPIRED,
+ options: { renewalThreshold: THRESHOLD, requestId: 'req-2', waitForRenew: true },
+ renewalKey: 'new',
+ expected: CacheAction.WaitForRenew,
+ },
+ {
+ name: 'renewal key mismatch while not expired without waitForRenew: refreshes in background',
+ entry: { time: 1, renewalKey: 'old', requestId: 'req-1' },
+ renewedAgo: NOT_EXPIRED,
+ options: { renewalThreshold: THRESHOLD, requestId: 'req-2', waitForRenew: false },
+ renewalKey: 'new',
+ expected: CacheAction.RefreshBackground,
+ },
+ {
+ name: 'same request (different span) and expired: serves stale, refreshes in background',
+ entry: { time: 1, renewalKey: 'rk', requestId: 'req-1-span-1' },
+ renewedAgo: EXPIRED,
+ options: { renewalThreshold: THRESHOLD, requestId: 'req-1-span-7', waitForRenew: true },
+ renewalKey: 'rk',
+ expected: CacheAction.RefreshSameRequest,
+ },
+ {
+ name: 'same request and renewal key mismatch: serves stale, refreshes in background',
+ entry: { time: 1, renewalKey: 'old', requestId: 'req-1-span-1' },
+ renewedAgo: NOT_EXPIRED,
+ options: { renewalThreshold: THRESHOLD, requestId: 'req-1-span-7', waitForRenew: true },
+ renewalKey: 'new',
+ expected: CacheAction.RefreshSameRequest,
+ },
+ {
+ name: 'renew cycle never serves stale: expired same request blocks on fetch',
+ entry: { time: 1, renewalKey: 'rk', requestId: 'req-1-span-1' },
+ renewedAgo: EXPIRED,
+ options: { renewalThreshold: THRESHOLD, requestId: 'req-1-span-7', waitForRenew: true, renewCycle: true },
+ renewalKey: 'rk',
+ expected: CacheAction.WaitForRenew,
+ },
+ {
+ name: 'renew cycle never serves stale: key mismatch without waitForRenew refreshes in background',
+ entry: { time: 1, renewalKey: 'old', requestId: 'req-1-span-1' },
+ renewedAgo: NOT_EXPIRED,
+ options: { renewalThreshold: THRESHOLD, requestId: 'req-1-span-7', waitForRenew: false, renewCycle: true },
+ renewalKey: 'new',
+ expected: CacheAction.RefreshBackground,
+ },
+ {
+ name: 'expired without a renewal key: keeps serving cache',
+ entry: { time: 1, requestId: 'req-1' },
+ renewedAgo: EXPIRED,
+ options: { renewalThreshold: THRESHOLD, requestId: 'req-2', waitForRenew: true },
+ expected: CacheAction.ServeCached,
+ },
+ {
+ name: 'expired without a renewal key but same request: refreshes in background',
+ entry: { time: 1, requestId: 'req-1-span-1' },
+ renewedAgo: EXPIRED,
+ options: { renewalThreshold: THRESHOLD, requestId: 'req-1-span-7', waitForRenew: true },
+ expected: CacheAction.RefreshSameRequest,
+ },
+ {
+ name: 'missing renewal threshold counts as expired',
+ entry: { time: 1, renewalKey: 'rk', requestId: 'req-1' },
+ renewedAgo: 0,
+ options: { requestId: 'req-2', waitForRenew: true },
+ renewalKey: 'rk',
+ expected: CacheAction.WaitForRenew,
+ },
+ {
+ name: 'entry without a timestamp counts as expired',
+ entry: { time: 0, renewalKey: 'rk', requestId: 'req-1' },
+ renewedAgo: 0,
+ options: { renewalThreshold: THRESHOLD, requestId: 'req-2', waitForRenew: false },
+ renewalKey: 'rk',
+ expected: CacheAction.RefreshBackground,
+ },
+ {
+ name: 'entry without a request id is never treated as the same request',
+ entry: { time: 1, renewalKey: 'rk' },
+ renewedAgo: EXPIRED,
+ options: { renewalThreshold: THRESHOLD, requestId: 'req-1', waitForRenew: true },
+ renewalKey: 'rk',
+ expected: CacheAction.WaitForRenew,
+ },
+ ];
+
+ cases.forEach(({ name, entry, renewedAgo, options, renewalKey, expected }) => {
+ it(name, () => {
+ expect(QueryCacheOpened.decideCacheAction(entry, renewedAgo, options, renewalKey)).toEqual(expected);
+ });
+ });
+});
+
+describe('QueryCache.isMemoryEntryUsable', () => {
+ const WINDOW = QueryCacheOpened.disablePeriod;
+ const HOUR = 3600;
+
+ type Case = {
+ name: string,
+ entry: Partial,
+ renewedAgo: number,
+ expiration: number,
+ renewalThreshold?: number,
+ renewalKey?: string,
+ expected: boolean,
+ };
+
+ const cases: Case[] = [
+ {
+ name: 'past expiration but still inside the in-memory window: unusable',
+ entry: { time: 1 },
+ renewedAgo: 61 * 1000,
+ expiration: 60,
+ expected: false,
+ },
+ {
+ name: 'past the in-memory window but nowhere near expiration: unusable',
+ entry: { time: 1 },
+ renewedAgo: WINDOW + 1,
+ expiration: HOUR,
+ expected: false,
+ },
+ {
+ name: 'exactly at the in-memory window: usable',
+ entry: { time: 1 },
+ renewedAgo: WINDOW,
+ expiration: HOUR,
+ expected: true,
+ },
+ {
+ name: 'exactly at expiration: usable',
+ entry: { time: 1 },
+ renewedAgo: 60 * 1000,
+ expiration: 60,
+ expected: true,
+ },
+ {
+ name: 'without a renewal key the renewal checks are skipped: no threshold and no timestamp still usable',
+ entry: { time: 0 },
+ renewedAgo: 1000,
+ expiration: HOUR,
+ expected: true,
+ },
+ {
+ name: 'renewal key without a renewal threshold: unusable',
+ entry: { time: 1, renewalKey: 'rk' },
+ renewedAgo: 1000,
+ expiration: HOUR,
+ renewalKey: 'rk',
+ expected: false,
+ },
+ {
+ name: 'renewal key with an entry that has no timestamp: unusable',
+ entry: { time: 0, renewalKey: 'rk' },
+ renewedAgo: 1000,
+ expiration: HOUR,
+ renewalThreshold: HOUR,
+ renewalKey: 'rk',
+ expected: false,
+ },
+ {
+ name: 'threshold below twice the window, entry inside the last window before expiry: unusable',
+ entry: { time: 1, renewalKey: 'rk' },
+ renewedAgo: 100 * 1000,
+ expiration: HOUR,
+ renewalThreshold: 300,
+ renewalKey: 'rk',
+ expected: false,
+ },
+ {
+ name: 'threshold below twice the window, entry exactly at the last window before expiry: usable',
+ entry: { time: 1, renewalKey: 'rk' },
+ renewedAgo: 100 * 1000,
+ expiration: HOUR,
+ renewalThreshold: 400,
+ renewalKey: 'rk',
+ expected: true,
+ },
+ {
+ name: 'renewal key mismatch: unusable',
+ entry: { time: 1, renewalKey: 'old' },
+ renewedAgo: 1000,
+ expiration: HOUR,
+ renewalThreshold: HOUR,
+ renewalKey: 'new',
+ expected: false,
+ },
+ {
+ name: 'every check satisfied: usable',
+ entry: { time: 1, renewalKey: 'rk' },
+ renewedAgo: 1000,
+ expiration: HOUR,
+ renewalThreshold: HOUR,
+ renewalKey: 'rk',
+ expected: true,
+ },
+ ];
+
+ cases.forEach(({ name, entry, renewedAgo, expiration, renewalThreshold, renewalKey, expected }) => {
+ it(name, () => {
+ expect(
+ QueryCacheOpened.isMemoryEntryUsable(entry, renewedAgo, expiration, renewalThreshold, renewalKey)
+ ).toEqual(expected);
+ });
+ });
+});
diff --git a/packages/cubejs-questdb-driver/CHANGELOG.md b/packages/cubejs-questdb-driver/CHANGELOG.md
index e80040a05a7dc..6f41928fda545 100644
--- a/packages/cubejs-questdb-driver/CHANGELOG.md
+++ b/packages/cubejs-questdb-driver/CHANGELOG.md
@@ -3,6 +3,10 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
+## [1.7.29](https://github.com/cube-js/cube/compare/v1.7.28...v1.7.29) (2026-08-27)
+
+**Note:** Version bump only for package @cubejs-backend/questdb-driver
+
## [1.7.28](https://github.com/cube-js/cube/compare/v1.7.27...v1.7.28) (2026-08-26)
**Note:** Version bump only for package @cubejs-backend/questdb-driver
diff --git a/packages/cubejs-questdb-driver/package.json b/packages/cubejs-questdb-driver/package.json
index 8fd04fda1a6c9..dcbf3eb0e7137 100644
--- a/packages/cubejs-questdb-driver/package.json
+++ b/packages/cubejs-questdb-driver/package.json
@@ -2,7 +2,7 @@
"name": "@cubejs-backend/questdb-driver",
"description": "Cube.js QuestDB database driver",
"author": "Cube Dev, Inc.",
- "version": "1.7.28",
+ "version": "1.7.29",
"repository": {
"type": "git",
"url": "https://github.com/cube-js/cube.git",
@@ -27,9 +27,9 @@
"lint:fix": "eslint --fix src/* --ext .ts"
},
"dependencies": {
- "@cubejs-backend/base-driver": "1.7.28",
- "@cubejs-backend/schema-compiler": "1.7.28",
- "@cubejs-backend/shared": "1.7.28",
+ "@cubejs-backend/base-driver": "1.7.29",
+ "@cubejs-backend/schema-compiler": "1.7.29",
+ "@cubejs-backend/shared": "1.7.29",
"@types/pg": "^8.6.0",
"moment": "^2.24.0",
"pg": "^8.7.0",
@@ -37,8 +37,8 @@
},
"license": "Apache-2.0",
"devDependencies": {
- "@cubejs-backend/linter": "1.7.28",
- "@cubejs-backend/testing-shared": "1.7.28",
+ "@cubejs-backend/linter": "1.7.29",
+ "@cubejs-backend/testing-shared": "1.7.29",
"testcontainers": "^10.28.0",
"typescript": "~5.2.2"
},
diff --git a/packages/cubejs-redshift-driver/CHANGELOG.md b/packages/cubejs-redshift-driver/CHANGELOG.md
index bdd37a47b0bb2..bc8b92cebc0ef 100644
--- a/packages/cubejs-redshift-driver/CHANGELOG.md
+++ b/packages/cubejs-redshift-driver/CHANGELOG.md
@@ -3,6 +3,10 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
+## [1.7.29](https://github.com/cube-js/cube/compare/v1.7.28...v1.7.29) (2026-08-27)
+
+**Note:** Version bump only for package @cubejs-backend/redshift-driver
+
## [1.7.28](https://github.com/cube-js/cube/compare/v1.7.27...v1.7.28) (2026-08-26)
**Note:** Version bump only for package @cubejs-backend/redshift-driver
diff --git a/packages/cubejs-redshift-driver/package.json b/packages/cubejs-redshift-driver/package.json
index 25a6ba696b00d..b2f4aad2915f4 100644
--- a/packages/cubejs-redshift-driver/package.json
+++ b/packages/cubejs-redshift-driver/package.json
@@ -2,7 +2,7 @@
"name": "@cubejs-backend/redshift-driver",
"description": "Cube.js Redshift database driver",
"author": "Cube Dev, Inc.",
- "version": "1.7.28",
+ "version": "1.7.29",
"repository": {
"type": "git",
"url": "https://github.com/cube-js/cube.git",
@@ -27,13 +27,13 @@
"dependencies": {
"@aws-sdk/client-redshift": "^3.22.0",
"@aws-sdk/credential-providers": "^3.22.0",
- "@cubejs-backend/base-driver": "1.7.28",
- "@cubejs-backend/postgres-driver": "1.7.28",
- "@cubejs-backend/shared": "1.7.28"
+ "@cubejs-backend/base-driver": "1.7.29",
+ "@cubejs-backend/postgres-driver": "1.7.29",
+ "@cubejs-backend/shared": "1.7.29"
},
"license": "Apache-2.0",
"devDependencies": {
- "@cubejs-backend/linter": "1.7.28",
+ "@cubejs-backend/linter": "1.7.29",
"typescript": "~5.2.2"
},
"publishConfig": {
diff --git a/packages/cubejs-schema-compiler/CHANGELOG.md b/packages/cubejs-schema-compiler/CHANGELOG.md
index 4f6f2f2362022..dc27743aabde5 100644
--- a/packages/cubejs-schema-compiler/CHANGELOG.md
+++ b/packages/cubejs-schema-compiler/CHANGELOG.md
@@ -3,6 +3,12 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
+## [1.7.29](https://github.com/cube-js/cube/compare/v1.7.28...v1.7.29) (2026-08-27)
+
+### Bug Fixes
+
+- **api-gateway:** Make pre-agg build job status per-entry and data-source aware ([#11666](https://github.com/cube-js/cube/issues/11666)) ([694955f](https://github.com/cube-js/cube/commit/694955f864375515d84d463e9e7ee89a156a4a2b))
+
## [1.7.28](https://github.com/cube-js/cube/compare/v1.7.27...v1.7.28) (2026-08-26)
### Features
diff --git a/packages/cubejs-schema-compiler/package.json b/packages/cubejs-schema-compiler/package.json
index 7d3e68b78148e..c1b0f19e20f30 100644
--- a/packages/cubejs-schema-compiler/package.json
+++ b/packages/cubejs-schema-compiler/package.json
@@ -2,7 +2,7 @@
"name": "@cubejs-backend/schema-compiler",
"description": "Cube schema compiler",
"author": "Cube Dev, Inc.",
- "version": "1.7.28",
+ "version": "1.7.29",
"repository": {
"type": "git",
"url": "https://github.com/cube-js/cube.git",
@@ -40,8 +40,8 @@
"@babel/standalone": "^7.24",
"@babel/traverse": "^7.24",
"@babel/types": "^7.24",
- "@cubejs-backend/native": "1.7.28",
- "@cubejs-backend/shared": "1.7.28",
+ "@cubejs-backend/native": "1.7.29",
+ "@cubejs-backend/shared": "1.7.29",
"antlr4": "^4.13.2",
"camelcase": "^6.2.0",
"cron-parser": "^4.9.0",
@@ -60,9 +60,9 @@
},
"devDependencies": {
"@clickhouse/client": "^1.12.0",
- "@cubejs-backend/linter": "1.7.28",
- "@cubejs-backend/mssql-driver": "1.7.28",
- "@cubejs-backend/query-orchestrator": "1.7.28",
+ "@cubejs-backend/linter": "1.7.29",
+ "@cubejs-backend/mssql-driver": "1.7.29",
+ "@cubejs-backend/query-orchestrator": "1.7.29",
"@types/babel__code-frame": "^7.0.6",
"@types/babel__generator": "^7.6.8",
"@types/babel__traverse": "^7.20.5",
diff --git a/packages/cubejs-server-core/CHANGELOG.md b/packages/cubejs-server-core/CHANGELOG.md
index ffed88e0060f2..6dc896fc73089 100644
--- a/packages/cubejs-server-core/CHANGELOG.md
+++ b/packages/cubejs-server-core/CHANGELOG.md
@@ -3,6 +3,13 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
+## [1.7.29](https://github.com/cube-js/cube/compare/v1.7.28...v1.7.29) (2026-08-27)
+
+### Bug Fixes
+
+- **api-gateway:** Make pre-agg build job status per-entry and data-source aware ([#11666](https://github.com/cube-js/cube/issues/11666)) ([694955f](https://github.com/cube-js/cube/commit/694955f864375515d84d463e9e7ee89a156a4a2b))
+- **server-core:** release orchestrators evicted from the LRU ([#11661](https://github.com/cube-js/cube/issues/11661)) ([b82c58a](https://github.com/cube-js/cube/commit/b82c58aa245919a36e3d20f79105b639d0c16863))
+
## [1.7.28](https://github.com/cube-js/cube/compare/v1.7.27...v1.7.28) (2026-08-26)
**Note:** Version bump only for package @cubejs-backend/server-core
diff --git a/packages/cubejs-server-core/package.json b/packages/cubejs-server-core/package.json
index e0e6fbca5a388..b4cd09f13adc9 100644
--- a/packages/cubejs-server-core/package.json
+++ b/packages/cubejs-server-core/package.json
@@ -2,7 +2,7 @@
"name": "@cubejs-backend/server-core",
"description": "Cube.js base component to wire all backend components together",
"author": "Cube Dev, Inc.",
- "version": "1.7.28",
+ "version": "1.7.29",
"repository": {
"type": "git",
"url": "https://github.com/cube-js/cube.git",
@@ -29,16 +29,16 @@
"unit": "jest --runInBand --forceExit --coverage dist/test"
},
"dependencies": {
- "@cubejs-backend/api-gateway": "1.7.28",
- "@cubejs-backend/base-driver": "1.7.28",
- "@cubejs-backend/cloud": "1.7.28",
- "@cubejs-backend/cubestore-driver": "1.7.28",
+ "@cubejs-backend/api-gateway": "1.7.29",
+ "@cubejs-backend/base-driver": "1.7.29",
+ "@cubejs-backend/cloud": "1.7.29",
+ "@cubejs-backend/cubestore-driver": "1.7.29",
"@cubejs-backend/dotenv": "^9.0.2",
- "@cubejs-backend/native": "1.7.28",
- "@cubejs-backend/query-orchestrator": "1.7.28",
- "@cubejs-backend/schema-compiler": "1.7.28",
- "@cubejs-backend/shared": "1.7.28",
- "@cubejs-backend/templates": "1.7.28",
+ "@cubejs-backend/native": "1.7.29",
+ "@cubejs-backend/query-orchestrator": "1.7.29",
+ "@cubejs-backend/schema-compiler": "1.7.29",
+ "@cubejs-backend/shared": "1.7.29",
+ "@cubejs-backend/templates": "1.7.29",
"codesandbox-import-utils": "^2.1.12",
"cross-spawn": "^7.0.1",
"fs-extra": "^8.1.0",
@@ -62,8 +62,8 @@
"ws": "^7.5.3"
},
"devDependencies": {
- "@cubejs-backend/linter": "1.7.28",
- "@cubejs-client/playground": "1.7.28",
+ "@cubejs-backend/linter": "1.7.29",
+ "@cubejs-client/playground": "1.7.29",
"@types/cross-spawn": "^6.0.2",
"@types/express": "^4.17.21",
"@types/fs-extra": "^9.0.8",
diff --git a/packages/cubejs-server-core/src/core/OrchestratorApi.ts b/packages/cubejs-server-core/src/core/OrchestratorApi.ts
index 12e1da161c4c9..2d99755616e44 100644
--- a/packages/cubejs-server-core/src/core/OrchestratorApi.ts
+++ b/packages/cubejs-server-core/src/core/OrchestratorApi.ts
@@ -240,11 +240,23 @@ export class OrchestratorApi {
}
public async release() {
- return Promise.all([
- ...Object.keys(this.seenDataSources).map(ds => this.releaseDriver(this.driverFactory, ds)),
- this.releaseDriver(this.options.externalDriverFactory),
- this.orchestrator.cleanup()
- ]);
+ try {
+ return await Promise.all([
+ ...Object.keys(this.seenDataSources).map(ds => this.releaseDriver(this.driverFactory, ds)),
+ this.releaseDriver(this.options.externalDriverFactory),
+ this.orchestrator.cleanup()
+ ]);
+ } catch (error) {
+ // `OrchestratorStorage` swallows this so that one orchestrator failing to
+ // close cannot fail shutdown, which leaves this as the only place an
+ // operator learns that a connection was not closed after all.
+ this.logger('Orchestrator Release Error', {
+ orchestratorId: this.options.redisPrefix,
+ error: (error as Error).stack || String(error),
+ });
+
+ throw error;
+ }
}
protected async releaseDriver(driverFn?: DriverFactoryByDataSource, dataSource: string = 'default') {
diff --git a/packages/cubejs-server-core/src/core/OrchestratorStorage.ts b/packages/cubejs-server-core/src/core/OrchestratorStorage.ts
index cc1f6de078b00..9b1b80c7947fe 100644
--- a/packages/cubejs-server-core/src/core/OrchestratorStorage.ts
+++ b/packages/cubejs-server-core/src/core/OrchestratorStorage.ts
@@ -4,14 +4,35 @@ import type { OrchestratorApi } from './OrchestratorApi';
export class OrchestratorStorage {
protected readonly storage: LRUCache;
+ protected readonly pendingReleases: Set> = new Set();
+
public constructor(options: { compilerCacheSize?: number, maxCompilerCacheKeepAlive?: number, updateCompilerCacheKeepAlive?: boolean } = { compilerCacheSize: 100 }) {
this.storage = new LRUCache({
max: options.compilerCacheSize,
ttl: options.maxCompilerCacheKeepAlive,
- updateAgeOnGet: options.updateCompilerCacheKeepAlive
+ updateAgeOnGet: options.updateCompilerCacheKeepAlive,
+ // Any removal reason ('evict' | 'set' | 'delete' | 'expire') means the api is not
+ // reachable through the cache anymore, so its connections have to be closed.
+ // Without it an evicted api keeps its Cube Store web socket open forever: the heartbeat
+ // interval holds a reference to the socket, so it is never garbage collected either.
+ // disposeAfter, not dispose: release() is async and calls into the drivers, while
+ // dispose runs synchronously inside set()/delete().
+ disposeAfter: (api: OrchestratorApi) => {
+ this.release(api);
+ },
});
}
+ protected release(api: OrchestratorApi) {
+ const pending: Promise = api.release()
+ .then(() => undefined, () => undefined)
+ .then(() => {
+ this.pendingReleases.delete(pending);
+ });
+
+ this.pendingReleases.add(pending);
+ }
+
public has(orchestratorId: string) {
return this.storage.has(orchestratorId);
}
@@ -37,7 +58,9 @@ export class OrchestratorStorage {
}
public async releaseConnections() {
- await Promise.all([...this.storage.values()].map(api => api.release()));
+ // clear() disposes every entry, which schedules release() for each of them.
this.storage.clear();
+
+ await Promise.all([...this.pendingReleases]);
}
}
diff --git a/packages/cubejs-server-core/test/unit/OrchestratorStorage.test.ts b/packages/cubejs-server-core/test/unit/OrchestratorStorage.test.ts
new file mode 100644
index 0000000000000..4a2cae2ed6a36
--- /dev/null
+++ b/packages/cubejs-server-core/test/unit/OrchestratorStorage.test.ts
@@ -0,0 +1,160 @@
+import { OrchestratorApi } from '../../src/core/OrchestratorApi';
+import { OrchestratorStorage } from '../../src/core/OrchestratorStorage';
+
+/**
+ * `OrchestratorStorage` only ever calls `release()` on what it holds, so the
+ * entry is stubbed down to that: the release lifecycle inside `OrchestratorApi`
+ * is its own unit.
+ */
+function createApi(release: () => Promise = async () => undefined) {
+ const api = { release: jest.fn(release) };
+
+ return { api: api as unknown as OrchestratorApi, release: api.release };
+}
+
+/**
+ * A real `OrchestratorApi` with a stubbed `QueryOrchestrator`, for the one case
+ * that is about what the api logs rather than what the storage does with it.
+ */
+function createRealApi(driverError: Error) {
+ const logger = jest.fn();
+ const driver = { release: async () => { throw driverError; } };
+
+ const api = new OrchestratorApi(
+ async () => driver as any,
+ logger,
+ {
+ cacheAndQueueDriver: 'memory',
+ contextToDbType: async () => 'postgres',
+ contextToExternalDbType: () => 'cubestore',
+ redisPrefix: 'tenant-1',
+ }
+ );
+
+ (api as any).orchestrator = { cleanup: async () => undefined };
+
+ // A data source the orchestrator has touched, so the release has a driver to
+ // close -- and this one fails to close.
+ api.addDataSeenSource('default');
+
+ return { api, logger };
+}
+
+// lru-cache runs `disposeAfter` once the operation that removed the entry has
+// finished, and the release it schedules is asynchronous.
+const flush = () => new Promise(resolve => setImmediate(resolve));
+
+describe('OrchestratorStorage', () => {
+ test('releases an orchestrator the LRU evicts, and keeps the one still cached', async () => {
+ const storage = new OrchestratorStorage({ compilerCacheSize: 1 });
+ const evicted = createApi();
+ const kept = createApi();
+
+ storage.set('evicted', evicted.api);
+ storage.set('kept', kept.api);
+ await flush();
+
+ expect(storage.has('evicted')).toBe(false);
+ expect(evicted.release).toHaveBeenCalledTimes(1);
+
+ expect(storage.has('kept')).toBe(true);
+ expect(kept.release).not.toHaveBeenCalled();
+ });
+
+ test('releases an orchestrator replaced under the same id', async () => {
+ const storage = new OrchestratorStorage({ compilerCacheSize: 10 });
+ const first = createApi();
+ const second = createApi();
+
+ storage.set('same-id', first.api);
+ storage.set('same-id', second.api);
+ await flush();
+
+ expect(first.release).toHaveBeenCalledTimes(1);
+ expect(second.release).not.toHaveBeenCalled();
+ });
+
+ test('setting the same instance again does not release it', async () => {
+ const storage = new OrchestratorStorage({ compilerCacheSize: 10 });
+ const { api, release } = createApi();
+
+ storage.set('same-id', api);
+ storage.set('same-id', api);
+ await flush();
+
+ expect(storage.has('same-id')).toBe(true);
+ expect(release).not.toHaveBeenCalled();
+ });
+
+ test('releaseConnections waits for the releases it schedules', async () => {
+ const storage = new OrchestratorStorage({ compilerCacheSize: 10 });
+ let finishRelease: () => void = () => undefined;
+ const { api, release } = createApi(() => new Promise(resolve => {
+ finishRelease = () => resolve();
+ }));
+
+ storage.set('a', api);
+
+ let settled = false;
+ const releasing = storage.releaseConnections().then(() => { settled = true; });
+ await flush();
+
+ // `clear()` only schedules the releases, so shutdown has to wait for them:
+ // returning here would let the process exit with the connections still open.
+ expect(release).toHaveBeenCalledTimes(1);
+ expect(settled).toBe(false);
+
+ finishRelease();
+ await releasing;
+
+ expect(settled).toBe(true);
+ expect(storage.has('a')).toBe(false);
+ });
+
+ test('releaseConnections resolves and clears even when a release fails', async () => {
+ const storage = new OrchestratorStorage({ compilerCacheSize: 10 });
+ const failing = createApi(async () => { throw new Error('driver is gone'); });
+ const ok = createApi();
+
+ storage.set('failing', failing.api);
+ storage.set('ok', ok.api);
+
+ await expect(storage.releaseConnections()).resolves.toBeUndefined();
+
+ expect(ok.release).toHaveBeenCalledTimes(1);
+ expect(storage.has('failing')).toBe(false);
+ expect(storage.has('ok')).toBe(false);
+ });
+
+ test('a release that fails is logged rather than lost', async () => {
+ const storage = new OrchestratorStorage({ compilerCacheSize: 1 });
+ const { api, logger } = createRealApi(new Error('driver is gone'));
+
+ storage.set('failing', api);
+ storage.set('next', createApi().api);
+ await flush();
+
+ // The storage swallows the rejection so shutdown cannot fail on it, which
+ // leaves the log as the only signal that a connection stayed open.
+ expect(logger).toHaveBeenCalledWith(
+ 'Orchestrator Release Error',
+ expect.objectContaining({
+ orchestratorId: 'tenant-1',
+ error: expect.stringContaining('driver is gone'),
+ })
+ );
+ });
+
+ test('forgets a release once it has finished', async () => {
+ const storage = new OrchestratorStorage({ compilerCacheSize: 1 });
+
+ storage.set('a', createApi().api);
+ storage.set('b', createApi().api);
+ await storage.releaseConnections();
+ await flush();
+
+ // Otherwise the set grows by one entry per eviction -- a smaller leak in
+ // place of the one being fixed.
+ expect((storage as any).pendingReleases.size).toBe(0);
+ });
+});
diff --git a/packages/cubejs-server/CHANGELOG.md b/packages/cubejs-server/CHANGELOG.md
index 5ed77caf533f5..6e525c596e8ee 100644
--- a/packages/cubejs-server/CHANGELOG.md
+++ b/packages/cubejs-server/CHANGELOG.md
@@ -3,6 +3,10 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
+## [1.7.29](https://github.com/cube-js/cube/compare/v1.7.28...v1.7.29) (2026-08-27)
+
+**Note:** Version bump only for package @cubejs-backend/server
+
## [1.7.28](https://github.com/cube-js/cube/compare/v1.7.27...v1.7.28) (2026-08-26)
**Note:** Version bump only for package @cubejs-backend/server
diff --git a/packages/cubejs-server/package.json b/packages/cubejs-server/package.json
index 118cf4da9aaf8..5abd908f2655a 100644
--- a/packages/cubejs-server/package.json
+++ b/packages/cubejs-server/package.json
@@ -2,7 +2,7 @@
"name": "@cubejs-backend/server",
"description": "Cube.js all-in-one server",
"author": "Cube Dev, Inc.",
- "version": "1.7.28",
+ "version": "1.7.29",
"types": "index.d.ts",
"repository": {
"type": "git",
@@ -40,11 +40,11 @@
"jest:shapshot": "jest --updateSnapshot test"
},
"dependencies": {
- "@cubejs-backend/cubestore-driver": "1.7.28",
+ "@cubejs-backend/cubestore-driver": "1.7.29",
"@cubejs-backend/dotenv": "^9.0.2",
- "@cubejs-backend/native": "1.7.28",
- "@cubejs-backend/server-core": "1.7.28",
- "@cubejs-backend/shared": "1.7.28",
+ "@cubejs-backend/native": "1.7.29",
+ "@cubejs-backend/server-core": "1.7.29",
+ "@cubejs-backend/shared": "1.7.29",
"@oclif/color": "^1.0.0",
"@oclif/command": "^1.8.13",
"@oclif/config": "^1.18.2",
@@ -61,8 +61,8 @@
"ws": "^7.1.2"
},
"devDependencies": {
- "@cubejs-backend/linter": "1.7.28",
- "@cubejs-backend/query-orchestrator": "1.7.28",
+ "@cubejs-backend/linter": "1.7.29",
+ "@cubejs-backend/query-orchestrator": "1.7.29",
"@oclif/dev-cli": "^1.23.1",
"@types/body-parser": "^1.19.0",
"@types/cors": "^2.8.8",
diff --git a/packages/cubejs-snowflake-driver/CHANGELOG.md b/packages/cubejs-snowflake-driver/CHANGELOG.md
index 52a016fa182a2..816c0454152bb 100644
--- a/packages/cubejs-snowflake-driver/CHANGELOG.md
+++ b/packages/cubejs-snowflake-driver/CHANGELOG.md
@@ -3,6 +3,10 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
+## [1.7.29](https://github.com/cube-js/cube/compare/v1.7.28...v1.7.29) (2026-08-27)
+
+**Note:** Version bump only for package @cubejs-backend/snowflake-driver
+
## [1.7.28](https://github.com/cube-js/cube/compare/v1.7.27...v1.7.28) (2026-08-26)
**Note:** Version bump only for package @cubejs-backend/snowflake-driver
diff --git a/packages/cubejs-snowflake-driver/package.json b/packages/cubejs-snowflake-driver/package.json
index bb1a62e7d40d7..608f99513d5b6 100644
--- a/packages/cubejs-snowflake-driver/package.json
+++ b/packages/cubejs-snowflake-driver/package.json
@@ -2,7 +2,7 @@
"name": "@cubejs-backend/snowflake-driver",
"description": "Cube.js Snowflake database driver",
"author": "Cube Dev, Inc.",
- "version": "1.7.28",
+ "version": "1.7.29",
"repository": {
"type": "git",
"url": "https://github.com/cube-js/cube.git",
@@ -29,8 +29,8 @@
},
"dependencies": {
"@aws-sdk/client-s3": "^3.726.0",
- "@cubejs-backend/base-driver": "1.7.28",
- "@cubejs-backend/shared": "1.7.28",
+ "@cubejs-backend/base-driver": "1.7.29",
+ "@cubejs-backend/shared": "1.7.29",
"snowflake-sdk": "^2.4.0"
},
"license": "Apache-2.0",
@@ -41,7 +41,7 @@
"extends": "../cubejs-linter"
},
"devDependencies": {
- "@cubejs-backend/linter": "1.7.28",
+ "@cubejs-backend/linter": "1.7.29",
"typescript": "~5.2.2",
"vitest": "^4"
}
diff --git a/packages/cubejs-sqlite-driver/CHANGELOG.md b/packages/cubejs-sqlite-driver/CHANGELOG.md
index 9d47520b90747..6457e155c5590 100644
--- a/packages/cubejs-sqlite-driver/CHANGELOG.md
+++ b/packages/cubejs-sqlite-driver/CHANGELOG.md
@@ -3,6 +3,10 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
+## [1.7.29](https://github.com/cube-js/cube/compare/v1.7.28...v1.7.29) (2026-08-27)
+
+**Note:** Version bump only for package @cubejs-backend/sqlite-driver
+
## [1.7.28](https://github.com/cube-js/cube/compare/v1.7.27...v1.7.28) (2026-08-26)
**Note:** Version bump only for package @cubejs-backend/sqlite-driver
diff --git a/packages/cubejs-sqlite-driver/package.json b/packages/cubejs-sqlite-driver/package.json
index 56d90e0fb9588..8c3111f63a37d 100644
--- a/packages/cubejs-sqlite-driver/package.json
+++ b/packages/cubejs-sqlite-driver/package.json
@@ -2,7 +2,7 @@
"name": "@cubejs-backend/sqlite-driver",
"description": "Cube.js Sqlite database driver",
"author": "Cube Dev, Inc.",
- "version": "1.7.28",
+ "version": "1.7.29",
"repository": {
"type": "git",
"url": "https://github.com/cube-js/cube.git",
@@ -18,13 +18,13 @@
"unit": "jest"
},
"dependencies": {
- "@cubejs-backend/base-driver": "1.7.28",
- "@cubejs-backend/shared": "1.7.28",
+ "@cubejs-backend/base-driver": "1.7.29",
+ "@cubejs-backend/shared": "1.7.29",
"sqlite3": "^5.1.7"
},
"license": "Apache-2.0",
"devDependencies": {
- "@cubejs-backend/linter": "1.7.28",
+ "@cubejs-backend/linter": "1.7.29",
"jest": "^29"
},
"publishConfig": {
diff --git a/packages/cubejs-templates/CHANGELOG.md b/packages/cubejs-templates/CHANGELOG.md
index b6be3ec1726bd..9f737c1a82367 100644
--- a/packages/cubejs-templates/CHANGELOG.md
+++ b/packages/cubejs-templates/CHANGELOG.md
@@ -3,6 +3,10 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
+## [1.7.29](https://github.com/cube-js/cube/compare/v1.7.28...v1.7.29) (2026-08-27)
+
+**Note:** Version bump only for package @cubejs-backend/templates
+
## [1.7.28](https://github.com/cube-js/cube/compare/v1.7.27...v1.7.28) (2026-08-26)
**Note:** Version bump only for package @cubejs-backend/templates
diff --git a/packages/cubejs-templates/package.json b/packages/cubejs-templates/package.json
index 489d65f155895..4d5ef4d341f09 100644
--- a/packages/cubejs-templates/package.json
+++ b/packages/cubejs-templates/package.json
@@ -1,6 +1,6 @@
{
"name": "@cubejs-backend/templates",
- "version": "1.7.28",
+ "version": "1.7.29",
"description": "Cube.js Templates helpers",
"author": "Cube Dev, Inc.",
"repository": {
@@ -31,7 +31,7 @@
"extends": "../cubejs-linter"
},
"dependencies": {
- "@cubejs-backend/shared": "1.7.28",
+ "@cubejs-backend/shared": "1.7.29",
"cross-spawn": "^7.0.3",
"fs-extra": "^9.1.0",
"node-fetch": "^2.6.1",
@@ -40,7 +40,7 @@
"tar": "^7.5.22"
},
"devDependencies": {
- "@cubejs-backend/linter": "1.7.28",
+ "@cubejs-backend/linter": "1.7.29",
"typescript": "~5.2.2"
}
}
diff --git a/packages/cubejs-testing-drivers/CHANGELOG.md b/packages/cubejs-testing-drivers/CHANGELOG.md
index 0f4588f3e03a5..67afdd0e66e9b 100644
--- a/packages/cubejs-testing-drivers/CHANGELOG.md
+++ b/packages/cubejs-testing-drivers/CHANGELOG.md
@@ -3,6 +3,10 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
+## [1.7.29](https://github.com/cube-js/cube/compare/v1.7.28...v1.7.29) (2026-08-27)
+
+**Note:** Version bump only for package @cubejs-backend/testing-drivers
+
## [1.7.28](https://github.com/cube-js/cube/compare/v1.7.27...v1.7.28) (2026-08-26)
**Note:** Version bump only for package @cubejs-backend/testing-drivers
diff --git a/packages/cubejs-testing-drivers/package.json b/packages/cubejs-testing-drivers/package.json
index 7fc12e08e3f20..6cf199f254894 100644
--- a/packages/cubejs-testing-drivers/package.json
+++ b/packages/cubejs-testing-drivers/package.json
@@ -1,6 +1,6 @@
{
"name": "@cubejs-backend/testing-drivers",
- "version": "1.7.28",
+ "version": "1.7.29",
"description": "Cube.js drivers test suite",
"author": "Cube Dev, Inc.",
"repository": {
@@ -87,29 +87,29 @@
"dist/src"
],
"dependencies": {
- "@cubejs-backend/athena-driver": "1.7.28",
- "@cubejs-backend/base-driver": "1.7.28",
- "@cubejs-backend/bigquery-driver": "1.7.28",
- "@cubejs-backend/clickhouse-driver": "1.7.28",
- "@cubejs-backend/crate-driver": "1.7.28",
- "@cubejs-backend/cubestore-driver": "1.7.28",
- "@cubejs-backend/databricks-jdbc-driver": "1.7.28",
+ "@cubejs-backend/athena-driver": "1.7.29",
+ "@cubejs-backend/base-driver": "1.7.29",
+ "@cubejs-backend/bigquery-driver": "1.7.29",
+ "@cubejs-backend/clickhouse-driver": "1.7.29",
+ "@cubejs-backend/crate-driver": "1.7.29",
+ "@cubejs-backend/cubestore-driver": "1.7.29",
+ "@cubejs-backend/databricks-jdbc-driver": "1.7.29",
"@cubejs-backend/dotenv": "^9.0.2",
- "@cubejs-backend/linter": "1.7.28",
- "@cubejs-backend/mssql-driver": "1.7.28",
- "@cubejs-backend/mysql-driver": "1.7.28",
- "@cubejs-backend/oracle-driver": "1.7.28",
- "@cubejs-backend/pinot-driver": "1.7.28",
- "@cubejs-backend/postgres-driver": "1.7.28",
- "@cubejs-backend/query-orchestrator": "1.7.28",
- "@cubejs-backend/questdb-driver": "1.7.28",
- "@cubejs-backend/server-core": "1.7.28",
- "@cubejs-backend/shared": "1.7.28",
- "@cubejs-backend/snowflake-driver": "1.7.28",
- "@cubejs-backend/testing-shared": "1.7.28",
- "@cubejs-backend/trino-driver": "1.7.28",
- "@cubejs-client/core": "1.7.28",
- "@cubejs-client/ws-transport": "1.7.28",
+ "@cubejs-backend/linter": "1.7.29",
+ "@cubejs-backend/mssql-driver": "1.7.29",
+ "@cubejs-backend/mysql-driver": "1.7.29",
+ "@cubejs-backend/oracle-driver": "1.7.29",
+ "@cubejs-backend/pinot-driver": "1.7.29",
+ "@cubejs-backend/postgres-driver": "1.7.29",
+ "@cubejs-backend/query-orchestrator": "1.7.29",
+ "@cubejs-backend/questdb-driver": "1.7.29",
+ "@cubejs-backend/server-core": "1.7.29",
+ "@cubejs-backend/shared": "1.7.29",
+ "@cubejs-backend/snowflake-driver": "1.7.29",
+ "@cubejs-backend/testing-shared": "1.7.29",
+ "@cubejs-backend/trino-driver": "1.7.29",
+ "@cubejs-client/core": "1.7.29",
+ "@cubejs-client/ws-transport": "1.7.29",
"@jest/globals": "^29",
"@types/jest": "^29",
"@types/node": "^22",
diff --git a/packages/cubejs-testing-shared/CHANGELOG.md b/packages/cubejs-testing-shared/CHANGELOG.md
index 1e2c0946dba4c..ae15a6c6955e6 100644
--- a/packages/cubejs-testing-shared/CHANGELOG.md
+++ b/packages/cubejs-testing-shared/CHANGELOG.md
@@ -3,6 +3,10 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
+## [1.7.29](https://github.com/cube-js/cube/compare/v1.7.28...v1.7.29) (2026-08-27)
+
+**Note:** Version bump only for package @cubejs-backend/testing-shared
+
## [1.7.28](https://github.com/cube-js/cube/compare/v1.7.27...v1.7.28) (2026-08-26)
**Note:** Version bump only for package @cubejs-backend/testing-shared
diff --git a/packages/cubejs-testing-shared/package.json b/packages/cubejs-testing-shared/package.json
index 0332a59ef0e21..4ada7f9c5bbe7 100644
--- a/packages/cubejs-testing-shared/package.json
+++ b/packages/cubejs-testing-shared/package.json
@@ -1,6 +1,6 @@
{
"name": "@cubejs-backend/testing-shared",
- "version": "1.7.28",
+ "version": "1.7.29",
"description": "Cube.js Testing Helpers",
"author": "Cube Dev, Inc.",
"repository": {
@@ -26,16 +26,16 @@
],
"dependencies": {
"@cubejs-backend/dotenv": "^9.0.2",
- "@cubejs-backend/query-orchestrator": "1.7.28",
- "@cubejs-backend/schema-compiler": "1.7.28",
- "@cubejs-backend/shared": "1.7.28",
+ "@cubejs-backend/query-orchestrator": "1.7.29",
+ "@cubejs-backend/schema-compiler": "1.7.29",
+ "@cubejs-backend/shared": "1.7.29",
"@testcontainers/kafka": "~10.28.0",
"dedent": "^0.7.0",
"node-fetch": "^2.6.7",
"testcontainers": "^10.28.0"
},
"devDependencies": {
- "@cubejs-backend/linter": "1.7.28",
+ "@cubejs-backend/linter": "1.7.29",
"@jest/globals": "^29",
"@types/dedent": "^0.7.0",
"@types/jest": "^29",
diff --git a/packages/cubejs-testing/CHANGELOG.md b/packages/cubejs-testing/CHANGELOG.md
index 03ae39ee3f006..a5ac78f7845fc 100644
--- a/packages/cubejs-testing/CHANGELOG.md
+++ b/packages/cubejs-testing/CHANGELOG.md
@@ -3,6 +3,10 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
+## [1.7.29](https://github.com/cube-js/cube/compare/v1.7.28...v1.7.29) (2026-08-27)
+
+**Note:** Version bump only for package @cubejs-backend/testing
+
## [1.7.28](https://github.com/cube-js/cube/compare/v1.7.27...v1.7.28) (2026-08-26)
**Note:** Version bump only for package @cubejs-backend/testing
diff --git a/packages/cubejs-testing/package.json b/packages/cubejs-testing/package.json
index eab5892e7a018..78c79290a296e 100644
--- a/packages/cubejs-testing/package.json
+++ b/packages/cubejs-testing/package.json
@@ -1,6 +1,6 @@
{
"name": "@cubejs-backend/testing",
- "version": "1.7.28",
+ "version": "1.7.29",
"description": "Cube.js e2e tests",
"author": "Cube Dev, Inc.",
"repository": {
@@ -92,15 +92,15 @@
"birdbox-fixtures"
],
"dependencies": {
- "@cubejs-backend/cubestore-driver": "1.7.28",
+ "@cubejs-backend/cubestore-driver": "1.7.29",
"@cubejs-backend/dotenv": "^9.0.2",
- "@cubejs-backend/ksql-driver": "1.7.28",
- "@cubejs-backend/postgres-driver": "1.7.28",
- "@cubejs-backend/query-orchestrator": "1.7.28",
- "@cubejs-backend/schema-compiler": "1.7.28",
- "@cubejs-backend/shared": "1.7.28",
- "@cubejs-backend/testing-shared": "1.7.28",
- "@cubejs-client/ws-transport": "1.7.28",
+ "@cubejs-backend/ksql-driver": "1.7.29",
+ "@cubejs-backend/postgres-driver": "1.7.29",
+ "@cubejs-backend/query-orchestrator": "1.7.29",
+ "@cubejs-backend/schema-compiler": "1.7.29",
+ "@cubejs-backend/shared": "1.7.29",
+ "@cubejs-backend/testing-shared": "1.7.29",
+ "@cubejs-client/ws-transport": "1.7.29",
"dedent": "^0.7.0",
"fs-extra": "^8.1.0",
"http-proxy": "^1.18.1",
@@ -111,8 +111,8 @@
},
"devDependencies": {
"@4tw/cypress-drag-drop": "^1.6.0",
- "@cubejs-backend/linter": "1.7.28",
- "@cubejs-client/core": "1.7.28",
+ "@cubejs-backend/linter": "1.7.29",
+ "@cubejs-client/core": "1.7.29",
"@jest/globals": "^29",
"@types/dedent": "^0.7.0",
"@types/http-proxy": "^1.17.5",
diff --git a/packages/cubejs-trino-driver/CHANGELOG.md b/packages/cubejs-trino-driver/CHANGELOG.md
index 75a49efbf1744..1bc0bc4c4871e 100644
--- a/packages/cubejs-trino-driver/CHANGELOG.md
+++ b/packages/cubejs-trino-driver/CHANGELOG.md
@@ -3,6 +3,10 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
+## [1.7.29](https://github.com/cube-js/cube/compare/v1.7.28...v1.7.29) (2026-08-27)
+
+**Note:** Version bump only for package @cubejs-backend/trino-driver
+
## [1.7.28](https://github.com/cube-js/cube/compare/v1.7.27...v1.7.28) (2026-08-26)
**Note:** Version bump only for package @cubejs-backend/trino-driver
diff --git a/packages/cubejs-trino-driver/package.json b/packages/cubejs-trino-driver/package.json
index 52574ce732d24..ac22e78d41f7c 100644
--- a/packages/cubejs-trino-driver/package.json
+++ b/packages/cubejs-trino-driver/package.json
@@ -2,7 +2,7 @@
"name": "@cubejs-backend/trino-driver",
"description": "Cube.js Trino database driver",
"author": "Cube Dev, Inc.",
- "version": "1.7.28",
+ "version": "1.7.29",
"repository": {
"type": "git",
"url": "https://github.com/cube-js/cube.git",
@@ -28,10 +28,10 @@
"lint:fix": "eslint --fix src/* --ext .ts"
},
"dependencies": {
- "@cubejs-backend/base-driver": "1.7.28",
- "@cubejs-backend/prestodb-driver": "1.7.28",
- "@cubejs-backend/schema-compiler": "1.7.28",
- "@cubejs-backend/shared": "1.7.28",
+ "@cubejs-backend/base-driver": "1.7.29",
+ "@cubejs-backend/prestodb-driver": "1.7.29",
+ "@cubejs-backend/schema-compiler": "1.7.29",
+ "@cubejs-backend/shared": "1.7.29",
"node-fetch": "^2.6.1",
"presto-client": "^1.2.0"
},
@@ -40,7 +40,7 @@
"access": "public"
},
"devDependencies": {
- "@cubejs-backend/linter": "1.7.28",
+ "@cubejs-backend/linter": "1.7.29",
"@types/jest": "^29",
"jest": "^29",
"testcontainers": "^10.28.0",
diff --git a/packages/cubejs-vertica-driver/CHANGELOG.md b/packages/cubejs-vertica-driver/CHANGELOG.md
index 7c1942b544787..24339799235d2 100644
--- a/packages/cubejs-vertica-driver/CHANGELOG.md
+++ b/packages/cubejs-vertica-driver/CHANGELOG.md
@@ -3,6 +3,10 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
+## [1.7.29](https://github.com/cube-js/cube/compare/v1.7.28...v1.7.29) (2026-08-27)
+
+**Note:** Version bump only for package @cubejs-backend/vertica-driver
+
## [1.7.28](https://github.com/cube-js/cube/compare/v1.7.27...v1.7.28) (2026-08-26)
**Note:** Version bump only for package @cubejs-backend/vertica-driver
diff --git a/packages/cubejs-vertica-driver/package.json b/packages/cubejs-vertica-driver/package.json
index ae1dabf1ec283..c5283b97ebae8 100644
--- a/packages/cubejs-vertica-driver/package.json
+++ b/packages/cubejs-vertica-driver/package.json
@@ -2,7 +2,7 @@
"name": "@cubejs-backend/vertica-driver",
"description": "Cube.js Vertica database driver",
"author": "Eduard Karacharov, Tim Brown, Cube Dev, Inc.",
- "version": "1.7.28",
+ "version": "1.7.29",
"repository": {
"type": "git",
"url": "https://github.com/cube-js/cube.git",
@@ -19,16 +19,16 @@
"lint:fix": "eslint --fix **/*.js"
},
"dependencies": {
- "@cubejs-backend/base-driver": "1.7.28",
- "@cubejs-backend/query-orchestrator": "1.7.28",
- "@cubejs-backend/schema-compiler": "1.7.28",
- "@cubejs-backend/shared": "1.7.28",
+ "@cubejs-backend/base-driver": "1.7.29",
+ "@cubejs-backend/query-orchestrator": "1.7.29",
+ "@cubejs-backend/schema-compiler": "1.7.29",
+ "@cubejs-backend/shared": "1.7.29",
"vertica-nodejs": "^1.0.3"
},
"license": "Apache-2.0",
"devDependencies": {
- "@cubejs-backend/linter": "1.7.28",
- "@cubejs-backend/testing-shared": "1.7.28",
+ "@cubejs-backend/linter": "1.7.29",
+ "@cubejs-backend/testing-shared": "1.7.29",
"@types/jest": "^29",
"jest": "^29",
"testcontainers": "^10.28.0"
diff --git a/rust/cubesql/CHANGELOG.md b/rust/cubesql/CHANGELOG.md
index 0c8e2df1763b1..e99357516190b 100644
--- a/rust/cubesql/CHANGELOG.md
+++ b/rust/cubesql/CHANGELOG.md
@@ -3,6 +3,10 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
+## [1.7.29](https://github.com/cube-js/cube/compare/v1.7.28...v1.7.29) (2026-08-27)
+
+**Note:** Version bump only for package @cubejs-backend/cubesql
+
## [1.7.28](https://github.com/cube-js/cube/compare/v1.7.27...v1.7.28) (2026-08-26)
### Features
diff --git a/rust/cubesql/package.json b/rust/cubesql/package.json
index bb8245128fb5b..ccfd3be4127ea 100644
--- a/rust/cubesql/package.json
+++ b/rust/cubesql/package.json
@@ -1,6 +1,6 @@
{
"name": "@cubejs-backend/cubesql",
- "version": "1.7.28",
+ "version": "1.7.29",
"description": "SQL API for Cube as proxy over MySQL protocol.",
"engines": {
"node": "^12.0.0 || ^14.0.0 || >=16.0.0"
diff --git a/rust/cubestore/CHANGELOG.md b/rust/cubestore/CHANGELOG.md
index 986e4dfbca598..82a693ecb526a 100644
--- a/rust/cubestore/CHANGELOG.md
+++ b/rust/cubestore/CHANGELOG.md
@@ -3,6 +3,12 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
+## [1.7.29](https://github.com/cube-js/cube/compare/v1.7.28...v1.7.29) (2026-08-27)
+
+### Features
+
+- **cubestore:** cap concurrent websocket connections per user ([#11667](https://github.com/cube-js/cube/issues/11667)) ([111eb3e](https://github.com/cube-js/cube/commit/111eb3ec049b9be1fa2f31186a7f83f58b5a0291))
+
## [1.7.28](https://github.com/cube-js/cube/compare/v1.7.27...v1.7.28) (2026-08-26)
### Bug Fixes
diff --git a/rust/cubestore/Cargo.lock b/rust/cubestore/Cargo.lock
index 34d2321545297..73cb99f8d5e29 100644
--- a/rust/cubestore/Cargo.lock
+++ b/rust/cubestore/Cargo.lock
@@ -1445,7 +1445,7 @@ dependencies = [
[[package]]
name = "cubestore"
-version = "1.7.28"
+version = "1.7.29"
dependencies = [
"actix-rt",
"anyhow",
diff --git a/rust/cubestore/cubestore/Cargo.toml b/rust/cubestore/cubestore/Cargo.toml
index bed8b87f6f0c0..7d87c8fa35a2f 100644
--- a/rust/cubestore/cubestore/Cargo.toml
+++ b/rust/cubestore/cubestore/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "cubestore"
-version = "1.7.28"
+version = "1.7.29"
authors = ["Cube Dev, Inc."]
edition = "2021"
license = "Apache-2.0"
diff --git a/rust/cubestore/package.json b/rust/cubestore/package.json
index 19cb3cf9850ea..7d39be393e2cb 100644
--- a/rust/cubestore/package.json
+++ b/rust/cubestore/package.json
@@ -1,6 +1,6 @@
{
"name": "@cubejs-backend/cubestore",
- "version": "1.7.28",
+ "version": "1.7.29",
"description": "Cube.js pre-aggregation storage layer.",
"main": "dist/src/index.js",
"typings": "dist/src/index.d.ts",
@@ -33,7 +33,7 @@
},
"license": "Apache-2.0",
"devDependencies": {
- "@cubejs-backend/linter": "1.7.28",
+ "@cubejs-backend/linter": "1.7.29",
"@types/jest": "^29",
"@types/node": "^18",
"jest": "^29",
@@ -43,7 +43,7 @@
"access": "public"
},
"dependencies": {
- "@cubejs-backend/shared": "1.7.28",
+ "@cubejs-backend/shared": "1.7.29",
"@octokit/core": "^3.2.5",
"source-map-support": "^0.5.19"
},