# # SPDX-FileCopyrightText: Hadad # SPDX-License-Identifier: Apache-2.0 # import random # Import the random module to enable random selection of elements from a list, which helps in load balancing host assignments from datetime import datetime # Import the datetime class to work with timestamps, particularly to check and compare current UTC time against host busy status from typing import Dict, List # Import type hinting classes for dictionaries and lists to improve code readability and static analysis, though not explicitly used here from config import auth # Import the 'auth' configuration, which is expected to be a list of host dictionaries containing credentials and identifiers for available hosts from src.utils.helper import busy, mark # Import 'busy', a dictionary tracking host busy states with expiration timestamps, and 'mark', a function to update the busy status of hosts when assigned # Initialize a global dictionary named 'mapping' that will maintain a persistent association between session IDs and their assigned hosts mapping = {} # This dictionary stores session_id keys mapped to host dictionaries, ensuring consistent host allocation for repeated requests within the same session # Define a function to get an available hosts for a given session def get_host(session_id: str, exclude_hosts: List[str] = None) -> dict: """ Obtain a host for the specified session ID, guaranteeing that the function never raises exceptions or returns None. This function implements a robust mechanism to ensure a host is always returned by dynamically excluding busy or failed hosts, and retrying until a suitable host becomes available. Args: session_id (str): A unique string identifier representing the current user or process session requesting a host. exclude_hosts (List[str], optional): An optional list of host identifiers to be excluded from selection, useful for avoiding problematic or previously failed hosts. Defaults to None. Returns: dict: A dictionary representing the selected host from the 'auth' configuration, including its credentials and identifier. Detailed Explanation: The function first checks if the session already has an assigned host in the 'mapping' dictionary. If so, it verifies whether the assigned host is neither excluded nor currently busy. If the assigned host is available, it refreshes its busy status to extend its reservation and returns it immediately, ensuring session affinity. If the assigned host is excluded or busy, the mapping is deleted to allow reassignment. The function then filters the list of all hosts from 'auth' to exclude busy hosts, explicitly excluded hosts, and hosts already tried in the current function call. From the filtered list, it randomly selects a host to distribute load evenly. If no suitable hosts are found, the function updates the set of tried hosts to include all currently busy and excluded hosts, and clears the exclude list to retry all hosts again. This loop continues indefinitely until a host becomes available, ensuring that the function never fails or returns None. This design supports high availability and fault tolerance by dynamically adapting to host availability and avoiding deadlocks or infinite retries on unavailable hosts. """ # Initialize exclude_hosts as an empty list if no value is provided to avoid errors during host filtering if exclude_hosts is None: exclude_hosts = [] # Assign an empty list to exclude_hosts to safely perform membership checks and list operations later # Create a set to track hosts that have been attempted and found unsuitable during this invocation of the function tried_hosts = set() # Using a set for efficient membership testing and to prevent repeated attempts on the same hosts within this call # Enter an infinite loop that will only exit when a valid host is found and returned, ensuring robustness and continuous operation while True: # Check if the current session ID already has a host assigned in the mapping dictionary if session_id in mapping: assigned_host = mapping[session_id] # Retrieve the previously assigned host dictionary for this session to maintain session consistency # Determine if the assigned host is not in the exclude list and is either not busy or its busy period has expired if ( assigned_host["jarvis"] not in exclude_hosts # Confirm the assigned host is not explicitly excluded for this request and ( assigned_host["jarvis"] not in busy # Check if the host is not currently marked as busy or busy[assigned_host["jarvis"]] <= datetime.utcnow() # Alternatively, check if the host's busy timestamp has expired, meaning it is free ) ): # Since the assigned host is available, update its busy timestamp to extend its reservation for this session mark(assigned_host["jarvis"]) # Call the helper function to refresh the busy status, preventing other sessions from using it simultaneously return assigned_host # Return the assigned host dictionary immediately to maintain session affinity and reduce latency else: # If the assigned host is either excluded or currently busy, remove the mapping to allow reassignment of a new host del mapping[session_id] # Delete the session-to-host association to enable the selection of a different host in subsequent steps # Capture the current UTC time once to use for filtering hosts based on their busy status now = datetime.utcnow() # Store the current time to compare against busy timestamps for all hosts # Generate a list of hosts that are eligible for selection by applying multiple filters: # 1. Hosts not currently busy or whose busy period has expired # 2. Hosts not included in the exclude_hosts list provided by the caller # 3. Hosts not already attempted in this function call to avoid redundant retries available_hosts = [ h for h in auth # Iterate over all hosts defined in the authentication configuration list if h["jarvis"] not in busy or busy[h["jarvis"]] <= now # Include only hosts that are free or whose busy reservation has expired if h["jarvis"] not in exclude_hosts # Exclude hosts explicitly marked to be avoided for this selection if h["jarvis"] not in tried_hosts # Exclude hosts that have already been tried and failed during this function call to prevent infinite loops ] # If there are any hosts available after filtering, proceed to select one randomly if available_hosts: selected = random.choice(available_hosts) # Randomly pick one host from the available list to distribute load fairly among hosts # Store the selected host in the global mapping dictionary to maintain session affinity for future requests with the same session ID mapping[session_id] = selected # Cache the selected host so subsequent calls with this session ID return the same host # Mark the selected host as busy by updating its busy timestamp, indicating it is currently reserved for this session mark(selected["jarvis"]) # Update the busy dictionary to reflect that this host is now occupied, preventing concurrent use # Return the selected host dictionary to the caller, completing the host assignment process for the session return selected else: # If no hosts are available that have not already been tried, update the tried_hosts set to include all hosts currently busy or excluded # This prevents immediate reattempts on these hosts in the next iteration, allowing time for busy hosts to become free tried_hosts.update( h["jarvis"] for h in auth # Iterate over all hosts in the auth list to identify those currently busy if h["jarvis"] in busy and busy[h["jarvis"]] > now # Add hosts whose busy timestamp is still in the future, indicating they are occupied ) tried_hosts.update(exclude_hosts) # Also add all explicitly excluded hosts to the tried_hosts set to avoid retrying them immediately # Create a set of all host identifiers from the auth configuration to compare against tried_hosts all_host_ids = {h["jarvis"] for h in auth} # Extract all host IDs to check if all hosts have been attempted # If the set of tried hosts now includes every host in the configuration, clear the tried_hosts set to reset the retry mechanism if tried_hosts >= all_host_ids: tried_hosts.clear() # Clear the tried_hosts set to allow all hosts to be considered again, enabling retries after some time # Clear the exclude_hosts list as well to allow all hosts to be eligible for selection in the next iteration exclude_hosts.clear() # Reset the exclude list so that no hosts are excluded in the upcoming retry cycle, maximizing availability