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Zd dlmZ	 d dl
mZ d dlmZmZmZmZ d dlmZ d dlmZmZmZmZmZ d dlmZ d dlmZmZ d dlmZmZm Z  d d	l!m"Z" e#e$Z%d
a&e 'dg dZ(de(_)e 'dg dZ*e 'dg dZ+ej,Z-e-j.Z/ej0Z1ej2Z3e3j4Z5ej6Z7e7j8Z9G dd de	j:Z;G dd dZ<G dd dZ=G dd dZ>e>e>j?e>j@e>_Ae>e>j?e>jBe>_CG dd dZDG dd dZEG dd dZFG d d! d!ZGdS )"    N)Future)AvgCountMaxRate)FetchRequest)ListOffsetsRequestOffsetForLeaderEpochRequest
OffsetSpecUNKNOWN_OFFSETIsolationLevel)MemoryRecords)DeserializerDeserializeWrapper)TopicPartitionOffsetAndMetadataOffsetAndTimestamp)TimerFConsumerRecord)topic	partitionleader_epochoffset	timestamptimestamp_typekeyvalueheaderschecksumserialized_key_sizeserialized_value_sizeserialized_header_sizeaJ  A single record (message) consumed from a topic partition.

Yielded by :meth:`~kafka.KafkaConsumer.poll` (inside the returned
``{TopicPartition: [ConsumerRecord, ...]}`` mapping) and by iterating
over a :class:`~kafka.KafkaConsumer`. ``key`` and ``value`` are decoded
by the consumer's configured deserializers.

Keyword Arguments:
    topic (str): The topic this record was received from.
    partition (int): The partition this record was received from.
    leader_epoch (int): The partition leader epoch for this record, or -1
        if unknown.
    offset (int): The position of this record in the topic partition.
    timestamp (int): The timestamp of this record, in milliseconds since
        the epoch (UTC), or -1 if unknown.
    timestamp_type (int): The type of the timestamp: 0 for CreateTime (set
        by the producer) or 1 for LogAppendTime (set by the broker).
    key: The (deserialized) key of the record, or None.
    value: The (deserialized) value of the record, or None.
    headers (list): A list of ``(key, value)`` header tuples, where key is
        a str and value is bytes.
    checksum (int): Deprecated. The CRC32 checksum of the record, or None.
        Removed in message format v2 (Kafka 0.11+).
    serialized_key_size (int): The size of the serialized, uncompressed key
        in bytes, or -1 if the key is None.
    serialized_value_size (int): The size of the serialized, uncompressed
        value in bytes, or -1 if the value is None.
    serialized_header_size (int): The size of the serialized, uncompressed
        headers in bytes, or -1 if there are no headers.
CompletedFetch)topic_partitionfetched_offsetresponse_versionpartition_datametric_aggregatorExceptionMetadata)r   r$   	exceptionc                   @   s   e Zd ZdS )RecordTooLargeErrorN)__name__
__module____qualname__ r.   r.   T/home/djax/ivt_ai_plugin/venv/lib/python3.10/site-packages/kafka/consumer/fetcher.pyr*   O   s    r*   c                   @   s  e Zd Zi dddddddddd	d
ddejddddddddddddddddddZdd Zedd  Zdmd!d"Z	d#d$ Z
d%d& Zd'd( Zd)d* Zdnd+d,Zdnd-d.Zdnd/d0Zdnd1d2Zdnd3d4Zdnd5d6Zdod7d8Zd9d: Zd;d< Zdnd=d>Zd?d@ ZdAdB ZdCdD ZdEdF ZdGdH ZdIdJ ZdndKdLZdndMdNZdOdP Z dQdR Z!dSdT Z"dUdV Z#dWdX Z$dYdZ Z%d[d\ Z&d]d^ Z'd_d` Z(dadb Z)dcdd Z*dedf Z+dgdh Z,didj Z-G dkdl dlZ.dS )pFetcherkey_deserializerNvalue_deserializerfetch_min_bytes   fetch_max_wait_msi  fetch_max_bytesi   max_partition_fetch_bytesi   max_poll_records
check_crcsTmetricsmetric_group_prefixconsumerrequest_timeout_msi0u  retry_backoff_msd   !enable_incremental_fetch_sessionsisolation_levelread_uncommittedclient_rack metadata_max_age_msi c                 K   sX  t  | j| _| jD ]}||v r|| | j|< q
dD ]&}| j| dur@t| j| ts@tjd|f tdd t| j| | j|< qzt	
| jd | _W n tyY   tddw || _|j| _| jj| _|| _t | _d| _i | _i | _d| _t | _| jd rt| jd | jd	 | _nd| _i | _t | _d| _ d| _!d| _"d| _#d| _$dS )
a_  Initialize a Kafka Message Fetcher.

        Keyword Arguments:
            key_deserializer (kafka.serializer.Deserializer): Takes a
                raw message key and returns a deserialized key.
                Default: None.
            value_deserializer (kafka.serializer.Deserializer): Takes a
                raw message value and returns a deserialized value.
                Default: None.
            enable_incremental_fetch_sessions: (bool): Use incremental fetch sessions
                when available / supported by kafka broker. See KIP-227. Default: True.
            fetch_min_bytes (int): Minimum amount of data the server should
                return for a fetch request, otherwise wait up to
                fetch_max_wait_ms for more data to accumulate. Default: 1.
            fetch_max_wait_ms (int): The maximum amount of time in milliseconds
                the server will block before answering the fetch request if
                there isn't sufficient data to immediately satisfy the
                requirement given by fetch_min_bytes. Default: 500.
            fetch_max_bytes (int): The maximum amount of data the server should
                return for a fetch request. This is not an absolute maximum, if
                the first message in the first non-empty partition of the fetch
                is larger than this value, the message will still be returned
                to ensure that the consumer can make progress. NOTE: consumer
                performs fetches to multiple brokers in parallel so memory
                usage will depend on the number of brokers containing
                partitions for the topic.
                Supported Kafka version >= 0.10.1.0. Default: 52428800 (50 MB).
            max_partition_fetch_bytes (int): The maximum amount of data
                per-partition the server will return. The maximum total memory
                used for a request = #partitions * max_partition_fetch_bytes.
                This size must be at least as large as the maximum message size
                the server allows or else it is possible for the producer to
                send messages larger than the consumer can fetch. If that
                happens, the consumer can get stuck trying to fetch a large
                message on a certain partition. Default: 1048576.
            check_crcs (bool): Automatically check the CRC32 of the records
                consumed. This ensures no on-the-wire or on-disk corruption to
                the messages occurred. This check adds some overhead, so it may
                be disabled in cases seeking extreme performance. Default: True
            isolation_level (str): Configure KIP-98 transactional consumer by
                setting to 'read_committed'. This will cause the consumer to
                skip records from aborted tranactions. Default: 'read_uncommitted'
        )r1   r2   Nz3%s does not implement kafka.serializer.Deserializer   )category
stacklevelrA   zUnrecognized isolation_levelr:   r;   )%copyDEFAULT_CONFIGconfig
isinstancer   warningswarnDeprecationWarningr   r   
build_from_isolation_level
ValueErrorErrorsKafkaConfigurationError_client_manager_net_subscriptionscollectionsdeque_completed_fetches_next_partition_records_paused_completed_fetches_paused_partition_records	_iterator_fetch_futuresFetchManagerMetrics_sensors_session_handlersset"_nodes_with_pending_fetch_requests_cached_list_offsets_exception _next_in_line_exception_metadata_reset_task_validation_task_cached_log_truncation)selfclientsubscriptionsconfigsr   r.   r.   r/   __init__g   sF   ,





zFetcher.__init__c                 C   s&   | j jd u s| j jdk rdS | jd S )N)r4   r4   Fr@   )rV   broker_versionrK   rk   r.   r.   r/   "_enable_incremental_fetch_sessions   s   
z*Fetcher._enable_incremental_fetch_sessionsc           
         s   | j ||d\}}|s|   |r|dfS t| j}| jdur)| jjs)|| j |s/|dfS t   fdd}|D ]}|| q:z| j	
| jj | W n
 tjyX   Y nw | j ||d\}}	|dfS )a  Drain buffered records, pipeline next fetches, and wait briefly
        for in-flight responses if no records are immediately available.

        Single-call replacement for the legacy
        ``fetched_records -> send_fetches -> client.poll -> fetched_records``
        loop in :meth:`KafkaConsumer._poll_once`. The caller no longer
        drives the event loop; the wait happens inside this method via a
        wakeup Future fired by any in-flight fetch's completion callback.

        Arguments:
            max_records (int, optional): cap on returned records.
            update_offsets (bool): advance subscription positions for
                consumed records.
            timeout_ms (int, optional): wall-clock cap on the wait phase.
                Only applies when no records are immediately available.

        Returns:
            tuple[dict[TopicPartition, list[ConsumerRecord]], bool]:
                ``(records, idle)``. ``idle`` is True when there were no
                buffered records, no in-flight fetches, and no pending
                offset-reset task -- i.e. nothing this fetcher could wait
                on. Callers in that state should sleep before retrying
                instead of busy-looping.
        )update_offsetsFNTc                    s    j s
 d  d S d S N)is_donesuccess)_wakeupr.   r/   _wake   s   z$Fetcher.fetch_records.<locals>._wake)fetched_recordssend_fetcheslistr`   rh   ru   appendr   add_bothrW   runrV   wait_forrS   KafkaTimeoutError)
rk   max_recordsrs   
timeout_msrecordspartial	waited_onrz   futrw   r.   rx   r/   fetch_records   s2   


zFetcher.fetch_recordsc                 C   s   | j | jS )zSend FetchRequests for all assigned partitions that do not already have
        an in-flight fetch or pending fetch data.

        Returns:
            List of Futures: each future resolves to a FetchResponse
        )rV   r   _send_fetches_asyncrq   r.   r.   r/   r|     s   zFetcher.send_fetchesc                    s   g }|    D ]8\}\}}td| | j| | jj||d}|| j	||t
  || j| || j| || q	| j| |  I d H  |S )NzSending FetchRequest to node %snode_id)_create_fetch_requestsitemslogdebugre   addrV   sendadd_callback_handle_fetch_responsetime	monotonicadd_errback_handle_fetch_errorr   _clear_pending_fetch_requestr~   r`   extend_clean_done_fetch_futures)rk   futuresr   requestfetch_offsetsfuturer.   r.   r/   r     s   zFetcher._send_fetches_asyncc                    s(   | j sd S tdd | j D | _ d S )Nc                 s   s    | ]}|j s|V  qd S rt   ru   .0r   r.   r.   r/   	<genexpr>P  s    
z4Fetcher._clean_done_fetch_futures.<locals>.<genexpr>)r`   rY   rZ   rq   r.   r.   r/   r   $  s   *
z!Fetcher._clean_done_fetch_futuresc                 C   s   t dd t| jD S )zVReturn True if there are any unprocessed (incomplete) FetchRequests
        in flight.c                 s   s    | ]}|j  V  qd S rt   r   r   r.   r.   r/   r   \  s    z,Fetcher.in_flight_fetches.<locals>.<genexpr>)anyr}   r`   rq   r.   r.   r/   in_flight_fetchesS  s   	zFetcher.in_flight_fetchesc                 C   T   | j d}| _ |r|| jdur| jjs| jS | j sdS | j| j|| _| jS )ae  Schedule pending offset resets and return the in-flight Task.

        Returns the cached Future for the in-flight reset task (shared
        across concurrent callers) or None if no reset is needed. Callers
        may discard the Future (fire-and-forget, e.g. consumer.poll) or
        await it via ``manager.wait_for(future, timeout_ms)`` to block
        until resets complete (e.g. consumer.position).

        Arguments:
            timeout_ms (int, optional): Maximum wall-clock the reset task
                should run, including time spent awaiting metadata refresh
                for unknown leaders. If None, uses ``request_timeout_ms``
                as a default upper bound so a permanently-unresolvable
                partition (deleted topic, etc.) doesn't spin forever. The
                first caller's timeout wins for the cached task; later
                callers' bounds are enforced via their own ``wait_for`` on
                the returned Future.

        Raises:
            NoOffsetForPartitionError: if a previous reset attempt left a
                cached non-retriable exception.
        N)rf   rh   ru   rX   partitions_needing_resetrV   	call_soon_reset_offsets_asyncrk   r   excr.   r.   r/   reset_offsets_if_needed^  s   
zFetcher.reset_offsets_if_neededc                 C   s0   | j | j||}|D ]
}||vrd||< q|S )av  Fetch offset for each partition passed in ``timestamps`` map.

        Blocks until offsets are obtained, a non-retriable exception is raised
        or ``timeout_ms`` passed.

        Arguments:
            timestamps: {TopicPartition: int} dict with timestamps to fetch
                offsets by. -1 for the latest available, -2 for the earliest
                available. Otherwise timestamp is treated as epoch milliseconds.
            timeout_ms (int, optional): The maximum time in milliseconds to block.

        Returns:
            {TopicPartition: OffsetAndTimestamp or None}: Mapping of partition to
                retrieved offset, timestamp, and leader_epoch. If offset does not
                exist for the provided timestamp, the value for the TopicPartition
                will be None.

        Raises:
            KafkaTimeoutError if timeout_ms provided
        N)rW   r   _fetch_offsets_by_times_async)rk   
timestampsr   offsetstpr.   r.   r/   offsets_by_times  s   zFetcher.offsets_by_timesc                    s`   si S t |d|f }t  t }	  si S | j| j }zd}d}| j||jI dH \}}	W n' tj	yE   | jj
 }Y n* tjy\   | jj
jrX| jj
 }nd}Y nw || |	sf|S  fdd|	D  |rz| j||jI dH  W n tjy   d}Y nw |r| jd d }
|jdurt|
|jd }
| jj|
I dH  |  q)	a  Fetch offsets for each partition in timestamps dict. This may send
        request to multiple nodes, based on who is Leader for partition.

        Per-node requests are dispatched concurrently; if any fails, the first
        exception encountered propagates and the remaining results are dropped.

        Arguments:
            timestamps (dict): {TopicPartition: int} mapping of partitions to
                timestamps or OffsetSpec sentinels.

        Returns:
            (fetched_offsets, partitions_to_retry):
                dict[TopicPartition, OffsetAndTimestamp],
                set[TopicPartition]

        Raises:
            KafkaTimeoutError: if offsets cannot be fully fetched before timeout_ms
        z,Failed to get offsets by timestamps in %s msTNFc                    s   i | ]}| | qS r.   r.   r   r   r   r.   r/   
<dictcomp>  s    z9Fetcher._fetch_offsets_by_times_async.<locals>.<dictcomp>r>     )r   rI   dictrV   r   _send_list_offsets_requestsr   r   rS   InvalidMetadataErrorclusterrequest_updateRetriableErrorneed_updateupdaterK   minrW   sleepmaybe_raise)rk   r   r   timerfetched_offsetsr   refresh_futurebackoffr   retrydelayr.   r   r/   r     sN   



z%Fetcher._fetch_offsets_by_times_asyncc                 C      |  |tj|S )a
  Fetch earliest (oldest) offset for each partition.

        Blocks until offsets are obtained, a non-retriable exception is raised
        or ``timeout_ms`` passed.

        Arguments:
            partitions ([TopicPartition]): List of partitions for list offsets.
            timeout_ms (int, optional): The maximum time in milliseconds to block.

        Returns:
            {TopicPartition: int}: Mapping of partition to retrieved offset.

        Raises:
            KafkaTimeoutError if timeout_ms provided.
        )beginning_or_end_offsetr
   EARLIESTrk   
partitionsr   r.   r.   r/   beginning_offsets     zFetcher.beginning_offsetsc                 C   r   )a  Fetch latest (most recent) offset for each partition.

        Blocks until offsets are obtained, a non-retriable exception is raised
        or ``timeout_ms`` passed.

        Arguments:
            partitions ([TopicPartition]): List of partitions for list offsets.
            timeout_ms (int, optional): The maximum time in milliseconds to block.

        Returns:
            {TopicPartition: int}: Mapping of partition to retrieved offset.

        Raises:
            KafkaTimeoutError if timeout_ms provided.
        )r   r
   LATESTr   r.   r.   r/   end_offsets  r   zFetcher.end_offsetsc                    sD   t  fdd|D }| j| j||}|D ]	}|| j||< q|S )aq  Fetch offset for each partition using ``timestamp``.

        Blocks until offsets are obtained, a non-retriable exception is raised
        or ``timeout_ms`` passed.

        Arguments:
            partitions ([TopicPartition]): List of partitions for list offsets.
            timestamp (int or OffsetSpec): OffsetSpec.LATEST (-1) for the latest
                available, OffsetSpec.EARLIEST (-2) for the earliest available.
                Otherwise timestamp is treated as epoch milliseconds.
            timeout_ms (int, optional): The maximum time in milliseconds to block.

        Returns:
            {TopicPartition: int}: Mapping of partition to retrieved offset.

        Raises:
            UnsupportedVersionError if broker does not support any compatible
                ListOffsetsRequest api version.
            KafkaTimeoutError if timeout_ms provided.
        c                    s   g | ]}| fqS r.   r.   r   r   r.   r/   
<listcomp>  s    z3Fetcher.beginning_or_end_offset.<locals>.<listcomp>)r   rW   r   r   r   )rk   r   r   r   r   r   r   r.   r   r/   r     s
   zFetcher.beginning_or_end_offsetc              
   C   s  |du r	| j d }|dkrtd| jdur2| j}d| _|j}| j|r2| j|j|jkr2|j	t
t}|}d}d}t| jD ]}| j|sT| j| j| qB| jdu rqt| jD ]}| j|sp| j|| _ nq_zV|dkr| js| js}nI| j }	| j|	jr|	| j|	j< qr|	j}|	j}| |	| _n%| jj}| j|r| j| j|< d| _qr|}| jj}|| || j||8 }|dksvW n ty }
 z|s|
t|||
| _W Y d}
~
nd}
~
ww t|t| jfS )a   Returns previously fetched records and updates consumed offsets.

        Arguments:
            max_records (int): Maximum number of records returned. Defaults
                to max_poll_records configuration.

        Raises:
            OffsetOutOfRangeError: if no subscription offset_reset_strategy
            CorruptRecordError: if message crc validation fails (check_crcs
                must be set to True)
            RecordTooLargeError: if a message is larger than the currently
                configured max_partition_fetch_bytes
            TopicAuthorizationError: if consumer is not authorized to fetch
                messages from the topic
            ValueError: if max_records is <= 0

        Returns: (records (dict), partial (bool))
            records: {TopicPartition: [messages]}
            partial: True if records returned did not fully drain any pending
                partition requests. This may be useful for choosing when to
                pipeline additional fetch requests.
        Nr8   r   zmax_records must be > 0)rK   rR   rg   r   rX   is_fetchablepositionr   r$   r)   rY   defaultdictr}   r]   	is_pausedr[   r~   popr\   r^   popleftr#   _parse_fetched_datanext_fetch_offset_append	Exceptionr(   r   bool)rk   r   rs   exc_metar   drainedrecords_remainingfetched_partitionr$   
completioner.   r.   r/   r{     sr   

 


zFetcher.fetched_recordsc           	      C   s  |sdS |j }| j|std| nq| j|s!td| nd| jj| j}|j|j	kr{td|j	| |
|}|rD|| | | jj| j}|d ur\| jr\| jj||j  |s`|swtd||j|j t|jd|j| jj| _t|S td||j|j	 |  dS )Nr   zMNot returning fetched records for partition %s since it is no longer assignedzWNot returning fetched records for assigned partition %s since it is no longer fetchablez@Returning fetched records at offset %d for assigned partition %szIUpdating fetch position for assigned partition %s to %s (leader epoch %s)rD   zMIgnoring fetched records for %s at offset %s since the current position is %d)r#   rX   is_assignedr   r   r   
assignmentr   r   r   taker   	highwaterrb   records_fetch_lagrecordr   r   lendrain)	rk   r   partr   rs   r   r   part_recordsr   r.   r.   r/   r   r  sH   


zFetcher._appendc                 C   s~   | j |std| d S | j |std| d S |r/|| j j| jks/td| d S td|| | j || d S )Nz=Skipping reset of partition %s since it is no longer assignedz>Skipping reset of partition %s since reset is no longer neededzLSkipping reset of partition %s since an alternative reset has been requestedz/Resetting offset for partition %s to offset %s.)	rX   r   r   r   is_offset_reset_neededr   reset_strategyinfoseek)rk   r   r   r   r.   r.   r/   _reset_offset_if_needed  s   zFetcher._reset_offset_if_neededc              	      s  |du r
| j d }t|}|js| jdurdS | j }|sO| j }|du r*dS td|t	  }|j
dur@t||j
d }|dkrN| jj|I dH  qi }|D ]}| jj| j}|rb|||< qS|sgdS | |}	|	s| jj }
| j d }|j
durt||j
}z| j|
|I dH  W n
 tjy   Y nw qtdt|  g }|	 D ])\}}t| }t	 | j d d  }| j|| || j| j||| q|D ]}|I dH  q|jrdS dS )am  Drive resets to completion or until the timer expires.

        Each iteration fans out per-node ListOffsets requests concurrently
        and awaits all of them. After a retriable failure (NotLeader, etc.)
        a partition's next_allowed_retry_time is set ``retry_backoff_ms`` in
        the future; the loop sleeps until that time and retries rather than
        relying on an external caller to redrive. If all partitions have
        unknown leaders, awaits a metadata refresh and retries within the
        remaining budget.

        Arguments:
            timeout_ms (int, optional): Hard upper bound on the loop's
                wall-clock. None falls back to ``request_timeout_ms`` so a
                deleted-topic / permanently-unknown-leader partition can't
                spin the loop forever. The metadata-refresh wait inside
                the loop is capped by ``min(remaining_timer, request_timeout_ms)``.

        Per-node failures are caught inside _reset_offsets_for_node and
        stuffed into self._cached_list_offsets_exception; the next call to
        reset_offsets_if_needed surfaces them.
        Nr=           r   r   zResetting offsets for %s) rK   r   expiredrf   rX   r   next_offset_reset_retry_timemaxr   r   r   r   rV   rW   r   r   r   _group_list_offset_requestsr   r   r   rS   r   r   r   rd   keysr   set_reset_pendingr~   r   _reset_offsets_for_node)rk   r   r   r   
next_retryr   offset_resetsr   tstimestamps_by_nodemetadata_updatewait_ms
node_tasksr   t_and_enode_partitions	expire_attaskr.   r.   r/   r     sd   









zFetcher._reset_offsets_asyncc              
      s   z|  ||I d H \}}W n< tyJ } z0| j|t | jd d   | jj	  t
|tjs?| js9|| _ntd| W Y d }~d S d }~ww |rc| j|t | jd d   | jj	  | D ]\}}|| \}	}
| ||	|j qgd S )Nr>   r   zKDiscarding error in ListOffsetResponse because another error is pending: %s)_send_list_offsets_requestr   rX   reset_failedr   r   rK   rV   r   r   rL   rS   r   rf   r   errorr   r   r   )rk   r   timestamps_and_epochsr   r   partitions_to_retryr  r   r   r   _epochr.   r.   r/   r     s(    
 zFetcher._reset_offsets_for_nodec           	         sn     |}|st  fdd| D }t }t }|D ]}|I dH \}}|| || q||fS )a  Fetch offsets for each partition in timestamps dict. This may send
        request to multiple nodes, based on who is Leader for partition.

        Per-node requests are dispatched concurrently; if any fails, the first
        exception encountered propagates and the remaining results are dropped.

        Arguments:
            timestamps (dict): {TopicPartition: int} mapping of fetching
                timestamps.

        Returns:
            (fetched_offsets, partitions_to_retry):
                dict[TopicPartition, OffsetAndTimestamp],
                set[TopicPartition]

        Raises:
            StaleMetadata: if no node has known leader for any partition.
        c                    s"   g | ]\}} j  j||qS r.   )rV   r   r  )r   r   r   rq   r.   r/   r   *  s    z7Fetcher._send_list_offsets_requests.<locals>.<listcomp>N)r   rS   StaleMetadatar   r   rd   r   )	rk   r   r   r   r   r
  foffsr   r.   rq   r/   r     s   


z#Fetcher._send_list_offsets_requestsc                 C   s   t t}| D ]?\}}| jj|}|d u r-| jj|j t	
d| | jj  q	|dkr>t	
d| | jj  q	d}||f|| |< q	t|S )Nz+Partition %s is unknown for fetching offsetr   zRLeader for partition %s unavailable for fetching offset, wait for metadata refresh)rY   r   r   r   rV   r   leader_for_partition	add_topicr   r   r   r   )rk   r   r   r   r   r   r   r.   r.   r/   r   7  s   
z#Fetcher._group_list_offset_requestsc                    s   t dd | D rdnd}t|t| j}tt}|	 D ]\}\}}t
|j||d}||j | q"t| jt|	 |d}	td|	| | jj|	|dI d	H }
| |
S )
a7  Send single ListOffsetsResponse to node_id

        Returns:
            (fetched_offsets, partitions_to_retry):
                dict[TopicPartition, OffsetAndTimestamp],
                set[TopicPartition]

        Raises:
            TopicAuthorizationFailedError: if any topic returned an auth error
        c                 s   s    | ]	}|d  d kV  qdS )r   Nr.   )r   resr.   r.   r/   r   S  s    z5Fetcher._send_list_offsets_request.<locals>.<genexpr>r4   r   )partition_indexcurrent_leader_epochr   )rA   topicsmin_versionz)Sending ListOffsetRequest %s to broker %sr   N)r   valuesr   r   min_version_for_isolation_levelrQ   rY   r   r}   r   _ListOffsetsPartitionr   r   r~   r   r   rV   r   _handle_list_offsets_response)rk   r   r	  r  by_topicr   r   r   datar   responser.   r.   r/   r  H  s&   


z"Fetcher._send_list_offsets_requestc              	   C   s  t  }t }t }|jD ]}|jD ]}t|j|j}|j}t	|}	|	tj
u rm|jdkrB|j}
t|
dkr9td|
r?|
d nt}n|j}|j}|j}|du rQd}|du rWd}td|||| |tkrlt|||||< q|	tju rytd| q|	tjtjtjtjtjfv rtd|	j| || q|	tju rtd	| || q|	tj u r||j! qtd
||	j || qq|rt |||fS )at  Parse a ListOffsets response.

        Returns:
            (fetched_offsets, partitions_to_retry):
                dict[TopicPartition, OffsetAndTimestamp],
                set[TopicPartition]

        Raises:
            TopicAuthorizationFailedError: if any topic returned an auth error
            ValueError: if ListOffsetsResponse v0 and > 1 offset returned
        r   r4   z,Expected ListOffsetsResponse with one offsetNr   z^Handling ListOffsetsResponse response for %s. Fetched offset %s, timestamp %s, leader_epoch %sz_Cannot search by timestamp for partition %s because the message format version is before 0.10.0zEAttempt to fetch offsets for partition %s failed due to %s, retrying.zReceived unknown topic or partition error in ListOffsets request for partition %s. The topic/partition may not exist or the user may not have Describe access to it.z;Attempt to fetch offsets for partition %s failed due to: %s)"r   rd   r  r   r   namer  
error_coderS   for_codeNoErrorAPI_VERSIONold_style_offsetsr   rR   r   r   r   r   r   r   r    UnsupportedForMessageFormatErrorNotLeaderForPartitionErrorReplicaNotAvailableErrorKafkaStorageErrorOffsetNotAvailableErrorLeaderNotAvailableErrorr+   r   UnknownTopicOrPartitionErrorwarningTopicAuthorizationFailedErrorr   )rk   r  r   r
  unauthorized_topics
topic_datapartition_infor   r  
error_typer   r   r   r   r.   r.   r/   r  g  sp   







3
z%Fetcher._handle_list_offsets_responsec                 C   s0   | j  D ]}| jj|}| j || qdS )a  Walk assigned partitions; mark any whose cluster leader epoch has
        advanced beyond the position's epoch as awaiting validation.

        Cheap fire-and-forget marker; the actual RPC fan-out runs in
        ``validate_offsets_if_needed`` -> ``_validate_offsets_async``.
        Idempotent: partitions already awaiting validation, awaiting
        reset, or with no recorded epoch are skipped inside
        ``maybe_validate_position``.
        N)rX   assigned_partitionsrV   r   leader_epoch_for_partition*maybe_validate_position_for_current_leader)rk   r   current_epochr.   r.   r/   maybe_validate_positions  s   
z Fetcher.maybe_validate_positionsc                 C   r   )aL  Schedule any pending position validations and return the in-flight Task.

        Mirrors :meth:`reset_offsets_if_needed`: returns a cached Future
        shared across callers so concurrent ``consumer.poll`` and
        ``consumer.position`` callers don't race the same partition into
        duplicate OffsetForLeaderEpoch requests.

        Raises:
            LogTruncationError: if a previous validation detected truncation
                on one or more partitions. The exception is cleared after
                being raised so subsequent calls will re-attempt validation.
        N)rj   ri   ru   rX   partitions_needing_validationrV   r   _validate_offsets_asyncr   r.   r.   r/   validate_offsets_if_needed  s   
z"Fetcher.validate_offsets_if_neededc                    s  |du r
| j d }t|}|js| jdurdS | j }|sO| j }|du r*dS td|t	  }|j
dur@t||j
d }|dkrN| jj|I dH  qi }|D ]}| jj| }|jdurk|jjdkrk|j||< qS|spdS | |}	|	s| jj }
| j d }|j
durt||j
}z| j|
|I dH  W n
 tjy   Y nw qtdt|  g }|	 D ](\}}t| }t	 | j d d  }| j|| || j| j || q|D ]}|I dH  q|jrdS dS )a  Drive offset validations to completion or until the timer expires.

        Same overall shape as ``_reset_offsets_async``: per-node fan-out.
        After a retriable failure (FencedLeaderEpoch, etc.) a partition's
        next_allowed_retry_time is set ``retry_backoff_ms`` in the future;
        the loop sleeps until that time and retries rather than relying on
        an external caller to redrive. Stops on first ``LogTruncationError``
        accumulation; the next caller surfaces it.
        Nr=   r   r   r   zValidating offsets for %s)!rK   r   r   rj   rX   r5  !next_offset_validation_retry_timer   r   r   r   r   rV   rW   r   r   r   r   '_group_offset_for_leader_epoch_requestsr   r   r   rS   r   r   r   rd   r   r   set_validation_pendingr~   r   _validate_offsets_for_node)rk   r   r   r   r   r   	positionsr   staterequests_by_noder   r   r  r   payloadr  r  r  r.   r.   r/   r6    sd   










zFetcher._validate_offsets_asyncc              
      s   z|  ||I d H }W n8 tyD } z,| jt|t | jd d   | jj	
  t|tjs9td|| W Y d }~d S d }~ww |r]| jd u rTt|| _d S | jj| d S d S )Nr>   r   z<Non-retriable error from OffsetForLeaderEpoch on node %s: %s)%_send_offset_for_leader_epoch_requestr   rX   validation_failedrd   r   r   rK   rV   r   r   rL   rS   r   r   r  rj   LogTruncationErrordivergent_offsetsr   )rk   r   partitions_to_positionstruncationsr  r.   r.   r/   r;    s.   

z"Fetcher._validate_offsets_for_nodec                 C   s   t t}| D ]5\}}|jdk rq	| jj|}|du r-| jj|j	 | jj
  q	|dkr8| jj
  q	||| |< q	t|S )a  Group {TopicPartition: OffsetAndMetadata} by leader node.

        Partitions whose leader is unknown trigger a metadata refresh and
        are dropped from this round. Partitions whose position lacks an
        epoch are also dropped - they can't be validated.
        r   Nr   )rY   r   r   r   r   rV   r   r  r  r   r   )rk   r<  by_noder   r   r   r.   r.   r/   r9  *  s   

z/Fetcher._group_offset_for_leader_epoch_requestsc           	         s   t t}| D ]$\}}| jj|}|du s|dk rd}||j t	|j
||jd q
tdt| d}td|| | jj||dI dH }| ||S )a  Send one OffsetForLeaderEpoch request and return any truncations.

        Returns:
            dict[TopicPartition, OffsetAndMetadata]: partitions whose log
            was truncated past their position. Successful validations
            update :class:`SubscriptionState` directly via
            ``complete_validation``; retriable per-partition errors leave
            ``next_allowed_retry_time`` set so the outer loop will retry.

        Raises:
            TopicAuthorizationFailedError: if any topic returned an auth error.
        Nr   r   )r   r  r   )
replica_idr  z3Sending OffsetForLeaderEpochRequest %s to broker %sr   )rY   r   r}   r   rV   r   r1  r   r~   _OffsetForLeaderPartitionr   r   r	   r   r   r   (_handle_offset_for_leader_epoch_response)	rk   r   rD  r  r   r   r  r   r  r.   r.   r/   r@  ?  s$   


z-Fetcher._send_offset_for_leader_epoch_requestc              	   C   s|  i }t  }t | jd d  }t  }|jD ]}|jD ]}t|j|j}	|	|	}
|
du r0qt
|j}|t
ju r|j}|j}|du rGd}| j|	rT| jj|	 jnd}|du s^||
kretd|	 q| j }|dk sr|dk r|rtd|	|j | j|	 qtd|	|j d||	< | j|	 q||jk rt|d	|}|rtd
|	|j| | j|	| qtd|	|j| |||	< | j|	 qt|j|j|}| j|	| q|t
jt
j t
j!t
j"t
j#t
j$fv rtd|	|j% | j&j'(  |)|	 q|t
j*u rtd|	 |)|	 q|t
j+u r|)|	j qtd|	|j% |)|	 qq|r4| j,|| |r<t
+||S )a  Parse an OffsetForLeaderEpoch response.

        Side effects: calls ``complete_validation`` / ``validation_failed``
        / ``request_position_validation`` on the subscription state as
        appropriate for each partition's response code.

        Returns:
            dict[TopicPartition, OffsetAndMetadata]: subset of requested
            partitions where end_offset < requested position (truncation).
        r>   r   Nr   zNSkipping validation completion for %s: position changed since request was sentr   zTruncation detected for %s at position %s (broker returned UNDEFINED end_offset/leader_epoch); resetting offset per auto_offset_reset policyzyTruncation detected for %s at position %s (broker returned UNDEFINED end_offset/leader_epoch), but no reset policy is setrD   zOTruncation detected for %s at position %s; seeking to first diverging offset %szdTruncation detected for %s at position %s (first diverging offset is %s), but no reset policy is setzKOffsetForLeaderEpoch for %s returned retriable %s; will retry after backoffz4OffsetForLeaderEpoch for %s: unknown topic/partitionz*OffsetForLeaderEpoch for %s failed with %s)-rd   r   r   rK   r  r   r   r   r   getrS   r  r  r   
end_offsetr   rX   r   r   r   r   r   has_default_offset_reset_policyr   r   request_offset_resetr*  complete_validationr   r   metadataFencedLeaderEpochErrorUnknownLeaderEpochErrorr$  r%  r&  r(  r+   rV   r   r   r   r)  r+  rA  )rk   r  requested_positionsrE  r,  retry_atr   r-  r.  r   	requestedr/  rK  	end_epochcurrenthas_reset_policy	divergent	validatedr.   r.   r/   rI  `  s   





R
z0Fetcher._handle_offset_for_leader_epoch_responsec                    s^   | j  }dd | j D  | j}|r |j  | j  | j	  fdd|D S )Nc                 S   s   h | ]}|j qS r.   )r#   )r   fetchr.   r.   r/   	<setcomp>  s    z0Fetcher._fetchable_partitions.<locals>.<setcomp>c                    s   g | ]}| vr|qS r.   r.   r   discardr.   r/   r         z1Fetcher._fetchable_partitions.<locals>.<listcomp>)
rX   fetchable_partitionsr[   rI   r\   r   r#   r   r]   r^   )rk   	fetchablerV  r.   r\  r/   _fetchable_partitions  s   
zFetcher._fetchable_partitionsc                 C   sl   | j j|  }|du r| jj|S | jj||s4| j j|   | jj|}t	d||| |S |S )a  Pick the node to fetch from for ``tp``: a cached preferred read
        replica (KIP-392) when valid and *still listed as a replica of
        ``tp``*, otherwise the partition leader. A preferred replica that
        has been demoted out of the partition's replica set (or fell out
        of cluster metadata entirely) is cleared so the next fetch goes
        to the leader.
        NzmPreferred read replica %s for partition %s no longer online or no longer a replica; falling back to leader %s)
rX   r   preferred_read_replicarV   r   r  is_replica_nodeclear_preferred_read_replicar   r   )rk   r   	preferredleaderr.   r.   r/   _select_read_replica  s   zFetcher._select_read_replicac                    s  d}t t j}|  D ]z}| |}| jj| j}|du s"|dkr/t	d| | j
j  q| j
|dkr?t	d|| q| j|dkrOt	d|| q|| jv r\t	d|| q| j
j|}|du rid}t|j||j|j| jd	 d
}||| |< t	d||j qi }| D ]X\} | jr|| jvrt|| j|< | j|  }	nt dtj}	t | j!}
t| jd | jd | jd | j!|	j"|	j#|	j$|	j%| jd |
|d} fdd D }||f||< q|S )zCreate fetch requests for all assigned partitions, grouped by node.

        FetchRequests skipped if no leader, or node has requests in flight

        Returns:
            dict: {node_id: (FetchRequest, {TopicPartition: fetch_offset}), ...}
           Nr   z<No leader found for partition %s. Requesting metadata updater   zMSkipping fetch for partition %s because node %s is awaiting reconnect backoffz<Skipping fetch for partition %s because node %s is throttledzSSkipping fetch for partition %s because there is a pending fetch request to node %sr7   )r   r  fetch_offsetlast_fetched_epochpartition_max_bytesz2Adding fetch request for partition %s at offset %dr5   r3   r6   rC   )max_wait_ms	min_bytes	max_bytesrA   
session_idsession_epochr  forgotten_topics_datarack_idr  max_versionc                    s   i | ]}| | j qS r.   )ri  r   next_partitionsr.   r/   r   B  r^  z2Fetcher._create_fetch_requests.<locals>.<dictcomp>)&rY   r   OrderedDictra  rg  rX   r   r   r   r   rV   r   r   connection_delayrU   throttle_delayre   r1  _FetchPartitionr   r   r   rK   r   rr   rc   FetchSessionHandler
build_nextFetchRequestDataFetchMetadataLEGACYr   r  rQ   idepochto_send	to_forget)rk   rs  r`  r   r   r   r  r.  requestssessionr  r   r   r.   rt  r/   r     sx   

	
zFetcher._create_fetch_requestsc                 C   s   |j dkr| jr|| jvrtd| dS | j| |sdS tdd |jD }| jr3t	| j|}nd}|jD ]"}|j
D ]}t|j|j}	||	 }
t|	|
|j ||}| j| q=q8| jrm| jjt | d  dS dS )z!The callback for fetch completion   zIUnable to find fetch session handler for node %s. Ignoring fetch responseNc                 S   s&   g | ]}|j D ]	}t|j|jqqS r.   r   r   r   r  r   r-  r&   r.   r.   r/   r   P  s    z2Fetcher._handle_fetch_response.<locals>.<listcomp>r   )r!  rr   rc   r   r  handle_responserd   	responsesrb   FetchResponseMetricAggregatorr   r   r   r  r"   r[   r~   fetch_latencyr   r   r   )rk   r   r   	send_timer  r   r'   r-  r&   r   ri  completed_fetchr.   r.   r/   r   G  s<   


zFetcher._handle_fetch_responsec                 C   sJ   t |tjr	tjntj}t|d|| || jv r#| j| | d S d S )NzFetch to node %s failed: %s)	rL   rS   	CancelledloggingINFOERRORr   rc   handle_error)rk   r   r)   levelr.   r.   r/   r   n  s
   
zFetcher._handle_fetch_errorc                 C   s(   z	| j | W d S  ty   Y d S w rt   )re   removeKeyError)rk   r   rw   r.   r.   r/   r   t  s
   z$Fetcher._clear_pending_fetch_requestc                 C   sr   |j }|du s|jdk rdS | jj||j|jr5td||j|j | jj|jdu r7| jj	  dS dS dS )aU  Apply a KIP-951 ``current_leader`` hint from a Fetch v12+ response.

        Updates the cluster's cached leader id/epoch when the broker advertises
        a newer leader. If the new leader id is not yet a known broker (v12 has
        no ``node_endpoints``), requests a metadata refresh so the consumer
        learns its address.
        Nr   z=Fetch response advertised new leader for %s: node %s epoch %s)
current_leaderr   rV   r   update_partition_leader	leader_idr   r   broker_metadatar   )rk   r   r&   rf  r.   r.   r/   _maybe_update_current_leaderz  s   

z$Fetcher._maybe_update_current_leaderc                 C   s  |j }|j}|jj}|jj}t|}d }z| j|s%t	
d| n|tju r9| jj| j}|d u s;|j|krct	
d|||j W |d u rT|jrT|j|dd |tjura| j| d S d S |jj}	|	d ur|	jdkrt	d||	j|	j | j| | jj  W |d u r|jr|j|dd |tjur| j| d S d S t|jj}
|jj}t	
d|
 || | j|||
| jd | jd | jd | j ||j| j!d	
}|
" s|
 dkr|j#d
k r||i}t$d|| jd f |t%d||f |dkr|| jj| _&|jj'}| jj| (|t)* | jd d  r8|d u s*|dk r1t	
d| nt	
d|| n|tj+tj,tj-tj.fv r\t	
d||j/ | 0||j | jj  n|tj1tj2fv rt	
d||j/ | 0||j | j| | jj  n|tj3u r| jj| j}|d u s|j|krt	
d|||j nh| jj| 4 }|d urt	
d||| nR| j5 rt	d|| | j6| n>t3||i|tj7u rt	8d|j9 t7t:|j9gt;|tj<rt	
d||  t;|tj=r| jj  n|dW |d u r|jr|j|dd |tjur)| j| |S |d u r<|jr<|j|dd |tjurI| j| w w )NzIIgnoring fetched records for partition %s since it is no longer fetchablezdDiscarding fetch response for partition %s since its offset %d does not match the expected offset %dr   zLFetch for %s diverged at epoch %s offset %s; marking position for validationzBPreparing to read %s bytes of data for partition %s with offset %dr1   r2   r9   )r1   r2   r9   rA   aborted_transactionsr'   on_drainrF   a'  There are some messages at [Partition=Offset]: %s  whose size is larger than the fetch size %s and hence cannot be ever returned. Please condier upgrading your broker to 0.10.1.0 or newer to avoid this issue. Alternatively, increase the fetch size on the client (using max_partition_fetch_bytes)r7   zFailed to make progress reading messages at %s=%s. Received a non-empty fetch response from the server, but no complete records were found.rE   g     @@z/Cleared preferred read replica for partition %sz6Updating preferred read replica for partition %s to %szError fetching partition %s: %sz9Fetch for %s returned %s; marking position for validationzqDiscarding stale fetch response for partition %s since the fetched offset %d does not match the current offset %dzHFetch offset %s out of range for %s on follower %s; retrying from leaderz6Fetch offset %s is out of range for topic-partition %sz%Not authorized to read from topic %s.z)Retriable error fetching partition %s: %sz$Unexpected error while fetching data)>r#   r$   r&   r  high_watermarkrS   r  rX   r   r   r   r   r   r   r   r'   r   move_partition_to_enddiverging_epochrK  r   r  request_position_validationrV   r   r   r   r   r  size_in_bytesPartitionRecordsrK   rQ   _on_partition_records_drainhas_nextr%   r*   
KafkaErrorr   rb  update_preferred_read_replicar   r   r$  r%  r)  r&  r+   r  rP  rQ  OffsetOutOfRangeErrorrd  rL  rM  r+  r*  r   rd   
issubclassr   r   )rk   r  r   ri  r  r   r/  parsed_recordsr   r  r   r  record_too_large_partitionsrb  clearedr.   r.   r/   r     s  
r

e





zFetcher._parse_fetched_datac                 C   s    |j dkr| j|j d S d S Nr   )
bytes_readrX   r  r#   )rk   partition_recordsr.   r.   r/   r  &  s   
z#Fetcher._on_partition_records_drainc                 C   sJ   | j d ur
| j   | j D ]}|  q| j  | j  d | _d S rt   )r\   r   r^   r  clearr]   rg   )rk   parkedr.   r.   r/   close0  s   





zFetcher.closec                   @   s|   e Zd Zdddejdddd fddZdd Z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S )zFetcher.PartitionRecordsNTc                 C   s   d S rt   r.   )xr.   r.   r/   <lambda>?  s    z!Fetcher.PartitionRecords.<lambda>c              	   C   s   || _ || _d| _|| _d| _d| _|| _t | _t	
t|pg dd d| _|	| _|| _t| j| ||||| _|
| _d | _d S )Nr   r   c                 S      | j S rt   )first_offset)txnr.   r.   r/   r  I  s    z3Fetcher.PartitionRecords.__init__.<locals>.<lambda>)r   )ri  r#   r   r   r  records_readrA   rd   aborted_producer_idsrY   rZ   sortedr  r'   r9   	itertools	dropwhile_maybe_skip_record_unpack_recordsrecord_iteratorr  _next_inline_exception)rk   ri  r   r   r1   r2   r9   rA   r  r'   r  r.   r.   r/   ro   :  s&   
z!Fetcher.PartitionRecords.__init__c                 C   s&   |j | jk rtd|j | j dS dS )Nz*Skipping message offset: %s (expecting %s)TF)r   ri  r   r   )rk   r   r.   r.   r/   r  S  s   z+Fetcher.PartitionRecords._maybe_skip_recordc                 C   s
   | j d uS rt   )r  rq   r.   r.   r/   __bool__`  s   
z!Fetcher.PartitionRecords.__bool__c                 C   sD   | j d ur d | _ d | _| jr| j| j| j| j | |  d S d S rt   )r  r  r'   r   r#   r  r  r  rq   r.   r.   r/   r   c  s   
zFetcher.PartitionRecords.drainc                 C   s   | j r| j d }| _ |d S rt   )r  )rk   r   r.   r.   r/   "_maybe_raise_next_inline_exceptionk  s   z;Fetcher.PartitionRecords._maybe_raise_next_inline_exceptionc              
   C   s^   |    g }z|t| jd| W |S  ty. } z|s ||| _W Y d }~|S d }~ww r  )r  r   r  islicer  r   r  )rk   nr   r   r.   r.   r/   r   p  s   zFetcher.PartitionRecords.takec                 c   sV   z|  }d }|d ur|}| jr"| s"td| j|jf |jdkr{|j| _| j	t
jkro| ro| |j |j}| |rUz| j| W n# tyT   Y nw | |rotd| j||j|j |j| _|  }q	|jr{|j| _|  }q	|D ]}| jr| std| j|jf |jd urt|jnd}	|jd urt|jnd}
| ||j|j |j}| ||j|j |j}|j }|rt!dd |D nd}|  j"d7  _"|  j#|j$7  _#|jd | _t%|j|j&| j|j|j'|j(|||j |j)|	|
|V  q}|  }|d us|r|jdkr|j| _| *  W d S  t+y*   t,d	 t-d	w )
Nz;Record batch for partition %s at offset %s failed crc check   zXSkipping aborted record batch from partition %s with producer_id %s and offsets %s to %sz5Record for partition %s at offset %s failed crc checkr   c                 s   s6    | ]\}}t |d |durt |nd V  qdS )zutf-8Nr   )r   encode)r   h_keyh_valr.   r.   r/   r     s    &
z;Fetcher.PartitionRecords._unpack_records.<locals>.<genexpr>r4   z)StopIteration raised unpacking messageset).
next_batchr9   validate_crcrS   CorruptRecordErrorr#   base_offsetmagicr   rA   r   READ_COMMITTEDhas_producer_id#_consume_aborted_transactions_up_tolast_offsetproducer_id_contains_abort_markerr  r  r  _is_batch_abortedr   r   next_offsetr   is_control_batchr   r   r   r   _deserializer   r   sumr  r  r  r   r   r   r   r   r   StopIterationr)   RuntimeError)rk   r   r   r1   r2   batch
last_batchr  r   key_size
value_sizer   r   r   header_sizer.   r.   r/   r  }  s   




D
z(Fetcher.PartitionRecords._unpack_recordsc                 C   sR   |d u r|S z| |||W S  ty(   ts tjdtd d}| || Y S w )NzAdeserializer does not implement deserialize(topic, headers, data))rG   T)deserialize	TypeError_LOGGED_DESERIALIZE_WARNINGrM   rN   rO   )rk   deserializerr   r   r  LOGGED_DESERIALIZE_WARNINGr.   r.   r/   r    s   z%Fetcher.PartitionRecords._deserializec                 C   sZ   | j sd S | j r'| j d j|kr+| j| j  j | j r)| j d j|ksd S d S d S d S r  )r  r  r  r   r   r  )rk   r   r.   r.   r/   r    s
   &z<Fetcher.PartitionRecords._consume_aborted_transactions_up_toc                 C   s   |j o|j| jv S rt   )is_transactionalr  r  )rk   r  r.   r.   r/   r    s   z*Fetcher.PartitionRecords._is_batch_abortedc                 C   s    |j sdS t|}|sdS |jS )NF)r  nextabort)rk   r  r   r.   r.   r/   r    s   z/Fetcher.PartitionRecords._contains_abort_markerrt   )r+   r,   r-   r   READ_UNCOMMITTEDro   r  r  r   r  r   r  r  r  r  r  r.   r.   r.   r/   r  9  s"    

Sr  )NTNrt   )NT)/r+   r,   r-   sysmaxsizerJ   ro   propertyrr   r   r|   r   r   r   r   r   r   r   r   r   r{   r   r   r   r   r   r   r  r  r4  r7  r6  r;  r9  r@  rI  ra  rg  r   r   r   r   r  r   r  r  r  r.   r.   r.   r/   r0   S   s    	
X

H	/

&

=


U4
L$K

;!i^' 
	r0   c                   @   s8   e Zd ZdZdd Zdd Zdd Zdd	 Zd
d ZdS )rz  a]  
    FetchSessionHandler maintains the fetch session state for connecting to a broker.

    Using the protocol outlined by KIP-227, clients can create incremental fetch sessions.
    These sessions allow the client to fetch information about a set of partition over
    and over, without explicitly enumerating all the partitions in the request and the
    response.

    FetchSessionHandler tracks the partitions which are in the session.  It also
    determines which partitions need to be included in each fetch request, and what
    the attached fetch session metadata should be for each request.
    c                 C   s   || _ tj| _i | _d S rt   )r   r}  INITIALnext_metadatasession_partitions)rk   r   r.   r.   r/   ro     s   
zFetchSessionHandler.__init__c              
      s"  | j jrtd| j | jt | _td| j S t| j	 }t	 }td|| ||   D ]	}| | j|< q4|| }|D ]}| j
| qDt ||@ D ]}| | j| krk| | j|< | qTtd| j | j || j	  t fddD }t||| j S )z
        Arguments:
            next_partitions (dict): TopicPartition -> TopicPartitionState

        Returns:
            FetchRequestData
        z5Built full fetch %s for node %s with %s partition(s).Nz;Building incremental partitions from next: %s, previous: %szRBuilt incremental fetch %s for node %s. Added %s, altered %s, removed %s out of %sc                    s"   i | ]}| B v r|| qS r.   r.   r   addedalteredru  r.   r/   r   !  s   " z2FetchSessionHandler.build_next.<locals>.<dictcomp>)r  is_fullr   r   r   r   r  r|  rd   r   r   r   rY   rv  )rk   ru  prev_tpsnext_tpsr   removedr  r.   r  r/   r{    s4   
zFetchSessionHandler.build_nextc              	   C   s  |j tjjkr+t|j }td| j| j|  |tj	u r#t
j| _dS | j | _dS | |}t| j }| jjr||krRtd| j|| ||  t
j| _dS |jt
jkrhtd| jt| t
j| _dS |jt
jkrztd| jt| dS td| j|jt| t
|j| _dS || rtd| j||  | j | _dS |jt
jkrtd	| j| jjt|t| jt|  t
j| _dS |jt
jkrtd
| jt| dS td| j|jt|t| jt|  | j | _dS )Nz<Node %s was unable to process the fetch request with %s: %s.FzFNode %s sent an invalid full fetch response with extra %s / omitted %sz5Node %s sent a full fetch response with %s partitionsTzQNode %s sent a empty full fetch response due to a quota violation (%s partitions)znNode %s sent a full fetch response that created a new incremental fetch session %s with %s response partitionszKNode %s sent an invalid incremental fetch response with extra partitions %szfNode %s sent an incremental fetch response closing session %s with %s response partitions (%s implied)zXNode %s sent a empty incremental fetch response due to a quota violation (%s partitions)zbNode %s sent an incremental fetch response for session %s with %s response partitions (%s implied))r  rS   r   errnor  r   r   r   r  FetchSessionIdNotFoundErrorr}  r  next_close_existing_response_partitionsrd   r  r   r  ro  INVALID_SESSION_IDr   r   THROTTLED_SESSION_IDnew_incrementalnext_incremental)rk   r  r/  response_tpssession_tpsr.   r.   r/   r  $  sx   






z#FetchSessionHandler.handle_responsec                 C   s   | j  | _ d S rt   )r  r  )rk   
_exceptionr.   r.   r/   r  e     z FetchSessionHandler.handle_errorc                 C   s   dd |j D S )Nc                 S   s&   h | ]}|j D ]	}t|j|jqqS r.   r  r  r.   r.   r/   r[  i  s    z;FetchSessionHandler._response_partitions.<locals>.<setcomp>)r  )rk   r  r.   r.   r/   r  h  s   z(FetchSessionHandler._response_partitionsN)	r+   r,   r-   __doc__ro   r{  r  r  r  r.   r.   r.   r/   rz    s    "Arz  c                   @   s`   e Zd ZdZdZdZdZdZdZdd Z	e
dd Zed	d
 Zdd Zedd Zdd ZdS )r}  ro  r  ir   r   c                 C   s   || _ || _d S rt   r  )rk   ro  r  r.   r.   r/   ro   w     
zFetchMetadata.__init__c                 C   s   | j | jkp| j | jkS rt   )r  INITIAL_EPOCHFINAL_EPOCHrq   r.   r.   r/   r  {  s   zFetchMetadata.is_fullc                 C   s$   |dk r| j S || jkrdS |d S )Nr   r4   )r  	MAX_EPOCH)cls
prev_epochr.   r.   r/   
next_epoch  s
   
zFetchMetadata.next_epochc                 C   s   |  | j| jS rt   )	__class__ro  r  rq   r.   r.   r/   r    r   z!FetchMetadata.next_close_existingc                 C   s   | ||  | jS rt   )r	  r  )r  ro  r.   r.   r/   r    s   zFetchMetadata.new_incrementalc                 C   s   |  | j| | jS rt   )r
  ro  r	  r  rq   r.   r.   r/   r    s   zFetchMetadata.next_incrementalN)r+   r,   r-   	__slots__r  r  r  r  r  ro   r  r  classmethodr	  r  r  r  r.   r.   r.   r/   r}  n  s     


r}  c                   @   sT   e Zd ZdZdd Zedd Zedd Zedd	 Zed
d Z	edd Z
dS )r|  )_to_send
_to_forget	_metadatac                 C   s"   |pt  | _|p
t | _|| _d S rt   )r   r  rd   r  r  )rk   r  r  rO  r.   r.   r/   ro     s   
zFetchRequestData.__init__c                 C   r  rt   )r  rq   r.   r.   r/   rO    s   zFetchRequestData.metadatac                 C      | j jS rt   )r  ro  rq   r.   r.   r/   r       zFetchRequestData.idc                 C   r  rt   )r  r  rq   r.   r.   r/   r    r  zFetchRequestData.epochc                 C   s@   t t}| j D ]\}}||j | q
dd | D S )Nc                 S      g | ]
\}}t ||d qS )r   r   )_FetchTopicr   r   r   r.   r.   r/   r         
z,FetchRequestData.to_send.<locals>.<listcomp>)rY   r   r}   r  r   r   r~   )rk   r&   r   r.  r.   r.   r/   r    s   
zFetchRequestData.to_sendc                 C   s:   t t}| jD ]}||j |j qdd | D S )Nc                 S   r  r  )_ForgottenTopicr  r.   r.   r/   r     r  z.FetchRequestData.to_forget.<locals>.<listcomp>)rY   r   r}   r  r   r~   r   r   )rk   r&   r   r.   r.   r/   r    s   

zFetchRequestData.to_forgetN)r+   r,   r-   r  ro   r  rO  r  r  r  r  r.   r.   r.   r/   r|    s    



r|  c                   @   s   e Zd ZdZdd ZdS )FetchMetricstotal_bytestotal_recordsc                 C   s   d| _ d| _d S r  r  rq   r.   r.   r/   ro     r  zFetchMetrics.__init__N)r+   r,   r-   r  ro   r.   r.   r.   r/   r    s    r  c                   @   s    e Zd ZdZdd Zdd ZdS )r  a  
    Since we parse the message data for each partition from each fetch
    response lazily, fetch-level metrics need to be aggregated as the messages
    from each partition are parsed. This class is used to facilitate this
    incremental aggregation.
    c                 C   s$   || _ || _t | _tt| _d S rt   )sensorsunrecorded_partitionsr  fetch_metricsrY   r   topic_fetch_metrics)rk   r  r   r.   r.   r/   ro     s   z&FetchResponseMetricAggregator.__init__c                 C   s   | j | | j j|7  _| j j|7  _| j|j  j|7  _| j|j  j|7  _| j sU| jj	| jj | jj
	| jj | j D ]\}}| j||j|j qFdS dS )z
        After each partition is parsed, we update the current metric totals
        with the total bytes and number of records parsed. After all partitions
        have reported, we write the metric.
        N)r  r  r  r  r  r  r   r  bytes_fetchedr   records_fetchedr   record_topic_fetch_metrics)rk   r   	num_bytesnum_recordsr   r:   r.   r.   r/   r     s   z$FetchResponseMetricAggregator.recordN)r+   r,   r-   r  ro   r   r.   r.   r.   r/   r    s    r  c                   @   s   e Zd Zdd Zdd ZdS )ra   c                 C   sJ  || _ d|f | _|d| _| j|d| jdt  | j|d| jdt  | j|d| jdt  | j d	| _	| j	|d
| jdt  | j	|d| jdt  |d| _
| j
|d| jdt  | j
|d| jdt  | j
|d| jdtt d |d| _| j|d| jdt  d S )Nz%s-fetch-manager-metricsbytes-fetchedfetch-size-avgz/The average number of bytes fetched per requestfetch-size-maxz/The maximum number of bytes fetched per requestbytes-consumed-ratez/The average number of bytes consumed per secondrecords-fetchedrecords-per-request-avgz-The average number of records in each requestrecords-consumed-ratez1The average number of records consumed per secondzfetch-latencyzfetch-latency-avgz+The average time taken for a fetch request.zfetch-latency-maxz)The max time taken for any fetch request.z
fetch-ratez(The number of fetch requests per second.)sampled_statzrecords-lagzrecords-lag-maxzNThe maximum lag in terms of number of records for any partition in self window)r:   
group_namesensorr   r   metric_namer   r   r   r!  r  r   r   )rk   r:   prefixr.   r.   r/   ro     sf   
zFetchManagerMetrics.__init__c                 C   sD  d d|dg}| j|}|sTd|ddi}| j|}|| jd| jd|f |t  || jd| jd|f |t	  || jd	| jd
|f |t
  || d d|dg}| j|}|sd|ddi}| j|}|| jd| jd|f |t  || jd| jd|f |t
  || d S )N.r   r%  rw   r&  z<The average number of bytes fetched per request for topic %sr'  z<The maximum number of bytes fetched per request for topic %sr(  z<The average number of bytes consumed per second for topic %sr)  r*  z:The average number of records in each request for topic %sr+  z>The average number of records consumed per second for topic %s)joinr:   
get_sensorreplacer.  r   r/  r-  r   r   r   r   )rk   r   r#  r$  r  r   metric_tagsr!  r.   r.   r/   r"    s^   
z.FetchManagerMetrics.record_topic_fetch_metricsN)r+   r,   r-   ro   r"  r.   r.   r.   r/   ra     s    ra   )HrY   rI   r  r  r  r   rM   kafka.errorserrorsrS   kafka.futurer   kafka.metrics.statsr   r   r   r   kafka.protocol.consumerr   r   r	   r
   r   r   kafka.recordr   kafka.serializerr   r   kafka.structsr   r   r   
kafka.utilr   	getLoggerr+   r   r  
namedtupler   r  r"   r(   
FetchTopicr  FetchPartitionry  ForgottenTopicr  ListOffsetsTopic_ListOffsetsTopicListOffsetsPartitionr  OffsetForLeaderTopic_OffsetForLeaderTopicOffsetForLeaderPartitionrH  r  r*   r0   rz  r}  r  r  r  r  r~  r|  r  r  ra   r.   r.   r.   r/   <module>   st    
              )$-!