Get returns n (or more) instances which form the replicas for the given key. This implementation guarantees: - Stability: given the same ring, two invocations returns the same set for same operation. - Consistency: adding/removing 1 instance from the ring returns set with no more than 1 difference f
(key uint32, op Operation, bufDescs []InstanceDesc, bufHosts []string, bufZones map[string]int)
| 378 | // - Stability: given the same ring, two invocations returns the same set for same operation. |
| 379 | // - Consistency: adding/removing 1 instance from the ring returns set with no more than 1 difference for same operation. |
| 380 | func (r *Ring) Get(key uint32, op Operation, bufDescs []InstanceDesc, bufHosts []string, bufZones map[string]int) (ReplicationSet, error) { |
| 381 | r.mtx.RLock() |
| 382 | defer r.mtx.RUnlock() |
| 383 | if r.ringDesc == nil || len(r.ringTokens) == 0 { |
| 384 | return ReplicationSet{}, ErrEmptyRing |
| 385 | } |
| 386 | |
| 387 | var ( |
| 388 | replicationFactor = r.cfg.ReplicationFactor |
| 389 | instances = bufDescs[:0] |
| 390 | start = searchToken(r.ringTokens, key) |
| 391 | iterations = 0 |
| 392 | maxInstancePerZone = replicationFactor / len(r.ringZones) |
| 393 | zonesWithExtraInstance = replicationFactor % len(r.ringZones) |
| 394 | |
| 395 | // We use a slice instead of a map because it's faster to search within a |
| 396 | // slice than lookup a map for a very low number of items. |
| 397 | distinctHosts = bufHosts[:0] |
| 398 | numOfInstanceByZone = resetZoneMap(bufZones) |
| 399 | ) |
| 400 | |
| 401 | for i := start; len(distinctHosts) < replicationFactor && iterations < len(r.ringTokens); i++ { |
| 402 | iterations++ |
| 403 | // Wrap i around in the ring. |
| 404 | i %= len(r.ringTokens) |
| 405 | token := r.ringTokens[i] |
| 406 | |
| 407 | info, ok := r.ringInstanceByToken[token] |
| 408 | if !ok { |
| 409 | // This should never happen unless a bug in the ring code. |
| 410 | return ReplicationSet{}, ErrInconsistentTokensInfo |
| 411 | } |
| 412 | |
| 413 | // We want n *distinct* instances. |
| 414 | if slices.Contains(distinctHosts, info.InstanceID) { |
| 415 | continue |
| 416 | } |
| 417 | |
| 418 | // Ignore if the instances don't have a zone set. |
| 419 | if r.cfg.ZoneAwarenessEnabled && info.Zone != "" { |
| 420 | maxNumOfInstance := maxInstancePerZone |
| 421 | // If we still have room for zones with extra instance, increase the instance threshold by 1 |
| 422 | if zonesWithExtraInstance > 0 { |
| 423 | maxNumOfInstance++ |
| 424 | } |
| 425 | |
| 426 | if numOfInstanceByZone[info.Zone] >= maxNumOfInstance { |
| 427 | continue |
| 428 | } |
| 429 | } |
| 430 | |
| 431 | distinctHosts = append(distinctHosts, info.InstanceID) |
| 432 | instance := r.ringDesc.Ingesters[info.InstanceID] |
| 433 | |
| 434 | // Check whether the replica set should be extended given we're including |
| 435 | // this instance. |
| 436 | if op.ShouldExtendReplicaSetOnState(instance.State) { |
| 437 | replicationFactor++ |