diff --git a/docs/index.rst b/docs/index.rst
index 6a592fe70..d1f0e5813 100644
--- a/docs/index.rst
+++ b/docs/index.rst
@@ -164,6 +164,7 @@ Contents
gallery
druid
misc
+ issue_code_reference
faq
diff --git a/docs/issue_code_reference.rst b/docs/issue_code_reference.rst
new file mode 100644
index 000000000..ef89d1e51
--- /dev/null
+++ b/docs/issue_code_reference.rst
@@ -0,0 +1,39 @@
+.. Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+.. http://www.apache.org/licenses/LICENSE-2.0
+
+.. Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+
+Issue Code Reference
+====================
+
+This page lists issue codes that may be displayed in Superset and provides additional context.
+
+Issue 1000
+""""""""""
+
+.. code-block:: text
+
+ The datasource is too large to query.
+
+It's likely your datasource has grown too large to run the current query, and is timing out. You can resolve this by reducing the size of your datasource or by modifying your query to only process a subset of your data.
+
+Issue 1001
+""""""""""
+
+.. code-block:: text
+
+ The database is under an unusual load.
+
+Your query may have timed out because of unusually high load on the database engine. You can make your query simpler, or wait until the database is under less load and try again.
diff --git a/superset-frontend/images/icons/close.svg b/superset-frontend/images/icons/close.svg
new file mode 100644
index 000000000..33448814e
--- /dev/null
+++ b/superset-frontend/images/icons/close.svg
@@ -0,0 +1,21 @@
+
+
diff --git a/superset-frontend/spec/javascripts/chart/chartActions_spec.js b/superset-frontend/spec/javascripts/chart/chartActions_spec.js
index a5258854c..13710744d 100644
--- a/superset-frontend/spec/javascripts/chart/chartActions_spec.js
+++ b/superset-frontend/spec/javascripts/chart/chartActions_spec.js
@@ -156,7 +156,7 @@ describe('chart actions', () => {
});
});
- it('should CHART_UPDATE_TIMEOUT action upon query timeout', () => {
+ it('should dispatch CHART_UPDATE_FAILED action upon query timeout', () => {
const unresolvingPromise = new Promise(() => {});
fetchMock.post(MOCK_URL, () => unresolvingPromise, {
overwriteRoutes: true,
@@ -169,7 +169,7 @@ describe('chart actions', () => {
// chart update, trigger query, update form data, fail
expect(fetchMock.calls(MOCK_URL)).toHaveLength(1);
expect(dispatch.callCount).toBe(5);
- expect(dispatch.args[4][0].type).toBe(actions.CHART_UPDATE_TIMEOUT);
+ expect(dispatch.args[4][0].type).toBe(actions.CHART_UPDATE_FAILED);
setupDefaultFetchMock();
});
});
diff --git a/superset-frontend/spec/javascripts/chart/chartReducers_spec.js b/superset-frontend/spec/javascripts/chart/chartReducers_spec.js
index 0ecd7abcb..f72ca40b8 100644
--- a/superset-frontend/spec/javascripts/chart/chartReducers_spec.js
+++ b/superset-frontend/spec/javascripts/chart/chartReducers_spec.js
@@ -40,7 +40,21 @@ describe('chart reducers', () => {
it('should update endtime on timeout', () => {
const newState = chartReducer(
charts,
- actions.chartUpdateTimeout('timeout', 60, chartKey),
+ actions.chartUpdateFailed(
+ {
+ statusText: 'timeout',
+ error: 'Request timed out',
+ errors: [
+ {
+ error_type: 'FRONTEND_TIMEOUT_ERROR',
+ extra: { timeout: 1 },
+ level: 'error',
+ message: 'Request timed out',
+ },
+ ],
+ },
+ chartKey,
+ ),
);
expect(newState[chartKey].chartUpdateEndTime).toBeGreaterThan(0);
expect(newState[chartKey].chartStatus).toEqual('failed');
diff --git a/superset-frontend/src/SqlLab/components/ResultSet.tsx b/superset-frontend/src/SqlLab/components/ResultSet.tsx
index dd2dd6f00..db154fba4 100644
--- a/superset-frontend/src/SqlLab/components/ResultSet.tsx
+++ b/superset-frontend/src/SqlLab/components/ResultSet.tsx
@@ -217,11 +217,14 @@ export default class ResultSet extends React.PureComponent<
return Query was stopped;
} else if (query.state === 'failed') {
return (
-
+
+
+
);
} else if (query.state === 'success' && query.ctas) {
const { tempSchema, tempTable } = query;
diff --git a/superset-frontend/src/SqlLab/main.less b/superset-frontend/src/SqlLab/main.less
index 7b3fb9c76..b09884d8f 100644
--- a/superset-frontend/src/SqlLab/main.less
+++ b/superset-frontend/src/SqlLab/main.less
@@ -409,6 +409,10 @@ div.tablePopover {
padding-right: 8px;
}
+.result-set-error-message {
+ padding-top: 16px;
+}
+
.filterable-table-container {
margin-top: 48px;
}
diff --git a/superset-frontend/src/chart/Chart.jsx b/superset-frontend/src/chart/Chart.jsx
index 956230446..ff5a57076 100644
--- a/superset-frontend/src/chart/Chart.jsx
+++ b/superset-frontend/src/chart/Chart.jsx
@@ -49,6 +49,7 @@ const propTypes = {
timeout: PropTypes.number,
vizType: PropTypes.string.isRequired,
triggerRender: PropTypes.bool,
+ owners: PropTypes.arrayOf(PropTypes.string),
// state
chartAlert: PropTypes.string,
chartStatus: PropTypes.string,
@@ -139,12 +140,26 @@ class Chart extends React.PureComponent {
}
renderErrorMessage() {
- const { chartAlert, chartStackTrace, queryResponse } = this.props;
+ const {
+ chartAlert,
+ chartStackTrace,
+ dashboardId,
+ owners,
+ queryResponse,
+ } = this.props;
+
+ const error = queryResponse?.errors?.[0];
+ if (error) {
+ const extra = error.extra || {};
+ extra.owners = owners;
+ error.extra = extra;
+ }
return (
);
diff --git a/superset-frontend/src/chart/chartAction.js b/superset-frontend/src/chart/chartAction.js
index 67f83b59c..1be26264a 100644
--- a/superset-frontend/src/chart/chartAction.js
+++ b/superset-frontend/src/chart/chartAction.js
@@ -62,11 +62,6 @@ export function chartUpdateStopped(key) {
return { type: CHART_UPDATE_STOPPED, key };
}
-export const CHART_UPDATE_TIMEOUT = 'CHART_UPDATE_TIMEOUT';
-export function chartUpdateTimeout(statusText, timeout, key) {
- return { type: CHART_UPDATE_TIMEOUT, statusText, timeout, key };
-}
-
export const CHART_UPDATE_FAILED = 'CHART_UPDATE_FAILED';
export function chartUpdateFailed(queryResponse, key) {
return { type: CHART_UPDATE_FAILED, queryResponse, key };
@@ -391,19 +386,16 @@ export function exploreJSON(
}),
);
};
-
- if (response.statusText === 'timeout') {
- appendErrorLog('timeout');
- return dispatch(
- chartUpdateTimeout(response.statusText, timeout, key),
- );
- } else if (response.name === 'AbortError') {
+ if (response.name === 'AbortError') {
appendErrorLog('abort');
return dispatch(chartUpdateStopped(key));
}
return getClientErrorObject(response).then(parsedResponse => {
- // query is processed, but error out.
- appendErrorLog(parsedResponse.error, parsedResponse.is_cached);
+ if (response.statusText === 'timeout') {
+ appendErrorLog('timeout');
+ } else {
+ appendErrorLog(parsedResponse.error, parsedResponse.is_cached);
+ }
return dispatch(chartUpdateFailed(parsedResponse, key));
});
});
diff --git a/superset-frontend/src/chart/chartReducer.js b/superset-frontend/src/chart/chartReducer.js
index d3e22365c..c50c0074b 100644
--- a/superset-frontend/src/chart/chartReducer.js
+++ b/superset-frontend/src/chart/chartReducer.js
@@ -85,21 +85,6 @@ export default function chartReducer(charts = {}, action) {
),
};
},
- [actions.CHART_UPDATE_TIMEOUT](state) {
- return {
- ...state,
- chartStatus: 'failed',
- chartAlert: `${t('Query timeout')} - ${t(
- `visualization queries are set to timeout at ${action.timeout} seconds. `,
- )}${t(
- 'Perhaps your data has grown, your database is under unusual load, ' +
- 'or you are simply querying a data source that is too large ' +
- 'to be processed within the timeout range. ' +
- 'If that is the case, we recommend that you summarize your data further.',
- )}`,
- chartUpdateEndTime: now(),
- };
- },
[actions.CHART_UPDATE_FAILED](state) {
return {
...state,
diff --git a/superset-frontend/src/components/ErrorMessage/ErrorMessageWithStackTrace.tsx b/superset-frontend/src/components/ErrorMessage/ErrorMessageWithStackTrace.tsx
index 5f3f6794e..04befca01 100644
--- a/superset-frontend/src/components/ErrorMessage/ErrorMessageWithStackTrace.tsx
+++ b/superset-frontend/src/components/ErrorMessage/ErrorMessageWithStackTrace.tsx
@@ -22,13 +22,14 @@ import { Alert, Collapse } from 'react-bootstrap';
import { t } from '@superset-ui/translation';
import getErrorMessageComponentRegistry from './getErrorMessageComponentRegistry';
-import { SupersetError } from './types';
+import { SupersetError, ErrorSource } from './types';
type Props = {
error?: SupersetError;
link?: string;
message?: string;
stackTrace?: string;
+ source?: ErrorSource;
};
export default function ErrorMessageWithStackTrace({
@@ -36,6 +37,7 @@ export default function ErrorMessageWithStackTrace({
message,
link,
stackTrace,
+ source,
}: Props) {
const [showStackTrace, setShowStackTrace] = useState(false);
@@ -45,7 +47,7 @@ export default function ErrorMessageWithStackTrace({
error.error_type,
);
if (ErrorMessageComponent) {
- return ;
+ return ;
}
}
diff --git a/superset-frontend/src/components/ErrorMessage/IssueCode.tsx b/superset-frontend/src/components/ErrorMessage/IssueCode.tsx
new file mode 100644
index 000000000..37543415f
--- /dev/null
+++ b/superset-frontend/src/components/ErrorMessage/IssueCode.tsx
@@ -0,0 +1,39 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import React from 'react';
+
+interface IssueCodeProps {
+ code: number;
+ message: string;
+}
+
+export default function IssueCode({ code, message }: IssueCodeProps) {
+ return (
+ <>
+ {message}{' '}
+
+
+
+ >
+ );
+}
diff --git a/superset-frontend/src/components/ErrorMessage/TimeoutErrorMessage.tsx b/superset-frontend/src/components/ErrorMessage/TimeoutErrorMessage.tsx
new file mode 100644
index 000000000..2b7ff01bb
--- /dev/null
+++ b/superset-frontend/src/components/ErrorMessage/TimeoutErrorMessage.tsx
@@ -0,0 +1,248 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import React, { useState } from 'react';
+import { Modal } from 'react-bootstrap';
+import { styled, supersetTheme } from '@superset-ui/style';
+import { t, tn } from '@superset-ui/translation';
+
+import { noOp } from 'src/utils/common';
+import Icon from '../Icon';
+import Button from '../../views/datasetList/Button';
+import { ErrorMessageComponentProps } from './types';
+import CopyToClipboard from '../CopyToClipboard';
+import IssueCode from './IssueCode';
+
+const ErrorAlert = styled.div`
+ align-items: center;
+ background-color: ${({ theme }) => theme.colors.error.light2};
+ border-radius: ${({ theme }) => theme.borderRadius}px;
+ border: 1px solid ${({ theme }) => theme.colors.error.base};
+ color: ${({ theme }) => theme.colors.error.dark2};
+ padding: ${({ theme }) => 2 * theme.gridUnit}px;
+ width: 100%;
+
+ .top-row {
+ display: flex;
+ justify-content: space-between;
+ }
+
+ .error-body {
+ padding-top: ${({ theme }) => theme.gridUnit}px;
+ padding-left: ${({ theme }) => 8 * theme.gridUnit}px;
+ }
+
+ .icon {
+ margin-right: ${({ theme }) => 2 * theme.gridUnit}px;
+ }
+
+ .link {
+ color: ${({ theme }) => theme.colors.error.dark2};
+ text-decoration: underline;
+ }
+`;
+
+const ErrorModal = styled(Modal)`
+ color: ${({ theme }) => theme.colors.error.dark2};
+
+ .icon {
+ margin-right: ${({ theme }) => 2 * theme.gridUnit}px;
+ }
+
+ .header {
+ align-items: center;
+ background-color: ${({ theme }) => theme.colors.error.light2};
+ display: flex;
+ justify-content: space-between;
+ font-size: ${({ theme }) => theme.typography.sizes.l}px;
+
+ // Remove clearfix hack as Superset is only used on modern browsers
+ ::before,
+ ::after {
+ content: unset;
+ }
+ }
+`;
+
+const LeftSideContent = styled.div`
+ align-items: center;
+ display: flex;
+`;
+
+interface TimeoutErrorExtra {
+ issue_codes: {
+ code: number;
+ message: string;
+ }[];
+ owners?: string[];
+ timeout: number;
+}
+
+function TimeoutErrorMessage({
+ error,
+ source,
+}: ErrorMessageComponentProps) {
+ const [isModalOpen, setIsModalOpen] = useState(false);
+ const [isMessageExpanded, setIsMessageExpanded] = useState(false);
+ const { extra } = error;
+
+ const isVisualization = (['dashboard', 'explore'] as (
+ | string
+ | undefined
+ )[]).includes(source);
+
+ const isExpandable = (['explore', 'sqllab'] as (
+ | string
+ | undefined
+ )[]).includes(source);
+
+ const title = isVisualization
+ ? tn(
+ 'We’re having trouble loading this visualization. Queries are set to timeout after %s second.',
+ 'We’re having trouble loading this visualization. Queries are set to timeout after %s seconds.',
+ extra.timeout,
+ extra.timeout,
+ )
+ : tn(
+ 'We’re having trouble loading these results. Queries are set to timeout after %s second.',
+ 'We’re having trouble loading these results. Queries are set to timeout after %s seconds.',
+ extra.timeout,
+ extra.timeout,
+ );
+
+ const message = (
+ <>
+
+ {tn(
+ 'Please reach out to the Chart Owner for assistance.',
+ 'Please reach out to the Chart Owners for assistance.',
+ extra.owners.length,
+ )}
+