o
    `j                     @   s   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
 d dlmZmZ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 eeZejjZG d
d dej Z!G dd dZ"dS )    N)errors)AnonMeasurable)AvgMaxRate)TransactionManager)ProduceRequestProduceResponse)TopicPartition)ensure_valid_topic_name)__version__c                       s  e Zd ZdZddedddddddd	d
e dZ fddZdd Zd@ddZ	dd Z
dd Zd@ddZdd Zdd Zdd Zdd Zd d! Zd"d# Zd$d% Zd&d' Zd(d) Zd*d+ Zd,d- Zd.d/ Zd0d1 Zd2d3 Zd4d5 Zd6d7 Zd8d9 Zd:d; Zd<d= Zd>d? Z   Z!S )ASenderz
    The background thread that handles the sending of produce requests to the
    Kafka cluster. This thread makes metadata requests to renew its view of the
    cluster and then sends produce requests to the appropriate nodes.
    i      infi0u  d   NFi`  zkafka-python-)max_request_sizeacksretriesrequest_timeout_msretry_backoff_msmetricsguarantee_message_ordertransaction_managertransactional_idtransaction_timeout_ms	client_idc                    s   t    t| j| _| jD ]}||v r||| j|< q| jd d | _|| _|| _|j	| _
d| _d| _t | _| jd rLt| jd | j| j
| _nd | _| jd | _tt| _d S )Nr   z-network-threadTFr   r   )super__init__copyDEFAULT_CONFIGconfigpopname_client_accumulatorcluster	_metadata_running_force_closeset_topics_to_addSenderMetrics_sensors_transaction_managercollectionsdefaultdictlist_in_flight_batches)selfclientmetadataaccumulatorconfigskey	__class__ S/home/djax/ivt_ai_plugin/venv/lib/python3.10/site-packages/kafka/producer/sender.pyr   /   s$   


zSender.__init__c                 C   sr   z| j |j }W n
 ty   Y d S w z
||j|f}W n
 ty'   Y d S w |d ||< |  t| d S )N)	r1   topic_partitionKeyErrorindexcreated
ValueErrorr!   heapqheapify)r2   batchqueueidxr:   r:   r;   #_maybe_remove_from_inflight_batchesE   s   z*Sender._maybe_remove_from_inflight_batchesc                 C   s   g }g }| j  D ]>\}}|rB|d \}}|| jjr9t| |jdu r,|| nt	
d|j|j|jf | j| n|s|| q	|D ]}| j |= qJ|S )z<Get the in-flight batches that has reached delivery timeout.r   Nz5%s batch created at %s gets unexpected final state %s)r1   itemshas_reached_delivery_timeoutr$   delivery_timeout_msrB   heappopfinal_stateappendErrorsIllegalStateErrorr=   r@   #maybe_update_next_batch_expiry_time)r2   nowexpired_batches	to_removetprE   _created_atrD   r:   r:   r;   _get_expired_inflight_batchesS   s$   



z$Sender._get_expired_inflight_batchesc                 C   s  t dt|  | jr&z|   W n ty"   t dt|  Y nw | jst dt|  | jsd| j	 s=| j
 dkrdz|   W n tyT   t dt|  Y nw | jsd| j	 s=| j
 dks=| jrl| j  z| j
  W n ty   t dt|  Y nw t dt|  dS )z(The main run loop for the sender thread.z'%s: Starting Kafka producer I/O thread.z/%s: Uncaught error in kafka producer I/O threadzO%s: Beginning shutdown of Kafka producer I/O thread, sending remaining records.r   z"%s: Failed to close network clientz8%s: Shutdown of Kafka producer I/O thread has completed.N)logdebugstrr'   run_once	Exception	exceptionr(   r$   has_undrainedr#   in_flight_request_countabort_incomplete_batchescloser2   r:   r:   r;   runj   s@   
z
Sender.runc              
   C   sJ  | j r| j| j   | j s| jrzc| j s"| j s"| j  | j s+| 	 r8| j
j| jd d W dS | j sG| j rG| j ra| jj}|durT| | | j
j| jd d W dS | j rt| jjpmt }| j| W n" tjy } ztdt| | | j| W Y d}~nd}~ww |  }| j
j|d dS )z"Run a single iteration of sending.r   
timeout_msNzG%s: Authentication exception while processing transactional request: %s)r*   r&   	add_topicr!   r-   is_transactionalhas_producer_idinit_producer_id#has_in_flight_transactional_request_maybe_send_pending_requestr#   pollr    has_fatal_erroris_bumping_epoch
last_error_maybe_abort_batcheshas_abortable_errorrN   TransactionAbortedErrorr$   abort_undrained_batchesSaslAuthenticationFailedErrorrW   rX   rY   authentication_failed_send_producer_data)r2   rn   r\   epoll_timeout_msr:   r:   r;   rZ      sB   




zSender.run_oncec                 C   s  |d u rt  n|}| jj| j|d}|\}}}|r'tdt|  | j  t	d}t
|D ]*}| j|sY| j|}tdt| || | jj|dd || t||}q/| jj| j|| jd |d}	|	 D ]}
|
D ]}|j|f}| j|j }t|| qpql| jd r|	 D ]}
|
D ]
}| jj|j qq| j  | jj|d}|| j|d |rtd	t| t| | j rt!d
d |D rd}nd}|D ]}d|j"|jt#t  |j d f }| $|t%&| q| j'r| j'(|	 |r| j )  dS | *|	}t|d || jj+|d  }|dk rd}|r8tdt| | tdt| t|| d}|, D ]*\}}|	| }tdt| | | jj-||dd.| j/|t  |0| j1|| q<|S )N)rQ   z5%s: Unknown leaders exist, requesting metadata updater   zD%s: Node %s not ready; delaying produce of accumulated batch (%f ms)F)wakeupr   r   z%%s: Expired %s batches in accumulatorc                 S   s   g | ]}|  qS r:   )in_retry.0rD   r:   r:   r;   
<listcomp>       z.Sender._send_producer_data.<locals>.<listcomp>TzCExpiring %d record(s) for %s: %s ms has passed since batch creation  r   z%%s: Nodes with data ready to send: %sz#%s: Created %d produce requests: %sz%s: Sending Produce Request: %r)2time	monotonicr$   readyr&   rW   rX   rY   request_updatefloatr0   r#   is_readyconnection_delaymaybe_connectremovemindrainr    valuesr@   r1   r=   rB   heappushmutedaddreset_next_batch_expiry_timerR   extendrV   lenr-   anyrecord_countint_complete_batch_with_exceptionrN   KafkaTimeoutErrorr,   update_produce_request_metricsreset_producer_id_create_produce_requestsnext_expiry_time_msrH   sendadd_callback_handle_produce_responseadd_errback_failed_produce)r2   rQ   resultready_nodesnext_ready_check_delayunknown_leaders_existnot_ready_timeout_msnodenode_delay_msbatches_by_node
batch_listrD   itemrE   rR   needs_transaction_state_resetexpired_batcherror_messagerequestsrw   node_idrequestbatchesr:   r:   r;   ru      s   








	
zSender._send_producer_datac              
   C   s   | j  r&| jjr&| j  r| j jpt }| j| | j	 s&| j
  | j | jj}|d u r4dS tdt| |j | jr| jsd }z|| rt| j |j}|d u r^| j | W n| jj|| jd dss| j | d }W nn%| j }|d u r| jj| jd | j d n| jj|| jd dsW q>|d ur|jrt| jd d  | j   }| j!||j}|"|j#| W dS W n+ t$y } zt%d	t| | | r| j | W Y d }~nW Y d }~nd }~ww | jr| jrD|d u r| j &| dS )
NFz$%s: Sending transactional request %sr   rc   r   )rd   futurer~   Tzs%s: Got an exception when trying to find a node to send a transactional request to. Going to back off and retry: %s)'r-   is_completingr$   has_incompleteis_abortingrn   rN   rq   rr   flush_in_progressbegin_flushnext_request_handlerrW   rX   rY   r   r'   r(   needs_coordinatorcoordinatorcoordinator_typelookup_coordinator_for_requestr#   await_readyr    least_loaded_noderk   r&   r   is_retryr   sleep%next_in_flight_request_correlation_idr   add_bothon_completer[   warningretry)r2   r\   r   target_nodetxn_correlation_idr   rv   r:   r:   r;   rj   $  sd   




"z"Sender._maybe_send_pending_requestc                 C   s.   | j jrtdt| | | j | d S d S )Nz4%s: Aborting producer batches due to fatal error: %s)r$   r   rW   errorrY   abort_batches)r2   excr:   r:   r;   ro   `  s   zSender._maybe_abort_batchesc                 C   s   d| _ | j  |   dS )zAStart closing the sender (won't complete until all data is sent).FN)r'   r$   r`   rx   ra   r:   r:   r;   initiate_closeh  s   
zSender.initiate_closec                 C   s   d| _ |   dS )z;Closes the sender without sending out any pending messages.TN)r(   r   ra   r:   r:   r;   force_closen  s   zSender.force_closec                 C   s0   || j jvrt| | j| |   d S d S N)r&   _topicsr   r*   r   rx   )r2   topicr:   r:   r;   re   s  s
   zSender.add_topicc                 C   s.   t dt| || |D ]}| || qd S )Nz0%s: Error sending produce request to node %d: %s)rW   r   rY   r   )r2   r   r   r   rD   r:   r:   r;   r   ~  s   zSender._failed_producec                 C   s   t dt| | |r3dd |D }|jD ]}|j}|jD ]}t||j}	||	 }
| |
| qqdS t	dd}|D ]}
| |
| q:dS )zHandle a produce response.z %s: Parsing produce response: %rc                 S   s   i | ]}|j |qS r:   )r=   rz   r:   r:   r;   
<dictcomp>  r}   z3Sender._handle_produce_response.<locals>.<dictcomp>r   )
error_codeN)
rW   rX   rY   	responsesr"   partition_responsesr
   r?   _complete_batch_PartitionProduceResponse)r2   r   	send_timer   responsebatches_by_partitiontopic_responser   partition_responserT   rD   	syntheticr:   r:   r;   r     s   


zSender._handle_produce_responsec                    s.   sfddS t  fdd}|S )z-Returns a fn mapping batch_index to exceptionc                    s    S r   r:   _)top_level_exceptionr:   r;   <lambda>  s    z.Sender._record_exceptions_fn.<locals>.<lambda>c                    sH   | vr	t dS |  }|p pj}tdkrjnt j}||S )Nz^Failed to append record because it was part of a batch which had one more more invalid recordsr   )rN   
KafkaErrordescriptionr   r9   InvalidRecordError)batch_indexrecord_errorerr_msgr   r   record_errorsrecord_errors_dictr   r:   r;   record_exceptions_fn  s   z:Sender._record_exceptions_fn.<locals>.record_exceptions_fn)dict)r2   r   r   r   r   r:   r   r;   _record_exceptions_fn  s
   zSender._record_exceptions_fnc                 C   s  |j }|dkrt|}|tju rtdt| |j d}|dkr[|j}|j	}|
||r8| | | j| | jrL| jj|rL| j|j||j | jd rY| jj|j dS |tju rht|jj}n|tju rstd}n||j}| ||| dS )aW  Complete or retry the given batch of records based on a broker response.

        Handles both the success path (including treating
        DuplicateSequenceNumberError as success, for max_in_flight > 1
        retry arrivals) and the error path, which delegates to
        _dispatch_error with a context-aware exception instance.

        Arguments:
            batch (ProducerBatch): The record batch
            partition_response (PartitionProduceResponse): Protocol-layer
                partition response from the broker (or a synthetic instance
                for the acks=0 case).
        r   z^%s: Received DUPLICATE_SEQUENCE_NUMBER for %s - records already committed, treating as successr   Nz5The producer is not authorized to do idempotent sends)r   rN   for_codeDuplicateSequenceNumberErrorrW   rX   rY   r=   base_offsetlog_append_time_mscompleterG   r$   
deallocater-   producer_id_and_epochmatchupdate_last_acked_offsetr   r    r   r   TopicAuthorizationFailedErrorr   ClusterAuthorizationFailedErrorr   _dispatch_error)r2   rD   r   r   	error_clsr   log_append_timer\   r:   r:   r;   r     s6   








zSender._complete_batchc                 C   s&   t |tr	|d}| j||dd dS )a  Complete a batch following a client-side failure.

        Called from _failed_produce for network errors and from
        _send_producer_data for locally-expired batches. The exception is
        used as-is (no reconstruction), so any dynamic message is
        preserved.

        Arguments:
            batch (ProducerBatch): The record batch
            exception (Exception or type): The client-side exception or its
                class (a bare class is instantiated with no message)
        N)r   )
isinstancetyper   )r2   rD   r\   r:   r:   r;   r     s   
z%Sender._complete_batch_with_exceptionc              	   C   s2  t |}|dur|jnd}| ||r9tdt| |j|j|j | j	
| | | | j	| | | n2| |||rjtdt| |j|| j|j | j|j | j	| | | | | n| ||r|jr|jd dur|jd nd}tdt| |j| jd |j d |j|rd	| nd
 tdt| |j| jr| j|jnd | j	| | | | | n| jr#| jj|||d}|tjkr| j  nH|tjkr
t|tj r| j!|j"r|dur|j#nd}t$dt| |j| | j%  n|tj&kr| j'| n|tj(kr#| j)| | j*r1| j*+|jj,|j |dur=|j+}	|j-}nd}	|jrP|jd durP|jd nd}| .||	|}
|/||
rk| | | j	| |tj0u r{tdt| |j t1|tj2r| j34  | jd r| j	j56|j dS dS )a  Apply the appropriate outcome for a failed batch.

        Single decision point for both broker-reported errors (with a
        partition_response) and client-side exceptions (partition_response
        is None). Handles split / retry / retention-reset / fail along with
        transaction-state transitions and post-error housekeeping
        (metadata refresh, partition unmuting).
        Nr<   zN%s: Got %s on topic-partition %s with %d records, splitting batch and retryingz%s: UnknownProducerIdError for %s appears to be retention-based (log_start_offset=%s, last_acked_offset=%s); resetting sequence and retryingr   zW%s: Got error produce response on topic-partition %s, retrying (%s attempts left): %s%sr   r   z. Error Message: %s z=%s: Retrying batch to topic-partition %s. Sequence number: %s)log_start_offsetz%s: The broker received an out of order sequence number for topic-partition %s at offset %s. This indicates data loss on the broker, and should be investigated.r:   z%s: Received unknown topic or partition error in produce request on partition %s. The topic/partition may not exist or the user may not have Describe access to itr   )7r   r   
_can_splitrW   r   rY   __name__r=   r   r$   split_and_reenqueuerG   r   _record_retries'_is_retention_based_unknown_producer_idr-   last_acked_offsetreset_sequence_for_partition	reenqueue
_can_retryargsr    attemptsrX   sequence_numberclassify_batch_errorr   ERROR_CLASS_NEEDS_EPOCH_BUMPbump_producer_id_and_epoch#ERROR_CLASS_NEEDS_PRODUCER_ID_RESETr   rN   OutOfOrderSequenceNumberErrorrg   producer_idr   r   r   ERROR_CLASS_FATALtransition_to_fatal_errorERROR_CLASS_ABORTABLEtransition_to_abortable_errorr,   r   r   r   r   complete_exceptionallyUnknownTopicOrPartitionError
issubclassInvalidMetadataErrorr&   r   r   r   )r2   rD   r\   r   r   r   r   classificationr   r   r   r:   r:   r;   r     s   	

"




&


zSender._dispatch_errorc                 C   s"   | j r| j |jj|j d S d S r   )r,   record_retriesr=   r   r   )r2   rD   r:   r:   r;   r   N  s   zSender._record_retriesc                 C   s   | | jjr	dS |j| jd krdS |jdurdS t|tjs"dS | j	rB| j	j
|sBtdt| |j|j| j	j
j| j	j
j dS dS )aq  
        We can retry a send if the error is transient, the number of
        attempts taken is fewer than the maximum allowed, and - for the
        idempotent producer - the batch's producer id/epoch still matches
        ours. A mismatched producer id/epoch (e.g. after a reset or future
        KIP-360 epoch bump) means retrying would violate idempotence.
        Fr   Nzx%s: Attempted to retry sending a batch but the producer id/epoch changed from %s/%s to %s/%s. This batch will be droppedT)rI   r$   rJ   r  r    rL   r  rN   RetriableErrorr-   r   r   rW   r   rY   r  producer_epochepoch)r2   rD   r   r:   r:   r;   r  R  s    
zSender._can_retryc                 C   st   |t jurdS | jsdS | jj|sdS || jjrdS |jdur%dS |du s-|dk r/dS | j	|j
}||kS )a  Detect retention-based UnknownProducerIdError (KAFKA-5793).

        The broker returns UnknownProducerIdError either because the producer
        state was legitimately removed by retention, or because of actual
        data loss. If the broker's log_start_offset is strictly greater than
        the last offset we acknowledged for this partition, then the records
        we previously wrote have been aged out - the producer can safely
        reset its sequence to 0 and resume.
        FNr   )rN   UnknownProducerIdErrorr-   r   r   rI   r$   rJ   rL   r  r=   )r2   rD   r   r   
last_ackedr:   r:   r;   r   k  s   


z.Sender._is_retention_based_unknown_producer_idc                 C   s4   |t jt jfv o|jdko|jdu o|| jj S )z
        We can split and retry a batch if the error indicates the batch is too
        large for the broker, the batch contains more than one record (so it
        can actually be split), and the delivery timeout has not been reached.
        r   N)rN   MessageSizeTooLargeErrorRecordListTooLargeErrorr   rL   rI   r$   rJ   )r2   rD   r   r:   r:   r;   r     s   zSender._can_splitc                 C   s>   i }|  D ]\}}|r| || jd | jd |||< q|S )a  
        Transfer the record batches into a list of produce requests on a
        per-node basis.

        Arguments:
            collated: {node_id: [ProducerBatch]}

        Returns:
            dict: {node_id: ProduceRequest} (version depends on client api_versions)
        r   r   )rH   _produce_requestr    )r2   collatedr   r   r   r:   r:   r;   r     s   

zSender._create_produce_requestsc                    s   d}d}t j  j}tt}|D ]}	|	jj}
||	jj|	j	
 d}||
 | q| jr1| jjnd}|dur9d}t ||| fdd| D ||dS )	zCreate a produce request from the given record batches.

        Returns:
            ProduceRequest (version depends on client api_versions)
        	   r   )r?   recordsN   c                    s   g | ]
\}} ||d qS ))r"   partition_datar:   )r{   r   
partitionsTopicr:   r;   r|     s    z+Sender._produce_request.<locals>.<listcomp>)r   r   rd   
topic_datamin_versionmax_version)r   TopicProduceDataPartitionProduceDatar.   r/   r0   r=   r   	partitionr"  bufferrM   r-   r   rH   )r2   r   r   timeoutr   r*  r)  	Partitionr(  rD   r   r-  r   r:   r&  r;   r    s2   

zSender._produce_requestc                 C   s   | j   dS )z6Wake up the selector associated with this send thread.N)r#   rx   ra   r:   r:   r;   rx     s   zSender.wakeupc                 C   s
   | j  S r   )r#   bootstrap_connectedra   r:   r:   r;   r1    s   
zSender.bootstrap_connectedc                 C   s   d| j d | j d f S )Nz)<Sender client_id=%s transactional_id=%s>r   r   )r    ra   r:   r:   r;   __str__  s   zSender.__str__r   )"r   
__module____qualname____doc__r   r   r   r   rG   rV   rb   rZ   ru   rj   ro   r   r   re   r   r   r   r   r   r   r   r  r   r   r   r  rx   r1  r2  __classcell__r:   r:   r8   r;   r      sP    
%
/f<3d!r   c                   @   sD   e Zd Zdd Z			dddZdd Zd	d
 Zdd Zdd ZdS )r+   c                    s  | _ | _| _d} j | _ jdt |dd  jdt |dd d} j | _ jdt |d	d d
} j | _	 jdt |dd  jdt |dd d} j | _
 jdt |dd  jdt |dd d} j | _ jdt |dd d} j | _ jdt |dd d} j | _ jdt |dd d} j | _ jdt |dd  jdt |d d  jd!t fd"d#d$d%  jd&t fd'd#d(d% d S ))Nz
batch-sizezbatch-size-avgz;The average number of bytes sent per partition per-request.)sensor_namer   zbatch-size-maxz7The max number of bytes sent per partition per-request.compression-ratezcompression-rate-avgz/The average compression rate of record batches.z
queue-timezrecord-queue-time-avgzFThe average time in ms record batches spent in the record accumulator.zrecord-queue-time-maxzFThe maximum time in ms record batches spent in the record accumulator.zrecords-per-requestrecord-send-ratez.The average number of records sent per second.zrecords-per-request-avgz*The average number of records per request.bytes	byte-ratez,The average number of bytes sent per second.record-retriesrecord-retry-ratez5The average per-second number of retried record sendsr   record-error-ratezEThe average per-second number of record sends that resulted in errorszrecord-size-maxz*The maximum record size across all batcheszrecord-size-avgz)The average maximum record size per batchzrequests-in-flightc                     s
    j  S r   )r#   r^   r   ra   r:   r;   r     s   
 z(SenderMetrics.__init__.<locals>.<lambda>z=The current number of in-flight requests awaiting a response.)r   zmetadata-agec                    s   | j j d S )Nr~   )r&   _last_successful_refresh_ms)r   rQ   ra   r:   r;   r     s    z?The age in seconds of the current producer metadata being used.)r   r#   r&   sensorbatch_size_sensor
add_metricr   r   compression_rate_sensorqueue_time_sensorrecords_per_request_sensorr   byte_rate_sensorretry_sensorerror_sensormax_record_size_sensorr   )r2   r   r3   r4   r7  r:   ra   r;   r     s   












zSenderMetrics.__init__producer-metricsNc           
      C   sD   | j }|||||}|r||}	|	|| d S ||| d S r   )r   metric_namer@  r   rB  )
r2   rK  
measurable
group_namer   tagsr7  mmetricr@  r:   r:   r;   rB    s   
zSenderMetrics.add_metricc                    s    fdd}| j |dse| jdt |dd  d  d | jdt |d	d  d
  d | jdt |dd  d  d | jdt |dd  d  d | jdt |dd  d  d d S d S )Nc                    s   d  | S )Nztopic.{0}.{1})format)r"   r   r:   r;   r7  &  s   z?SenderMetrics.maybe_register_topic_metrics.<locals>.sensor_namezrecords-per-batchr9  zproducer-topic-metrics.z"Records sent per second for topic )r7  rM  r   r;  r:  zBytes per second for topic r8  z$Average Compression ratio for topic r=  r<  z$Record retries per second for topic r>  zrecord-errorsz#Record errors per second for topic )r   
get_sensorrB  r   r   )r2   r   r7  r:   rR  r;   maybe_register_topic_metrics$  s8   





z*SenderMetrics.maybe_register_topic_metricsc           
      C   s  |  D ]}d}d}|D ]q}|jj}| | | jd| d }||j | jd| d }||j	  | jd| d }	|	|j
  | j|j	  |jra| j|j|j  | j|j
  | j|j ||j7 }||j	 7 }q|r| j| | j| qd S )Nr   topic.z.records-per-batchz.bytesz.compression-rate)r   r=   r   rT  r   rS  recordr   r"  size_in_bytescompression_raterA  drainedrD  r@   rC  rI  max_record_sizerE  rF  )
r2   batches_map
node_batchr"  total_bytesrD   r   topic_records_counttopic_byte_ratetopic_compression_rater:   r:   r;   r   F  s<   




z,SenderMetrics.update_produce_request_metricsc                 C   6   | j | | jd| d }|r|| d S d S )NrU  z.record-retries)rG  rV  r   rS  r2   r   countr@  r:   r:   r;   r  k  
   zSenderMetrics.record_retriesc                 C   ra  )NrU  z.record-errors)rH  rV  r   rS  rb  r:   r:   r;   r   q  rd  zSenderMetrics.record_errors)rJ  NNN)	r   r3  r4  r   rB  rT  r   r  r   r:   r:   r:   r;   r+     s    I
"%r+   )#r.   r   rB   logging	threadingr   kafkar   rN   kafka.metrics.measurabler   kafka.metrics.statsr   r   r   "kafka.producer.transaction_managerr   kafka.protocol.producerr   r	   kafka.structsr
   
kafka.utilr   kafka.versionr   	getLoggerr   rW   TopicProduceResponsePartitionProduceResponser   Threadr   r+   r:   r:   r:   r;   <module>   s.    
     8