1818import logging
1919import traceback
2020from queue import Queue , Empty
21- from time import time
21+ from time import monotonic
2222
2323import grpc
2424
3333 create_sync_channel ,
3434 handle_rpc_error ,
3535 is_channel_ready ,
36- log_dropped_throttled ,
3736)
37+ from skywalking .utils .reporter_log import log_dropped_throttled
3838from skywalking .profile .profile_task import ProfileTask
3939from skywalking .profile .snapshot import TracingThreadSnapshot
4040from skywalking .protocol .common .Common_pb2 import KeyStringValuePair
4545from skywalking .trace .segment import Segment
4646
4747
48+ def _queue_get_within_batch (queue : Queue , block : bool , batch_deadline : float ):
49+ """
50+ Get one item within an absolute batch window (monotonic deadline).
51+
52+ Avoids int(elapsed) truncation that could let queue waits approach
53+ agent_queue_timeout + 1s and collide with a tight RPC deadline.
54+ Returns None when the window is exhausted or the queue is empty.
55+ """
56+ remaining = batch_deadline - monotonic ()
57+ if remaining <= 0 :
58+ return None
59+ try :
60+ if block :
61+ return queue .get (block = True , timeout = remaining )
62+ return queue .get (block = False )
63+ except Empty :
64+ return None
65+
66+
4867class GrpcProtocol (Protocol ):
4968 def __init__ (self ):
5069 self .properties_sent = False
@@ -147,23 +166,15 @@ def report_segment(self, queue: Queue, block: bool = True):
147166 # Gate before dequeue so disconnect windows keep segments in the queue (Node buffer parity).
148167 if not self .is_ready ():
149168 return
150- start = None
151169 sent = 0
152170
153171 def generator ():
154- nonlocal start , sent
172+ nonlocal sent
155173
174+ batch_deadline = monotonic () + float (config .agent_queue_timeout )
156175 while True :
157- try :
158- timeout = config .agent_queue_timeout # type: int
159- if not start : # make sure first time through queue is always checked
160- start = time ()
161- else :
162- timeout -= int (time () - start )
163- if timeout <= 0 : # this is to make sure we exit eventually instead of being fed continuously
164- return
165- segment = queue .get (block = block , timeout = timeout ) # type: Segment
166- except Empty :
176+ segment = _queue_get_within_batch (queue , block , batch_deadline ) # type: Segment
177+ if segment is None :
167178 return
168179
169180 queue .task_done ()
@@ -223,23 +234,15 @@ def generator():
223234 def report_log (self , queue : Queue , block : bool = True ):
224235 if not self .is_ready ():
225236 return
226- start = None
227237 sent = 0
228238
229239 def generator ():
230- nonlocal start , sent
240+ nonlocal sent
231241
242+ batch_deadline = monotonic () + float (config .agent_queue_timeout )
232243 while True :
233- try :
234- timeout = config .agent_queue_timeout # type: int
235- if not start : # make sure first time through queue is always checked
236- start = time ()
237- else :
238- timeout -= int (time () - start )
239- if timeout <= 0 : # this is to make sure we exit eventually instead of being fed continuously
240- return
241- log_data = queue .get (block = block , timeout = timeout ) # type: LogData
242- except Empty :
244+ log_data = _queue_get_within_batch (queue , block , batch_deadline ) # type: LogData
245+ if log_data is None :
243246 return
244247
245248 queue .task_done ()
@@ -261,23 +264,15 @@ def generator():
261264 def report_meter (self , queue : Queue , block : bool = True ):
262265 if not self .is_ready ():
263266 return
264- start = None
265267 sent = 0
266268
267269 def generator ():
268- nonlocal start , sent
270+ nonlocal sent
269271
272+ batch_deadline = monotonic () + float (config .agent_queue_timeout )
270273 while True :
271- try :
272- timeout = config .agent_queue_timeout # type: int
273- if not start : # make sure first time through queue is always checked
274- start = time ()
275- else :
276- timeout -= int (time () - start )
277- if timeout <= 0 : # this is to make sure we exit eventually instead of being fed continuously
278- return
279- meter_data = queue .get (block = block , timeout = timeout ) # type: MeterData
280- except Empty :
274+ meter_data = _queue_get_within_batch (queue , block , batch_deadline ) # type: MeterData
275+ if meter_data is None :
281276 return
282277
283278 queue .task_done ()
@@ -298,23 +293,15 @@ def generator():
298293 def report_snapshot (self , queue : Queue , block : bool = True ):
299294 if not self .is_ready ():
300295 return
301- start = None
302296 sent = 0
303297
304298 def generator ():
305- nonlocal start , sent
299+ nonlocal sent
306300
301+ batch_deadline = monotonic () + float (config .agent_queue_timeout )
307302 while True :
308- try :
309- timeout = config .agent_queue_timeout # type: int
310- if not start : # make sure first time through queue is always checked
311- start = time ()
312- else :
313- timeout -= int (time () - start )
314- if timeout <= 0 : # this is to make sure we exit eventually instead of being fed continuously
315- return
316- snapshot = queue .get (block = block , timeout = timeout ) # type: TracingThreadSnapshot
317- except Empty :
303+ snapshot = _queue_get_within_batch (queue , block , batch_deadline ) # type: TracingThreadSnapshot
304+ if snapshot is None :
318305 return
319306
320307 queue .task_done ()
0 commit comments