
    Pmjc'                       d Z ddlm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mZ ddlmZ ddlmZ  e                                            dz  d	z  Zd
ZdZ ej                    Z ej                    j        Zd<dZd=dZed>d            Zd?dZ ddd@dZ!dAd Z"dBd#Z#d=d$Z$dCd)Z%dDd+Z&ddd,dEd/Z'dFd0Z(dd1dd2dGd6Z)dHd7Z*dId;Z+dS )Ja  Profile-local durable audit ledger for cron execution attempts.

The ledger records what is known about each attempt; it is not a retry queue.
Interrupted attempts become ``unknown`` only after their exact owner process is
proved gone. Terminal states are immutable.
    )annotationsN)contextmanager)AnyDictIteratorListOptional)get_hermes_home)nowcronzexecutions.dbi  )	completedfailedunknownreturnsqlite3.Connectionc                 z    t           j                            dd           t          j        t           d          S )NT)parentsexist_ok   )timeout)EXECUTIONS_FILEparentmkdirsqlite3connect     5/home/thesage/.hermes/hermes-agent/cron/executions.py_connectr      s3       ===??A6666r   connNonec                    ddl m} t          j        | _        |                     d            || d           |                     d           |                     d           |                     d           |                     d	           d S )
Nr   )apply_wal_with_fallbackzPRAGMA busy_timeout=5000zcron/executions.db)db_labelzPRAGMA synchronous=FULLa  CREATE TABLE IF NOT EXISTS executions (
             id TEXT PRIMARY KEY,
             job_id TEXT NOT NULL,
             source TEXT NOT NULL,
             process_id TEXT NOT NULL,
             pid INTEGER NOT NULL,
             process_started_at INTEGER,
             status TEXT NOT NULL CHECK(status IN
               ('claimed','running','completed','failed','unknown')),
             claimed_at TEXT NOT NULL,
             started_at TEXT,
             finished_at TEXT,
             error TEXT
           )zeCREATE INDEX IF NOT EXISTS idx_executions_job_claimed ON executions(job_id, claimed_at DESC, id DESC)zhCREATE INDEX IF NOT EXISTS idx_executions_status_claimed ON executions(status, claimed_at DESC, id DESC))hermes_stater#   r   Rowrow_factoryexecute)r    r#   s     r   _initialize_schemar)       s    444444{DLL+,,,D+?@@@@LL*+++LL	    	LL	:   	LL	:    r   Iterator[sqlite3.Connection]c               #     K   t           5  t                      } 	 t          |            | 5  | V  ddd           n# 1 swxY w Y   |                                  n# |                                  w xY w	 ddd           dS # 1 swxY w Y   dS )a  Open a connection, commit/rollback on exit, always close.

    ``sqlite3.Connection.__enter__``/``__exit__`` only commit or roll back
    the transaction; it does not close the connection. Relying on that alone
    leaks a connection (and its WAL/SHM file descriptors) on every call,
    since closing then depends on the garbage collector. Schema init runs
    inside the ``try`` too, so a PRAGMA/DDL failure after a successful
    ``connect()`` still closes the connection instead of leaking it.
    N)_lockr   r)   close)r    s    r   _transactionr.   A   s      
  zz	t$$$  


               JJLLLLDJJLLLLL                 sH   BA<AA 	 AA 	ABA22BB
BrowOptional[sqlite3.Row]Optional[Dict[str, Any]]c                (    | t          |           nd S )Ndict)r/   s    r   _recordr5   V   s    4999T1r   delivery_outcomerecordr7   Optional[str]c               P    	 ddl m}  || |           dS # t          $ r Y dS w xY w)zFProject durable state to monitoring without affecting ledger behavior.r   )emit_execution_stater6   N)agent.monitoring.cron_healthr;   	Exception)r8   r7   r;   s      r   _emit_execution_stater>   Z   sX    EEEEEEV6FGGGGGG   s    
%%pidintOptional[int]c                H    	 ddl m}  ||           S # t          $ r Y d S w xY w)Nr   )get_process_start_time)gateway.statusrC   r=   )r?   rC   s     r   _process_start_timerE   f   sL    999999%%c***   tts    
!!
started_atboolc                    	 ddl m}  ||           sdS n# t          $ r Y dS w xY w|| t          j                    k    S t          |           }|d uo||k    S )Nr   )_pid_existsFT)rD   rI   r=   osgetpidrE   )r?   rF   rI   currents       r   _owner_is_liverM   n   s    ......{3 	5	   ttbikk!!!#&&G$87j#88s    
$$c                x    t          dt          t                              }|                     d|f           d S )Nr   zDELETE FROM executions WHERE id IN (
             SELECT id FROM executions
             WHERE status IN ('completed','failed','unknown')
             ORDER BY claimed_at DESC, id DESC LIMIT -1 OFFSET ?
           ))maxr@   MAX_TERMINAL_EXECUTIONSr(   )r    limits     r   _prune_unlockedrR   {   sF    3.//00ELL	
 
    r   job_idstrsourceDict[str, Any]c                  t                                                      }t          j                    j        }t          j                    }t                      5 }|                    d|t          |           t          |          t          |t          |          |f           |                    d|f                                          }ddd           n# 1 swxY w Y   t          |          }t          |           |S )z<Persist a claimed attempt before executor/provider dispatch.zINSERT INTO executions
               (id, job_id, source, process_id, pid, process_started_at,
                status, claimed_at)
               VALUES (?, ?, ?, ?, ?, ?, 'claimed', ?)#SELECT * FROM executions WHERE id=?N)_hermes_now	isoformatuuiduuid4hexrJ   rK   r.   r(   rT   _PROCESS_IDrE   fetchoner5   r>   )rS   rU   r   execution_idr?   r    r/   r8   s           r   create_executionra      s   
--
!
!
#
#C:<<#L
)++C	 4: 3v;;F[# %%s,	
 	
 	
 ll1L?
 

(** 	               S\\F&!!!Ms   A3CC Cr`   c                ~   t                                                      }t                      5 }|                    d|| f          }|j        dk    r	 ddd           dS t          |                    d| f                                                    }ddd           n# 1 swxY w Y   t          |           |S )z7Transition one claimed attempt to running exactly once.zcUPDATE executions SET status='running', started_at=?
               WHERE id=? AND status='claimed'   NrX   )rY   rZ   r.   r(   rowcountr5   r_   r>   )r`   r   r    curr8   s        r   mark_execution_runningrf      s   
--
!
!
#
#C	 
4ll2,
 

 <1
 
 
 
 
 
 
 
 1L?
 

(** 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 &!!!Ms   %B#!6B##B'*B')errorr7   successrg   c                  t                                                      }|rdnd}|rdn|rt          |          nd}t                      5 }|                    d|||| f          }|j        dk    r	 ddd           dS t          |           t          |                    d| f                                                    }	ddd           n# 1 swxY w Y   t          |	|           |	S )	zDWrite a terminal result once; terminal attempts cannot be rewritten.r   r   Nzunknown failureztUPDATE executions SET status=?, finished_at=?, error=?
               WHERE id=? AND status IN ('claimed','running')rc   rX   r6   )
rY   rZ   rT   r.   r(   rd   rR   r5   r_   r>   )
r`   rh   rg   r7   r   statusdetailr    re   r8   s
             r   finish_executionrl      sZ   
 --
!
!
#
#C#1[[FNTT#M3u:::<MF	 4llAS&,/
 

 <1        	1L?
 

(**                &3CDDDDMs   'C ACCCc            	        t                                                      } d}g }t                      5 }|                    d                                          }|D ]}|d         t
          k    rt          t          |d                   |d                   r>|                    d| d|d         f          }||j        z  }|j        rSt          |                    d	|d         f          
                                          }||                    |           |rt          |           d
d
d
           n# 1 swxY w Y   |D ]}t          |           |S )zDMark provably abandoned attempts unknown without scheduling retries.r   zsSELECT id, process_id, pid, process_started_at FROM executions
               WHERE status IN ('claimed','running')
process_idr?   process_started_atzUPDATE executions SET status='unknown', finished_at=?, error=?
                   WHERE id=? AND status IN ('claimed','running')z}Scheduler restarted after this execution's owner exited before a durable terminal state; whether side effects ran is unknown.idrX   N)rY   rZ   r.   r(   fetchallr^   rM   r@   rd   r5   r_   appendrR   r>   )r   changed	recoveredr    rowsr/   re   r8   s           r   recover_interrupted_executionsrv      s   
--
!
!
#
#CG&(I	 "4||8
 
 (** 	  	- 	-C< K//c#e*oos3G/HII ,,EHT C s|#G| - 9CI<" "(**  %$$V,,, 	"D!!!5" " " " " " " " " " " " " " "6  & &f%%%%Ns   C=D<<E E 2   )rS   rQ   before_claimed_atrQ   rx   List[Dict[str, Any]]c           
     b   g }g }| 7|                     d           |                     t          |                      |7|                     d           |                     t          |                     |rdd                    |          z   nd}|                     t          dt	          t          |          d                               t                      5 }|                    d	|z   d
z   |                                          }ddd           n# 1 swxY w Y   d |D             S )zFReturn indexed, newest-first execution history with cursor pagination.Nzjob_id=?zclaimed_at < ?z WHERE z AND  rc   i  zSELECT * FROM executionsz* ORDER BY claimed_at DESC, id DESC LIMIT ?c                ,    g | ]}t          |          S r   r3   .0r/   s     r   
<listcomp>z#list_executions.<locals>.<listcomp>  s    &&&#DII&&&r   )	rr   rT   joinrO   minr@   r.   r(   rq   )rS   rQ   rx   clausesparamswherer    ru   s           r   list_executionsr      sd   
 GFz"""c&kk"""$'(((c+,,---18@IW----bE
MM#aSZZ--..///	 4||&.:;
 
 (**	 	               '&&&&&s   /DD!Dc                <    t          | d          }|r|d         nd S )Nrc   )rS   rQ   r   )r   )rS   ru   s     r   latest_executionr     s'    &222D$477$r   job_ids	List[str]Dict[str, Dict[str, Any]]c                L   d t                               |           D             }|si S d                    d |D                       }t                      5 }|                    d| d|                                          }ddd           n# 1 swxY w Y   d |D             S )z9Load latest execution for many jobs in one indexed query.c                0    g | ]}|t          |          S r   )rT   )r~   rS   s     r   r   z%latest_executions.<locals>.<listcomp>  s#    HHHVHS[[HHHr   ,c              3     K   | ]}d V  dS )?Nr   )r~   _s     r   	<genexpr>z$latest_executions.<locals>.<genexpr>  s"      //AC//////r   z@SELECT e.* FROM executions e
                WHERE e.job_id IN (z)
                  AND e.id=(SELECT e2.id FROM executions e2
                            WHERE e2.job_id=e.job_id
                            ORDER BY e2.claimed_at DESC, e2.id DESC LIMIT 1)Nc                :    i | ]}|d          t          |          S )rS   r3   r}   s     r   
<dictcomp>z%latest_executions.<locals>.<dictcomp>  s$    555CM499555r   )r4   fromkeysr   r.   r(   rq   )r   cleanplaceholdersr    ru   s        r   latest_executionsr   	  s   HHt}}W'='=HHHE 	88///////L	 4||P$0P P P
 
 
 (** 	               655555s   -BBB)r   r   )r    r   r   r!   )r   r*   )r/   r0   r   r1   )r8   r1   r7   r9   r   r!   )r?   r@   r   rA   )r?   r@   rF   rA   r   rG   )rS   rT   rU   rT   r   rV   )r`   rT   r   r1   )
r`   rT   rh   rG   rg   r9   r7   r9   r   r1   )r   r@   )rS   r9   rQ   r@   rx   r9   r   ry   )rS   rT   r   r1   )r   r   r   r   ),__doc__
__future__r   rJ   r   	threadingr[   
contextlibr   typingr   r   r   r   r	   hermes_constantsr
   hermes_timer   rY   resolver   rP   _TERMINAL_STATESRLockr,   r\   r]   r^   r   r)   r.   r5   r>   rE   rM   rR   ra   rf   rl   rv   r   r   r   r   r   r   <module>r      su    # " " " " " 				       % % % % % % 6 6 6 6 6 6 6 6 6 6 6 6 6 6 , , , , , , * * * * * *!/##++--6H 5 	djll7 7 7 7
   B    (2 2 2 2
 LP	 	 	 	 	 	   
9 
9 
9 
9	 	 	 	   ,   & AE&*     0" " " "L  $"'+' ' ' ' ' '0% % % %
6 6 6 6 6 6r   