// MaybeAdd adds the specified replica if bq.shouldQueue specifies it // should be queued. Replicas are added to the queue using the priority // returned by bq.shouldQueue. If the queue is too full, the replica may // not be added, as the replica with the lowest priority will be // dropped. func (bq *baseQueue) MaybeAdd(repl *Replica, now hlc.Timestamp) { // Load the system config. cfg, cfgOk := bq.gossip.GetSystemConfig() requiresSplit := cfgOk && bq.requiresSplit(cfg, repl) bq.mu.Lock() defer bq.mu.Unlock() if bq.mu.stopped { return } if !repl.IsInitialized() { return } if !cfgOk { log.VEvent(1, bq.ctx, "no system config available. skipping") return } if requiresSplit { // Range needs to be split due to zone configs, but queue does // not accept unsplit ranges. log.VEventf(1, bq.ctx, "%s: split needed; not adding", repl) return } should, priority := bq.impl.shouldQueue(now, repl, cfg) if _, err := bq.addInternal(repl, should, priority); !isExpectedQueueError(err) { log.Errorf(bq.ctx, "unable to add %s: %s", repl, err) } }
// processReplica processes a single replica. This should not be // called externally to the queue. bq.mu.Lock should not be held // while calling this method. func (bq *baseQueue) processReplica(repl *Replica, clock *hlc.Clock) error { bq.processMu.Lock() defer bq.processMu.Unlock() // Load the system config. cfg, ok := bq.gossip.GetSystemConfig() if !ok { log.VEventf(1, bq.ctx, "no system config available. skipping") return nil } if bq.requiresSplit(cfg, repl) { // Range needs to be split due to zone configs, but queue does // not accept unsplit ranges. log.VEventf(3, bq.ctx, "%s: split needed; skipping", repl) return nil } sp := repl.store.Tracer().StartSpan(bq.name) ctx := opentracing.ContextWithSpan(context.Background(), sp) defer sp.Finish() log.Tracef(ctx, "processing replica %s", repl) // If the queue requires a replica to have the range lease in // order to be processed, check whether this replica has range lease // and renew or acquire if necessary. if bq.needsLease { // Create a "fake" get request in order to invoke redirectOnOrAcquireLease. if err := repl.redirectOnOrAcquireLease(ctx); err != nil { if _, harmless := err.GetDetail().(*roachpb.NotLeaseHolderError); harmless { log.VEventf(3, bq.ctx, "%s: not holding lease; skipping", repl) return nil } return errors.Wrapf(err.GoError(), "%s: could not obtain lease", repl) } log.Trace(ctx, "got range lease") } log.VEventf(3, bq.ctx, "%s: processing", repl) start := timeutil.Now() if err := bq.impl.process(ctx, clock.Now(), repl, cfg); err != nil { return err } log.VEventf(2, bq.ctx, "%s: done: %s", repl, timeutil.Since(start)) log.Trace(ctx, "done") return nil }
// addInternal adds the replica the queue with specified priority. If // the replica is already queued, updates the existing // priority. Expects the queue lock is held by caller. func (bq *baseQueue) addInternal(repl *Replica, should bool, priority float64) (bool, error) { if bq.mu.stopped { return false, errQueueStopped } if bq.mu.disabled { log.Event(bq.ctx, "queue disabled") return false, errQueueDisabled } if !repl.IsInitialized() { // We checked this above in MaybeAdd(), but we need to check it // again for Add(). return false, errors.New("replica not initialized") } // If the replica is currently in purgatory, don't re-add it. if _, ok := bq.mu.purgatory[repl.RangeID]; ok { return false, nil } item, ok := bq.mu.replicas[repl.RangeID] if !should { if ok { log.Eventf(bq.ctx, "%s: removing", item.value) bq.remove(item) } return false, errReplicaNotAddable } else if ok { if item.priority != priority { log.Eventf(bq.ctx, "%s: updating priority: %0.3f -> %0.3f", repl, item.priority, priority) } // Replica has already been added; update priority. bq.mu.priorityQ.update(item, priority) return false, nil } log.VEventf(3, bq.ctx, "%s: adding: priority=%0.3f", repl, priority) item = &replicaItem{value: repl.RangeID, priority: priority} heap.Push(&bq.mu.priorityQ, item) bq.mu.replicas[repl.RangeID] = item // If adding this replica has pushed the queue past its maximum size, // remove the lowest priority element. if pqLen := bq.mu.priorityQ.Len(); pqLen > bq.maxSize { bq.remove(bq.mu.priorityQ[pqLen-1]) } // Signal the processLoop that a replica has been added. select { case bq.incoming <- struct{}{}: default: // No need to signal again. } return true, nil }
// MaybeRemove removes the specified replica from the queue if enqueued. func (bq *baseQueue) MaybeRemove(repl *Replica) { bq.mu.Lock() defer bq.mu.Unlock() if bq.mu.stopped { return } if item, ok := bq.mu.replicas[repl.RangeID]; ok { log.VEventf(3, bq.ctx, "%s: removing", item.value) bq.remove(item) } }