o
    ;iO                     @   sX  d dl Z d dlZd dlZd dlZd dlZd dlZd dlZd dlmZ d dl	m
Z
mZmZmZ d dlmZ d dlmZ d dlmZ d dlmZ d dlmZ d d	lmZ d
dlmZ d
dlmZ d
dlmZ  d
dl!m"Z"m#Z# d
dl$m%Z% d
dl&m'Z' d
dl(m)Z) dZ*G dd dZ+e"e+e,dZ-e#j.				 	d=de/deej0 dee1 de/de/de2de+fdd Z3G d!d" d"Z4e"e4Z5e#j.			#	#	 	$	%	d>d&e1d'e1d(e1d)e1d*e6d+ee/ d,ee/ d-e6d.e6d/e/d0e/d1e/d2ee/ de4fd3d4Z7e#j.d&e1d'e1de8e9e1e
f  fd5d6Z:	d?g dddd7dee/ d8e8e1 de/dee/ de2f
d9d:Z;e"e;e,dZ<G d;d< d<Z=dS )@    N)defaultdict)AnyCallableOptionalUnion)urlparse)get_cluster_info)_PartialFunctionFlags)_Cls)_Dict)api_pb2   )UserException)validate_http_server_config)_forward)synchronize_apisynchronizer)_Client)logger)InvalidError
   c                   @   s   e Zd Z					d$dededeej dee d	ed
ede	fddZ
	d%deej dedee	ee f fddZdd Zdd Zdd Zdedede	fddZdedefddZdd Zd d! Zd"d# ZdS )&_FlashManagerN   r   Fclientportprocesshealth_check_urlstartup_timeoutexit_grace_period
h2_enabledc                 C   sP   || _ || _|| _|| _|| _|| _t|||d| _d| _d| _	t
jd | _d S )N)r   r   Fr   MODAL_TASK_ID)r   r   r   r   r   r   _forward_tunneltunnel_managerstoppednum_heartbeat_failuresosenvirontask_id)selfr   r   r   r   r   r   r    r)   L/home/ubuntu/.local/lib/python3.10/site-packages/modal/experimental/flash.py__init__   s   
z_FlashManager.__init__      ?timeoutreturnc              	      s   t  }dtt f fdd}t  | |k rsz6|  }r#d|fW S tjtd| jddI d H \}}z|  |	 I d H  W W dS  tyN   Y W dS w  tj
yW     ttjfyj   td	I d H  Y nw t  | |k sdtd
| j dfS )Nr.   c                      s0    d ur   d urtd j d j S d S )NzProcess z exited with code )poll	Exceptionpid
returncoder)   r   r)   r*   check_process_is_running:   s   zJ_FlashManager.is_port_connection_healthy.<locals>.check_process_is_runningF	localhostr,   r-   )TN皙?zWaited too long for port z to start accepting connections)time	monotonicr   r0   asynciowait_foropen_connectionr   closewait_closedCancelledErrorOSErrorTimeoutErrorsleep)r(   r   r-   
start_timer4   error_writerr)   r3   r*   is_port_connection_healthy5   s.   

"z(_FlashManager.is_port_connection_healthyc              
      s   | j  I d H | _t| jj}|j}|jpd}z| ||I d H  W n tt	t
jfyA   |  I d H  | j jt  I d H   w t
| ||| _t
|  | _d S )Ni  )r"   
__aenter__tunnelr   urlhostnamer   _wait_for_port_successr0   KeyboardInterruptr:   r?   _deregister	__aexit__sysexc_infocreate_task_run_heartbeatheartbeat_task_drain_container
drain_task)r(   
parsed_urlhostr   r)   r)   r*   _startQ   s   
z_FlashManager._startc                    s*   t | jjjt dd dI d H  d S )Nr   r-   retry)r:   shieldr   stubFlashContainerDeregisterr   FlashContainerDeregisterRequestr(   r)   r)   r*   rN   a   s   z_FlashManager._deregisterc              
      s  	 z2| j tkr3td| j d| jj d |  I dH  | jr0| jj	
tj| jdI dH  W dS W n1 tjyD   td Y dS  tye } ztd|  td	I dH  W Y d}~nd}~ww z
td	I dH  W n tjy   td Y dS w q)
zt
        Background task that checks if we've encountered too many failures and drains the container if so.
        Tz[Modal Flash] Draining task  on z due to too many failures.N)r'   [Modal Flash] Shutting down...z([Modal Flash] Error draining container:    )r$   _MAX_FAILURESr   warningr'   rI   rJ   stopr   r]   ContainerStopr   ContainerStopRequestr:   r?   r0   rD   rB   r(   er)   r)   r*   rU   j   s6   



z_FlashManager._drain_containerrX   c              
      s*  t  }t  | | jk rz9| j| jdI d H \}}|rF| jjjtj	dd||ddd dI d H }t
d|j d| jj d| j  W d	S W n' tjyU   t
d
   tyn } zt
d|  W Y d }~nd }~ww z
tdI d H  W n tjy   t
d
  w t  | | jk std
)Nr3   r      priorityweightrX   r   rZ   zListening at z over z for task_id TzIWaited too long for port to start accepting connections. Shutting down...z7Error waiting for port to start accepting connections: rc   )r8   r9   r   rG   r   r   r]   FlashContainerRegisterr   FlashContainerRegisterRequestr   inforJ   rI   r'   r:   r?   re   r0   rD   rB   rA   )r(   rX   r   rC   port_check_resprE   resprj   r)   r)   r*   rL      sD   $


z$_FlashManager._wait_for_port_successc              
      s8  	 zK| j | jdI d H \}}|r)| jjjtjdd||ddd dI d H }d| _n#t	d| j
 d	| jj d
| d| j  |  jd7  _|  I d H  W n0 tjyd   td |  I d H  Y d S  ty} } zt	d|  W Y d }~nd }~ww z
tdI d H  W n tjy   |  I d H  Y d S w q)NTr3   r   rk   rl   rZ   r   z&[Modal Flash] Deregistering container ra   z due to error: z, num_heartbeat_failures: rc   rb   z [Modal Flash] Heartbeat failed: )rG   r   r   r]   ro   r   rp   r$   r   rD   r'   rI   rJ   rN   r:   r?   re   r0   rB   )r(   rX   r   rr   port_check_errorrs   rj   r)   r)   r*   rS      sR   

z_FlashManager._run_heartbeatc                 C   s   | j jS N)rI   rJ   r`   r)   r)   r*   get_container_url   s   z_FlashManager.get_container_urlc              
      s   z*| j r*| j   ztj| j ddI d H  W n tjtjfy)   td Y nw W n tyE } zt	d|  W Y d }~nd }~ww d| _
td| jj d d S )Nrk   r6   z4[Modal Flash] Heartbeat task did not stop within 5s.z[Modal Flash] Error stopping: Tz2[Modal Flash] No longer accepting new requests on .)rT   cancelr:   r;   rA   r?   r   re   r0   rD   r#   rI   rJ   ri   r)   r)   r*   rf      s    
z_FlashManager.stopc                    sX   | j s|  I d H  t| jI d H  td| jj d | j	j
t  I d H  d S )Nz [Modal Flash] Closing tunnel on rw   )r#   rf   r:   rB   r   r   re   rI   rJ   r"   rO   rP   rQ   r`   r)   r)   r*   r=      s   z_FlashManager.closeNNr   r   F)r,   )__name__
__module____qualname__r   intr   
subprocessPopenstrboolr+   floattupler0   rG   rY   rN   rU   rL   rS   rv   rf   r=   r)   r)   r)   r*   r      sJ    

	#r   )target_moduler   Fr   r   r   r   r   r   r.   c              	      s8   t  I dH }t|| |||||d}| I dH  |S )a  
    Forward a port to the Modal Flash service, exposing that port as a stable web endpoint.
    This is a highly experimental method that can break or be removed at any time without warning.
    Do not use this method unless explicitly instructed to do so by Modal support.
    N)r   r   r   r   r   )r   from_envr   rY   )r   r   r   r   r   r   r   managerr)   r)   r*   flash_forward   s   	r   c                   @   sF  e Zd ZdZdededededededee d	ee d
ee dededededefddZ	dd Z
dd ZdedefddZdedededededefddZdeeef fd d!Zd"edeeeee f  fd#d$Zd%d& Zd'efd(d)Z	*	+	,	,	,d2ded-eeeef  dededee d	ee d
ee defd.d/Zd0d1 Zd,S )3_FlashPrometheusAutoscaleri  r   app_namecls_namemetrics_endpointtarget_metrictarget_metric_valuemin_containersmax_containersbuffer_containersscale_up_tolerancescale_down_tolerance%scale_up_stabilization_window_seconds'scale_down_stabilization_window_secondsautoscaling_interval_secondsc                 C   s   dd l }|| jkrtd| j || jkrtd| j |dkr&td|| _|| _|| _|| _|| _|| _|| _	|| _
|	| _|
| _|| _|| _|| _|| _t||}|j| _| | _| | _tj| d| ddd| _d | _d S )	Nr   zDscale_up_stabilization_window_seconds must be less than or equal to zFscale_down_stabilization_window_seconds must be less than or equal to z*target_metric_value must be greater than 0-z-autoscaling-decisionsT)create_if_missing)aiohttp_max_window_secondsr   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r
   	from_name_class_service_functionfnclsClientSessionhttp_clientr   autoscaling_decisions_dictautoscaler_thread)r(   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   
FlashClassr)   r)   r*   r+   
  sD   





z#_FlashPrometheusAutoscaler.__init__c                    s,   | j j| jdI d H  t|  | _d S )Nr   )r   hydrater   r:   rR   _run_autoscaler_loopr   r`   r)   r)   r*   startC  s   z _FlashPrometheusAutoscaler.startc              
      s.  	 zt    jddI d H }jdg I d H }t|ts*td|  d}t|ts9td|  g }|D ]'}t|trVt	|dksVt|d t
rVt|d tsbtd|  g } nq; fdd	|D }j|d
I d H }| |f j||jjjjjd}td|dt      d jd|I d H  jd|I d H  |I d H  t     jk rtjt      I d H  W nB tjy   td j I d H  Y d S  ty } ztd|  tt  tjI d H  W Y d }~nd }~ww q)NTcurrent_replicasr   autoscaling_decisionsz5[Modal Flash] Invalid item in autoscaling decisions: r   rc   c                    s&   g | ]\}}| j  kr||fqS r)   )r   ).0	timestampdecisionautoscaling_timer(   r)   r*   
<listcomp>_  s
    zC_FlashPrometheusAutoscaler._run_autoscaler_loop.<locals>.<listcomp>)r   )r   r   r   r   r   z2[Modal Flash] Scaling to actual_target_containers=z+ containers.  Autoscaling decision made in z	 seconds.z)[Modal Flash] Shutting down autoscaler...z#[Modal Flash] Error in autoscaler: )r8   r   get
isinstancer}   r   re   listr   lenr   _compute_target_containersappend_make_scaling_decisionr   r   r   r   r   put_set_target_slotsr   r:   rB   r?   r   r=   r0   rD   	traceback
format_exc)r(   r   r   itemcurrent_target_containersactual_target_containersrj   r)   r   r*   r   G  s~   





z/_FlashPrometheusAutoscaler._run_autoscaler_loopr   r.   c                    s   |   I dH }t||kr td| dt| d t|}|dkr&dS | |I dH \}}| j|||t|| jd}td|S )zF
        Gets metrics from container to autoscale up or down.
        Nz[Modal Flash] Current replicas z' is less than the number of containers z,. Setting current_replicas = num_containers.r   rc   )n_current_replicas
sum_metricn_containers_with_metricsn_total_containersr   )_get_all_containersr   r   rq   _get_scaling_info_calculate_desired_replicasr   max)r(   r   
containersr   r   desired_replicasr)   r)   r*   r     s(   
z5_FlashPrometheusAutoscaler._compute_target_containersr   r   r   r   c                 C   s   | j pd}|| }|| }t|| d}	|dd| j  | |  |	 }
|||  |	 }|
| }|| }|	}|d| j krDt|| }n|d| j k rRt|| }td| d| j d| d| d| d	| d
| d|	 d| d| d|  |S )zK
        Calculate the desired number of replicas to autoscale to.
        r   rc   g      ?z [Modal Flash] Current replicas: z, target metric: ztarget metric value: z , current sum of metric values: z%, number of containers with metrics: z", number of containers unhealthy: z<, number of containers missing metric (includes unhealthy): z$, number of provisioned containers: z, scale up ratio: z, scale down ratio: z, desired replicas: )	r   r   r   mathceilr   r   re   r   )r(   r   r   r   r   r   r   n_containers_missing_metricn_containers_unhealthynum_provisioned_containersscale_up_target_metric_valuescale_down_target_metric_valuescale_up_ratioscale_down_ratior   r)   r)   r*   r     sR   

	
z6_FlashPrometheusAutoscaler._calculate_desired_replicasc                    s|   d}d}t j fdd|D  I dH }|D ]#}|du s* j|vs*t| j dkr+q|| j d j7 }|d7 }q||fS )z6Get metrics using container exposed metrics endpoints.r   c              
      s.   g | ]}  d |j d|j d j qS )zhttps://:/)_get_metricsrX   r   r   )r   	containerr`   r)   r*   r     s     z@_FlashPrometheusAutoscaler._get_scaling_info.<locals>.<listcomp>Nrc   )r:   gatherr   r   value)r(   r   r   r   container_metrics_listcontainer_metricsr)   r`   r*   r     s    



z,_FlashPrometheusAutoscaler._get_scaling_inforJ   c           
   
      sD  ddl m}m} z| jj|ddI d H }|  W n0 tjy-   t	d|  Y d S  t
yJ } zt	d| d|  W Y d }~d S d }~ww z	| I d H }W n0 tjyg   t	d|  Y d S  t
y } zt	d	| d|  W Y d }~d S d }~ww tt}||D ]}|jD ]}	||	j  |	g7  < qq|S )
Nr   )Sampletext_string_to_metric_families   r6   z+[Modal Flash] Timeout getting metrics from z)[Modal Flash] Error getting metrics from z: z0[Modal Flash] Timeout reading metrics body from z.[Modal Flash] Error reading metrics body from )prometheus_client.parserr   r   r   r   raise_for_statusr:   rA   r   re   r0   textr   r   samplesname)
r(   rJ   r   r   responserj   	text_bodymetricsfamilysampler)   r)   r*   r     s:   
z'_FlashPrometheusAutoscaler._get_metricsc                    s,   t j| jjd}| jj|I d H }|jS )Nfunction_id)r   FlashContainerListRequestr   	object_idr   r]   FlashContainerListr   )r(   reqrs   r)   r)   r*   r     s   z._FlashPrometheusAutoscaler._get_all_containerstarget_slotsc                    s,   t j| jj|d}| jj|I d H  d S )N)r   r   )r   !FlashSetTargetSlotsMetricsRequestr   r   r   r]   FlashSetTargetSlotsMetrics)r(   r   r   r)   r)   r*   r     s   z,_FlashPrometheusAutoscaler._set_target_slotsr   ,  Nr   c                    s   |s|S |j dd d |d \}}	|	|kr1||   fdd|D }
|
r)t|
n|	}t||}n!|	|k rP||   fdd|D }
|
rHt|
n|	}t||}n|}|dur[t||}|durdt||}|durl||7 }|S )	a]  
        Return the target number of containers following (simplified) Kubernetes HPA
        stabilization-window semantics.

        Args:
            current_replicas: Current number of running Pods/containers.
            autoscaling_decisions: List of (timestamp, desired_replicas) pairs, where
                                   timestamp is a UNIX epoch float (seconds).
                                   The list *must* contain at least one entry and should
                                   already include the most-recent measurement.
            scale_up_stabilization_window_seconds: 0 disables the up-window.
            scale_down_stabilization_window_seconds: 0 disables the down-window.
            min_containers / max_containers: Clamp the final decision to this range.

        Returns:
            The target number of containers.
        c                 S   s   | d S )Nr   r)   )recr)   r)   r*   <lambda>C  s    zC_FlashPrometheusAutoscaler._make_scaling_decision.<locals>.<lambda>)keyc                       g | ]
\}}| kr|qS r)   r)   r   tsdesiredwindow_startr)   r*   r   J      zE_FlashPrometheusAutoscaler._make_scaling_decision.<locals>.<listcomp>c                    r   r)   r)   r   r   r)   r*   r   Q  r   N)sortminr   )r(   r   r   r   r   r   r   r   now_tslatest_desireddesired_candidates	candidatenew_replicasr)   r   r*   r   "  s,   

z1_FlashPrometheusAutoscaler._make_scaling_decisionc                    s   | j   | j I d H  d S ru   )r   rx   r`   r)   r)   r*   rf   d  s   
z_FlashPrometheusAutoscaler.stop)r   r   NNN)rz   r{   r|   r   r   r   r   r   r}   r+   r   r   r   r   r   r   dictr   r   r   r   r   r   rf   r)   r)   r)   r*   r     s    	

9C
:" 		
Br   r7   r      r   r   r   r   r   r   r   r   r   r   r   r   r   c                    sj   zddl }W n ty   tdw t I dH }t|| ||||||||||	|
|d}| I dH  |S )aH  
    Autoscale a Flash service based on containers' Prometheus metrics.

    The package `prometheus_client` is required to use this method.

    This is a highly experimental method that can break or be removed at any time without warning.
    Do not use this method unless explicitly instructed to do so by Modal support.
    r   Nz?The package `prometheus_client` is required to use this method.)r   r   r   r   r   r   r   r   r   r   r   r   r   r   )prometheus_clientImportErrorr   r   r   r   )r   r   r   r   r   r   r   r   r   r   r   r   r   r  r   
autoscalerr)   r)   r*   flash_prometheus_autoscalerl  s2   #r  c                    sb   t  I dH }t| |j}|dusJ |j|dI dH  tj|jd}|j	
|I dH }|jS )a   
    Return a list of flash containers for a deployed Flash service.

    This is a highly experimental method that can break or be removed at any time without warning.
    Do not use this method unless explicitly instructed to do so by Modal support.
    Nr   r   )r   r   r
   r   r   r   r   r   r   r]   r   r   )r   r   r   r   r   rs   r)   r)   r*   flash_get_containers  s   r  )proxy_regionsr   r   r   r  c                   s|   | du rt dt| ||| ddlm m} |tj| ||pd|p"d|dddttdt	f  f d	 f fd
d}|S )ak  Decorator for Flash-enabled HTTP servers on Modal classes.

    Args:
        port: The local port to forward to the HTTP server.
        proxy_regions: The regions to proxy the HTTP server to.
        startup_timeout: The maximum time to wait for the HTTP server to start.
        exit_grace_period: The time to wait for the HTTP server to exit gracefully.

    NzePositional arguments are not allowed. Did you forget parentheses? Suggestion: `@modal.http_server()`.r   )_PartialFunction_PartialFunctionParams)r   r  r   r   r   )http_configobj.r.   c                    s8   t j}t|  r| |}n | |}|d |S )Nz`http_server`)r	   HTTP_WEB_INTERFACEr   stackvalidate_obj_compatibility)r  flagspfr	  paramsr)   r*   wrapper  s   

z_http_server.<locals>.wrapper)
r   r   modal._partial_functionr	  r
  r   
HTTPConfigr   r   r   )r   r  r   r   r   r
  r  r)   r  r*   _http_server  s"   (

r  c                   @   sF   e Zd ZU dZee ed< dejfddZ	dd Z
dd	 Zd
d ZdS )_FlashContainerEntryai  
    A class that manages the lifecycle of Flash manager for Flash containers.

    It is intentional that stop() runs before exit handlers and close().
    This ensures the container is deregistered first, preventing new requests from being routed to it
    while exit handlers execute and the exit grace period elapses, before finally closing the tunnel.
    flash_managerr  c                 C   s   || _ d | _d S ru   )r  r  )r(   r  r)   r)   r*   r+     s   
z_FlashContainerEntry.__init__c              
   C   s   | j t krJzt j}|dkrW d S W n	 ty   Y nw zt| j j| j j| j j	| j j
d| _W d S  tyI } ztd|  t d }~ww d S )Nr   )r   r   r   z[Modal Flash] Startup failed: )r  r   r  r   rankr   r   r   r   r   r   r  r0   r   re   r   )r(   r  rj   r)   r)   r*   enter  s,   z_FlashContainerEntry.enterc                 C      | j r
| j   d S d S ru   )r  rf   r`   r)   r)   r*   rf        z_FlashContainerEntry.stopc                 C   r  ru   )r  r=   r`   r)   r)   r*   r=     r  z_FlashContainerEntry.closeN)rz   r{   r|   __doc__r   FlashManager__annotations__r   r  r+   r  rf   r=   r)   r)   r)   r*   r    s   
 r  ry   )NNr7   r7   r   r   r  Nru   )>r:   r   r%   r~   rP   r8   r   collectionsr   typingr   r   r   r   urllib.parser   modal._clustered_functionsr   r  r	   	modal.clsr
   
modal.dictr   modal_protor   _runtime.container_io_managerr   _serverr   _tunnelr   r!   _utils.async_utilsr   r   r   r   configr   	exceptionr   rd   r   rz   r  create_blockingr}   r   r   r   r   r   FlashPrometheusAutoscalerr   r  r   r  r  r  http_serverr  r)   r)   r)   r*   <module>   s    J  d
	
<$
0