From 47caa5b4454ffb3cf47e752189c9030dc280098d Mon Sep 17 00:00:00 2001 From: UltraDev Date: Thu, 15 Sep 2022 13:46:03 +0200 Subject: [PATCH 1/3] Add thread to remove expired instruments --- sourceplusplus/control/LiveInstrumentRemote.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/sourceplusplus/control/LiveInstrumentRemote.py b/sourceplusplus/control/LiveInstrumentRemote.py index 3aac03c..a3bf750 100644 --- a/sourceplusplus/control/LiveInstrumentRemote.py +++ b/sourceplusplus/control/LiveInstrumentRemote.py @@ -1,5 +1,6 @@ import sys import threading +import time from nopdb import nopdb from vertx import EventBus @@ -17,12 +18,15 @@ class LiveInstrumentRemote(object): instruments = {} eb = None dbg = None + cleanupThread = None def __init__(self, eb: EventBus): LiveInstrumentRemote.eb = eb LiveInstrumentRemote.dbg = nopdb.get_nopdb() LiveInstrumentRemote.dbg.start() threading.settrace(sys.gettrace()) + LiveInstrumentRemote.cleanupThread = threading.Thread(target=self.cleanup) + LiveInstrumentRemote.cleanupThread.start() def add_live_instrument(self, command: LiveInstrumentCommand): for inst_dict in command.instruments: @@ -79,3 +83,12 @@ def handle_instrument_command(self, command: LiveInstrumentCommand): self.add_live_instrument(command) elif command.command_type == CommandType.REMOVE_LIVE_INSTRUMENT: self.remove_live_instrument(command) + + def cleanup(self): + while True: + delete = [] + for key, val in LiveInstrumentRemote.instruments.items(): + if "expires_at" in val[1] and val[1]["expires_at"] < round(time.time() * 1000): + delete.append(key) + for key in delete: + del LiveInstrumentRemote.instruments[key] From bdb5ed9c97868daf73e1a967c159994d8548eab7 Mon Sep 17 00:00:00 2001 From: UltraDev Date: Thu, 15 Sep 2022 13:46:57 +0200 Subject: [PATCH 2/3] Add delay --- sourceplusplus/control/LiveInstrumentRemote.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/sourceplusplus/control/LiveInstrumentRemote.py b/sourceplusplus/control/LiveInstrumentRemote.py index a3bf750..5d9e2cd 100644 --- a/sourceplusplus/control/LiveInstrumentRemote.py +++ b/sourceplusplus/control/LiveInstrumentRemote.py @@ -86,9 +86,14 @@ def handle_instrument_command(self, command: LiveInstrumentCommand): def cleanup(self): while True: + time.sleep(1) delete = [] for key, val in LiveInstrumentRemote.instruments.items(): if "expires_at" in val[1] and val[1]["expires_at"] < round(time.time() * 1000): delete.append(key) for key in delete: - del LiveInstrumentRemote.instruments[key] + instrument = LiveInstrumentRemote.instruments.pop(key) + LiveInstrumentRemote.eb.send(address="spp.processor.status.live-instrument-removed", body={ + "instrument": instrument.to_json(), + "occurredAt": round(time.time() * 1000) + }) From d561c1793bc423334f654510aabd37338eb784b6 Mon Sep 17 00:00:00 2001 From: Brandon Fergerson Date: Thu, 15 Sep 2022 17:24:03 +0400 Subject: [PATCH 3/3] test: instrument expiration --- .../control/LiveInstrumentRemote.py | 8 ++- tests/instrument_expiration/__init__.py | 0 tests/instrument_expiration/tests.py | 55 +++++++++++++++++++ 3 files changed, 60 insertions(+), 3 deletions(-) create mode 100644 tests/instrument_expiration/__init__.py create mode 100644 tests/instrument_expiration/tests.py diff --git a/sourceplusplus/control/LiveInstrumentRemote.py b/sourceplusplus/control/LiveInstrumentRemote.py index 5d9e2cd..362fccb 100644 --- a/sourceplusplus/control/LiveInstrumentRemote.py +++ b/sourceplusplus/control/LiveInstrumentRemote.py @@ -10,6 +10,7 @@ from sourceplusplus.models.instrument.LiveBreakpoint import LiveBreakpoint from sourceplusplus.models.instrument.LiveLog import LiveLog from sourceplusplus.models.instrument.LiveMeter import LiveMeter +from sourceplusplus.models.instrument.common import LiveInstrument from sourceplusplus.models.instrument.common.LiveInstrumentType import LiveInstrumentType from sourceplusplus.models.instrument.common.LiveSourceLocation import LiveSourceLocation @@ -25,7 +26,7 @@ def __init__(self, eb: EventBus): LiveInstrumentRemote.dbg = nopdb.get_nopdb() LiveInstrumentRemote.dbg.start() threading.settrace(sys.gettrace()) - LiveInstrumentRemote.cleanupThread = threading.Thread(target=self.cleanup) + LiveInstrumentRemote.cleanupThread = threading.Thread(target=self.cleanup, daemon=True) LiveInstrumentRemote.cleanupThread.start() def add_live_instrument(self, command: LiveInstrumentCommand): @@ -89,10 +90,11 @@ def cleanup(self): time.sleep(1) delete = [] for key, val in LiveInstrumentRemote.instruments.items(): - if "expires_at" in val[1] and val[1]["expires_at"] < round(time.time() * 1000): + instrument: LiveInstrument = val[1] + if instrument.expires_at is not None and instrument.expires_at <= round(time.time() * 1000): delete.append(key) for key in delete: - instrument = LiveInstrumentRemote.instruments.pop(key) + instrument: LiveInstrument = LiveInstrumentRemote.instruments.pop(key)[1] LiveInstrumentRemote.eb.send(address="spp.processor.status.live-instrument-removed", body={ "instrument": instrument.to_json(), "occurredAt": round(time.time() * 1000) diff --git a/tests/instrument_expiration/__init__.py b/tests/instrument_expiration/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/instrument_expiration/tests.py b/tests/instrument_expiration/tests.py new file mode 100644 index 0000000..df793ec --- /dev/null +++ b/tests/instrument_expiration/tests.py @@ -0,0 +1,55 @@ +import json +import time +import unittest +from unittest.mock import MagicMock + +from sourceplusplus.control.LiveInstrumentRemote import LiveInstrumentRemote +from sourceplusplus.models.command.LiveInstrumentCommand import LiveInstrumentCommand + + +class TestSum(unittest.TestCase): + + def test_breakpoint_expires(self): + eb_mock = MagicMock() + instrument_remote = LiveInstrumentRemote(eb_mock) + + # Add breakpoint + raw_command = { + "commandType": "ADD_LIVE_INSTRUMENT", + "instruments": [ + { + "location": { + "source": "E2ETest.py", + "line": 18, "service": None, + "serviceInstance": None, + "commitId": None, + "fileChecksum": None + }, + "condition": None, + "expiresAt": round(time.time() * 1000) + 2000, + "hitLimit": 1, + "id": "3145bbee-8d81-4184-8c3d-f97f208a6e15", + "applyImmediately": False, + "applied": False, + "pending": True, + "throttle": { + "limit": 1, + "step": "SECOND" + }, + "meta": {}, + "type": "BREAKPOINT" + } + ], + "locations": [] + } + command = LiveInstrumentCommand.from_json(json.dumps(raw_command)) + instrument_remote.add_live_instrument(command) + + # Ensure breakpoint was added + self.assertEqual(len(instrument_remote.instruments), 1) + + # Wait for breakpoint to expire + time.sleep(5) + + # Ensure breakpoint was removed + self.assertEqual(len(instrument_remote.instruments), 0)