Skip to content

Commit 1939958

Browse files
committed
feat: Add adaptive poll rate interval logic
Signed-off-by: Cagri Yonca <cagri@ibm.com>
1 parent 6407963 commit 1939958

7 files changed

Lines changed: 516 additions & 88 deletions

File tree

src/instana/agent/host.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -315,7 +315,7 @@ def report_data_payload(
315315

316316
def report_metrics(self, payload: Dict[str, Any]) -> Optional[Response]:
317317
metrics = payload.get("metrics", [])
318-
if len(metrics) > 0:
318+
if len(metrics) > 0 and len(metrics.get("plugins", [])) > 0:
319319
metric_bundle = metrics["plugins"][0]["data"]
320320
response = self.client.post(
321321
self.__data_url(),

src/instana/collector/base.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,9 @@ def __init__(self, agent: Type["BaseAgent"]) -> None:
4949
# How often to report snapshot data (in seconds)
5050
self.snapshot_data_interval = 300
5151

52+
# Timestamp in seconds of the last time we sent metrics data
53+
self.metrics_data_last_sent = 0
54+
5255
# List of helpers that help out in data collection
5356
self.helpers = []
5457

@@ -58,6 +61,7 @@ def __init__(self, agent: Type["BaseAgent"]) -> None:
5861
self.background_report_lock = threading.RLock()
5962

6063
# Reporting interval for the background thread(s)
64+
# Default is 1 but can be changed by the agent options
6165
self.report_interval = 1
6266

6367
# Flag to indicate if start/shutdown state

src/instana/collector/host.py

Lines changed: 24 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -72,29 +72,46 @@ def should_send_snapshot_data(self) -> bool:
7272
delta = int(time()) - self.snapshot_data_last_sent
7373
return delta > self.snapshot_data_interval
7474

75+
def should_send_metrics(self) -> bool:
76+
"""
77+
Determines if metrics data should be sent based on poll_rate.
78+
"""
79+
poll_rate = 1
80+
if hasattr(self.agent, "options") and hasattr(self.agent.options, "poll_rate"):
81+
poll_rate = self.agent.options.poll_rate
82+
83+
delta = int(time()) - self.metrics_data_last_sent
84+
return delta >= poll_rate
85+
7586
def prepare_payload(self) -> DefaultDict[Any, Any]:
7687
payload = DictionaryOfStan()
7788
payload["spans"] = []
7889
payload["profiles"] = []
7990
payload["metrics"]["plugins"] = []
8091

8192
try:
93+
# Always collect and send spans immediately (every 1 second)
8294
if not self.span_queue.empty():
8395
payload["spans"] = format_span(self.queued_spans())
8496

8597
if not self.profile_queue.empty():
8698
payload["profiles"] = self.queued_profiles()
8799

88-
with_snapshot = self.should_send_snapshot_data()
100+
# Only collect metrics based on poll_rate interval
101+
if self.should_send_metrics():
102+
with_snapshot = self.should_send_snapshot_data()
103+
104+
plugins = []
105+
for helper in self.helpers:
106+
plugins.extend(helper.collect_metrics(with_snapshot=with_snapshot))
89107

90-
plugins = []
91-
for helper in self.helpers:
92-
plugins.extend(helper.collect_metrics(with_snapshot=with_snapshot))
108+
payload["metrics"]["plugins"] = plugins
93109

94-
payload["metrics"]["plugins"] = plugins
110+
if with_snapshot is True:
111+
self.snapshot_data_last_sent = int(time())
95112

96-
if with_snapshot is True:
97-
self.snapshot_data_last_sent = int(time())
113+
# Update metrics last sent timestamp
114+
self.metrics_data_last_sent = int(time())
98115
except Exception:
99116
logger.debug("non-fatal prepare_payload:", exc_info=True)
100117

src/instana/options.py

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -351,12 +351,15 @@ class StandardOptions(BaseOptions):
351351

352352
AGENT_DEFAULT_HOST = "localhost"
353353
AGENT_DEFAULT_PORT = 42699
354+
DEFAULT_POLL_RATE = 1
355+
MAX_POLL_RATE = 5
354356

355357
def __init__(self, **kwds: Dict[str, Any]) -> None:
356358
super(StandardOptions, self).__init__()
357359

358360
self.agent_host = os.environ.get("INSTANA_AGENT_HOST", self.AGENT_DEFAULT_HOST)
359361
self.agent_port = os.environ.get("INSTANA_AGENT_PORT", self.AGENT_DEFAULT_PORT)
362+
self.poll_rate = self.DEFAULT_POLL_RATE
360363

361364
if not isinstance(self.agent_port, int):
362365
self.agent_port = int(self.agent_port)
@@ -506,6 +509,34 @@ def set_disable_tracing(self, tracing_config: Sequence[Dict[str, Any]]) -> None:
506509
self.disabled_spans.extend(disabled_spans)
507510
self.enabled_spans.extend(enabled_spans)
508511

512+
def set_poll_rate(self, plugin_config: Dict[str, Any]) -> None:
513+
"""Set poll rate from agent plugin configuration."""
514+
poll_rate_value = plugin_config.get("poll_rate")
515+
if poll_rate_value is None:
516+
return
517+
518+
try:
519+
poll_rate = int(poll_rate_value)
520+
except (ValueError, TypeError):
521+
logger.debug(
522+
f"Invalid poll_rate type, defaulting to {self.DEFAULT_POLL_RATE}"
523+
)
524+
self.poll_rate = self.DEFAULT_POLL_RATE
525+
return
526+
527+
if poll_rate in (self.DEFAULT_POLL_RATE, self.MAX_POLL_RATE):
528+
self.poll_rate = poll_rate
529+
logger.debug(
530+
f"Poll rate set to {self.poll_rate} seconds from agent configuration"
531+
)
532+
return
533+
534+
logger.debug(
535+
f"Invalid poll_rate value {poll_rate}, defaulting to "
536+
f"{self.DEFAULT_POLL_RATE}"
537+
)
538+
self.poll_rate = self.DEFAULT_POLL_RATE
539+
509540
def set_from(self, res_data: Dict[str, Any]) -> None:
510541
"""
511542
Set the source identifiers given to use by the Instana Host agent.
@@ -516,13 +547,19 @@ def set_from(self, res_data: Dict[str, Any]) -> None:
516547
logger.debug(f"options.set_from: Wrong data type - {type(res_data)}")
517548
return
518549

550+
# Extract poll_rate from plugin.python.poll_rate
551+
if "plugin" in res_data and isinstance(res_data["plugin"], dict):
552+
python_plugin = res_data["plugin"].get("python")
553+
if isinstance(python_plugin, dict):
554+
self.set_poll_rate(python_plugin)
555+
519556
if "secrets" in res_data:
520557
self.set_secrets(res_data["secrets"])
521558

522559
if "tracing" in res_data:
523560
self.set_tracing(res_data["tracing"])
524-
525561
else:
562+
# Rely on extra headers if no tracing configuration comes from the agent
526563
if "extraHeaders" in res_data:
527564
self.set_extra_headers(res_data["extraHeaders"])
528565

0 commit comments

Comments
 (0)