o
    NK&hH                     @   s  U d dl Z d dlmZ d dlmZ d dlmZmZ d dlm	Z	m
Z
 d dlmZ d dlmZmZmZ d dlmZ d d	lmZmZ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 m!Z!m"Z" d dl#m$Z$ e%e&d< esxd dlm'Z' neZ'G dd deZ(G dd dZ)dS )    N)Iterable)suppress)IntEnumauto)chaincount)choice)SIGINTSIGTERMSignals)signal)AnyCallableOptional)OS_IS_WINDOWS)ServerKilled)error_loggerlogger)RestartOrder)ProcessStateWorkerWorkerProcess)	RestarterSIGKILL)r   c                   @   s   e Zd Ze Ze ZdS )MonitorCycleN)__name__
__module____qualname__r   BREAKCONTINUE r    r    G/var/www/html/venv/lib/python3.10/site-packages/sanic/worker/manager.pyr      s    
r   c                   @   s"  e Zd ZdZejZdZdefddZ							
dTde	de
def dee	ef dedee dededede	defddZdefddZdUdee	 ddfddZdd Zdd Zd d! Zd"d# Zd$d% Zdejfd&eee	  fd'd(Zd)efd*d+Zd,d- Zd.d/ Zedee fd0d1Z ede!e"e	ef  fd2d3Z#ed4d5 Z$ed6d7 Z%ed8d9 Z&d:d; Z'd<d= Z(d>d? Z)d@eddfdAdBZ*edCdD Z+dEdF Z,dGdH Z-dVdIdJZ.dee/ fdKdLZ0dVdMdNZ1dOe	dee/ fdPdQZ2de	de
def dee	ef dedee dedededdfdRdSZ3dS )WWorkerManagera  Manage all of the processes.

    This class is used to manage all of the processes. It is instantiated
    by Sanic when in multiprocess mode (which is OOTB default) and is used
    to start, stop, and restart the worker processes.

    You can access it to interact with it **ONLY** when on the main process.

    Therefore, you should really only access it from within the
    `main_process_ready` event listener.

    ```python
    from sanic import Sanic

    app = Sanic("MyApp")

    @app.main_process_ready
    async def ready(app: Sanic, _):
        app.manager.manage("MyProcess", my_process, {"foo": "bar"})
    ```

    See [Worker Manager](/en/guide/deployment/manager) for more information.
    z
Sanic-Mainnumberc                 C   s   || _ || _i | _i | _t | _|\| _| _|| _d| j	i| j| j
< d| _|| _|| _t | _|dkr7tdt|D ]}|   q;tt| j tt| j d S )NpidFr   zCannot serve with no workers)
num_servercontext	transientdurabler   	restartermonitor_publishermonitor_subscriberworker_stater$   	MAIN_NAME_shutting_down_serve_server_settingsr   _server_countRuntimeErrorrangecreate_serversignal_funcr	   shutdown_signalr
   )selfr#   serveserver_settingsr&   monitor_pubsubr,   _r    r    r!   __init__<   s$   	
zWorkerManager.__init__FNT    namefunc.kwargsr'   restartabletracked
auto_startworkersidentreturnc
                 C   s   || j v s
|| jv rtd| d|dur|n|}|r"|s"td|r'| j n| j}
t|	p.||||| j| j||||
}||
|j< |S )aF  Instruct Sanic to manage a custom process.

        Args:
            name (str): A name for the worker process
            func (Callable[..., Any]): The function to call in the background process
            kwargs (Dict[str, Any]): Arguments to pass to the function
            transient (bool, optional): Whether to mark the process as transient. If `True`
                then the Worker Manager will restart the process along
                with any global restart (ex: auto-reload), defaults to `False`
            restartable (Optional[bool], optional): Whether to mark the process as restartable. If
                `True` then the Worker Manager will be able to restart the process
                if prompted. If `transient=True`, this property will be implied
                to be `True`, defaults to `None`
            tracked (bool, optional): Whether to track the process after completion,
                defaults to `True`
            auto_start (bool, optional): Whether to start the process immediately, defaults to `True`
            workers (int, optional): The number of worker processes to run. Defaults to `1`.
            ident (str, optional): The identifier for the worker. If not provided, the name
                passed will be used. Defaults to `""`.

        Returns:
            Worker: The Worker instance
        Worker z already existsNz8Cannot create a transient worker that is not restartable)r'   r(   
ValueErrorr   r&   r,   r?   )r7   r?   r@   rA   r'   rB   rC   rD   rE   rF   	containerworkerr    r    r!   manage[   s,   #
zWorkerManager.managec              	   C   s<   t | j}| jtj d| | j| jddtj |ddS )z_Create a new server process.

        Returns:
            Worker: The Worker instance
        -T2)r'   rB   rF   )nextr1   rL   r   SERVER_LABELr/   r0   SERVER_IDENTIFIER)r7   server_numberr    r    r!   r4      s   
zWorkerManager.create_serverc                 C   s`   |sdd | j  D }|std dS t|}n| j | }|jD ]}|  q"| j |j= dS )zShutdown a server process.

        Args:
            name (Optional[str], optional): The name of the server process to shutdown.
                If `None` then a random server will be chosen. Defaults to `None`.
        c                 S   s   g | ]}|j tjr|qS r    )r?   
startswithr   rP   .0rK   r    r    r!   
<listcomp>   s    z1WorkerManager.shutdown_server.<locals>.<listcomp>z6Server shutdown failed because a server was not found.N)r'   valuesr   errorr   	processes	terminater?   )r7   r?   serversrK   processr    r    r!   shutdown_server   s   



zWorkerManager.shutdown_serverc                 C   s,   |    |   |   |   |   dS )zRun the worker manager.N)startmonitorjoinrZ   cleanupr7   r    r    r!   run   s
   zWorkerManager.runc                 C   s:   | j D ]}|jD ]}|js|tjd q|  qqdS )zStart the worker processes.TN)rE   rY   rD   	set_stater   NONEr^   r7   rK   r\   r    r    r!   r^      s   


zWorkerManager.startc                 C   s   t jdddid t }| jD ]1}t jd|j d|jj ddid |jtjk r@t jd|j ddid |	|j |
  q|rI| 
  dS dS )	zJoin the worker processes.zJoining processes	verbosityr=   extrazFound z - zJoining N)r   debugsetrY   r$   stater?   r   JOINEDaddr`   )r7   joinedr\   r    r    r!   r`      s   
zWorkerManager.joinc                 C   s"   | j s| jD ]}|  qdS dS )zTerminate the worker processes.N)r.   rY   rZ   r7   r\   r    r    r!   rZ      s
   

zWorkerManager.terminatec                 C   s   | j D ]}|  qdS )zCleanup the worker processes.N)rY   exitrp   r    r    r!   ra      s   

zWorkerManager.cleanupprocess_namesc                 K   s,   | j jdt| jt| j||d| dS )a  Restart the worker processes.

        Args:
            process_names (Optional[List[str]], optional): The names of the processes to restart.
                If `None` then all processes will be restarted. Defaults to `None`.
            restart_order (RestartOrder, optional): The order in which to restart the processes.
                Defaults to `RestartOrder.SHUTDOWN_FIRST`.
        )transient_processesdurable_processesrr   restart_orderNr    )r)   restartlistrs   rt   )r7   rr   ru   rA   r    r    r!   rv      s   
zWorkerManager.restart
num_workerc                 C   s   |dkrt d|| j }|dkrtd| d d S td| j d| d |dkrCt|D ]}|  }|jD ]}|  q:q1ntt|D ]}| 	  qI|| _d S )Nr   zCannot scale to 0 workers.z$No change needed. There are already z	 workers.zScaling from z to z workers)
rI   r%   r   infor3   r4   rY   r^   absr]   )r7   rx   changer;   rK   r\   r    r    r!   scale  s&   





zWorkerManager.scalec                 C   sb   |    	 z|  }|tju rW dS |tju rW q|   |   W n ty/   ts, Y dS w q)a  Monitor the worker processes.

        First, wait for all of the workers to acknowledge that they are ready.
        Then, wait for messages from the workers. If a message is received
        then it is processed and the state of the worker is updated.

        Also used to restart, shutdown, and scale the workers.

        Raises:
            ServerKilled: Raised when a worker fails to come online.
        TN)	wait_for_ack_poll_monitorr   r   r   _sync_states_cleanup_non_tracked_workersInterruptedErrorr   )r7   cycler    r    r!   r_     s    

zWorkerManager.monitorc                 C   s   d}d| j d  d}|  sD| jdr*| j }|dkr%| j| q| j }d}|d7 }|| j kr>td	|  | 	  |  rd
S d
S )z?Wait for all of the workers to acknowledge that they are ready.r   zIt seems that one or more of your workers failed to come online in the allowed time. Sanic is shutting down to avoid a deadlock. The current threshold is 
   z~s. If this problem persists, please check out the documentation https://sanic.dev/en/guide/deployment/manager.html#worker-ack.皙?__TERMINATE_EARLY__a|  One of your worker processes terminated before startup was completed. Please solve any errors experienced during startup. If you do not see an exception traceback in your error logs, try running Sanic in a single process using --single-process or single_process=True. Once you are confident that the server is able to start without errors you can switch back to multiprocess mode.r=   zCNot all workers acknowledged a successful startup. Shutting down.

N)
	THRESHOLD_all_workers_ackr+   pollrecvr*   sendr   rX   kill)r7   missesmessagemonitor_msgr    r    r!   r}   3  s.   
	
zWorkerManager.wait_for_ackc                 C   s   t | j t | j  S )zGet all of the workers.)rw   r'   rW   r(   rb   r    r    r!   rE   U  s   zWorkerManager.workersc                 C   s   t | j | j S N)r   r'   itemsr(   rb   r    r    r!   all_workersZ  s   zWorkerManager.all_workersc                 c   s,    | j D ]}|jD ]	}|jsq	|V  q	qdS )zGet all of the processes.N)rE   rY   r$   rf   r    r    r!   rY   ^  s   

zWorkerManager.processesc                 c   s"    | j  D ]}|jE dH  qdS )z#Get all of the transient processes.N)r'   rW   rY   r7   rK   r    r    r!   rs   g  s   z!WorkerManager.transient_processesc                 c   s"    | j  D ]}|jE d H  qd S r   )r(   rW   rY   r   r    r    r!   rt   m  s   zWorkerManager.durable_processesc              
   C   s   | j D ]<}td|j|j tt% ztt	|jt
 W n ty/   t|jt
 Y nw W d   n1 s:w   Y  qt)zKill all of the processes.zKilling %s [%s]N)rY   r   ry   r?   r$   r   ProcessLookupErroroskillpggetpgidr   OSErrorr   r   rp   r    r    r!   r   r  s   

zWorkerManager.killc                 C   sp   | j r#td tt |   W d   dS 1 sw   Y  dS tdt|j | j	d | 
  dS )zHandle the shutdown signal.zShutdown interrupted. Killing.Nz"Received signal %s. Shutting down.)r.   r   ry   r   r   r   r   r?   r*   r   shutdown)r7   r   framer    r    r!   r6   }  s   



zWorkerManager.shutdown_signalc                 C   s&   | j D ]
}| r|  qd| _dS )zShutdown the worker manager.TN)rY   is_aliverZ   r.   rp   r    r    r!   r     s
   

zWorkerManager.shutdownrK   c                 C   s   |j rtd|j d d S | rtd|j d d S | j|jd  | j|jd  |jD ]
}| j	|jd  q2t
d|j ~d S )NrH   z" is tracked and cannot be removed.z+ has alive processes and cannot be removed.zRemoved worker %s)rC   r   rX   r?   has_alive_processesr'   popr(   rY   r,   r   ry   rf   r    r    r!   remove_worker  s    
zWorkerManager.remove_workerc                 C   s   t  S )z'Get the process ID of the main process.)r   getpidrb   r    r    r!   r$     s   zWorkerManager.pidc                 C   s*   dd | j  D }t|ot|| jkS )Nc                 S   s(   g | ]}| d r| dtjjkqS )serverrl   )getr   ACKEDr?   )rU   r,   r    r    r!   rV     s    z2WorkerManager._all_workers_ack.<locals>.<listcomp>)r,   rW   alllenr%   )r7   ackedr    r    r!   r     s   zWorkerManager._all_workers_ackc              	   C   s~   | j D ]9}z| j|j d}W n ty    |tjd Y qw | s,|j	r*dnd}|r<|j
j|kr<|t| d qd S )Nrl   TFAILED	COMPLETED)rY   r,   r?   r   KeyErrorrd   r   
TERMINATEDr   exitcoderl   )r7   r\   rl   r    r    r!   r     s   
zWorkerManager._sync_statesc                 C   s(   dd | j D }|D ]}| | q
d S )Nc                 S   s   g | ]}|j s| s|qS r    )rC   r   rT   r    r    r!   rV     s    z>WorkerManager._cleanup_non_tracked_workers.<locals>.<listcomp>)rE   r   )r7   	to_removerK   r    r    r!   r     s   z*WorkerManager._cleanup_non_tracked_workersc                 C   s   | j drS| j  }tjd| ddid |stjS |dkr'|   tjS t|t	r@t
|dks8t
|dkr@| j|  tjS t|tsNtd	| tjS | |S d S )
Nr   zMonitor message: rg      rh   __TERMINATE__      z'Monitor received an invalid message: %s)r+   r   r   r   rj   r   r   _handle_terminate
isinstancetupler   _handle_manager   strr   rX   _handle_message)r7   r   r    r    r!   r~     s&   




zWorkerManager._poll_monitorc                 C   s   |    d S r   )r   rb   r    r    r!   r     s   zWorkerManager._handle_terminater   c                 C   s   t jd|ddid |dd}|dr!| t|d  tjS |d	 }t|dkr/|d nd }d
d |dD }|rCd|v rCd }d|v rJt	j
nt	j}| j|||d d S )NzIncoming monitor message: %srg   r=   rh   :r   	__SCALE__r   c                 S   s   g | ]}|  qS r    )strip)rU   r?   r    r    r!   rV     s    z1WorkerManager._handle_message.<locals>.<listcomp>,__ALL_PROCESSES__STARTUP_FIRST)rr   reloaded_filesru   )r   rj   splitrS   r|   intr   r   r   r   r   SHUTDOWN_FIRSTrv   )r7   r   split_messagerY   r   rr   orderr    r    r!   r     s4   
zWorkerManager._handle_messagec	              
   C   s`   z| j ||||||||d}	W n ty   td| Y d S w |s$d S |	jD ]}
|
  q'd S )N)r'   rB   rC   rD   rE   zFailed to manage worker %s)rL   	Exceptionr   	exceptionrY   r^   )r7   r?   r@   rA   r'   rB   rC   rD   rE   rK   r\   r    r    r!   r     s&   



zWorkerManager._handle_manage)FNTTr=   r>   r   )rG   N)4r   r   r   __doc__r   r   r-   r   r<   r   r   r   dictboolr   r   rL   r4   r]   rc   r^   r`   rZ   ra   r   r   rw   rv   r|   r_   r}   propertyrE   r   r   r   rY   rs   rt   r   r6   r   r   r$   r   r   r   r   r~   r   r   r   r    r    r    r!   r"       s    
$

	

:	

"








	
r"   )*r   collections.abcr   
contextlibr   enumr   r   	itertoolsr   r   randomr   r   r	   r
   r   r5   typingr   r   r   sanic.compatr   sanic.exceptionsr   	sanic.logr   r   sanic.worker.constantsr   sanic.worker.processr   r   r   sanic.worker.restarterr   r   __annotations__r   r   r"   r    r    r    r!   <module>   s*   
 