def test__clear_buffered_writes_already_called__original_flush_timer_elapsed__writes_not_sent( self, ): writer = self.MockBufferedTagWriter(SystemTimeStamper(), 2, MockManualResetTimer()) item1 = object() item2 = object() writer.mock_create_item.configure_mock(side_effect=[item1, item2]) writer.mock_buffer_value.configure_mock(side_effect=None) writer.mock_clear_buffer.configure_mock(side_effect=None) writer.write("tag1", tbase.DataType.DOUBLE, 1.1, timestamp=self.timestamp) elapsed_handlers = list(writer.timer.elapsed) writer.clear_buffered_writes() writer.write("tag2", tbase.DataType.BOOLEAN, False, timestamp=self.timestamp) # Simulate queuing the timer event in parallel to ClearBufferedWrites # by raising the event using the handlers that were there earlier. # (The second write should have replaced them with *new* event handlers. It # should also stop the original timer, but if the timer is already elapsing, it # should be ignored.) # The strict mock will assert the writes were never sent. assert elapsed_handlers is not None for h in elapsed_handlers: h()
def __init__(self, stamper=None, buffer_size=None, flush_timer=None): assert buffer_size is not None super().__init__( stamper or SystemTimeStamper(), buffer_size, flush_timer or ManualResetTimer.null_timer, ) self._timer = flush_timer self._time_stamper = stamper self.mock_buffer_value = Mock(side_effect=NotImplementedError) self.mock_clear_buffer = Mock(side_effect=NotImplementedError) self.mock_copy_buffer = Mock(side_effect=NotImplementedError) self.mock_create_item = Mock(side_effect=NotImplementedError) self.mock_send_writes = Mock(side_effect=NotImplementedError) self.mock_send_writes_async = Mock(side_effect=NotImplementedError)
def create_writer( self, *, buffer_size: Optional[int] = None, max_buffer_time: Optional[datetime.timedelta] = None ) -> tbase.BufferedTagWriter: """Create a tag writer that buffers tag values until :meth:`~BufferedTagWriter.send_buffered_writes()` is called on the returned object, ``buffer_size`` writes have been buffered, or ``max_buffer_time`` time has past since buffering a value, at which point the writes will be sent automatically. Args: buffer_size: The maximum number of tag writes to buffer before automatically sending them to the server. max_buffer_time: The amount of time before writes are sent. Returns: The created writer. Close the writer to free resources. Raises: ValueError: if ``buffer_size`` and ``max_buffer_time`` are both None. ValueError: if ``buffer_size`` is less than one. """ if buffer_size is None and max_buffer_time is None: raise ValueError("must provide either buffer_size or max_buffer_time") if buffer_size is not None: if buffer_size < 1: raise ValueError("buffer_size cannot be 0 or negative") else: buffer_size = 0 if max_buffer_time is not None: if max_buffer_time.total_seconds() < 0.001: raise ValueError("max_buffer_time must be at least 1 millisecond") timer = ManualResetTimer(max_buffer_time) else: timer = ManualResetTimer.null_timer return HttpBufferedTagWriter( self._http_client, SystemTimeStamper(), buffer_size, timer )
def test__during_heavy_use__prevents_collisions(self): uut = SystemTimeStamper() times = [] num_times = 500 def worker(): while len(times) < num_times: times.append(uut.timestamp) num_threads = max(os.cpu_count(), 4) threads = [threading.Thread(target=worker) for i in range(num_threads)] begin = _gmt_now() [t.start() for t in threads] [t.join() for t in threads] times.sort() num_after_1us = 0 for i, t in enumerate(times): _assert_time_within_tolerance(begin, t) if i > 0: difference = (t - times[i - 1]).total_seconds() * 1000000 assert difference >= 1, ( "Expected unique timestamps (not within 1 microsecond), but time " + '{} ("{}") is {}us from the previous one'.format( i, t, difference)) if difference == 1: num_after_1us += 1 assert num_after_1us > len(times) * 0.9, ( "Expected at least 90% of the {} timestamps to be just ".format( len(times)) + "1 microsecond after the previous timestamp, but only " + "{} of the timestamps satisfy that requirement -- ".format( num_after_1us) + "the test didn't run fast enough to ensure correct behavior")
def setup_method(self, method): super().setup_method(method) self._uut = HttpBufferedTagWriter(self._client, SystemTimeStamper(), 10, ManualResetTimer.null_timer)
def test__constructed__returns_current_time(self): uut = SystemTimeStamper() _assert_time_within_tolerance(_gmt_now(), uut.timestamp)
def test__after_delay__returns_current_time(self): uut = SystemTimeStamper() _assert_time_within_tolerance(_gmt_now(), uut.timestamp) time.sleep(0.05) _assert_time_within_tolerance(_gmt_now(), uut.timestamp)