Commit a121cd0e by David Kaltschmidt

Fix race condition on add/remove query row

parent f19ffee5
...@@ -33,16 +33,6 @@ import { ensureQueries, generateQueryKey, hasQuery } from './utils/query'; ...@@ -33,16 +33,6 @@ import { ensureQueries, generateQueryKey, hasQuery } from './utils/query';
const MAX_HISTORY_ITEMS = 100; const MAX_HISTORY_ITEMS = 100;
function makeHints(transactions: QueryTransaction[]) {
const hintsByIndex = [];
transactions.forEach(qt => {
if (qt.hints && qt.hints.length > 0) {
hintsByIndex[qt.rowIndex] = qt.hints[0];
}
});
return hintsByIndex;
}
function makeTimeSeriesList(dataList, options) { function makeTimeSeriesList(dataList, options) {
return dataList.map((seriesData, index) => { return dataList.map((seriesData, index) => {
const datapoints = seriesData.datapoints || []; const datapoints = seriesData.datapoints || [];
...@@ -222,30 +212,32 @@ export class Explore extends React.PureComponent<ExploreProps, ExploreState> { ...@@ -222,30 +212,32 @@ export class Explore extends React.PureComponent<ExploreProps, ExploreState> {
}; };
onAddQueryRow = index => { onAddQueryRow = index => {
const { queries, queryTransactions } = this.state;
// Local cache // Local cache
this.queryExpressions[index + 1] = ''; this.queryExpressions[index + 1] = '';
// Add row by generating new react key this.setState(state => {
const nextQueries = [ const { queries, queryTransactions } = state;
...queries.slice(0, index + 1),
{ query: '', key: generateQueryKey() },
...queries.slice(index + 1),
];
// Ongoing transactions need to update their row indices // Add row by generating new react key
const nextQueryTransactions = queryTransactions.map(qt => { const nextQueries = [
if (qt.rowIndex > index) { ...queries.slice(0, index + 1),
return { { query: '', key: generateQueryKey() },
...qt, ...queries.slice(index + 1),
rowIndex: qt.rowIndex + 1, ];
};
}
return qt;
});
this.setState({ queries: nextQueries, queryTransactions: nextQueryTransactions }); // Ongoing transactions need to update their row indices
const nextQueryTransactions = queryTransactions.map(qt => {
if (qt.rowIndex > index) {
return {
...qt,
rowIndex: qt.rowIndex + 1,
};
}
return qt;
});
return { queries: nextQueries, queryTransactions: nextQueryTransactions };
});
}; };
onChangeDatasource = async option => { onChangeDatasource = async option => {
...@@ -265,25 +257,24 @@ export class Explore extends React.PureComponent<ExploreProps, ExploreState> { ...@@ -265,25 +257,24 @@ export class Explore extends React.PureComponent<ExploreProps, ExploreState> {
this.queryExpressions[index] = value; this.queryExpressions[index] = value;
if (override) { if (override) {
// Replace query row this.setState(state => {
const { queries, queryTransactions } = this.state; // Replace query row
const nextQuery: Query = { const { queries, queryTransactions } = state;
key: generateQueryKey(index), const nextQuery: Query = {
query: value, key: generateQueryKey(index),
}; query: value,
const nextQueries = [...queries]; };
nextQueries[index] = nextQuery; const nextQueries = [...queries];
nextQueries[index] = nextQuery;
// Discard ongoing transaction related to row query // Discard ongoing transaction related to row query
const nextQueryTransactions = queryTransactions.filter(qt => qt.rowIndex !== index); const nextQueryTransactions = queryTransactions.filter(qt => qt.rowIndex !== index);
this.setState( return {
{
queries: nextQueries, queries: nextQueries,
queryTransactions: nextQueryTransactions, queryTransactions: nextQueryTransactions,
}, };
this.onSubmit }, this.onSubmit);
);
} }
}; };
...@@ -383,36 +374,39 @@ export class Explore extends React.PureComponent<ExploreProps, ExploreState> { ...@@ -383,36 +374,39 @@ export class Explore extends React.PureComponent<ExploreProps, ExploreState> {
}; };
onModifyQueries = (action: object, index?: number) => { onModifyQueries = (action: object, index?: number) => {
const { datasource, queries, queryTransactions } = this.state; const { datasource } = this.state;
if (datasource && datasource.modifyQuery) { if (datasource && datasource.modifyQuery) {
let nextQueries;
let nextQueryTransactions;
if (index === undefined) {
// Modify all queries
nextQueries = queries.map((q, i) => ({
key: generateQueryKey(i),
query: datasource.modifyQuery(this.queryExpressions[i], action),
}));
// Discard all ongoing transactions
nextQueryTransactions = [];
} else {
// Modify query only at index
nextQueries = [
...queries.slice(0, index),
{
key: generateQueryKey(index),
query: datasource.modifyQuery(this.queryExpressions[index], action),
},
...queries.slice(index + 1),
];
// Discard transactions related to row query
nextQueryTransactions = queryTransactions.filter(qt => qt.rowIndex !== index);
}
this.queryExpressions = nextQueries.map(q => q.query);
this.setState( this.setState(
{ state => {
queries: nextQueries, const { queries, queryTransactions } = state;
queryTransactions: nextQueryTransactions, let nextQueries;
let nextQueryTransactions;
if (index === undefined) {
// Modify all queries
nextQueries = queries.map((q, i) => ({
key: generateQueryKey(i),
query: datasource.modifyQuery(this.queryExpressions[i], action),
}));
// Discard all ongoing transactions
nextQueryTransactions = [];
} else {
// Modify query only at index
nextQueries = [
...queries.slice(0, index),
{
key: generateQueryKey(index),
query: datasource.modifyQuery(this.queryExpressions[index], action),
},
...queries.slice(index + 1),
];
// Discard transactions related to row query
nextQueryTransactions = queryTransactions.filter(qt => qt.rowIndex !== index);
}
this.queryExpressions = nextQueries.map(q => q.query);
return {
queries: nextQueries,
queryTransactions: nextQueryTransactions,
};
}, },
() => this.onSubmit() () => this.onSubmit()
); );
...@@ -420,23 +414,25 @@ export class Explore extends React.PureComponent<ExploreProps, ExploreState> { ...@@ -420,23 +414,25 @@ export class Explore extends React.PureComponent<ExploreProps, ExploreState> {
}; };
onRemoveQueryRow = index => { onRemoveQueryRow = index => {
const { queries, queryTransactions } = this.state;
if (queries.length <= 1) {
return;
}
// Remove from local cache // Remove from local cache
this.queryExpressions = [...this.queryExpressions.slice(0, index), ...this.queryExpressions.slice(index + 1)]; this.queryExpressions = [...this.queryExpressions.slice(0, index), ...this.queryExpressions.slice(index + 1)];
// Remove row from react state this.setState(
const nextQueries = [...queries.slice(0, index), ...queries.slice(index + 1)]; state => {
const { queries, queryTransactions } = state;
if (queries.length <= 1) {
return null;
}
// Remove row from react state
const nextQueries = [...queries.slice(0, index), ...queries.slice(index + 1)];
// Discard transactions related to row query // Discard transactions related to row query
const nextQueryTransactions = queryTransactions.filter(qt => qt.rowIndex !== index); const nextQueryTransactions = queryTransactions.filter(qt => qt.rowIndex !== index);
this.setState( return {
{ queries: nextQueries,
queries: nextQueries, queryTransactions: nextQueryTransactions,
queryTransactions: nextQueryTransactions, };
}, },
() => this.onSubmit() () => this.onSubmit()
); );
...@@ -708,6 +704,7 @@ export class Explore extends React.PureComponent<ExploreProps, ExploreState> { ...@@ -708,6 +704,7 @@ export class Explore extends React.PureComponent<ExploreProps, ExploreState> {
// Copy state, but copy queries including modifications // Copy state, but copy queries including modifications
return { return {
...this.state, ...this.state,
queryTransactions: [],
queries: ensureQueries(this.queryExpressions.map(query => ({ query }))), queries: ensureQueries(this.queryExpressions.map(query => ({ query }))),
}; };
} }
...@@ -758,7 +755,6 @@ export class Explore extends React.PureComponent<ExploreProps, ExploreState> { ...@@ -758,7 +755,6 @@ export class Explore extends React.PureComponent<ExploreProps, ExploreState> {
queryTransactions.filter(qt => qt.resultType === 'Logs' && qt.done).map(qt => qt.result) queryTransactions.filter(qt => qt.resultType === 'Logs' && qt.done).map(qt => qt.result)
); );
const loading = queryTransactions.some(qt => !qt.done); const loading = queryTransactions.some(qt => !qt.done);
const queryHints = makeHints(queryTransactions);
return ( return (
<div className={exploreClass} ref={this.getRef}> <div className={exploreClass} ref={this.getRef}>
...@@ -837,7 +833,6 @@ export class Explore extends React.PureComponent<ExploreProps, ExploreState> { ...@@ -837,7 +833,6 @@ export class Explore extends React.PureComponent<ExploreProps, ExploreState> {
<QueryRows <QueryRows
history={history} history={history}
queries={queries} queries={queries}
queryHints={queryHints}
request={this.request} request={this.request}
onAddQueryRow={this.onAddQueryRow} onAddQueryRow={this.onAddQueryRow}
onChangeQuery={this.onChangeQuery} onChangeQuery={this.onChangeQuery}
......
import React, { PureComponent } from 'react'; import React, { PureComponent } from 'react';
import { QueryTransaction } from 'app/types/explore';
// TODO make this datasource-plugin-dependent // TODO make this datasource-plugin-dependent
import QueryField from './PromQueryField'; import QueryField from './PromQueryField';
import QueryTransactions from './QueryTransactions'; import QueryTransactions from './QueryTransactions';
function getFirstHintFromTransactions(transactions: QueryTransaction[]) {
const transaction = transactions.find(qt => qt.hints && qt.hints.length > 0);
if (transaction) {
return transaction.hints[0];
}
return undefined;
}
class QueryRow extends PureComponent<any, {}> { class QueryRow extends PureComponent<any, {}> {
onChangeQuery = (value, override?: boolean) => { onChangeQuery = (value, override?: boolean) => {
const { index, onChangeQuery } = this.props; const { index, onChangeQuery } = this.props;
...@@ -45,8 +55,9 @@ class QueryRow extends PureComponent<any, {}> { ...@@ -45,8 +55,9 @@ class QueryRow extends PureComponent<any, {}> {
}; };
render() { render() {
const { history, query, queryHint, request, supportsLogs, transactions } = this.props; const { history, query, request, supportsLogs, transactions } = this.props;
const transactionWithError = transactions.find(t => t.error); const transactionWithError = transactions.find(t => t.error);
const hint = getFirstHintFromTransactions(transactions);
const queryError = transactionWithError ? transactionWithError.error : null; const queryError = transactionWithError ? transactionWithError.error : null;
return ( return (
<div className="query-row"> <div className="query-row">
...@@ -56,7 +67,7 @@ class QueryRow extends PureComponent<any, {}> { ...@@ -56,7 +67,7 @@ class QueryRow extends PureComponent<any, {}> {
<div className="query-row-field"> <div className="query-row-field">
<QueryField <QueryField
error={queryError} error={queryError}
hint={queryHint} hint={hint}
initialQuery={query} initialQuery={query}
history={history} history={history}
onClickHintFix={this.onClickHintFix} onClickHintFix={this.onClickHintFix}
...@@ -93,7 +104,6 @@ export default class QueryRows extends PureComponent<any, {}> { ...@@ -93,7 +104,6 @@ export default class QueryRows extends PureComponent<any, {}> {
index={index} index={index}
query={q.query} query={q.query}
transactions={transactions.filter(t => t.rowIndex === index)} transactions={transactions.filter(t => t.rowIndex === index)}
queryHint={queryHints[index]}
{...handlers} {...handlers}
/> />
))} ))}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment