github.com/m3db/m3@v1.5.0/src/query/executor/engine.go (about)

     1  // Copyright (c) 2018 Uber Technologies, Inc.
     2  //
     3  // Permission is hereby granted, free of charge, to any person obtaining a copy
     4  // of this software and associated documentation files (the "Software"), to deal
     5  // in the Software without restriction, including without limitation the rights
     6  // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     7  // copies of the Software, and to permit persons to whom the Software is
     8  // furnished to do so, subject to the following conditions:
     9  //
    10  // The above copyright notice and this permission notice shall be included in
    11  // all copies or substantial portions of the Software.
    12  //
    13  // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    14  // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    15  // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    16  // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    17  // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    18  // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
    19  // THE SOFTWARE.
    20  
    21  package executor
    22  
    23  import (
    24  	"context"
    25  	"time"
    26  
    27  	"github.com/m3db/m3/src/query/block"
    28  	"github.com/m3db/m3/src/query/models"
    29  	"github.com/m3db/m3/src/query/parser"
    30  	"github.com/m3db/m3/src/query/storage"
    31  	"github.com/m3db/m3/src/x/opentracing"
    32  
    33  	"github.com/uber-go/tally"
    34  )
    35  
    36  type engine struct {
    37  	opts    EngineOptions
    38  	metrics *engineMetrics
    39  }
    40  
    41  // QueryOptions can be used to pass custom flags to engine.
    42  type QueryOptions struct {
    43  	QueryContextOptions models.QueryContextOptions
    44  }
    45  
    46  // NewEngine returns a new instance of QueryExecutor.
    47  func NewEngine(
    48  	engineOpts EngineOptions,
    49  ) Engine {
    50  	return &engine{
    51  		metrics: newEngineMetrics(engineOpts.InstrumentOptions().MetricsScope()),
    52  		opts:    engineOpts,
    53  	}
    54  }
    55  
    56  type engineMetrics struct {
    57  	all       *counterWithDecrement
    58  	compiling *counterWithDecrement
    59  	planning  *counterWithDecrement
    60  	executing *counterWithDecrement
    61  
    62  	activeHist    tally.Histogram
    63  	compilingHist tally.Histogram
    64  	planningHist  tally.Histogram
    65  	executingHist tally.Histogram
    66  }
    67  
    68  type counterWithDecrement struct {
    69  	start tally.Counter
    70  	end   tally.Counter
    71  }
    72  
    73  func (c *counterWithDecrement) Inc() {
    74  	c.start.Inc(1)
    75  }
    76  
    77  func (c *counterWithDecrement) Dec() {
    78  	c.end.Inc(1)
    79  }
    80  
    81  func newCounterWithDecrement(scope tally.Scope) *counterWithDecrement {
    82  	return &counterWithDecrement{
    83  		start: scope.Counter("start"),
    84  		end:   scope.Counter("end"),
    85  	}
    86  }
    87  
    88  func newEngineMetrics(scope tally.Scope) *engineMetrics {
    89  	durationBuckets := tally.MustMakeExponentialDurationBuckets(time.Millisecond, 10, 5)
    90  	return &engineMetrics{
    91  		all:           newCounterWithDecrement(scope.SubScope(all.String())),
    92  		compiling:     newCounterWithDecrement(scope.SubScope(compiling.String())),
    93  		planning:      newCounterWithDecrement(scope.SubScope(planning.String())),
    94  		executing:     newCounterWithDecrement(scope.SubScope(executing.String())),
    95  		activeHist:    scope.Histogram(all.durationString(), durationBuckets),
    96  		compilingHist: scope.Histogram(compiling.durationString(), durationBuckets),
    97  		planningHist:  scope.Histogram(planning.durationString(), durationBuckets),
    98  		executingHist: scope.Histogram(executing.durationString(), durationBuckets),
    99  	}
   100  }
   101  
   102  func (e *engine) ExecuteProm(
   103  	ctx context.Context,
   104  	query *storage.FetchQuery,
   105  	opts *QueryOptions,
   106  	fetchOpts *storage.FetchOptions,
   107  ) (storage.PromResult, error) {
   108  	return e.opts.Store().FetchProm(ctx, query, fetchOpts)
   109  }
   110  
   111  func (e *engine) ExecuteExpr(
   112  	ctx context.Context,
   113  	parser parser.Parser,
   114  	opts *QueryOptions,
   115  	fetchOpts *storage.FetchOptions,
   116  	params models.RequestParams,
   117  ) (block.Block, error) {
   118  	req := newRequest(e, params, fetchOpts, e.opts.InstrumentOptions())
   119  	nodes, edges, err := req.compile(ctx, parser)
   120  	if err != nil {
   121  		return nil, err
   122  	}
   123  
   124  	pp, err := req.plan(ctx, nodes, edges)
   125  	if err != nil {
   126  		return nil, err
   127  	}
   128  
   129  	state, err := req.generateExecutionState(ctx, pp)
   130  	if err != nil {
   131  		return nil, err
   132  	}
   133  
   134  	// free up resources
   135  	sp, ctx := opentracing.StartSpanFromContext(ctx, "executing")
   136  	defer sp.Finish()
   137  
   138  	scope := e.opts.InstrumentOptions().MetricsScope()
   139  	queryCtx := models.NewQueryContext(ctx, scope,
   140  		opts.QueryContextOptions)
   141  
   142  	if err := state.Execute(queryCtx); err != nil {
   143  		state.sink.closeWithError(err)
   144  		return nil, err
   145  	}
   146  
   147  	return state.sink.getValue()
   148  }
   149  
   150  func (e *engine) Options() EngineOptions {
   151  	return e.opts
   152  }
   153  
   154  func (e *engine) Close() error {
   155  	return nil
   156  }