github.com/m3db/m3@v1.5.1-0.20231129193456-75a402aa583b/src/query/executor/state.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 "fmt" 26 27 "github.com/m3db/m3/src/query/executor/transform" 28 "github.com/m3db/m3/src/query/models" 29 "github.com/m3db/m3/src/query/parser" 30 "github.com/m3db/m3/src/query/plan" 31 "github.com/m3db/m3/src/query/storage" 32 "github.com/m3db/m3/src/query/util/execution" 33 "github.com/m3db/m3/src/x/instrument" 34 35 "github.com/pkg/errors" 36 ) 37 38 // ExecutionState represents the execution hierarchy. 39 type ExecutionState struct { 40 plan plan.PhysicalPlan 41 sources []parser.Source 42 sink sink 43 storage storage.Storage 44 } 45 46 // CreateSource creates a source node. 47 func CreateSource( 48 ID parser.NodeID, 49 params SourceParams, storage storage.Storage, 50 options transform.Options, 51 ) (parser.Source, *transform.Controller) { 52 controller := &transform.Controller{ID: ID} 53 return params.Node(controller, storage, options), controller 54 } 55 56 // CreateScalarSource creates a scalar source node. 57 func CreateScalarSource( 58 ID parser.NodeID, 59 params ScalarParams, 60 options transform.Options, 61 ) (parser.Source, *transform.Controller) { 62 controller := &transform.Controller{ID: ID} 63 return params.Node(controller, options), controller 64 } 65 66 // CreateTransform creates a transform node which works on functions and 67 // contains state. 68 func CreateTransform( 69 ID parser.NodeID, 70 params transform.Params, 71 options transform.Options, 72 ) (transform.OpNode, *transform.Controller) { 73 controller := &transform.Controller{ID: ID} 74 return params.Node(controller, options), controller 75 } 76 77 // SourceParams are defined by sources. 78 type SourceParams interface { 79 parser.Params 80 Node(ctrl *transform.Controller, storage storage.Storage, 81 opts transform.Options) parser.Source 82 } 83 84 // ScalarParams are defined by sources. 85 type ScalarParams interface { 86 parser.Params 87 Node(ctrl *transform.Controller, opts transform.Options) parser.Source 88 } 89 90 // GenerateExecutionState creates an execution state from the physical plan. 91 func GenerateExecutionState( 92 pplan plan.PhysicalPlan, 93 storage storage.Storage, 94 fetchOpts *storage.FetchOptions, 95 instrumentOpts instrument.Options, 96 ) (*ExecutionState, error) { 97 result := pplan.ResultStep 98 state := &ExecutionState{ 99 plan: pplan, 100 storage: storage, 101 } 102 103 step, ok := pplan.Step(result.Parent) 104 if !ok { 105 return nil, fmt.Errorf("incorrect parent reference in result node, "+ 106 "parentId: %s", result.Parent) 107 } 108 109 options, err := transform.NewOptions(transform.OptionsParams{ 110 FetchOptions: fetchOpts, 111 TimeSpec: pplan.TimeSpec, 112 Debug: pplan.Debug, 113 BlockType: pplan.BlockType, 114 InstrumentOptions: instrumentOpts, 115 }) 116 if err != nil { 117 return nil, err 118 } 119 120 controller, err := state.createNode(step, options) 121 if err != nil { 122 return nil, err 123 } 124 125 if len(state.sources) == 0 { 126 return nil, errors.New("empty sources for the execution state") 127 } 128 129 sink := newResultNode() 130 state.sink = sink 131 controller.AddTransform(sink) 132 133 return state, nil 134 } 135 136 // createNode helps to create an execution node recursively. 137 func (s *ExecutionState) createNode( 138 step plan.LogicalStep, 139 options transform.Options, 140 ) (*transform.Controller, error) { 141 // TODO: consider using a registry instead of casting to an interface. 142 sourceParams, ok := step.Transform.Op.(SourceParams) 143 if ok { 144 source, controller := CreateSource(step.ID(), sourceParams, 145 s.storage, options) 146 s.sources = append(s.sources, source) 147 return controller, nil 148 } 149 150 scalarParams, ok := step.Transform.Op.(ScalarParams) 151 if ok { 152 source, controller := CreateScalarSource(step.ID(), scalarParams, options) 153 s.sources = append(s.sources, source) 154 return controller, nil 155 } 156 157 transformParams, ok := step.Transform.Op.(transform.Params) 158 if !ok { 159 return nil, fmt.Errorf("invalid transform step: %s", step) 160 } 161 162 transformNode, controller := CreateTransform(step.ID(), 163 transformParams, options) 164 for _, parentID := range step.Parents { 165 parentStep, ok := s.plan.Step(parentID) 166 if !ok { 167 return nil, fmt.Errorf("incorrect parent reference, parentId: "+ 168 "%s, node: %s", parentID, step.ID()) 169 } 170 171 parentController, err := s.createNode(parentStep, options) 172 if err != nil { 173 return nil, err 174 } 175 176 parentController.AddTransform(transformNode) 177 } 178 179 return controller, nil 180 } 181 182 // Execute the sources in parallel and return the first error. 183 func (s *ExecutionState) Execute(queryCtx *models.QueryContext) error { 184 requests := make([]execution.Request, 0, len(s.sources)) 185 for _, source := range s.sources { 186 requests = append(requests, sourceRequest{ 187 source: source, 188 queryCtx: queryCtx, 189 }) 190 } 191 192 return execution.ExecuteParallel(queryCtx.Ctx, requests) 193 } 194 195 // String representation of the state. 196 func (s *ExecutionState) String() string { 197 return fmt.Sprintf("plan: %s\nsources: %s\n", s.plan, s.sources) 198 } 199 200 type sourceRequest struct { 201 source parser.Source 202 queryCtx *models.QueryContext 203 } 204 205 // Process processes the new request. 206 func (s sourceRequest) Process(ctx context.Context) error { 207 // make sure to propagate the new context.Context object down. 208 return s.source.Execute(s.queryCtx.WithContext(ctx)) 209 }