
    AҐih                         d 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Zddlm	Z	 ddl
mZmZ ddlmZmZmZmZ ddlmZmZmZmZmZmZmZmZmZmZmZmZmZm Z m!Z! ddl"m#Z#  G d	 d
      Z$y)z
Dirty Arbiter Process

Asyncio-based arbiter that manages the dirty worker pool and routes
requests from HTTP workers to available dirty workers.
    N)util   )get_app_workers_attributeparse_dirty_app_spec)
DirtyErrorDirtyNoWorkersAvailableErrorDirtyTimeoutErrorDirtyWorkerError)DirtyProtocolmake_error_responsemake_responseSTASH_OP_PUTSTASH_OP_GETSTASH_OP_DELETESTASH_OP_KEYSSTASH_OP_CLEARSTASH_OP_INFOSTASH_OP_ENSURESTASH_OP_DELETE_TABLESTASH_OP_TABLESSTASH_OP_EXISTSMANAGE_OP_ADDMANAGE_OP_REMOVE)DirtyWorkerc                   V   e Zd ZdZdj	                         D  cg c]  }t        t        d|z         c}}}} ZdZd&dZ	d Z
d Zd	 Zd
 Zd Zd Zd Zd Zd Zd Zd Zd Zd Zd Zd Zd Zd'dZd Zd Zd Zd Zd Zd Z d(dZ!d Z"d  Z#d! Z$d" Z%d# Z&d)d$Z'd% Z(yc c}}}} w )*DirtyArbitera4  
    Dirty arbiter that manages the dirty worker pool.

    The arbiter runs an asyncio event loop and handles:
    - Spawning and managing dirty worker processes
    - Accepting connections from HTTP workers
    - Routing requests to available dirty workers
    - Monitoring worker health via heartbeat
    z*HUP QUIT INT TERM TTIN TTOU USR1 USR2 CHLDzSIG%s   Nc                 N   || _         || _        d| _        t        j                         | _        || _        t        j                  d      | _	        |xs* t        j                  j                  | j                  d      | _        i | _        i | _        i | _        i | _        i | _        d| _        d| _        d| _        | j                   j*                  | _        d| _        d| _        i | _        i | _        i | _        i | _        i | _        g | _        i | _        | jA                          y)z
        Initialize the dirty arbiter.

        Args:
            cfg: Gunicorn config
            log: Logger
            socket_path: Path to the arbiter's Unix socket
            pidfile: Well-known PID file location for orphan detection
        Nzgunicorn-dirty-)prefixzarbiter.sockr   T)!cfglogpidosgetpidppidpidfiletempfilemkdtemptmpdirpathjoinsocket_pathworkersworker_socketsworker_connectionsworker_queuesworker_consumers_worker_rr_index
worker_agealivedirty_workersnum_workers_server_loop_pending_requests	app_specsapp_worker_mapworker_app_map_app_rr_indices_pending_respawnsstash_tables_parse_app_specs)selfr    r!   r,   r&   s        O/var/www/descvideos/venv/lib/python3.12/site-packages/gunicorn/dirty/arbiter.py__init__zDirtyArbiter.__init__B   s    IIK	 &&.?@& 
"'',,KK+
  "$ " !
8811
!#   !!#  	    c                 ,   | j                   j                  D ]H  }t        |      \  }}|	 t        |      }|||d| j                  |<   t               | j                  |<   J y# t        $ r'}| j
                  j                  d||       Y d}~Xd}~ww xY w)a  
        Parse all app specifications from config.

        Populates self.app_specs with parsed information about each app,
        including the import path and worker count limits.

        Worker count priority:
        1. Config override (e.g., "module:Class:2") - highest priority
        2. Class attribute (e.g., workers = 2 on the class)
        3. None (all workers) - default
        Nz,Could not read workers attribute from %s: %s)import_pathworker_countoriginal_spec)
r    
dirty_appsr   r   	Exceptionr!   warningr:   setr;   )rA   specrF   rG   es        rB   r@   zDirtyArbiter._parse_app_specsy   s     HH'' 	5D(<T(B%K ##<[#IL  + ,!%+DNN;' 03uD,)	5 ! HH$$F#Q s   A##	B,BBc                 p    d}| j                   j                         D ]  }|d   }|t        ||      } |S )a  
        Calculate minimum number of workers required by app specs.

        Returns the maximum worker_count across all apps that have limits.
        Apps with worker_count=None don't impose a minimum.

        Returns:
            int: Minimum workers required (at least 1)
        r   rG   )r:   valuesmax)rA   min_requiredrM   rG   s       rB   _get_minimum_workersz!DirtyArbiter._get_minimum_workers   sI     NN))+ 	?D/L'"<>	? rD   c                    g }| j                   j                         D ]b  \  }}|d   }t        | j                  j	                  |t                           }||j                  |       L||k  sR|j                  |       d |S )a  
        Determine which apps a new worker should load.

        Returns a list of import paths for apps that need more workers.
        Apps with workers=None (all workers) are always included.
        Apps with worker limits are included only if they haven't
        reached their limit yet.

        Returns:
            List of import paths to load, or empty list if no apps need workers
        rG   )r:   itemslenr;   getrL   append)rA   	app_pathsrF   rM   rG   current_workerss         rB   _get_apps_for_new_workerz%DirtyArbiter._get_apps_for_new_worker   s     	!%!5!5!7 		.K/L!$"5"5"9"9+su"MNO #  - </  -		. rD   c                     t        |      | j                  |<   |D ]E  }|| j                  vrt               | j                  |<   | j                  |   j	                  |       G y)a?  
        Register which apps a worker has loaded.

        Updates both app_worker_map and worker_app_map to track the
        bidirectional relationship between workers and apps.

        Args:
            worker_pid: The PID of the worker
            app_paths: List of app import paths loaded by this worker
        N)listr<   r;   rL   addrA   
worker_pidrY   app_paths       rB   _register_worker_appsz"DirtyArbiter._register_worker_apps   s`     +/y/J'! 	:Ht22203##H-)--j9	:rD   c                     | j                   j                  |g       }|D ]/  }|| j                  v s| j                  |   j                  |       1 y)z
        Unregister a worker's apps when it exits.

        Removes the worker from all tracking maps.

        Args:
            worker_pid: The PID of the worker to unregister
        N)r<   popr;   discardr_   s       rB   _unregister_workerzDirtyArbiter._unregister_worker   sV     ''++J;	 " 	BH4...##H-55jA	BrD   c                    t        j                         | _        | j                  j	                  d| j                         | j
                  rD	 t        | j
                  d      5 }|j                  t        | j                               ddd       | j                  t         j                  d<   | j                  j                  |        | j                          t!        j"                  d       	 t%        j&                  | j)                                | j-                          y# 1 sw Y   xY w# t        $ r&}| j                  j                  d|       Y d}~d}~ww xY w# t*        $ r Y Zw xY w# | j-                          w xY w)z&Run the dirty arbiter (blocking call).z Dirty arbiter starting (pid: %s)wNzFailed to write PID file: %sGUNICORN_DIRTY_SOCKETzdirty-arbiter)r#   r$   r"   r!   infor&   openwritestrIOErrorrK   r,   environr    on_dirty_startinginit_signalsr   _setproctitleasynciorun
_run_asyncKeyboardInterrupt_cleanup_sync)rA   frN   s      rB   rt   zDirtyArbiter.run   s(   99;8$((C <<D$,,, +GGCM*+ /3.>.>

*+ 	""4( 	 	?+	!KK)*  -+ + D  !?CCD" ! 		  sT   D. #%D"D. .#E  "D+'D. .	E7EE 	E,)E/ +E,,E/ /Fc                 N   | j                   D ]&  }t        j                  |t        j                         ( t        j                  t        j                  | j                         t        j                  t        j
                  | j                         t        j                  t        j                  | j                         t        j                  t        j                  | j                         t        j                  t        j                  | j                         t        j                  t        j                  | j                         t        j                  t        j                  | j                         t        j                  t        j                  | j                         y)zSet up signal handlers.N)SIGNALSsignalSIG_DFLSIGTERM_signal_handlerSIGQUITSIGINTSIGHUPSIGUSR1SIGCHLDSIGTTINSIGTTOU)rA   sigs     rB   rq   zDirtyArbiter.init_signals
  s    << 	/CMM#v~~.	/ 	fnnd&:&:;fnnd&:&:;fmmT%9%9:fmmT%9%9:fnnd&:&:;fnnd&:&:;fnnd&:&:;fnnd&:&:;rD   c                 (    |t         j                  k(  r+ j                  r j                  j                   fd       y|t         j                  k(  r j
                  j                          y|t         j                  k(  r+ j                  r j                  j                   fd       y|t         j                  k(  rf xj                  dz  c_	         j
                  j                  d j                          j                  r j                  j                   fd       y|t         j                  k(  r j                         } j                  |k  r j
                  j                  d|       y xj                  dz  c_	         j
                  j                  d j                          j                  r j                  j                   fd	       yd
 _         j                  r& j                  j                   j                         yy)zHandle signals.c                  J    t        j                   j                               S N)rs   create_task_handle_sigchldrA   s   rB   <lambda>z.DirtyArbiter._signal_handler.<locals>.<lambda>  s    G//0D0D0FG rD   Nc                  J    t        j                   j                               S r   )rs   r   reloadr   s   rB   r   z.DirtyArbiter._signal_handler.<locals>.<lambda>+  s    G//> rD   r   z'SIGTTIN: Increasing dirty workers to %sc                  J    t        j                   j                               S r   rs   r   manage_workersr   s   rB   r   z.DirtyArbiter._signal_handler.<locals>.<lambda>6      G//0C0C0EF rD   zASIGTTOU: Cannot decrease below %s workers (required by app specs)z'SIGTTOU: Decreasing dirty workers to %sc                  J    t        j                   j                               S r   r   r   s   rB   r   z.DirtyArbiter._signal_handler.<locals>.<lambda>I  r   rD   F)r{   r   r8   call_soon_threadsafer   r!   reopen_filesr   r   r6   rj   r   rS   rK   r4   	_shutdown)rA   r   framemin_workerss   `   rB   r~   zDirtyArbiter._signal_handler  s   &.. zz

//G &.. HH!!#&--zz

//> &.. !HHMMC**,zz

//F &.. 335K;.  .
 !HHMMC**,zz

//F  
::JJ++DNN; rD   c                 R    | j                   r| j                   j                          yy)zInitiate async shutdown.N)r7   closer   s    rB   r   zDirtyArbiter._shutdownR  s    <<LL  rD   c                   K   t        j                         | _        t        j                  j                  | j                        rt        j                  | j                         t        j                  | j                  | j                         d{   | _
        t        j                  | j                  d       | j                  j                  d| j                         | j                          d{    t        j                  | j!                               }	 | j                  4 d{    | j                  j#                          d{    ddd      d{    |j)                          	 | d{    | j+                          d{    y7 7 7 o7 O7 A# 1 d{  7  sw Y   QxY w# t         j$                  t&        f$ r Y ow xY w7 \# t         j$                  $ r Y ow xY w7 _# |j)                          	 | d{  7   n# t         j$                  $ r Y nw xY w| j+                          d{  7   w xY ww)z/Main async loop - start server, manage workers.)r*   Ni  zDirty arbiter listening on %s)rs   get_running_loopr8   r#   r*   existsr,   unlinkstart_unix_serverhandle_clientr7   chmodr!   rj   r   r   _worker_monitorserve_foreverCancelledErrorRuntimeErrorcancelstop)rA   monitor_tasks     rB   ru   zDirtyArbiter._run_asyncW  s    --/
 77>>$**+IId&&' %66!!
 
 	!!5)5t7G7GH !!### **4+?+?+AB	|| 3 3ll002223 3 !""" ))+;
 	$323 3 3 3&&5 		 #))   !"""))  ))+s-  BI	FA"I	5F6'I	F9 .F/F9 2F$F F$F9  F"!F9 %I	6G ;G<G  I	G3I	I	F9  F$"F9 $F6*F-+F62F9 9GG5 GG5 G G0-I	/G00I	5IHHHIH)&I(H))I?I II	c                   K   | j                   rt        j                  d       d{    t        j                         | j
                  k7  r3| j                  j                  d       d| _         | j                          y| j                          d{    | j                          d{    | j                   ryy7 7 -7 w)z1Periodically check worker health and manage pool.g      ?Nz+Parent changed, shutting down dirty arbiterF)r4   rs   sleepr#   getppidr%   r!   rK   r   murder_workersr   r   s    rB   r   zDirtyArbiter._worker_monitor  s     jj--$$$ zz|tyy(  !NO"
 %%'''%%''' jj$ ('s:   %CB?A+CCC+C,C=CCCc                 z   K   | j                          | j                  r| j                          d{    yy7 w)z#Handle SIGCHLD - reap dead workers.N)reap_workersr4   r   r   s    rB   r   zDirtyArbiter._handle_sigchld  s3     ::%%''' 's   0;9;c                   K   | j                   j                  d       	 | j                  r	 t        j                  |       d{   }|j                  d      }|t        j                  k(  r| j                  ||       d{    nv|t        j                  k(  r| j                  ||       d{    nH|t        j                  k(  r| j                  ||       d{    n| j                  ||       d{    | j                  r|j#                          	 |j%                          d{    y7 # t
        j                  $ r Y Aw xY w7 7 7 u7 \# t        $ r&}| j                   j!                  d|       Y d}~wd}~ww xY w7 Z# t        $ r Y yw xY w# |j#                          	 |j%                          d{  7   w # t        $ r Y w w xY wxY ww)a
  
        Handle a connection from an HTTP worker.

        Routes requests to available dirty workers and returns responses.
        Supports both regular responses and streaming (chunk-based) responses.
        Also handles stash (shared state) operations.
        z&New client connection from HTTP workerNtypezClient connection error: %s)r!   debugr4   r   read_message_asyncrs   IncompleteReadErrorrW   MSG_TYPE_STASHhandle_stash_requestMSG_TYPE_STATUShandle_status_requestMSG_TYPE_MANAGEhandle_manage_requestroute_requestrJ   errorr   wait_closed)rA   readerwritermessagemsg_typerN   s         rB   r   zDirtyArbiter.handle_client  s     	?@	**$1$D$DV$LLG #;;v. };;;33GVDDD!>!>>44WfEEE!>!>>44WfEEE ,,Wf===' **. LLN((***/ M22  E F F > 	=HHNN8!<<	=
 +  LLN((*** s  GE D7 D5D7 	9E E-E 0E1-E EE 9E:E GF /F
0F 4G5D7 7E
E EE E E E 	F!F=F FF 
F 	FGFGG-G GGG	GGGGGc                   K   |j                  dd      }|j                  d      }| j                  |       d{   }|h| j                  st        d      }n%|r| j                  rt        |      }nt        d      }t        ||      }t        j                  ||       d{    y|| j                  vr| j                  |       d{    | j                  |   }t        j                         j                         }	|j                  |||	f       d{    	 |	 d{    y7 7 7 a7 7 # t        $ rC}
t        |t!        d|
 |            }t        j                  ||       d{  7   Y d}
~
yd}
~
ww xY ww)a  
        Route a request to an available dirty worker via queue.

        Each worker has a dedicated queue and consumer task. Requests are
        submitted to the queue and processed sequentially by the consumer.

        For streaming responses, messages (chunks) are forwarded directly
        to the client_writer as they arrive from the worker.

        Args:
            request: Request message dict
            client_writer: StreamWriter to send responses to client
        idunknownra   NzNo dirty workers availablezRequest failed: 	worker_id)rW   _get_available_workerr-   r   r:   r   r   r   write_message_asyncr0   _start_worker_consumerrs   r   create_futureputrJ   r
   )rA   requestclient_writer
request_idra   r`   r   responsequeuefuturerN   s              rB   r   zDirtyArbiter.route_request  sp     [[y1
;;z*  55h??
<<"#?@dnn4X>"#?@*:u=H33M8LLL T///--j999"":.))+99; ii-8999	MLL5 @ M
 : 	:  	M* #3A3!7:NH  33M8LLL	Ms   8FD)A(F#D+$'FD-AFD/FD3 #D1$D3 (F+F-F/F1D3 3	E?<3E:/E20E:5F:E??Fc                     K   t        j                          j                  <    fd}t        j                   |             }| j                  <   yw)z3Start a consumer task for a worker's request queue.c                    K   j                   ry	 j                          d {   \  } }}	 j                  | |       d {    |j                         s|j	                  d        j                          	 j                   rxy y 7 e7 G# t
        $ r+}|j                         s|j                  |       Y d }~Sd }~ww xY w# j                          w xY w# t        j                  $ r Y y w xY wwr   )
r4   rW   _execute_on_workerdone
set_resultrJ   set_exception	task_doners   r   )r   r   r   rN   r   rA   r`   s       rB   consumerz5DirtyArbiter._start_worker_consumer.<locals>.consumer  s     **;@99;5F2G]F
*"55&    &{{}"--d3
 ) **5F
 % 4%{{}"0034 )-- s   C3C B
C B B%B *C :C3C3
C B 	C!B=8C =CC CC C0-C3/C00C3N)rs   Queuer0   r   r1   )rA   r`   r   taskr   s   ``  @rB   r   z#DirtyArbiter._start_worker_consumer  sK     ).:&	$ ""8:.,0j)s   AAc                   K   |j                  dd      }	 | j                  |       d{   \  }}t        j                  ||       d{    	 	 t	        j
                  t        j                  |      | j                  j                         d{   }|j                  d      }	|	t        j                  k(  rt        j                  ||       d{    |	t        j                  k(  rt        j                  ||       d{    y|	t        j                  t        j                  fv rt        j                  ||       d{    y| j                   j#                  d|	       7 B7 $7 # t        j                  $ rL t        |t        d| j                  j                              }t        j                  ||       d{  7   Y yw xY w7 7 7 # t$        $ rq}
| j                   j'                  d||
       | j)                  |       t        |t+        d	|
 |
            }t        j                  ||       d{  7   Y d}
~
yd}
~
ww xY ww)a  
        Execute request on a specific worker (called by consumer).

        Handles both regular responses and streaming (chunk-based) responses.
        For streaming, chunk and end messages are forwarded directly to the
        client_writer as they arrive from the worker.
        r   r   N)timeoutzWorker timeoutr   z$Unknown message type from worker: %sz Error executing on worker %s: %szWorker communication failed: r   )rW   _get_worker_connectionr   r   rs   wait_forr   r    dirty_timeoutTimeoutErrorr   r	   MSG_TYPE_CHUNKMSG_TYPE_ENDMSG_TYPE_RESPONSEMSG_TYPE_ERRORr!   rK   rJ   r   _close_worker_connectionr
   )rA   r`   r   r   r   r   r   r   r   r   rN   s              rB   r   zDirtyArbiter._execute_on_worker  s%     [[y1
0	M#'#>#>z#JJNFF33FGDDD $+$4$4%88@ $ 6 6% G #;;v. };;;';;M7SSS }999';;M7SSS  ? ? - < < > >';;M7SSS   !GRC 	 KD
 ++ 2")*:DHH<R<RS H (;;M8TTT T
 T T  	MHHNN=z1M))*5* #@!D+57H
  33M8LLL	Ms   IG E, G E/G AE4 E2E4 =G G1G GG I;G G	G IG /G 2E4 4AG
GGG IGG G G 	I&A!II
IIIIc                   K   |r4| j                   r(|| j                  v rt        | j                  |         }n$yt        | j                  j	                               }|sy|rG| j                   r;| j
                  j                  |d      }|dz   t        |      z  | j
                  |<   n"| j                  }|dz   t        |      z  | _        ||t        |      z     S w)a  
        Get an available worker PID using round-robin selection.

        If app_path is provided, only returns workers that have loaded
        that specific app. Uses per-app round-robin to ensure fair
        distribution among eligible workers.

        Args:
            app_path: Optional import path of the target app. If None,
                     returns any worker using global round-robin.

        Returns:
            Worker PID or None if no eligible workers are available.
        Nr   r   )	r:   r;   r]   r-   keysr=   rW   rV   r2   )rA   ra   eligible_pidsidxs       rB   r   z"DirtyArbiter._get_available_workerG  s        4... $T%8%8%B C  !!2!2!45M &&**8Q7C.1Ag]9K-KD  *''C%(1WM0B$BD!S3}#5566s   CCc                   K   || j                   v r| j                   |   S | j                  j                  |      }|st        d|       t	        d      D ]@  }t
        j                  j                  |      r n-t        j                  d       d{    B t        d|       t        j                  |       d{   \  }}||f| j                   |<   ||fS 7 I7 w)z%Get or create connection to a worker.zNo socket for worker 2   皙?NzWorker socket not ready: )r/   r.   rW   r   ranger#   r*   r   rs   r   open_unix_connection)rA   r`   r,   _r   r   s         rB   r   z#DirtyArbiter._get_worker_connectionq  s     000**:66))--j94ZLABB r 	HAww~~k*--$$$	H
 8FGG&;;KHH/5v.>
+v~ % Is$   BC C,C ?C C C c                 ~    || j                   v r/| j                   j                  |      \  }}|j                          yy)zClose connection to a worker.N)r/   rd   r   )rA   r`   _readerr   s       rB   r   z%DirtyArbiter._close_worker_connection  s8    000"5599*EOGVLLN 1rD   c                   K   |j                  dd      }t        j                         }g }| j                  j	                         D ]f  \  }}	 |j
                  j                         }t        ||z
  d      }	|j                  ||j                  t        |dg       t        |dd      |	d       h |j                  d	 
       | j                  |t!        |      | j"                  r#t%        | j"                  j'                               ng d}
t)        ||
      }t+        j,                  ||       d{    y# t        t        t        f$ r d}	Y w xY w7 !w)z
        Handle a status query request.

        Returns information about the dirty arbiter and its workers.

        Args:
            message: Status request message
            client_writer: StreamWriter to send response to client
        r   r      NrY   bootedF)r"   ageappsr   last_heartbeatc                     | d   S )Nr    )rh   s    rB   r   z4DirtyArbiter.handle_status_request.<locals>.<lambda>  s
    % rD   key)arbiter_pidr-   rG   r   )rW   time	monotonicr-   rU   tmplast_updateroundOSError
ValueErrorAttributeErrorrX   r   getattrsortr"   rV   r:   r]   r   r   r   r   )rA   r   r   r   nowworkers_infor"   workerr  r   resultr   s               rB   r   z"DirtyArbiter.handle_status_request  s>     [[y1
nn<<--/ 	KC&$jj446!&s['8!!< zzR8!&(E:"0! 	 	01  88#-37>>D,,./r	
 !V4//xHHH+ Z8 &!%&* 	Is7   A	E)D55B:E/E0E5EEEEc                 T   K   |j                  dd      }|j                  d      }t        dt        |j                  dd                  }	 |t        k(  rd}t	        |      D ]K  } j                         }| xj                  dz  c_        |dz  }t        j                  d       d{    M |dk(  r)d	d
|ddt         j                         j                  d}n]d	d
||t         j                         j                  d}n5|t        k(  r j                         }	d}
t	        |      D ]  } j                  |	k  r nt         j                        dk  r n xj                  dz  c_        t         j                  j                          fd      } j                  |t         j"                         |
dz  }
t        j                  d       d{     d	d||
t         j                         j                  d}n9t%        d|       }t'        ||      }t)        j*                  ||       d{    y j,                  j/                  d|t        k(  rd
nd||j                  d|j                  dd                   t1        ||      }t)        j*                  ||       d{    y7 7 7 ~7 # t2        $ rc} j,                  j5                  d|       t'        |t%        t7        |                  }t)        j*                  ||       d{  7   Y d}~yd}~ww xY ww)z
        Handle a worker management request.

        Supports adding or removing dirty workers via protocol messages.

        Args:
            message: Manage request message
            client_writer: StreamWriter to send response to client
        r   r   opr   countr   Nr   Tr^   z)All apps have reached their worker limits)success	operation	requestedspawnedreasontotal_workerstarget_workers)r  r  r  r  r  r  c                 6    j                   |    j                  S r   r-   r   prA   s    rB   r   z4DirtyArbiter.handle_manage_request.<locals>.<lambda>  s    4<<?3F3F rD   r   remove)r  r  r  removedr  r  zUnknown manage operation: z6Worker management: %s %d workers (spawned/removed: %d)r  r  zManage operation error: %s)rW   rQ   intr   r   spawn_workerr6   rs   r   rV   r-   r   rS   minr   kill_workerr{   r}   r   r   r   r   r!   rj   r   rJ   r   rm   )rA   r   r   r   r  r  r  r   r  r   r  
oldest_pidr   r   rN   s   `              rB   r   z"DirtyArbiter.handle_manage_request  s     [[y1
[[As7;;w234N	M]"u -A!..0F)((A-(1!--,,,- a<#'%*%*#$"M),T\\):*.*:*:F $(%*%*#*),T\\):*.*:*:F ''"779u -A'';64<<(A-$$)$ "%T\\%6%6%8)F"HJ$$Z@qLG!--,,,-   $!)!&&%(%6&*&6&6 #%?t#DE.z5A#77xPPPHHMMR#%#6%H **Y

9a0HIK
 %Z8H33M8LLLA -R - Q M 	MHHNN7;*:z#a&7IJH33M8LLL	Ms   A	L(AJ9 *J0+D*J9 J3A J9 6J57J9 ;L(<A.J9 *J7+J9 /L(0J9 3J9 5J9 7J9 9	L%AL LL L( L%%L(c           	      d  K   |j                  dd      }|j                  d      }|j                  dd      }|j                  d      }|j                  d      }|j                  d      }	 d	}	|t        k(  r3|| j                  vri | j                  |<   || j                  |   |<   d
}	nX|t        k(  r?|| j                  vrddi}	n;|| j                  |   vrddi}	n$| j                  |   |   }	n|t        k(  r7|| j                  v r%|| j                  |   v r| j                  |   |= d
}	nd}	n|t
        k(  rl|| j                  vrg }	nt        | j                  |   j                               }
|r.|
D cg c]#  }t        j                  t        |      |      r|% }
}|
}	n[|t        k(  r/|| j                  v r| j                  |   j                          d
}	n#|t        k(  r0|| j                  vrddi}	nt        | j                  |         |d}	n|t        k(  r || j                  vri | j                  |<   d
}	n|t        k(  r!|| j                  v r| j                  |= d
}	nd}	n|t         k(  r$t        | j                  j                               }	nj|t"        k(  r(|| j                  vrd}	nP|d
}	nK|| j                  |   v }	n9t%        d|       }t'        ||      }t)        j*                  ||       d	{    y	t-        |	t.              r{d|	v rw|	d   }|dk(  rt%        d|       }n(|dk(  rt%        d|       }nt%        t        |	            }d|j1                         j3                  dd       d|_        t'        ||      }nt7        ||	      }t)        j*                  ||       d	{    y	c c}w 7 7 # t8        $ rc}| j:                  j=                  d|       t'        |t%        t        |                  }t)        j*                  ||       d	{  7   Y d	}~y	d	}~ww xY ww)a!  
        Handle a stash operation directly in the arbiter.

        All stash tables are stored in arbiter memory for simplicity
        and fast access.

        Args:
            message: Stash operation message
            client_writer: StreamWriter to send response to client
        r   r   r  table r   valuepatternNTr   key_not_foundFtable_not_found)sizer"  zUnknown stash operation: zTable not found: zKey not found: Stashr   ErrorzStash operation error: %s)rW   r   r?   r   r   r   r]   r   fnmatchrm   r   clearr   rV   r   r   r   r   r   r   r   r   
isinstancedicttitlereplace
error_typer   rJ   r!   r   )rA   r   r   r   r  r"  r   r$  r%  r  all_keyskr   r   r1  rN   s                   rB   r   z!DirtyArbiter.handle_stash_request  s/     [[y1
[[GR(kk% G$++i(a	MF\! 1 11/1D%%e,05!!%(-|# 1 11%7F 1 1% 88%7F!..u5c:F&D---#9J9J59Q2Q))%05!F"F}$ 1 11F#D$5$5e$<$A$A$CDH/7 $I!'.s1vw'G %& $I $I%F~%D---%%e,224}$ 1 11%'89F !$D$5$5e$< =!&F
 & 1 11/1D%%e,,,D---))%0!F"F&d//4467& 1 11"F[!F D$5$5e$<<F #%>rd#CD.z5A#77xPPP &$'Gv,=#G_
!22&):5''BCE?2&'>?E&s6{3E%*:+;+;+=+E+Ec2+N*Ou#U .z5A(V<33M8LLL{$IX Q" M 	MHHNN6:*:z#a&7IJH33M8LLL	Ms|   A)P0,DO 9(N8!EO <N==O P0B0O 2N?3O 7P08O ?O 	P-
AP(P P(#P0(P--P0c                 h   K    j                   sy j                  } j                   rmt         j                        |k  rU j	                         }|nBt        j                  d       d{     j                   rt         j                        |k  rUt         j                        |kD  rt         j                  j                          fd      } j                  |t        j                         t        j                  d       d{    t         j                        |kD  r~yy7 7 !w)z%Maintain the number of dirty workers.Nr   c                 6    j                   |    j                  S r   r  r  s    rB   r   z-DirtyArbiter.manage_workers.<locals>.<lambda>  s    4<<?+>+> rD   r   )r4   r6   rV   r-   r  rs   r   r  r   r  r{   r}   )rA   r6   r  r   s   `   rB   r   zDirtyArbiter.manage_workers  s     zz&& jjS.<&&(F~--$$$ jjS.< $,,+-T\\..0!>@JZ8--$$$ $,,+- % %s1   A)D2,D.-(D2A8D2D0D2,D20D2c                 F   | j                   r| j                   j                  d      }n6|r$t        | j                  j	                               }n| j                         }|s| j                  j                  d       y| xj                  dz  c_        t        j                  j                  | j                  d| j                   d      }t        | j                  | j                  || j                  | j                  |      }t        j                          }|dk7  rr||_        || j"                  |<   || j$                  |<   | j'                  ||       | j                  j)                  | |       | j                  j+                  d||       |S t        j,                         |_        	 t/        j0                  d	| j                  j2                   d
       |j5                          t        j6                  d       y# t8        $ r7}t        j6                  |j:                  |j:                  nd       Y d}~yd}~wt<        $ r^ | j                  j?                  d       |j@                  st        j6                  | jB                         t        j6                  d       Y yw xY w)a  
        Spawn a new dirty worker.

        Worker app assignment follows these priorities:
        1. If there are pending respawns (from dead workers), use those apps
        2. Otherwise, determine apps for a new worker based on allocation
        3. If force_all_apps=True, spawn with all apps regardless of limits

        Args:
            force_all_apps: If True, spawn worker with all apps ignoring limits

        Returns:
            Worker PID in parent process, or None if no apps need workers
        r   z)No apps need more workers, skipping spawnNr   zworker-z.sock)r   r%   rY   r    r!   r,   z,Spawned dirty worker (pid: %s) with apps: %szdirty-worker []z!Exception in dirty worker process)"r>   rd   r]   r:   r   r[   r!   r   r3   r#   r*   r+   r)   r   r"   r    forkr-   r.   rb   dirty_post_forkrj   r$   r   rr   	proc_nameinit_process_exit
SystemExitcoderJ   	exceptionr   WORKER_BOOT_ERROR)rA   force_all_appsrY   r,   r
  r"   rN   s          rB   r  zDirtyArbiter.spawn_worker  s     !!..2215IT^^0023I 557IHHNNFG1ggllKK74??"359
 #
 ggi!8FJ &DLL'2D$ &&sI6HH$$T62HHMMHy*J YY[

	0B0B/C1EF!HHQK 	:HHqvv1QVVq99 	HHBC==//0HHQK		s    (AG; ;	J -H66A'J J c                     	 t        j                  ||       y# t        $ r=}|j                  t        j                  k(  r| j                  |       Y d}~yY d}~yd}~ww xY w)zKill a worker by PID.N)r#   killr  errnoESRCH_cleanup_worker)rA   r"   r   rN   s       rB   r  zDirtyArbiter.kill_worker  sK    	*GGC 	*ww%++%$$S)) &	*s    	A.AAc                    | j                  |       || j                  v r*| j                  |   j                          | j                  |= | j                  j	                  |d       || j
                  v r5t        | j
                  |         }|r| j                  j                  |       | j                  |       | j                  j	                  |d      }|r| j                  j                  | |       | j                  j	                  |d      }|r7t        j                  j!                  |      r	 t        j"                  |       yyy# t$        $ r Y yw xY w)z
        Clean up after a worker exits.

        Saves the dead worker's app list to pending respawns so the
        replacement worker gets the same apps.
        N)r   r1   r   r0   rd   r<   r]   r>   rX   rf   r-   r    dirty_worker_exitr.   r#   r*   r   r   r  )rA   r"   	dead_appsr
  r,   s        rB   rF  zDirtyArbiter._cleanup_worker  s.    	%%c* $'''!!#&--/%%c* 	sD) $%%%T0056I&&--i8 	$!!#t,HH&&tV4))--c48277>>+6		+& 7;  s   2E
 
	EEc                 ,  K   | j                   j                  syt        | j                  j	                               D ]  \  }}	 t        j                         |j                  j                         z
  | j                   j                  k  rN	 |j                  sD| j                  j                  d|       d|_        | j                  |t        j                          | j                  |t        j"                          y# t        t        f$ r Y w xY ww)z!Kill workers that have timed out.NzDIRTY WORKER TIMEOUT (pid:%s)T)r    r   r]   r-   rU   r   r   r   r  r  r  abortedr!   criticalr  r{   SIGABRTSIGKILL)rA   r"   r
  s      rB   r   zDirtyArbiter.murder_workers  s     xx%% 2 2 45 	6KC>>#fjj&<&<&>>$((BXBXX Y
 >>!!"A3G!%  fnn5  fnn5	6 Z( s,   ADAC?
A5D?DDDDc                 \   	 	 t        j                  dt         j                        \  }}|syd}t        j                  |      rt        j                  |      }nGt        j
                  |      r2t        j                  |      }| j                  j                  d||       || j                  k(  r| j                  j                  d|       | j                  |       | j                  j                  d|       # t        $ r(}|j                  t        j                  k7  r Y d}~yd}~ww xY w)zReap dead worker processes.Nz)Dirty worker (pid:%s) killed by signal %sz$Dirty worker failed to boot (pid:%s)zDirty worker exited (pid:%s))r#   waitpidWNOHANG	WIFEXITEDWEXITSTATUSWIFSIGNALEDWTERMSIGr!   rK   r@  r   rF  rj   r  rD  ECHILD)rA   wpidstatusexitcoder   rN   s         rB   r   zDirtyArbiter.reap_workers*  s    	!zz"bjj9f<<'!~~f5H^^F+++f-CHH$$%P%)30 t555HHNN#I4P$$T*<dC# $  	ww%,,& '	s   *C: CC: :	D+D&&D+c                   K   | j                   j                  d       t        | j                  j                        D ]/  }| j                          t        j                  d       d{    1 t        | j                  j                               }|| j                  j                  d D ]"  }| j                  |t        j                         $ y7 hw)z!Reload workers (SIGHUP handling).zReloading dirty workersr   N)r!   rj   r   r    r5   r  rs   r   r]   r-   r   r  r{   r}   )rA   r   old_workersr"   s       rB   r   zDirtyArbiter.reloadC  s     /0 txx--. 	%A--$$$	%
 4<<,,./txx5567 	2CS&..1	2	 %s   A&C(C)A)Cc                   K   | j                   j                         D ]  }|j                           |rt        j                  nt        j
                  }t        j                         | j                  j                  z   }t        | j                  j                               D ]  }| j                  ||        | j                  rht        j                         |k  rQ| j                          t        j                  d       d{    | j                  rt        j                         |k  rQt        | j                  j                               D ]"  }| j                  |t        j                          $ | j                          y7 w)zStop all workers.r   N)r1   rP   r   r{   r}   r   r   r    dirty_graceful_timeoutr]   r-   r   r  r   rs   r   rN  )rA   gracefulr   r   limitr"   s         rB   r   zDirtyArbiter.stopQ  s!     ))002 	DKKM	 !)fnnfnn		dhh=== ))+, 	'CS#&	' lltyy{U2--$$$ lltyy{U2
 ))+, 	2CS&..1	2 %s   DFF'F-AFc                    | j                   rIt        j                  j                  | j                         r 	 t        j                  | j                          t        j                  j                  | j                        r 	 t        j                  | j                         	 t        j                  | j                        D ]?  }t        j                  t        j                  j                  | j                  |             A t        j                  | j                         | j                  j                  d| j                         y# t
        $ r Y w xY w# t
        $ r Y w xY w# t
        $ r Y Pw xY w)zSynchronous cleanup on exit.zDirty arbiter exiting (pid: %s)N)r&   r#   r*   r   r   r  r,   listdirr)   r+   rmdirr!   rj   r"   )rA   rx   s     rB   rw   zDirtyArbiter._cleanup_synch  s    <<BGGNN4<<8		$,,'
 77>>$**+		$**+
	ZZ, 8		"'',,t{{A678HHT[[! 	7B%      		s6   E  E  B E% 	EE	E"!E"%	E10E1)NNr   )F)T))__name__
__module____qualname____doc__splitr  r{   rz   r@  rC   r@   rS   r[   rb   rf   rt   rq   r~   r   ru   r   r   r   r   r   r   r   r   r   r   r   r   r   r  r  rF  r   r   r   r   rw   ).0xr  r{   s   0000rB   r   r   1   s     <AACE Ewvw{+ EG 5 n 5D"6:$B"!@<8<t!
&P((&P2Mh14:Mx(7T*'IR\M|sMj%.FP*"H6&22.CY!Es   B#
r   )%rg  rs   rD  r+  r#   r{   r'   r   gunicornr   appr   r   errorsr   r   r	   r
   protocolr   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r
  r   r   r   rD   rB   <module>ro     s]   
    	     @     "  OC OCrD   