GetReplicationSetForListRule is similar to ring.GetReplicationSetForOperation but does NOT require quorum. Because it does not require quorum it returns healthy instance in the AZ with failed instances unlike GetReplicationSetForOperation. This is important for ruler because healthy instances in the
(r ring.ReadRing, cfg *RingConfig)
| 134 | // GetReplicationSetForOperation. This is important for ruler because healthy instances in the AZ with failed |
| 135 | // instance could be evaluating some rule groups. |
| 136 | func GetReplicationSetForListRule(r ring.ReadRing, cfg *RingConfig) (ring.ReplicationSet, map[string]struct{}, error) { |
| 137 | healthy, unhealthy, err := r.GetAllInstanceDescs(ListRuleRingOp) |
| 138 | if err != nil { |
| 139 | return ring.ReplicationSet{}, make(map[string]struct{}), err |
| 140 | } |
| 141 | ringZones := make(map[string]struct{}) |
| 142 | zoneFailures := make(map[string]struct{}) |
| 143 | for _, instance := range healthy { |
| 144 | ringZones[instance.Zone] = struct{}{} |
| 145 | } |
| 146 | for _, instance := range unhealthy { |
| 147 | ringZones[instance.Zone] = struct{}{} |
| 148 | zoneFailures[instance.Zone] = struct{}{} |
| 149 | } |
| 150 | // Max errors and max unavailable zones are mutually exclusive. We initialise both |
| 151 | // to 0, and then we update them whether zone-awareness is enabled or not. |
| 152 | maxErrors := 0 |
| 153 | maxUnavailableZones := 0 |
| 154 | // Because ring's Get method returns a number of ruler equal to the replication factor even if there is only 1 zone |
| 155 | // and ZoneAwarenessEnabled, we can consider that ZoneAwarenessEnabled is disabled if there is only 1 zone since |
| 156 | // rules are still replicated to rulers in the same zone. |
| 157 | if cfg.ZoneAwarenessEnabled && len(ringZones) > 1 { |
| 158 | numReplicatedZones := min(len(ringZones), r.ReplicationFactor()) |
| 159 | // Given that quorum is not required, we only need at least one of the zone to be healthy to succeed. But we |
| 160 | // also need to handle case when RF < number of zones. |
| 161 | maxUnavailableZones = numReplicatedZones - 1 |
| 162 | if len(zoneFailures) > maxUnavailableZones { |
| 163 | return ring.ReplicationSet{}, zoneFailures, ring.ErrTooManyUnhealthyInstances |
| 164 | } |
| 165 | } else { |
| 166 | numRequired := max(len(healthy)+len(unhealthy), r.ReplicationFactor()) |
| 167 | // quorum is not required so 1 replica is enough to handle the request |
| 168 | numRequired -= r.ReplicationFactor() - 1 |
| 169 | if len(healthy) < numRequired { |
| 170 | return ring.ReplicationSet{}, zoneFailures, ring.ErrTooManyUnhealthyInstances |
| 171 | } |
| 172 | |
| 173 | maxErrors = len(healthy) - numRequired |
| 174 | } |
| 175 | return ring.ReplicationSet{ |
| 176 | Instances: healthy, |
| 177 | MaxErrors: maxErrors, |
| 178 | MaxUnavailableZones: maxUnavailableZones, |
| 179 | }, zoneFailures, nil |
| 180 | } |