perf(sqllab): Rendering perf improvement using immutable state (#20877)

* perf(sqllab): Rendering perf improvement using immutable state

- keep queryEditors immutable during active state
- add unsavedQueryEditor to store all active changes
- refactor each component to subscribe the related unsaved editor state only

* revert ISaveableDatasource type cast

* missing trigger prop

* a default of an empty object and optional operator
This commit is contained in:
JUST.in DO IT
2022-08-23 08:17:19 -07:00
committed by GitHub
parent 4ca4a5c7cb
commit f77b910e2c
32 changed files with 1929 additions and 606 deletions

View File

@@ -123,6 +123,17 @@ const fieldConverter = mapping => obj =>
const convertQueryToServer = fieldConverter(queryServerMapping);
const convertQueryToClient = fieldConverter(queryClientMapping);
export function getUpToDateQuery(rootState, queryEditor, key) {
const {
sqlLab: { unsavedQueryEditor },
} = rootState;
const id = key ?? queryEditor.id;
return {
...queryEditor,
...(id === unsavedQueryEditor.id && unsavedQueryEditor),
};
}
export function resetState() {
return { type: RESET_STATE };
}
@@ -167,24 +178,26 @@ export function scheduleQuery(query) {
);
}
export function estimateQueryCost(query) {
const { dbId, schema, sql, templateParams } = query;
const endpoint =
schema === null
? `/superset/estimate_query_cost/${dbId}/`
: `/superset/estimate_query_cost/${dbId}/${schema}/`;
return dispatch =>
Promise.all([
dispatch({ type: COST_ESTIMATE_STARTED, query }),
export function estimateQueryCost(queryEditor) {
return (dispatch, getState) => {
const { dbId, schema, sql, selectedText, templateParams } =
getUpToDateQuery(getState(), queryEditor);
const requestSql = selectedText || sql;
const endpoint =
schema === null
? `/superset/estimate_query_cost/${dbId}/`
: `/superset/estimate_query_cost/${dbId}/${schema}/`;
return Promise.all([
dispatch({ type: COST_ESTIMATE_STARTED, query: queryEditor }),
SupersetClient.post({
endpoint,
postPayload: {
sql,
sql: requestSql,
templateParams: JSON.parse(templateParams || '{}'),
},
})
.then(({ json }) =>
dispatch({ type: COST_ESTIMATE_RETURNED, query, json }),
dispatch({ type: COST_ESTIMATE_RETURNED, query: queryEditor, json }),
)
.catch(response =>
getClientErrorObject(response).then(error => {
@@ -194,12 +207,13 @@ export function estimateQueryCost(query) {
t('Failed at retrieving results');
return dispatch({
type: COST_ESTIMATE_FAILED,
query,
query: queryEditor,
error: message,
});
}),
),
]);
};
}
export function startQuery(query) {
@@ -357,6 +371,34 @@ export function runQuery(query) {
};
}
export function runQueryFromSqlEditor(
database,
queryEditor,
defaultQueryLimit,
tempTable,
ctas,
ctasMethod,
) {
return function (dispatch, getState) {
const qe = getUpToDateQuery(getState(), queryEditor, queryEditor.id);
const query = {
dbId: qe.dbId,
sql: qe.selectedText || qe.sql,
sqlEditorId: qe.id,
tab: qe.name,
schema: qe.schema,
tempTable,
templateParams: qe.templateParams,
queryLimit: qe.queryLimit || defaultQueryLimit,
runAsync: database ? database.allow_run_async : false,
ctas,
ctas_method: ctasMethod,
updateTabState: !qe.selectedText,
};
dispatch(runQuery(query));
};
}
export function reRunQuery(query) {
// run Query with a new id
return function (dispatch) {
@@ -364,8 +406,23 @@ export function reRunQuery(query) {
};
}
export function validateQuery(query) {
return function (dispatch) {
export function validateQuery(queryEditor, sql) {
return function (dispatch, getState) {
const {
sqlLab: { unsavedQueryEditor },
} = getState();
const qe = {
...queryEditor,
...(queryEditor.id === unsavedQueryEditor.id && unsavedQueryEditor),
};
const query = {
dbId: qe.dbId,
sql,
sqlEditorId: qe.id,
schema: qe.schema,
templateParams: qe.templateParams,
};
dispatch(startQueryValidation(query));
const postPayload = {
@@ -620,6 +677,7 @@ export function switchQueryEditor(queryEditor, displayLimit) {
return function (dispatch) {
if (
isFeatureEnabled(FeatureFlag.SQLLAB_BACKEND_PERSISTENCE) &&
queryEditor &&
!queryEditor.loaded
) {
SupersetClient.get({
@@ -723,6 +781,17 @@ export function removeQueryEditor(queryEditor) {
};
}
export function removeAllOtherQueryEditors(queryEditor) {
return function (dispatch, getState) {
const { sqlLab } = getState();
sqlLab.queryEditors?.forEach(otherQueryEditor => {
if (otherQueryEditor.id !== queryEditor.id) {
dispatch(removeQueryEditor(otherQueryEditor));
}
});
};
}
export function removeQuery(query) {
return function (dispatch) {
const sync = isFeatureEnabled(FeatureFlag.SQLLAB_BACKEND_PERSISTENCE)
@@ -921,8 +990,9 @@ export function queryEditorSetSql(queryEditor, sql) {
return { type: QUERY_EDITOR_SET_SQL, queryEditor, sql };
}
export function queryEditorSetAndSaveSql(queryEditor, sql) {
return function (dispatch) {
export function queryEditorSetAndSaveSql(targetQueryEditor, sql) {
return function (dispatch, getState) {
const queryEditor = getUpToDateQuery(getState(), targetQueryEditor);
// saved query and set tab state use this action
dispatch(queryEditorSetSql(queryEditor, sql));
if (isFeatureEnabled(FeatureFlag.SQLLAB_BACKEND_PERSISTENCE)) {