FrontendLoop handles connection from frontend.
(frontend schedulerpb.SchedulerForFrontend_FrontendLoopServer)
| 220 | |
| 221 | // FrontendLoop handles connection from frontend. |
| 222 | func (s *Scheduler) FrontendLoop(frontend schedulerpb.SchedulerForFrontend_FrontendLoopServer) error { |
| 223 | frontendAddress, frontendCtx, err := s.frontendConnected(frontend) |
| 224 | if err != nil { |
| 225 | return err |
| 226 | } |
| 227 | defer s.frontendDisconnected(frontendAddress) |
| 228 | |
| 229 | // Response to INIT. If scheduler is not running, we skip for-loop, send SHUTTING_DOWN and exit this method. |
| 230 | if s.State() == services.Running { |
| 231 | if err := frontend.Send(&schedulerpb.SchedulerToFrontend{Status: schedulerpb.OK}); err != nil { |
| 232 | return err |
| 233 | } |
| 234 | } |
| 235 | |
| 236 | // We stop accepting new queries in Stopping state. By returning quickly, we disconnect frontends, which in turns |
| 237 | // cancels all their queries. |
| 238 | for s.State() == services.Running { |
| 239 | msg, err := frontend.Recv() |
| 240 | if err != nil { |
| 241 | // No need to report this as error, it is expected when query-frontend performs SendClose() (as frontendSchedulerWorker does). |
| 242 | if err == io.EOF { |
| 243 | return nil |
| 244 | } |
| 245 | return err |
| 246 | } |
| 247 | |
| 248 | if s.State() != services.Running { |
| 249 | break // break out of the loop, and send SHUTTING_DOWN message. |
| 250 | } |
| 251 | |
| 252 | var resp *schedulerpb.SchedulerToFrontend |
| 253 | |
| 254 | switch msg.GetType() { |
| 255 | case schedulerpb.ENQUEUE: |
| 256 | |
| 257 | // If there is a logical plan in the request body, we will fragment it before enqueueing |
| 258 | // otherwise, it will be a single request and is the root and can be enqueued directly |
| 259 | byteLP, err := getPlanFromHTTPRequest(msg.HttpRequest) |
| 260 | if err != nil { |
| 261 | return err |
| 262 | } |
| 263 | if len(byteLP) != 0 { |
| 264 | err = s.fragmentAndEnqueueRequest(frontendCtx, frontendAddress, msg, byteLP) |
| 265 | } else { |
| 266 | err = s.enqueueRequest(frontendCtx, frontendAddress, msg, plan_fragments.Fragment{FragmentID: 0, IsRoot: true}) |
| 267 | } |
| 268 | |
| 269 | switch err { |
| 270 | case nil: |
| 271 | resp = &schedulerpb.SchedulerToFrontend{Status: schedulerpb.OK} |
| 272 | case queue.ErrTooManyRequests: |
| 273 | resp = &schedulerpb.SchedulerToFrontend{Status: schedulerpb.TOO_MANY_REQUESTS_PER_TENANT} |
| 274 | default: |
| 275 | resp = &schedulerpb.SchedulerToFrontend{Status: schedulerpb.ERROR, Error: err.Error()} |
| 276 | } |
| 277 | |
| 278 | case schedulerpb.CANCEL: |
| 279 | s.cancelRequestAndRemoveFromPending(frontendAddress, msg.QueryID, 0, true) |
nothing calls this directly
no test coverage detected