Selaa lähdekoodia

Fix race condition on add/remove query row

David Kaltschmidt 7 vuotta sitten
vanhempi
commit
a121cd0e49

+ 81 - 86
public/app/features/explore/Explore.tsx

@@ -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> {
   };
   };
 
 
   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
-    const nextQueries = [
-      ...queries.slice(0, index + 1),
-      { query: '', key: generateQueryKey() },
-      ...queries.slice(index + 1),
-    ];
+    this.setState(state => {
+      const { queries, queryTransactions } = state;
 
 
-    // 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;
-    });
+      // Add row by generating new react key
+      const nextQueries = [
+        ...queries.slice(0, index + 1),
+        { query: '', key: generateQueryKey() },
+        ...queries.slice(index + 1),
+      ];
 
 
-    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> {
     this.queryExpressions[index] = value;
     this.queryExpressions[index] = value;
 
 
     if (override) {
     if (override) {
-      // Replace query row
-      const { queries, queryTransactions } = this.state;
-      const nextQuery: Query = {
-        key: generateQueryKey(index),
-        query: value,
-      };
-      const nextQueries = [...queries];
-      nextQueries[index] = nextQuery;
+      this.setState(state => {
+        // Replace query row
+        const { queries, queryTransactions } = state;
+        const nextQuery: Query = {
+          key: generateQueryKey(index),
+          query: value,
+        };
+        const nextQueries = [...queries];
+        nextQueries[index] = nextQuery;
 
 
-      // Discard ongoing transaction related to row query
-      const nextQueryTransactions = queryTransactions.filter(qt => qt.rowIndex !== index);
+        // Discard ongoing transaction related to row query
+        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> {
   };
   };
 
 
   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(
-        {
-          queries: nextQueries,
-          queryTransactions: nextQueryTransactions,
+        state => {
+          const { queries, queryTransactions } = state;
+          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> {
   };
   };
 
 
   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
-    const nextQueries = [...queries.slice(0, index), ...queries.slice(index + 1)];
+    this.setState(
+      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
-    const nextQueryTransactions = queryTransactions.filter(qt => qt.rowIndex !== index);
+        // Discard transactions related to row query
+        const nextQueryTransactions = queryTransactions.filter(qt => qt.rowIndex !== index);
 
 
-    this.setState(
-      {
-        queries: nextQueries,
-        queryTransactions: nextQueryTransactions,
+        return {
+          queries: nextQueries,
+          queryTransactions: nextQueryTransactions,
+        };
       },
       },
       () => this.onSubmit()
       () => this.onSubmit()
     );
     );
@@ -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> {
       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> {
             <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}

+ 13 - 3
public/app/features/explore/QueryRows.tsx

@@ -1,9 +1,19 @@
 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, {}> {
   };
   };
 
 
   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, {}> {
         <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, {}> {
             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}
           />
           />
         ))}
         ))}