Created
April 7, 2020 15:36
-
-
Save skokenes/18cb98db46821961ef66b5905a527810 to your computer and use it in GitHub Desktop.
Qlik Core Bug Example: crashing when empty dimension added to hypercube
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
version: "3.5" | |
services: | |
qix-test: | |
container_name: "qix-test" | |
image: qlikcore/engine:12.612.0 | |
command: -S AcceptEULA=yes -S SystemLogVerbosity=3 -S DocumentDirectory=/data | |
volumes: | |
- ./data:/data | |
ports: | |
- 9077:9076 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const { connectSession, qAsk, qAskReplay, invalidations } = require("rxq"); | |
const { filter, switchMapTo } = require("rxjs/operators"); | |
const { concat, timer } = require("rxjs"); | |
// Create a session with an engine | |
const session = connectSession({ | |
host: "localhost", | |
port: 9077, | |
}); | |
// Log when socket is closed. In this case, that would indicate the Engine crashed. | |
session.notifications$ | |
.pipe(filter((m) => m.type === "socket:close")) | |
.subscribe(() => { | |
console.log("ENGINE CRASHED, SOCKET CLOSED"); | |
}); | |
// Open the sales_orders qvf | |
const app = session.global$.pipe(qAskReplay("OpenDoc", "sales_orders.qvf")); | |
// Create a parent object | |
const rootObject = app.pipe( | |
qAskReplay("CreateSessionObject", { | |
qInfo: { | |
qType: "root", | |
qId: "root", | |
}, | |
}) | |
); | |
/* | |
Initially set the full property tree for the object. | |
Include a child object with a hypercube. | |
The child object has 3 dimensions and 1 measure, sorted in column order 0,1,2,3 | |
*/ | |
const updateInitialPropTree = rootObject.pipe( | |
qAskReplay("SetFullPropertyTree", { | |
qProperty: { | |
qInfo: { | |
qType: "root", | |
qId: "root", | |
}, | |
}, | |
qChildren: [ | |
{ | |
qProperty: { | |
qInfo: { | |
qType: "child", | |
qId: "child_1", | |
}, | |
qHyperCubeDef: { | |
qDimensions: [ | |
{ | |
qDef: { | |
qFieldDefs: ["Product Category"], | |
}, | |
}, | |
{ | |
qDef: { | |
qFieldDefs: ["Product Class"], | |
}, | |
}, | |
{ | |
qDef: { | |
qFieldDefs: ["Product Color"], | |
}, | |
}, | |
], | |
qMeasures: [ | |
{ | |
qDef: { | |
qDef: "AVG([Unit Price])", | |
qAggrFunc: "Expr", | |
}, | |
qSortBy: { | |
qSortByAscii: 1, | |
qExpression: { | |
qv: "AVG([Unit Price])", | |
}, | |
}, | |
}, | |
], | |
qMode: "S", | |
qInitialDataFetch: [], | |
qSuppressZero: false, | |
qSuppressMissing: false, | |
qInterColumnSortOrder: [0, 1, 2, 3], | |
qColumnOrder: [0, 1, 2, 3], | |
}, | |
}, | |
qChildren: [], | |
}, | |
], | |
}) | |
); | |
/* | |
Update the full property tree of the parent object | |
Add an empty dimension to the child object at the end, AFTER the measure in column order | |
The child object now has 4 dimensions and 1 measure, sorted in column order 0,1,2,4,3 | |
This will cause the engine to crash if you are pulling layouts and data pages | |
*/ | |
const addEmptyDimension = rootObject.pipe( | |
qAskReplay("SetFullPropertyTree", { | |
qProperty: { | |
qInfo: { | |
qType: "root", | |
qId: "root", | |
}, | |
}, | |
qChildren: [ | |
{ | |
qProperty: { | |
qInfo: { | |
qType: "child", | |
qId: "child_1", | |
}, | |
qHyperCubeDef: { | |
qDimensions: [ | |
{ | |
qDef: { | |
qFieldDefs: ["Product Category"], | |
}, | |
}, | |
{ | |
qDef: { | |
qFieldDefs: ["Product Class"], | |
}, | |
}, | |
{ | |
qDef: { | |
qFieldDefs: ["Product Color"], | |
}, | |
}, | |
{ | |
qDef: { | |
qFieldDefs: [""], | |
}, | |
}, | |
], | |
qMeasures: [ | |
{ | |
qDef: { | |
qDef: "AVG([Unit Price])", | |
qAggrFunc: "Expr", | |
}, | |
qSortBy: { | |
qSortByAscii: 1, | |
qExpression: { | |
qv: "AVG([Unit Price])", | |
}, | |
}, | |
}, | |
], | |
qMode: "S", | |
qInitialDataFetch: [], | |
qSuppressZero: false, | |
qSuppressMissing: false, | |
qInterColumnSortOrder: [0, 1, 2, 4, 3], | |
qColumnOrder: [0, 1, 2, 4, 3], | |
}, | |
}, | |
qChildren: [], | |
}, | |
], | |
}) | |
); | |
// Get the layouts of the child object on invalidation | |
const childObject = updateInitialPropTree.pipe( | |
switchMapTo(app), | |
qAskReplay("GetObject", "child_1") | |
); | |
const layouts = childObject.pipe(invalidations(true), qAskReplay("GetLayout")); | |
layouts.subscribe(console.log, (err) => console.log("layout err", err)); | |
// Get the hypercube data of the child object on invalidation | |
const data = layouts.pipe( | |
switchMapTo(childObject), | |
qAskReplay("GetHyperCubeData", "/qHyperCubeDef", [ | |
{ qTop: 0, qLeft: 0, qWidth: 4, qHeight: 300 }, | |
]) | |
); | |
data.subscribe(console.log, (err) => { | |
console.log("data fetch err", err); | |
}); | |
/* | |
1. Create the intial full property tree with child object | |
2. wait a couple of seconds, or however long you want really | |
3. Try updating the full property tree with the empty dimension | |
*/ | |
concat(updateInitialPropTree, timer(2000), addEmptyDimension).subscribe(); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{ | |
"name": "hypercube-crash-bug", | |
"version": "1.0.0", | |
"description": "", | |
"main": "index.js", | |
"scripts": { | |
"test": "echo \"Error: no test specified\" && exit 1" | |
}, | |
"author": "", | |
"license": "ISC", | |
"dependencies": { | |
"rxjs": "^6.5.5", | |
"rxq": "^2.0.4" | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment