|
18 | 18 | from re import search
|
19 | 19 | from typing import TYPE_CHECKING, Dict, List, Optional, Protocol, Tuple
|
20 | 20 |
|
| 21 | +from aws_advanced_python_wrapper.utils.log import Logger |
21 | 22 | from .host_availability import HostAvailability
|
22 | 23 |
|
23 | 24 | if TYPE_CHECKING:
|
|
29 | 30 | from .utils.messages import Messages
|
30 | 31 | from .utils.properties import Properties, WrapperProperties
|
31 | 32 |
|
| 33 | +logger = Logger(__name__) |
| 34 | + |
32 | 35 |
|
33 | 36 | class HostSelector(Protocol):
|
34 | 37 | """
|
@@ -168,23 +171,92 @@ def _update_cache_properties_for_round_robin_cluster_info(self, round_robin_clus
|
168 | 171 |
|
169 | 172 | for pair in host_weight_pairs:
|
170 | 173 | match = search(RoundRobinHostSelector._HOST_WEIGHT_PAIRS_PATTERN, pair)
|
| 174 | + message = "RoundRobinHostSelector.RoundRobinInvalidHostWeightPairs" |
171 | 175 | if match:
|
172 | 176 | host_name = match.group("host")
|
173 | 177 | host_weight = match.group("weight")
|
174 | 178 | else:
|
175 |
| - raise AwsWrapperError(Messages.get("RoundRobinHostSelector.RoundRobinInvalidHostWeightPairs")) |
| 179 | + logger.error(message, pair) |
| 180 | + raise AwsWrapperError(Messages.get_formatted(message, pair)) |
176 | 181 |
|
177 | 182 | if len(host_name) == 0 or len(host_weight) == 0:
|
178 |
| - raise AwsWrapperError(Messages.get("RoundRobinHostSelector.RoundRobinInvalidHostWeightPairs")) |
| 183 | + logger.error(message, pair) |
| 184 | + raise AwsWrapperError(Messages.get_formatted(message, pair)) |
179 | 185 | try:
|
180 | 186 | weight: int = int(host_weight)
|
181 | 187 |
|
182 | 188 | if weight < RoundRobinHostSelector._DEFAULT_WEIGHT:
|
183 |
| - raise AwsWrapperError(Messages.get("RoundRobinHostSelector.RoundRobinInvalidHostWeightPairs")) |
| 189 | + logger.error(message, pair) |
| 190 | + raise AwsWrapperError(Messages.get_formatted(message, pair)) |
184 | 191 |
|
185 | 192 | round_robin_cluster_info.cluster_weights_dict[host_name] = weight
|
186 | 193 | except ValueError:
|
187 |
| - raise AwsWrapperError(Messages.get("RoundRobinHostSelector.RoundRobinInvalidHostWeightPairs")) |
| 194 | + logger.error(message, pair) |
| 195 | + raise AwsWrapperError(Messages.get_formatted(message, pair)) |
188 | 196 |
|
189 | 197 | def clear_cache(self):
|
190 | 198 | RoundRobinHostSelector._round_robin_cache.clear()
|
| 199 | + |
| 200 | + |
| 201 | +class WeightedRandomHostSelector(HostSelector): |
| 202 | + _DEFAULT_WEIGHT: int = 1 |
| 203 | + _HOST_WEIGHT_PAIRS_PATTERN = r"((?P<host>[^:/?#]*):(?P<weight>.*))" |
| 204 | + _host_weight_map: Dict[str, int] = {} |
| 205 | + |
| 206 | + def get_host(self, hosts: Tuple[HostInfo, ...], role: HostRole, props: Optional[Properties] = None) -> HostInfo: |
| 207 | + |
| 208 | + eligible_hosts: List[HostInfo] = [host for host in hosts if host.role == role and host.get_availability() == HostAvailability.AVAILABLE] |
| 209 | + eligible_hosts.sort(key=lambda host: host.host, reverse=False) |
| 210 | + if len(eligible_hosts) == 0: |
| 211 | + message = "HostSelector.NoHostsMatchingRole" |
| 212 | + logger.error(message, role) |
| 213 | + raise AwsWrapperError(Messages.get_formatted("HostSelector.NoHostsMatchingRole", role)) |
| 214 | + |
| 215 | + self._update_host_weight_map_from_string(props) |
| 216 | + |
| 217 | + default_weight: int = WeightedRandomHostSelector._DEFAULT_WEIGHT |
| 218 | + if props is not None: |
| 219 | + default_weight = WrapperProperties.WEIGHTED_RANDOM_DEFAULT_WEIGHT.get_int(props) |
| 220 | + if default_weight < WeightedRandomHostSelector._DEFAULT_WEIGHT: |
| 221 | + logger.error("WeightedRandomHostSelector.WeightedRandomInvalidDefaultWeight") |
| 222 | + raise AwsWrapperError(Messages.get("WeightedRandomHostSelector.WeightedRandomInvalidDefaultWeight")) |
| 223 | + |
| 224 | + selection_list: List[HostInfo] = [] |
| 225 | + for host in eligible_hosts: |
| 226 | + if host.host in self._host_weight_map: |
| 227 | + selection_list = selection_list + self._host_weight_map[host.host] * [host] |
| 228 | + else: |
| 229 | + selection_list = selection_list + default_weight * [host] |
| 230 | + |
| 231 | + return random.choice(selection_list) |
| 232 | + |
| 233 | + def _update_host_weight_map_from_string(self, props: Optional[Properties] = None) -> None: |
| 234 | + if props is not None: |
| 235 | + host_weights: Optional[str] = WrapperProperties.WEIGHTED_RANDOM_HOST_WEIGHT_PAIRS.get(props) |
| 236 | + if host_weights is not None and len(host_weights) != 0: |
| 237 | + host_weight_pairs: List[str] = host_weights.split(",") |
| 238 | + |
| 239 | + for pair in host_weight_pairs: |
| 240 | + match = search(WeightedRandomHostSelector._HOST_WEIGHT_PAIRS_PATTERN, pair) |
| 241 | + message = "WeightedRandomHostSelector.WeightedRandomInvalidHostWeightPairs" |
| 242 | + if match: |
| 243 | + host_name = match.group("host") |
| 244 | + host_weight = match.group("weight") |
| 245 | + else: |
| 246 | + logger.error(message, pair) |
| 247 | + raise AwsWrapperError(Messages.get_formatted(message, pair)) |
| 248 | + |
| 249 | + if len(host_name) == 0 or len(host_weight) == 0: |
| 250 | + logger.error(message, pair) |
| 251 | + raise AwsWrapperError(Messages.get_formatted(message, pair)) |
| 252 | + try: |
| 253 | + weight: int = int(host_weight) |
| 254 | + |
| 255 | + if weight < WeightedRandomHostSelector._DEFAULT_WEIGHT: |
| 256 | + logger.error(message, pair) |
| 257 | + raise AwsWrapperError(Messages.get_formatted(message, pair)) |
| 258 | + |
| 259 | + self._host_weight_map[host_name] = weight |
| 260 | + except ValueError: |
| 261 | + logger.error(message, pair) |
| 262 | + raise AwsWrapperError(Messages.get_formatted(message, pair)) |
0 commit comments