// blockingRPC is used for queries that need to wait for a // minimum index. This is used to block and wait for changes. func (s *Server) blockingRPC(b *structs.BlockingQuery, tables MDBTables, run func() (uint64, error)) error { var timeout <-chan time.Time var notifyCh chan struct{} // Fast path non-blocking if b.MinQueryIndex == 0 { goto RUN_QUERY } // Sanity check that we have tables to block on if len(tables) == 0 { panic("no tables to block on") } // Restrict the max query time if b.MaxQueryTime > maxQueryTime { b.MaxQueryTime = maxQueryTime } // Ensure a time limit is set if we have an index if b.MinQueryIndex > 0 && b.MaxQueryTime == 0 { b.MaxQueryTime = maxQueryTime } // Setup a query timeout if b.MaxQueryTime > 0 { timeout = time.After(b.MaxQueryTime) } // Setup a notification channel for changes SETUP_NOTIFY: if b.MinQueryIndex > 0 { notifyCh = make(chan struct{}, 1) s.fsm.State().Watch(tables, notifyCh) } // Run the query function RUN_QUERY: idx, err := run() // Check for minimum query time if err == nil && idx <= b.MinQueryIndex { select { case <-notifyCh: goto SETUP_NOTIFY case <-timeout: } } return err }
// parseWait is used to parse the ?wait and ?index query params // Returns true on error func parseWait(resp http.ResponseWriter, req *http.Request, b *structs.BlockingQuery) bool { query := req.URL.Query() if wait := query.Get("wait"); wait != "" { dur, err := time.ParseDuration(wait) if err != nil { resp.WriteHeader(400) resp.Write([]byte("Invalid wait time")) return true } b.MaxQueryTime = dur } if idx := query.Get("index"); idx != "" { index, err := strconv.ParseUint(idx, 10, 64) if err != nil { resp.WriteHeader(400) resp.Write([]byte("Invalid index")) return true } b.MinQueryIndex = index } return false }