sentry-arroyo 2.23.0__tar.gz → 2.25.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {sentry_arroyo-2.23.0/sentry_arroyo.egg-info → sentry_arroyo-2.25.0}/PKG-INFO +1 -1
- {sentry_arroyo-2.23.0 → sentry_arroyo-2.25.0}/arroyo/backends/kafka/__init__.py +6 -1
- {sentry_arroyo-2.23.0 → sentry_arroyo-2.25.0}/arroyo/backends/kafka/configuration.py +71 -0
- {sentry_arroyo-2.23.0 → sentry_arroyo-2.25.0}/arroyo/processing/processor.py +7 -6
- {sentry_arroyo-2.23.0 → sentry_arroyo-2.25.0}/arroyo/utils/metricDefs.json +1 -1
- {sentry_arroyo-2.23.0 → sentry_arroyo-2.25.0}/arroyo/utils/metric_defs.py +18 -0
- {sentry_arroyo-2.23.0 → sentry_arroyo-2.25.0}/arroyo/utils/metrics.py +45 -1
- {sentry_arroyo-2.23.0 → sentry_arroyo-2.25.0/sentry_arroyo.egg-info}/PKG-INFO +1 -1
- {sentry_arroyo-2.23.0 → sentry_arroyo-2.25.0}/sentry_arroyo.egg-info/SOURCES.txt +1 -0
- {sentry_arroyo-2.23.0 → sentry_arroyo-2.25.0}/setup.py +1 -1
- sentry_arroyo-2.25.0/tests/backends/test_kafka_producer.py +125 -0
- sentry_arroyo-2.25.0/tests/utils/test_metrics.py +60 -0
- sentry_arroyo-2.23.0/tests/utils/test_metrics.py +0 -33
- {sentry_arroyo-2.23.0 → sentry_arroyo-2.25.0}/LICENSE +0 -0
- {sentry_arroyo-2.23.0 → sentry_arroyo-2.25.0}/MANIFEST.in +0 -0
- {sentry_arroyo-2.23.0 → sentry_arroyo-2.25.0}/README.md +0 -0
- {sentry_arroyo-2.23.0 → sentry_arroyo-2.25.0}/arroyo/__init__.py +0 -0
- {sentry_arroyo-2.23.0 → sentry_arroyo-2.25.0}/arroyo/backends/__init__.py +0 -0
- {sentry_arroyo-2.23.0 → sentry_arroyo-2.25.0}/arroyo/backends/abstract.py +0 -0
- {sentry_arroyo-2.23.0 → sentry_arroyo-2.25.0}/arroyo/backends/kafka/commit.py +0 -0
- {sentry_arroyo-2.23.0 → sentry_arroyo-2.25.0}/arroyo/backends/kafka/consumer.py +0 -0
- {sentry_arroyo-2.23.0 → sentry_arroyo-2.25.0}/arroyo/backends/local/__init__.py +0 -0
- {sentry_arroyo-2.23.0 → sentry_arroyo-2.25.0}/arroyo/backends/local/backend.py +0 -0
- {sentry_arroyo-2.23.0 → sentry_arroyo-2.25.0}/arroyo/backends/local/storages/__init__.py +0 -0
- {sentry_arroyo-2.23.0 → sentry_arroyo-2.25.0}/arroyo/backends/local/storages/abstract.py +0 -0
- {sentry_arroyo-2.23.0 → sentry_arroyo-2.25.0}/arroyo/backends/local/storages/memory.py +0 -0
- {sentry_arroyo-2.23.0 → sentry_arroyo-2.25.0}/arroyo/commit.py +0 -0
- {sentry_arroyo-2.23.0 → sentry_arroyo-2.25.0}/arroyo/dlq.py +0 -0
- {sentry_arroyo-2.23.0 → sentry_arroyo-2.25.0}/arroyo/errors.py +0 -0
- {sentry_arroyo-2.23.0 → sentry_arroyo-2.25.0}/arroyo/processing/__init__.py +0 -0
- {sentry_arroyo-2.23.0 → sentry_arroyo-2.25.0}/arroyo/processing/strategies/__init__.py +0 -0
- {sentry_arroyo-2.23.0 → sentry_arroyo-2.25.0}/arroyo/processing/strategies/abstract.py +0 -0
- {sentry_arroyo-2.23.0 → sentry_arroyo-2.25.0}/arroyo/processing/strategies/batching.py +0 -0
- {sentry_arroyo-2.23.0 → sentry_arroyo-2.25.0}/arroyo/processing/strategies/buffer.py +0 -0
- {sentry_arroyo-2.23.0 → sentry_arroyo-2.25.0}/arroyo/processing/strategies/commit.py +0 -0
- {sentry_arroyo-2.23.0 → sentry_arroyo-2.25.0}/arroyo/processing/strategies/filter.py +0 -0
- {sentry_arroyo-2.23.0 → sentry_arroyo-2.25.0}/arroyo/processing/strategies/guard.py +0 -0
- {sentry_arroyo-2.23.0 → sentry_arroyo-2.25.0}/arroyo/processing/strategies/healthcheck.py +0 -0
- {sentry_arroyo-2.23.0 → sentry_arroyo-2.25.0}/arroyo/processing/strategies/noop.py +0 -0
- {sentry_arroyo-2.23.0 → sentry_arroyo-2.25.0}/arroyo/processing/strategies/produce.py +0 -0
- {sentry_arroyo-2.23.0 → sentry_arroyo-2.25.0}/arroyo/processing/strategies/reduce.py +0 -0
- {sentry_arroyo-2.23.0 → sentry_arroyo-2.25.0}/arroyo/processing/strategies/run_task.py +0 -0
- {sentry_arroyo-2.23.0 → sentry_arroyo-2.25.0}/arroyo/processing/strategies/run_task_in_threads.py +0 -0
- {sentry_arroyo-2.23.0 → sentry_arroyo-2.25.0}/arroyo/processing/strategies/run_task_with_multiprocessing.py +0 -0
- {sentry_arroyo-2.23.0 → sentry_arroyo-2.25.0}/arroyo/processing/strategies/unfold.py +0 -0
- {sentry_arroyo-2.23.0 → sentry_arroyo-2.25.0}/arroyo/py.typed +0 -0
- {sentry_arroyo-2.23.0 → sentry_arroyo-2.25.0}/arroyo/types.py +0 -0
- {sentry_arroyo-2.23.0 → sentry_arroyo-2.25.0}/arroyo/utils/__init__.py +0 -0
- {sentry_arroyo-2.23.0 → sentry_arroyo-2.25.0}/arroyo/utils/clock.py +0 -0
- {sentry_arroyo-2.23.0 → sentry_arroyo-2.25.0}/arroyo/utils/codecs.py +0 -0
- {sentry_arroyo-2.23.0 → sentry_arroyo-2.25.0}/arroyo/utils/concurrent.py +0 -0
- {sentry_arroyo-2.23.0 → sentry_arroyo-2.25.0}/arroyo/utils/logging.py +0 -0
- {sentry_arroyo-2.23.0 → sentry_arroyo-2.25.0}/arroyo/utils/profiler.py +0 -0
- {sentry_arroyo-2.23.0 → sentry_arroyo-2.25.0}/arroyo/utils/retries.py +0 -0
- {sentry_arroyo-2.23.0 → sentry_arroyo-2.25.0}/examples/transform_and_produce/__init__.py +0 -0
- {sentry_arroyo-2.23.0 → sentry_arroyo-2.25.0}/examples/transform_and_produce/batched.py +0 -0
- {sentry_arroyo-2.23.0 → sentry_arroyo-2.25.0}/examples/transform_and_produce/script.py +0 -0
- {sentry_arroyo-2.23.0 → sentry_arroyo-2.25.0}/examples/transform_and_produce/simple.py +0 -0
- {sentry_arroyo-2.23.0 → sentry_arroyo-2.25.0}/requirements.txt +0 -0
- {sentry_arroyo-2.23.0 → sentry_arroyo-2.25.0}/sentry_arroyo.egg-info/dependency_links.txt +0 -0
- {sentry_arroyo-2.23.0 → sentry_arroyo-2.25.0}/sentry_arroyo.egg-info/not-zip-safe +0 -0
- {sentry_arroyo-2.23.0 → sentry_arroyo-2.25.0}/sentry_arroyo.egg-info/requires.txt +0 -0
- {sentry_arroyo-2.23.0 → sentry_arroyo-2.25.0}/sentry_arroyo.egg-info/top_level.txt +0 -0
- {sentry_arroyo-2.23.0 → sentry_arroyo-2.25.0}/setup.cfg +0 -0
- {sentry_arroyo-2.23.0 → sentry_arroyo-2.25.0}/tests/backends/__init__.py +0 -0
- {sentry_arroyo-2.23.0 → sentry_arroyo-2.25.0}/tests/backends/mixins.py +0 -0
- {sentry_arroyo-2.23.0 → sentry_arroyo-2.25.0}/tests/backends/test_commit.py +0 -0
- {sentry_arroyo-2.23.0 → sentry_arroyo-2.25.0}/tests/backends/test_kafka.py +0 -0
- {sentry_arroyo-2.23.0 → sentry_arroyo-2.25.0}/tests/backends/test_local.py +0 -0
- {sentry_arroyo-2.23.0 → sentry_arroyo-2.25.0}/tests/processing/__init__.py +0 -0
- {sentry_arroyo-2.23.0 → sentry_arroyo-2.25.0}/tests/processing/strategies/__init__.py +0 -0
- {sentry_arroyo-2.23.0 → sentry_arroyo-2.25.0}/tests/processing/strategies/test_all.py +0 -0
- {sentry_arroyo-2.23.0 → sentry_arroyo-2.25.0}/tests/processing/strategies/test_batching.py +0 -0
- {sentry_arroyo-2.23.0 → sentry_arroyo-2.25.0}/tests/processing/strategies/test_buffer.py +0 -0
- {sentry_arroyo-2.23.0 → sentry_arroyo-2.25.0}/tests/processing/strategies/test_commit.py +0 -0
- {sentry_arroyo-2.23.0 → sentry_arroyo-2.25.0}/tests/processing/strategies/test_filter.py +0 -0
- {sentry_arroyo-2.23.0 → sentry_arroyo-2.25.0}/tests/processing/strategies/test_guard.py +0 -0
- {sentry_arroyo-2.23.0 → sentry_arroyo-2.25.0}/tests/processing/strategies/test_noop.py +0 -0
- {sentry_arroyo-2.23.0 → sentry_arroyo-2.25.0}/tests/processing/strategies/test_produce.py +0 -0
- {sentry_arroyo-2.23.0 → sentry_arroyo-2.25.0}/tests/processing/strategies/test_reduce.py +0 -0
- {sentry_arroyo-2.23.0 → sentry_arroyo-2.25.0}/tests/processing/strategies/test_run_task.py +0 -0
- {sentry_arroyo-2.23.0 → sentry_arroyo-2.25.0}/tests/processing/strategies/test_run_task_in_threads.py +0 -0
- {sentry_arroyo-2.23.0 → sentry_arroyo-2.25.0}/tests/processing/strategies/test_run_task_with_multiprocessing.py +0 -0
- {sentry_arroyo-2.23.0 → sentry_arroyo-2.25.0}/tests/processing/strategies/test_unfold.py +0 -0
- {sentry_arroyo-2.23.0 → sentry_arroyo-2.25.0}/tests/processing/test_processor.py +0 -0
- {sentry_arroyo-2.23.0 → sentry_arroyo-2.25.0}/tests/test_commit.py +0 -0
- {sentry_arroyo-2.23.0 → sentry_arroyo-2.25.0}/tests/test_dlq.py +0 -0
- {sentry_arroyo-2.23.0 → sentry_arroyo-2.25.0}/tests/test_kip848_e2e.py +0 -0
- {sentry_arroyo-2.23.0 → sentry_arroyo-2.25.0}/tests/test_types.py +0 -0
- {sentry_arroyo-2.23.0 → sentry_arroyo-2.25.0}/tests/utils/__init__.py +0 -0
- {sentry_arroyo-2.23.0 → sentry_arroyo-2.25.0}/tests/utils/test_concurrent.py +0 -0
- {sentry_arroyo-2.23.0 → sentry_arroyo-2.25.0}/tests/utils/test_retries.py +0 -0
|
@@ -1,9 +1,14 @@
|
|
|
1
|
-
from .configuration import
|
|
1
|
+
from .configuration import (
|
|
2
|
+
build_kafka_configuration,
|
|
3
|
+
build_kafka_consumer_configuration,
|
|
4
|
+
build_kafka_producer_configuration,
|
|
5
|
+
)
|
|
2
6
|
from .consumer import KafkaConsumer, KafkaPayload, KafkaProducer
|
|
3
7
|
|
|
4
8
|
__all__ = [
|
|
5
9
|
"build_kafka_configuration",
|
|
6
10
|
"build_kafka_consumer_configuration",
|
|
11
|
+
"build_kafka_producer_configuration",
|
|
7
12
|
"KafkaConsumer",
|
|
8
13
|
"KafkaPayload",
|
|
9
14
|
"KafkaProducer",
|
|
@@ -49,6 +49,77 @@ def stats_callback(stats_json: str) -> None:
|
|
|
49
49
|
)
|
|
50
50
|
|
|
51
51
|
|
|
52
|
+
def producer_stats_callback(stats_json: str) -> None:
|
|
53
|
+
stats = json.loads(stats_json)
|
|
54
|
+
metrics = get_metrics()
|
|
55
|
+
|
|
56
|
+
# Extract broker-level int_latency metrics
|
|
57
|
+
brokers = stats.get("brokers", {})
|
|
58
|
+
for broker_id, broker_stats in brokers.items():
|
|
59
|
+
int_latency = broker_stats.get("int_latency", {})
|
|
60
|
+
if int_latency:
|
|
61
|
+
p99_latency_ms = int_latency.get("p99", 0) / 1000.0
|
|
62
|
+
metrics.timing(
|
|
63
|
+
"arroyo.producer.librdkafka.p99_int_latency",
|
|
64
|
+
p99_latency_ms,
|
|
65
|
+
tags={"broker_id": str(broker_id)},
|
|
66
|
+
)
|
|
67
|
+
avg_latency_ms = int_latency.get("avg", 0) / 1000.0
|
|
68
|
+
metrics.timing(
|
|
69
|
+
"arroyo.producer.librdkafka.avg_int_latency",
|
|
70
|
+
avg_latency_ms,
|
|
71
|
+
tags={"broker_id": str(broker_id)},
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
outbuf_latency = broker_stats.get("outbuf_latency", {})
|
|
75
|
+
if outbuf_latency:
|
|
76
|
+
p99_latency_ms = outbuf_latency.get("p99", 0) / 1000.0
|
|
77
|
+
metrics.timing(
|
|
78
|
+
"arroyo.producer.librdkafka.p99_outbuf_latency",
|
|
79
|
+
p99_latency_ms,
|
|
80
|
+
tags={"broker_id": str(broker_id)},
|
|
81
|
+
)
|
|
82
|
+
avg_latency_ms = outbuf_latency.get("avg", 0) / 1000.0
|
|
83
|
+
metrics.timing(
|
|
84
|
+
"arroyo.producer.librdkafka.avg_outbuf_latency",
|
|
85
|
+
avg_latency_ms,
|
|
86
|
+
tags={"broker_id": str(broker_id)},
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
rtt = broker_stats.get("rtt", {})
|
|
90
|
+
if rtt:
|
|
91
|
+
p99_rtt_ms = rtt.get("p99", 0) / 1000.0
|
|
92
|
+
metrics.timing(
|
|
93
|
+
"arroyo.producer.librdkafka.p99_rtt",
|
|
94
|
+
p99_rtt_ms,
|
|
95
|
+
tags={"broker_id": str(broker_id)},
|
|
96
|
+
)
|
|
97
|
+
avg_rtt_ms = rtt.get("avg", 0) / 1000.0
|
|
98
|
+
metrics.timing(
|
|
99
|
+
"arroyo.producer.librdkafka.avg_rtt",
|
|
100
|
+
avg_rtt_ms,
|
|
101
|
+
tags={"broker_id": str(broker_id)},
|
|
102
|
+
)
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
def build_kafka_producer_configuration(
|
|
106
|
+
default_config: Mapping[str, Any],
|
|
107
|
+
bootstrap_servers: Optional[Sequence[str]] = None,
|
|
108
|
+
override_params: Optional[Mapping[str, Any]] = None,
|
|
109
|
+
) -> KafkaBrokerConfig:
|
|
110
|
+
broker_config = build_kafka_configuration(
|
|
111
|
+
default_config, bootstrap_servers, override_params
|
|
112
|
+
)
|
|
113
|
+
|
|
114
|
+
broker_config.update(
|
|
115
|
+
{
|
|
116
|
+
"statistics.interval.ms": STATS_COLLECTION_FREQ_MS,
|
|
117
|
+
"stats_cb": producer_stats_callback,
|
|
118
|
+
}
|
|
119
|
+
)
|
|
120
|
+
return broker_config
|
|
121
|
+
|
|
122
|
+
|
|
52
123
|
def build_kafka_consumer_configuration(
|
|
53
124
|
default_config: Mapping[str, Any],
|
|
54
125
|
group_id: str,
|
|
@@ -29,7 +29,7 @@ from arroyo.processing.strategies.abstract import (
|
|
|
29
29
|
)
|
|
30
30
|
from arroyo.types import BrokerValue, Message, Partition, Topic, TStrategyPayload
|
|
31
31
|
from arroyo.utils.logging import handle_internal_error
|
|
32
|
-
from arroyo.utils.metrics import
|
|
32
|
+
from arroyo.utils.metrics import get_consumer_metrics
|
|
33
33
|
|
|
34
34
|
logger = logging.getLogger(__name__)
|
|
35
35
|
|
|
@@ -89,8 +89,9 @@ ConsumerCounter = Literal[
|
|
|
89
89
|
|
|
90
90
|
|
|
91
91
|
class MetricsBuffer:
|
|
92
|
-
def __init__(self) -> None:
|
|
93
|
-
self.metrics =
|
|
92
|
+
def __init__(self, consumer: Consumer[Any]) -> None:
|
|
93
|
+
self.metrics = get_consumer_metrics(consumer.member_id)
|
|
94
|
+
self.__consumer = consumer
|
|
94
95
|
self.__timers: MutableMapping[ConsumerTiming, float] = defaultdict(float)
|
|
95
96
|
self.__counters: MutableMapping[ConsumerCounter, int] = defaultdict(int)
|
|
96
97
|
self.__reset()
|
|
@@ -142,7 +143,7 @@ class StreamProcessor(Generic[TStrategyPayload]):
|
|
|
142
143
|
) -> None:
|
|
143
144
|
self.__consumer = consumer
|
|
144
145
|
self.__processor_factory = processor_factory
|
|
145
|
-
self.__metrics_buffer = MetricsBuffer()
|
|
146
|
+
self.__metrics_buffer = MetricsBuffer(consumer)
|
|
146
147
|
|
|
147
148
|
self.__processing_strategy: Optional[
|
|
148
149
|
ProcessingStrategy[TStrategyPayload]
|
|
@@ -236,7 +237,7 @@ class StreamProcessor(Generic[TStrategyPayload]):
|
|
|
236
237
|
logger.info("New partitions assigned: %r", partitions)
|
|
237
238
|
logger.info("Member id: %r", self.__consumer.member_id)
|
|
238
239
|
self.__metrics_buffer.metrics.increment(
|
|
239
|
-
"arroyo.consumer.partitions_assigned.count", len(partitions)
|
|
240
|
+
"arroyo.consumer.partitions_assigned.count", len(partitions)
|
|
240
241
|
)
|
|
241
242
|
|
|
242
243
|
current_partitions = dict(self.__consumer.tell())
|
|
@@ -262,7 +263,7 @@ class StreamProcessor(Generic[TStrategyPayload]):
|
|
|
262
263
|
logger.info("Partitions to revoke: %r", partitions)
|
|
263
264
|
|
|
264
265
|
self.__metrics_buffer.metrics.increment(
|
|
265
|
-
"arroyo.consumer.partitions_revoked.count", len(partitions)
|
|
266
|
+
"arroyo.consumer.partitions_revoked.count", len(partitions)
|
|
266
267
|
)
|
|
267
268
|
|
|
268
269
|
if partitions:
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"arroyo.strategies.run_task_with_multiprocessing.batch.size.msg": {"name": "arroyo.strategies.run_task_with_multiprocessing.batch.size.msg", "type": "Time", "description": "Number of messages in a multiprocessing batch"}, "arroyo.strategies.run_task_with_multiprocessing.batch.size.bytes": {"name": "arroyo.strategies.run_task_with_multiprocessing.batch.size.bytes", "type": "Time", "description": "Number of bytes in a multiprocessing batch"}, "arroyo.strategies.run_task_with_multiprocessing.output_batch.size.msg": {"name": "arroyo.strategies.run_task_with_multiprocessing.output_batch.size.msg", "type": "Time", "description": "Number of messages in a multiprocessing batch after the message transformation"}, "arroyo.strategies.run_task_with_multiprocessing.output_batch.size.bytes": {"name": "arroyo.strategies.run_task_with_multiprocessing.output_batch.size.bytes", "type": "Time", "description": "Number of bytes in a multiprocessing batch after the message transformation"}, "arroyo.consumer.run.count": {"name": "arroyo.consumer.run.count", "type": "Counter", "description": "Number of times the consumer is spinning"}, "arroyo.consumer.invalid_message.count": {"name": "arroyo.consumer.invalid_message.count", "type": "Counter", "description": "Number of times the consumer encountered an invalid message."}, "arroyo.strategies.reduce.batch_time": {"name": "arroyo.strategies.reduce.batch_time", "type": "Time", "description": "How long it took the Reduce step to fill up a batch"}, "arroyo.strategies.run_task_with_multiprocessing.batch.backpressure": {"name": "arroyo.strategies.run_task_with_multiprocessing.batch.backpressure", "type": "Counter", "description": "Incremented when a strategy after multiprocessing applies\nbackpressure to multiprocessing. May be a reason why CPU cannot be\nsaturated."}, "arroyo.strategies.run_task_with_multiprocessing.batch.input.overflow": {"name": "arroyo.strategies.run_task_with_multiprocessing.batch.input.overflow", "type": "Counter", "description": "Incremented when multiprocessing cannot fill the input batch\nbecause not enough memory was allocated. This results in batches smaller\nthan configured. Increase `input_block_size` to fix."}, "arroyo.strategies.run_task_with_multiprocessing.batch.output.overflow": {"name": "arroyo.strategies.run_task_with_multiprocessing.batch.output.overflow", "type": "Counter", "description": "Incremented when multiprocessing cannot pull results in batches\nequal to the input batch size, because not enough memory was allocated.\nThis can be devastating for throughput. Increase `output_block_size` to\nfix."}, "arroyo.strategies.run_task_with_multiprocessing.batch.input.resize": {"name": "arroyo.strategies.run_task_with_multiprocessing.batch.input.resize", "type": "Counter", "description": "Arroyo has decided to re-allocate a block in order to combat input\nbuffer overflow. This behavior can be disabled by explicitly setting\n`input_block_size` to a not-None value in `RunTaskWithMultiprocessing`."}, "arroyo.strategies.run_task_with_multiprocessing.batch.output.resize": {"name": "arroyo.strategies.run_task_with_multiprocessing.batch.output.resize", "type": "Counter", "description": "Arroyo has decided to re-allocate a block in order to combat output\nbuffer overflow. This behavior can be disabled by explicitly setting\n`output_block_size` to a not-None value in `RunTaskWithMultiprocessing`."}, "arroyo.strategies.run_task_with_multiprocessing.batches_in_progress": {"name": "arroyo.strategies.run_task_with_multiprocessing.batches_in_progress", "type": "Gauge", "description": "How many batches are being processed in parallel by multiprocessing."}, "arroyo.strategies.run_task_with_multiprocessing.processes": {"name": "arroyo.strategies.run_task_with_multiprocessing.processes", "type": "Counter", "description": "A subprocess by multiprocessing unexpectedly died.\n\"sigchld.detected\",\nGauge: Shows how many processes the multiprocessing strategy is\nconfigured with."}, "arroyo.strategies.run_task_with_multiprocessing.pool.create": {"name": "arroyo.strategies.run_task_with_multiprocessing.pool.create", "type": "Counter", "description": "Incremented when the multiprocessing pool is created (or re-created)."}, "arroyo.consumer.poll.time": {"name": "arroyo.consumer.poll.time", "type": "Time", "description": "(unitless) spent polling librdkafka for new messages."}, "arroyo.consumer.processing.time": {"name": "arroyo.consumer.processing.time", "type": "Time", "description": "(unitless) spent in strategies (blocking in strategy.submit or\nstrategy.poll)"}, "arroyo.consumer.backpressure.time": {"name": "arroyo.consumer.backpressure.time", "type": "Time", "description": "(unitless) spent pausing the consumer due to backpressure (MessageRejected)"}, "arroyo.consumer.dlq.time": {"name": "arroyo.consumer.dlq.time", "type": "Time", "description": "(unitless) spent in handling `InvalidMessage` exceptions and sending\nmessages to the the DLQ."}, "arroyo.consumer.join.time": {"name": "arroyo.consumer.join.time", "type": "Time", "description": "(unitless) spent in waiting for the strategy to exit, such as during\nshutdown or rebalancing."}, "arroyo.consumer.callback.time": {"name": "arroyo.consumer.callback.time", "type": "Time", "description": "(unitless) spent in librdkafka callbacks. This metric's timings\noverlap other timings, and might spike at the same time."}, "arroyo.consumer.shutdown.time": {"name": "arroyo.consumer.shutdown.time", "type": "Time", "description": "(unitless) spent in shutting down the consumer. This metric's\ntimings overlap other timings, and might spike at the same time."}, "arroyo.consumer.run.callback": {"name": "arroyo.consumer.run.callback", "type": "Time", "description": "A regular duration metric where each datapoint is measuring the time it\ntook to execute a single callback. This metric is distinct from the\narroyo.consumer.*.time metrics as it does not attempt to accumulate time\nspent per second in an attempt to keep monitoring overhead low.\nThe metric is tagged by the name of the internal callback function being\nexecuted, as 'callback_name'. Possible values are on_partitions_assigned\nand on_partitions_revoked."}, "arroyo.consumer.run.close_strategy": {"name": "arroyo.consumer.run.close_strategy", "type": "Time", "description": "Duration metric measuring the time it took to flush in-flight messages\nand shut down the strategies."}, "arroyo.consumer.run.create_strategy": {"name": "arroyo.consumer.run.create_strategy", "type": "Time", "description": "Duration metric measuring the time it took to create the processing strategy."}, "arroyo.consumer.partitions_revoked.count": {"name": "arroyo.consumer.partitions_revoked.count", "type": "Counter", "description": "How many partitions have been revoked just now."}, "arroyo.consumer.partitions_assigned.count": {"name": "arroyo.consumer.partitions_assigned.count", "type": "Counter", "description": "How many partitions have been assigned just now."}, "arroyo.consumer.latency": {"name": "arroyo.consumer.latency", "type": "Time", "description": "Consumer latency in seconds. Recorded by the commit offsets strategy."}, "arroyo.consumer.pause": {"name": "arroyo.consumer.pause", "type": "Counter", "description": "Metric for when the underlying rdkafka consumer is being paused.\nThis flushes internal prefetch buffers."}, "arroyo.consumer.resume": {"name": "arroyo.consumer.resume", "type": "Counter", "description": "Metric for when the underlying rdkafka consumer is being resumed.\nThis might cause increased network usage as messages are being re-fetched."}, "arroyo.consumer.librdkafka.total_queue_size": {"name": "arroyo.consumer.librdkafka.total_queue_size", "type": "Gauge", "description": "Queue size of background queue that librdkafka uses to prefetch messages."}, "arroyo.processing.strategies.healthcheck.touch": {"name": "arroyo.processing.strategies.healthcheck.touch", "type": "Counter", "description": "Counter metric to measure how often the healthcheck file has been touched."}, "arroyo.strategies.filter.dropped_messages": {"name": "arroyo.strategies.filter.dropped_messages", "type": "Counter", "description": "Number of messages dropped in the FilterStep strategy"}, "arroyo.consumer.dlq.dropped_messages": {"name": "arroyo.consumer.dlq.dropped_messages", "type": "Counter", "description": "how many messages are dropped due to errors producing to the dlq"}, "arroyo.consumer.dlq_buffer.len": {"name": "arroyo.consumer.dlq_buffer.len", "type": "Gauge", "description": "Current length of the DLQ buffer deque"}, "arroyo.consumer.dlq_buffer.exceeded": {"name": "arroyo.consumer.dlq_buffer.exceeded", "type": "Counter", "description": "Number of times the DLQ buffer size has been exceeded, causing messages to be dropped"}, "arroyo.consumer.dlq_buffer.assigned_partitions": {"name": "arroyo.consumer.dlq_buffer.assigned_partitions", "type": "Gauge", "description": "Number of partitions being tracked in the DLQ buffer"}}
|
|
1
|
+
{"arroyo.strategies.run_task_with_multiprocessing.batch.size.msg": {"name": "arroyo.strategies.run_task_with_multiprocessing.batch.size.msg", "type": "Time", "description": "Number of messages in a multiprocessing batch"}, "arroyo.strategies.run_task_with_multiprocessing.batch.size.bytes": {"name": "arroyo.strategies.run_task_with_multiprocessing.batch.size.bytes", "type": "Time", "description": "Number of bytes in a multiprocessing batch"}, "arroyo.strategies.run_task_with_multiprocessing.output_batch.size.msg": {"name": "arroyo.strategies.run_task_with_multiprocessing.output_batch.size.msg", "type": "Time", "description": "Number of messages in a multiprocessing batch after the message transformation"}, "arroyo.strategies.run_task_with_multiprocessing.output_batch.size.bytes": {"name": "arroyo.strategies.run_task_with_multiprocessing.output_batch.size.bytes", "type": "Time", "description": "Number of bytes in a multiprocessing batch after the message transformation"}, "arroyo.consumer.run.count": {"name": "arroyo.consumer.run.count", "type": "Counter", "description": "Number of times the consumer is spinning"}, "arroyo.consumer.invalid_message.count": {"name": "arroyo.consumer.invalid_message.count", "type": "Counter", "description": "Number of times the consumer encountered an invalid message."}, "arroyo.strategies.reduce.batch_time": {"name": "arroyo.strategies.reduce.batch_time", "type": "Time", "description": "How long it took the Reduce step to fill up a batch"}, "arroyo.strategies.run_task_with_multiprocessing.batch.backpressure": {"name": "arroyo.strategies.run_task_with_multiprocessing.batch.backpressure", "type": "Counter", "description": "Incremented when a strategy after multiprocessing applies\nbackpressure to multiprocessing. May be a reason why CPU cannot be\nsaturated."}, "arroyo.strategies.run_task_with_multiprocessing.batch.input.overflow": {"name": "arroyo.strategies.run_task_with_multiprocessing.batch.input.overflow", "type": "Counter", "description": "Incremented when multiprocessing cannot fill the input batch\nbecause not enough memory was allocated. This results in batches smaller\nthan configured. Increase `input_block_size` to fix."}, "arroyo.strategies.run_task_with_multiprocessing.batch.output.overflow": {"name": "arroyo.strategies.run_task_with_multiprocessing.batch.output.overflow", "type": "Counter", "description": "Incremented when multiprocessing cannot pull results in batches\nequal to the input batch size, because not enough memory was allocated.\nThis can be devastating for throughput. Increase `output_block_size` to\nfix."}, "arroyo.strategies.run_task_with_multiprocessing.batch.input.resize": {"name": "arroyo.strategies.run_task_with_multiprocessing.batch.input.resize", "type": "Counter", "description": "Arroyo has decided to re-allocate a block in order to combat input\nbuffer overflow. This behavior can be disabled by explicitly setting\n`input_block_size` to a not-None value in `RunTaskWithMultiprocessing`."}, "arroyo.strategies.run_task_with_multiprocessing.batch.output.resize": {"name": "arroyo.strategies.run_task_with_multiprocessing.batch.output.resize", "type": "Counter", "description": "Arroyo has decided to re-allocate a block in order to combat output\nbuffer overflow. This behavior can be disabled by explicitly setting\n`output_block_size` to a not-None value in `RunTaskWithMultiprocessing`."}, "arroyo.strategies.run_task_with_multiprocessing.batches_in_progress": {"name": "arroyo.strategies.run_task_with_multiprocessing.batches_in_progress", "type": "Gauge", "description": "How many batches are being processed in parallel by multiprocessing."}, "arroyo.strategies.run_task_with_multiprocessing.processes": {"name": "arroyo.strategies.run_task_with_multiprocessing.processes", "type": "Counter", "description": "A subprocess by multiprocessing unexpectedly died.\n\"sigchld.detected\",\nGauge: Shows how many processes the multiprocessing strategy is\nconfigured with."}, "arroyo.strategies.run_task_with_multiprocessing.pool.create": {"name": "arroyo.strategies.run_task_with_multiprocessing.pool.create", "type": "Counter", "description": "Incremented when the multiprocessing pool is created (or re-created)."}, "arroyo.consumer.poll.time": {"name": "arroyo.consumer.poll.time", "type": "Time", "description": "(unitless) spent polling librdkafka for new messages."}, "arroyo.consumer.processing.time": {"name": "arroyo.consumer.processing.time", "type": "Time", "description": "(unitless) spent in strategies (blocking in strategy.submit or\nstrategy.poll)"}, "arroyo.consumer.backpressure.time": {"name": "arroyo.consumer.backpressure.time", "type": "Time", "description": "(unitless) spent pausing the consumer due to backpressure (MessageRejected)"}, "arroyo.consumer.dlq.time": {"name": "arroyo.consumer.dlq.time", "type": "Time", "description": "(unitless) spent in handling `InvalidMessage` exceptions and sending\nmessages to the the DLQ."}, "arroyo.consumer.join.time": {"name": "arroyo.consumer.join.time", "type": "Time", "description": "(unitless) spent in waiting for the strategy to exit, such as during\nshutdown or rebalancing."}, "arroyo.consumer.callback.time": {"name": "arroyo.consumer.callback.time", "type": "Time", "description": "(unitless) spent in librdkafka callbacks. This metric's timings\noverlap other timings, and might spike at the same time."}, "arroyo.consumer.shutdown.time": {"name": "arroyo.consumer.shutdown.time", "type": "Time", "description": "(unitless) spent in shutting down the consumer. This metric's\ntimings overlap other timings, and might spike at the same time."}, "arroyo.consumer.run.callback": {"name": "arroyo.consumer.run.callback", "type": "Time", "description": "A regular duration metric where each datapoint is measuring the time it\ntook to execute a single callback. This metric is distinct from the\narroyo.consumer.*.time metrics as it does not attempt to accumulate time\nspent per second in an attempt to keep monitoring overhead low.\nThe metric is tagged by the name of the internal callback function being\nexecuted, as 'callback_name'. Possible values are on_partitions_assigned\nand on_partitions_revoked."}, "arroyo.consumer.run.close_strategy": {"name": "arroyo.consumer.run.close_strategy", "type": "Time", "description": "Duration metric measuring the time it took to flush in-flight messages\nand shut down the strategies."}, "arroyo.consumer.run.create_strategy": {"name": "arroyo.consumer.run.create_strategy", "type": "Time", "description": "Duration metric measuring the time it took to create the processing strategy."}, "arroyo.consumer.partitions_revoked.count": {"name": "arroyo.consumer.partitions_revoked.count", "type": "Counter", "description": "How many partitions have been revoked just now."}, "arroyo.consumer.partitions_assigned.count": {"name": "arroyo.consumer.partitions_assigned.count", "type": "Counter", "description": "How many partitions have been assigned just now."}, "arroyo.consumer.latency": {"name": "arroyo.consumer.latency", "type": "Time", "description": "Consumer latency in seconds. Recorded by the commit offsets strategy."}, "arroyo.consumer.pause": {"name": "arroyo.consumer.pause", "type": "Counter", "description": "Metric for when the underlying rdkafka consumer is being paused.\nThis flushes internal prefetch buffers."}, "arroyo.consumer.resume": {"name": "arroyo.consumer.resume", "type": "Counter", "description": "Metric for when the underlying rdkafka consumer is being resumed.\nThis might cause increased network usage as messages are being re-fetched."}, "arroyo.consumer.librdkafka.total_queue_size": {"name": "arroyo.consumer.librdkafka.total_queue_size", "type": "Gauge", "description": "Queue size of background queue that librdkafka uses to prefetch messages."}, "arroyo.processing.strategies.healthcheck.touch": {"name": "arroyo.processing.strategies.healthcheck.touch", "type": "Counter", "description": "Counter metric to measure how often the healthcheck file has been touched."}, "arroyo.strategies.filter.dropped_messages": {"name": "arroyo.strategies.filter.dropped_messages", "type": "Counter", "description": "Number of messages dropped in the FilterStep strategy"}, "arroyo.consumer.dlq.dropped_messages": {"name": "arroyo.consumer.dlq.dropped_messages", "type": "Counter", "description": "how many messages are dropped due to errors producing to the dlq"}, "arroyo.consumer.dlq_buffer.len": {"name": "arroyo.consumer.dlq_buffer.len", "type": "Gauge", "description": "Current length of the DLQ buffer deque"}, "arroyo.consumer.dlq_buffer.exceeded": {"name": "arroyo.consumer.dlq_buffer.exceeded", "type": "Counter", "description": "Number of times the DLQ buffer size has been exceeded, causing messages to be dropped"}, "arroyo.consumer.dlq_buffer.assigned_partitions": {"name": "arroyo.consumer.dlq_buffer.assigned_partitions", "type": "Gauge", "description": "Number of partitions being tracked in the DLQ buffer"}, "arroyo.producer.librdkafka.p99_int_latency": {"name": "arroyo.producer.librdkafka.p99_int_latency", "type": "Time", "description": "Internal producer queue latency from librdkafka statistics.\nTagged by broker_id."}, "arroyo.producer.librdkafka.p99_outbuf_latency": {"name": "arroyo.producer.librdkafka.p99_outbuf_latency", "type": "Time", "description": "Output buffer latency from librdkafka statistics.\nTagged by broker_id."}, "arroyo.producer.librdkafka.p99_rtt": {"name": "arroyo.producer.librdkafka.p99_rtt", "type": "Time", "description": "Round-trip time to brokers from librdkafka statistics.\nTagged by broker_id."}, "arroyo.producer.librdkafka.avg_int_latency": {"name": "arroyo.producer.librdkafka.avg_int_latency", "type": "Time", "description": "Average internal producer queue latency from librdkafka statistics.\nTagged by broker_id."}, "arroyo.producer.librdkafka.avg_outbuf_latency": {"name": "arroyo.producer.librdkafka.avg_outbuf_latency", "type": "Time", "description": "Average output buffer latency from librdkafka statistics.\nTagged by broker_id."}, "arroyo.producer.librdkafka.avg_rtt": {"name": "arroyo.producer.librdkafka.avg_rtt", "type": "Time", "description": "Average round-trip time to brokers from librdkafka statistics.\nTagged by broker_id."}}
|
|
@@ -106,4 +106,22 @@ MetricName = Literal[
|
|
|
106
106
|
"arroyo.consumer.dlq_buffer.exceeded",
|
|
107
107
|
# Gauge: Number of partitions being tracked in the DLQ buffer
|
|
108
108
|
"arroyo.consumer.dlq_buffer.assigned_partitions",
|
|
109
|
+
# Time: Internal producer queue latency from librdkafka statistics.
|
|
110
|
+
# Tagged by broker_id.
|
|
111
|
+
"arroyo.producer.librdkafka.p99_int_latency",
|
|
112
|
+
# Time: Output buffer latency from librdkafka statistics.
|
|
113
|
+
# Tagged by broker_id.
|
|
114
|
+
"arroyo.producer.librdkafka.p99_outbuf_latency",
|
|
115
|
+
# Time: Round-trip time to brokers from librdkafka statistics.
|
|
116
|
+
# Tagged by broker_id.
|
|
117
|
+
"arroyo.producer.librdkafka.p99_rtt",
|
|
118
|
+
# Time: Average internal producer queue latency from librdkafka statistics.
|
|
119
|
+
# Tagged by broker_id.
|
|
120
|
+
"arroyo.producer.librdkafka.avg_int_latency",
|
|
121
|
+
# Time: Average output buffer latency from librdkafka statistics.
|
|
122
|
+
# Tagged by broker_id.
|
|
123
|
+
"arroyo.producer.librdkafka.avg_outbuf_latency",
|
|
124
|
+
# Time: Average round-trip time to brokers from librdkafka statistics.
|
|
125
|
+
# Tagged by broker_id.
|
|
126
|
+
"arroyo.producer.librdkafka.avg_rtt",
|
|
109
127
|
]
|
|
@@ -45,6 +45,42 @@ class Metrics(Protocol):
|
|
|
45
45
|
raise NotImplementedError
|
|
46
46
|
|
|
47
47
|
|
|
48
|
+
class ConsumerMetricsWrapper(Metrics):
|
|
49
|
+
"""
|
|
50
|
+
A wrapper around a metrics backend that automatically adds consumer_member_id
|
|
51
|
+
to all metrics calls.
|
|
52
|
+
"""
|
|
53
|
+
|
|
54
|
+
def __init__(self, metrics: Metrics, consumer_member_id: str) -> None:
|
|
55
|
+
self.__metrics = metrics
|
|
56
|
+
self.__consumer_member_id = consumer_member_id
|
|
57
|
+
|
|
58
|
+
def _add_consumer_tag(self, tags: Optional[Tags]) -> Tags:
|
|
59
|
+
"""Add consumer_member_id to the provided tags."""
|
|
60
|
+
consumer_tags = {"consumer_member_id": self.__consumer_member_id}
|
|
61
|
+
if tags:
|
|
62
|
+
return {**consumer_tags, **tags}
|
|
63
|
+
return consumer_tags
|
|
64
|
+
|
|
65
|
+
def increment(
|
|
66
|
+
self,
|
|
67
|
+
name: MetricName,
|
|
68
|
+
value: Union[int, float] = 1,
|
|
69
|
+
tags: Optional[Tags] = None,
|
|
70
|
+
) -> None:
|
|
71
|
+
self.__metrics.increment(name, value, tags=self._add_consumer_tag(tags))
|
|
72
|
+
|
|
73
|
+
def gauge(
|
|
74
|
+
self, name: MetricName, value: Union[int, float], tags: Optional[Tags] = None
|
|
75
|
+
) -> None:
|
|
76
|
+
self.__metrics.gauge(name, value, tags=self._add_consumer_tag(tags))
|
|
77
|
+
|
|
78
|
+
def timing(
|
|
79
|
+
self, name: MetricName, value: Union[int, float], tags: Optional[Tags] = None
|
|
80
|
+
) -> None:
|
|
81
|
+
self.__metrics.timing(name, value, tags=self._add_consumer_tag(tags))
|
|
82
|
+
|
|
83
|
+
|
|
48
84
|
class DummyMetricsBackend(Metrics):
|
|
49
85
|
"""
|
|
50
86
|
Default metrics backend that does not record anything.
|
|
@@ -133,4 +169,12 @@ def get_metrics() -> Metrics:
|
|
|
133
169
|
return _metrics_backend
|
|
134
170
|
|
|
135
171
|
|
|
136
|
-
|
|
172
|
+
def get_consumer_metrics(consumer_member_id: str) -> Metrics:
|
|
173
|
+
"""
|
|
174
|
+
Get a metrics backend that automatically adds consumer_member_id to all metrics.
|
|
175
|
+
"""
|
|
176
|
+
base_metrics = get_metrics()
|
|
177
|
+
return ConsumerMetricsWrapper(base_metrics, consumer_member_id)
|
|
178
|
+
|
|
179
|
+
|
|
180
|
+
__all__ = ["configure_metrics", "Metrics", "MetricName", "Tags", "get_consumer_metrics"]
|
|
@@ -65,6 +65,7 @@ tests/backends/__init__.py
|
|
|
65
65
|
tests/backends/mixins.py
|
|
66
66
|
tests/backends/test_commit.py
|
|
67
67
|
tests/backends/test_kafka.py
|
|
68
|
+
tests/backends/test_kafka_producer.py
|
|
68
69
|
tests/backends/test_local.py
|
|
69
70
|
tests/processing/__init__.py
|
|
70
71
|
tests/processing/test_processor.py
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import json
|
|
2
|
+
from unittest import mock
|
|
3
|
+
|
|
4
|
+
from arroyo.backends.kafka.configuration import producer_stats_callback
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
@mock.patch("arroyo.backends.kafka.configuration.get_metrics")
|
|
8
|
+
def test_producer_stats_callback_with_both_latencies(
|
|
9
|
+
mock_get_metrics: mock.Mock,
|
|
10
|
+
) -> None:
|
|
11
|
+
mock_metrics = mock.Mock()
|
|
12
|
+
mock_get_metrics.return_value = mock_metrics
|
|
13
|
+
|
|
14
|
+
stats_json = json.dumps(
|
|
15
|
+
{
|
|
16
|
+
"brokers": {
|
|
17
|
+
"1": {
|
|
18
|
+
"int_latency": {"p99": 2000, "avg": 1000},
|
|
19
|
+
"outbuf_latency": {"p99": 4000, "avg": 2000},
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
producer_stats_callback(stats_json)
|
|
26
|
+
|
|
27
|
+
assert mock_metrics.timing.call_count == 4
|
|
28
|
+
mock_metrics.timing.assert_any_call(
|
|
29
|
+
"arroyo.producer.librdkafka.p99_int_latency",
|
|
30
|
+
2.0,
|
|
31
|
+
tags={"broker_id": "1"},
|
|
32
|
+
)
|
|
33
|
+
mock_metrics.timing.assert_any_call(
|
|
34
|
+
"arroyo.producer.librdkafka.avg_int_latency",
|
|
35
|
+
1.0,
|
|
36
|
+
tags={"broker_id": "1"},
|
|
37
|
+
)
|
|
38
|
+
mock_metrics.timing.assert_any_call(
|
|
39
|
+
"arroyo.producer.librdkafka.p99_outbuf_latency",
|
|
40
|
+
4.0,
|
|
41
|
+
tags={"broker_id": "1"},
|
|
42
|
+
)
|
|
43
|
+
mock_metrics.timing.assert_any_call(
|
|
44
|
+
"arroyo.producer.librdkafka.avg_outbuf_latency",
|
|
45
|
+
2.0,
|
|
46
|
+
tags={"broker_id": "1"},
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
@mock.patch("arroyo.backends.kafka.configuration.get_metrics")
|
|
51
|
+
def test_producer_stats_callback_no_brokers(mock_get_metrics: mock.Mock) -> None:
|
|
52
|
+
mock_metrics = mock.Mock()
|
|
53
|
+
mock_get_metrics.return_value = mock_metrics
|
|
54
|
+
|
|
55
|
+
stats_json = json.dumps({})
|
|
56
|
+
|
|
57
|
+
producer_stats_callback(stats_json)
|
|
58
|
+
|
|
59
|
+
mock_metrics.timing.assert_not_called()
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
@mock.patch("arroyo.backends.kafka.configuration.get_metrics")
|
|
63
|
+
def test_producer_stats_callback_empty_broker_stats(
|
|
64
|
+
mock_get_metrics: mock.Mock,
|
|
65
|
+
) -> None:
|
|
66
|
+
mock_metrics = mock.Mock()
|
|
67
|
+
mock_get_metrics.return_value = mock_metrics
|
|
68
|
+
|
|
69
|
+
stats_json = json.dumps({"brokers": {"1": {}}})
|
|
70
|
+
|
|
71
|
+
producer_stats_callback(stats_json)
|
|
72
|
+
|
|
73
|
+
mock_metrics.timing.assert_not_called()
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
@mock.patch("arroyo.backends.kafka.configuration.get_metrics")
|
|
77
|
+
def test_producer_stats_callback_with_all_metrics(mock_get_metrics: mock.Mock) -> None:
|
|
78
|
+
mock_metrics = mock.Mock()
|
|
79
|
+
mock_get_metrics.return_value = mock_metrics
|
|
80
|
+
|
|
81
|
+
stats_json = json.dumps(
|
|
82
|
+
{
|
|
83
|
+
"brokers": {
|
|
84
|
+
"1": {
|
|
85
|
+
"int_latency": {"p99": 2000, "avg": 1000}, # 2/1 milliseconds
|
|
86
|
+
"outbuf_latency": {"p99": 4000, "avg": 2000}, # 4/2 milliseconds
|
|
87
|
+
"rtt": {"p99": 1500, "avg": 750}, # 1.5/0.75 milliseconds
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
)
|
|
92
|
+
|
|
93
|
+
producer_stats_callback(stats_json)
|
|
94
|
+
|
|
95
|
+
assert mock_metrics.timing.call_count == 6
|
|
96
|
+
mock_metrics.timing.assert_any_call(
|
|
97
|
+
"arroyo.producer.librdkafka.p99_int_latency",
|
|
98
|
+
2.0,
|
|
99
|
+
tags={"broker_id": "1"},
|
|
100
|
+
)
|
|
101
|
+
mock_metrics.timing.assert_any_call(
|
|
102
|
+
"arroyo.producer.librdkafka.avg_int_latency",
|
|
103
|
+
1.0,
|
|
104
|
+
tags={"broker_id": "1"},
|
|
105
|
+
)
|
|
106
|
+
mock_metrics.timing.assert_any_call(
|
|
107
|
+
"arroyo.producer.librdkafka.p99_outbuf_latency",
|
|
108
|
+
4.0,
|
|
109
|
+
tags={"broker_id": "1"},
|
|
110
|
+
)
|
|
111
|
+
mock_metrics.timing.assert_any_call(
|
|
112
|
+
"arroyo.producer.librdkafka.avg_outbuf_latency",
|
|
113
|
+
2.0,
|
|
114
|
+
tags={"broker_id": "1"},
|
|
115
|
+
)
|
|
116
|
+
mock_metrics.timing.assert_any_call(
|
|
117
|
+
"arroyo.producer.librdkafka.p99_rtt",
|
|
118
|
+
1.5,
|
|
119
|
+
tags={"broker_id": "1"},
|
|
120
|
+
)
|
|
121
|
+
mock_metrics.timing.assert_any_call(
|
|
122
|
+
"arroyo.producer.librdkafka.avg_rtt",
|
|
123
|
+
0.75,
|
|
124
|
+
tags={"broker_id": "1"},
|
|
125
|
+
)
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import pytest
|
|
2
|
+
|
|
3
|
+
from arroyo.utils.metrics import Gauge, MetricName, configure_metrics, get_metrics, get_consumer_metrics
|
|
4
|
+
from tests.metrics import Gauge as GaugeCall
|
|
5
|
+
from tests.metrics import Increment, Timing, TestingMetricsBackend, _TestingMetricsBackend
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def test_gauge_simple() -> None:
|
|
9
|
+
backend = TestingMetricsBackend
|
|
10
|
+
|
|
11
|
+
name: MetricName = "name" # type: ignore
|
|
12
|
+
tags = {"tag": "value"}
|
|
13
|
+
gauge = Gauge(backend, name, tags)
|
|
14
|
+
|
|
15
|
+
with gauge:
|
|
16
|
+
pass
|
|
17
|
+
|
|
18
|
+
assert backend.calls == [
|
|
19
|
+
GaugeCall(name, 0.0, tags),
|
|
20
|
+
GaugeCall(name, 1.0, tags),
|
|
21
|
+
GaugeCall(name, 0.0, tags),
|
|
22
|
+
]
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def test_configure_metrics() -> None:
|
|
26
|
+
assert get_metrics() == TestingMetricsBackend
|
|
27
|
+
|
|
28
|
+
with pytest.raises(AssertionError):
|
|
29
|
+
configure_metrics(_TestingMetricsBackend())
|
|
30
|
+
|
|
31
|
+
# Can be reset to something else with force
|
|
32
|
+
configure_metrics(_TestingMetricsBackend(), force=True)
|
|
33
|
+
assert get_metrics() != TestingMetricsBackend
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def test_consumer_metrics_wrapper() -> None:
|
|
37
|
+
"""Test that ConsumerMetricsWrapper automatically adds consumer_member_id to all metrics."""
|
|
38
|
+
# Reset to a fresh backend
|
|
39
|
+
backend = _TestingMetricsBackend()
|
|
40
|
+
configure_metrics(backend, force=True)
|
|
41
|
+
|
|
42
|
+
consumer_member_id = "test-consumer-123"
|
|
43
|
+
consumer_metrics = get_consumer_metrics(consumer_member_id)
|
|
44
|
+
|
|
45
|
+
# Test increment
|
|
46
|
+
consumer_metrics.increment("arroyo.consumer.run.count", 5, tags={"extra": "tag"})
|
|
47
|
+
|
|
48
|
+
# Test gauge
|
|
49
|
+
consumer_metrics.gauge("arroyo.consumer.librdkafka.total_queue_size", 10.5)
|
|
50
|
+
|
|
51
|
+
# Test timing
|
|
52
|
+
consumer_metrics.timing("arroyo.consumer.poll.time", 100, tags={"another": "tag"})
|
|
53
|
+
|
|
54
|
+
expected_calls = [
|
|
55
|
+
Increment("arroyo.consumer.run.count", 5, {"consumer_member_id": consumer_member_id, "extra": "tag"}),
|
|
56
|
+
GaugeCall("arroyo.consumer.librdkafka.total_queue_size", 10.5, {"consumer_member_id": consumer_member_id}),
|
|
57
|
+
Timing("arroyo.consumer.poll.time", 100, {"consumer_member_id": consumer_member_id, "another": "tag"}),
|
|
58
|
+
]
|
|
59
|
+
|
|
60
|
+
assert backend.calls == expected_calls
|
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
import pytest
|
|
2
|
-
|
|
3
|
-
from arroyo.utils.metrics import Gauge, MetricName, configure_metrics, get_metrics
|
|
4
|
-
from tests.metrics import Gauge as GaugeCall
|
|
5
|
-
from tests.metrics import TestingMetricsBackend, _TestingMetricsBackend
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
def test_gauge_simple() -> None:
|
|
9
|
-
backend = TestingMetricsBackend
|
|
10
|
-
|
|
11
|
-
name: MetricName = "name" # type: ignore
|
|
12
|
-
tags = {"tag": "value"}
|
|
13
|
-
gauge = Gauge(backend, name, tags)
|
|
14
|
-
|
|
15
|
-
with gauge:
|
|
16
|
-
pass
|
|
17
|
-
|
|
18
|
-
assert backend.calls == [
|
|
19
|
-
GaugeCall(name, 0.0, tags),
|
|
20
|
-
GaugeCall(name, 1.0, tags),
|
|
21
|
-
GaugeCall(name, 0.0, tags),
|
|
22
|
-
]
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
def test_configure_metrics() -> None:
|
|
26
|
-
assert get_metrics() == TestingMetricsBackend
|
|
27
|
-
|
|
28
|
-
with pytest.raises(AssertionError):
|
|
29
|
-
configure_metrics(_TestingMetricsBackend())
|
|
30
|
-
|
|
31
|
-
# Can be reset to something else with force
|
|
32
|
-
configure_metrics(_TestingMetricsBackend(), force=True)
|
|
33
|
-
assert get_metrics() != TestingMetricsBackend
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{sentry_arroyo-2.23.0 → sentry_arroyo-2.25.0}/arroyo/processing/strategies/run_task_in_threads.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|