Skip to content

Commit 6c452c7

Browse files
refactor: cache constant tags
1 parent 91c8691 commit 6c452c7

13 files changed

Lines changed: 265 additions & 36 deletions

File tree

datadog/__init__.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,10 @@
1313
# stdlib
1414
import logging
1515
import os
16-
from typing import Any, List, Optional # noqa: F401
16+
import sys
17+
18+
if sys.version_info[0] >= 3:
19+
from typing import Any, List, Optional # noqa: F401
1720

1821
# datadog
1922
from datadog import api

datadog/api/__init__.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,11 @@
33
# Copyright 2015-Present Datadog, Inc
44
# flake8: noqa
55

6-
from typing import Optional
6+
import sys
7+
8+
9+
if sys.version_info[0] >= 3:
10+
from typing import Optional # noqa: F401
711

812
# API settings
913
_api_key = None # type: Optional[str]

datadog/api/http_client.py

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,16 +12,19 @@
1212
import copy
1313
import logging
1414
import platform
15+
import sys
1516
import urllib
16-
from typing import TYPE_CHECKING
17+
1718
from threading import Lock
1819

1920
# datadog
2021
from datadog.api.exceptions import ProxyError, ClientError, HTTPError, HttpTimeout
2122

22-
if TYPE_CHECKING:
23-
import types # noqa: F401
24-
from typing import Optional # noqa: F401
23+
if sys.version_info[:2] >= (3, 5):
24+
from typing import TYPE_CHECKING
25+
if TYPE_CHECKING:
26+
import types # noqa: F401
27+
from typing import Optional # noqa: F401
2528

2629

2730
# 3p

datadog/dogstatsd/aggregator.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
import threading
2-
from typing import Any, Dict, List, Optional
2+
import sys
3+
4+
if sys.version_info[:2] >= (3, 5):
5+
from typing import Any, Dict, List, Optional # noqa: F401
36

47
from datadog.dogstatsd.metrics import (
58
CountMetric,

datadog/dogstatsd/base.py

Lines changed: 158 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,9 @@
1919
import time
2020
from threading import Lock, RLock
2121
import weakref
22-
from typing import TYPE_CHECKING
22+
23+
if sys.version_info[:2] >= (3, 5):
24+
from typing import TYPE_CHECKING # noqa: F401
2325

2426
try:
2527
import queue
@@ -29,7 +31,13 @@
2931

3032

3133
# pylint: disable=unused-import
32-
from typing import Any, Optional, List, Text, Type, Union
34+
if sys.version_info[:2] >= (3, 5):
35+
from typing import Any, Optional, List, Text, Type, Union, Iterable, Callable, overload # noqa: F401
36+
37+
try:
38+
from typing import SupportsIndex
39+
except ImportError:
40+
SupportsIndex = int # type: ignore[assignment,misc]
3341
# pylint: enable=unused-import
3442

3543
# Datadog libraries
@@ -45,8 +53,102 @@
4553
from datadog.util.format import normalize_tags, validate_cardinality
4654
from datadog.version import __version__
4755

48-
if TYPE_CHECKING:
49-
from socket import socket as _Socket
56+
57+
if sys.version_info[:2] >= (3, 5):
58+
if TYPE_CHECKING:
59+
from socket import socket as _Socket
60+
61+
BaseListClass = List[str]
62+
else:
63+
BaseListClass = list
64+
65+
class TagList(BaseListClass):
66+
"""A list subclass that calls on_change() after any mutation."""
67+
68+
def __init__(self, iterable = (), on_change = None):
69+
# type: (Iterable[str], Optional[Callable[[], None]]) -> None
70+
super(TagList, self).__init__(iterable)
71+
self._on_change = on_change
72+
73+
def _notify(self):
74+
# type: () -> None
75+
if self._on_change is not None:
76+
self._on_change()
77+
78+
if sys.version_info[:2] >= (3, 5):
79+
@overload
80+
def __setitem__(self, index, value): # noqa: F811
81+
# type: (SupportsIndex, str) -> None
82+
pass
83+
84+
@overload
85+
def __setitem__(self, index, value): # noqa: F811
86+
# type: (slice, Iterable[str]) -> None
87+
pass
88+
89+
def __setitem__(self, index, value): # noqa: F811
90+
# type: (Union[SupportsIndex, slice], Union[str, Iterable[str]]) -> None
91+
super(TagList, self).__setitem__(index, value) # type: ignore
92+
self._notify()
93+
94+
def __delitem__(self, index): # noqa: F811
95+
# type: (Union[SupportsIndex, slice]) -> None
96+
super(TagList, self).__delitem__(index)
97+
self._notify()
98+
99+
def __iadd__(self, other): # type: ignore[misc,override] # noqa: F811
100+
# type: (Iterable[str]) -> "TagList"
101+
super(TagList, self).__iadd__(other)
102+
self._notify()
103+
return self
104+
105+
def __imul__(self, n): # noqa: F811
106+
# type: (SupportsIndex) -> "TagList"
107+
super(TagList, self).__imul__(n)
108+
self._notify()
109+
return self
110+
111+
def append(self, value): # noqa: F811
112+
# type: (str) -> None
113+
super(TagList, self).append(value)
114+
self._notify()
115+
116+
def extend(self, iterable): # noqa: F811
117+
# type: (Iterable[str]) -> None
118+
super(TagList, self).extend(iterable)
119+
self._notify()
120+
121+
def insert(self, index, value): # noqa: F811
122+
# type: (SupportsIndex, str) -> None
123+
super(TagList, self).insert(index, value)
124+
self._notify()
125+
126+
def remove(self, value): # noqa: F811
127+
# type: (str) -> None
128+
super(TagList, self).remove(value)
129+
self._notify()
130+
131+
def pop(self, index = -1): # noqa: F811
132+
# type: (SupportsIndex) -> str
133+
value = super(TagList, self).pop(index)
134+
self._notify()
135+
return value
136+
137+
def clear(self): # noqa: F811
138+
# type: () -> None
139+
super(TagList, self).__delitem__(slice(None))
140+
self._notify()
141+
142+
def sort(self, *args, **kwargs):
143+
# type: (*Any, **Any) -> None
144+
super(TagList, self).sort(*args, **kwargs)
145+
self._notify()
146+
147+
def reverse(self):
148+
# type: () -> None
149+
super(TagList, self).reverse()
150+
self._notify()
151+
50152

51153
# Logging
52154
log = logging.getLogger("datadog.dogstatsd")
@@ -442,9 +544,18 @@ def __init__(
442544
value = os.environ.get(var, "")
443545
if value:
444546
env_tags.append("{name}:{value}".format(name=tag_name, value=value))
547+
548+
# This lock is used for all cases where client configuration is being changed: buffering,
549+
# aggregation, sender mode.
550+
self._config_lock = RLock()
551+
445552
if constant_tags is None:
446553
constant_tags = []
447-
self.constant_tags = constant_tags + env_tags
554+
555+
self._constant_tags_str = ""
556+
self._constant_tags = TagList()
557+
self.constant_tags = TagList(constant_tags + env_tags)
558+
448559
if namespace is not None:
449560
namespace = text(namespace)
450561
self.namespace = namespace
@@ -476,10 +587,6 @@ def __init__(
476587

477588
self._reset_buffer()
478589

479-
# This lock is used for all cases where client configuration is being changed: buffering,
480-
# aggregation, sender mode.
481-
self._config_lock = RLock()
482-
483590
self._disable_buffering = disable_buffering
484591
self._disable_aggregation = disable_aggregation
485592

@@ -1267,9 +1374,18 @@ def _serialize_metric(
12671374
parts.append("|@")
12681375
parts.append(text(sample_rate))
12691376

1270-
if tags:
1377+
with self._config_lock:
1378+
constant_tags_str = self._constant_tags_str
1379+
1380+
if tags or constant_tags_str:
12711381
parts.append("|#")
1272-
parts.append(",".join(normalize_tags(tags)))
1382+
if tags:
1383+
parts.append(",".join(normalize_tags(tags)))
1384+
if constant_tags_str:
1385+
parts.append(",")
1386+
parts.append(constant_tags_str)
1387+
else:
1388+
parts.append(constant_tags_str)
12731389

12741390
if self._container_id:
12751391
parts.append("|c:")
@@ -1323,8 +1439,6 @@ def _report(self, metric, metric_type, value, tags, sample_rate, timestamp=0, sa
13231439

13241440
validate_cardinality(cardinality)
13251441

1326-
# Resolve the full tag list
1327-
tags = self._add_constant_tags(tags)
13281442
payload = self._serialize_metric(
13291443
metric, metric_type, value, tags, sample_rate, timestamp, cardinality
13301444
)
@@ -1642,13 +1756,40 @@ def service_check(
16421756

16431757
self._send(string)
16441758

1759+
@staticmethod
1760+
def _normalize_and_join_tags(tags):
1761+
# type: (List[str]) -> str
1762+
"""Normalize a tag list and join into a comma-separated string."""
1763+
if tags:
1764+
return ",".join(normalize_tags(tags))
1765+
1766+
return ""
1767+
1768+
def _rebuild_constant_tags_str(self):
1769+
# type: () -> None
1770+
with self._config_lock:
1771+
self._constant_tags_str = self._normalize_and_join_tags(self._constant_tags)
1772+
1773+
@property
1774+
def constant_tags(self):
1775+
# type: () -> TagList
1776+
return self._constant_tags
1777+
1778+
@constant_tags.setter
1779+
def constant_tags(self, value):
1780+
# type: (Union[TagList, List[str]]) -> None
1781+
with self._config_lock:
1782+
self._constant_tags = TagList(value or [], on_change=self._rebuild_constant_tags_str)
1783+
self._rebuild_constant_tags_str()
1784+
16451785
def _add_constant_tags(self, tags):
16461786
# type: (Optional[List[str]]) -> Optional[List[str]]
1647-
if self.constant_tags:
1648-
if tags:
1649-
return tags + self.constant_tags
1787+
with self._config_lock:
1788+
if self._constant_tags:
1789+
if tags:
1790+
return tags + self._constant_tags
16501791

1651-
return self.constant_tags
1792+
return list(self._constant_tags)
16521793
return tags
16531794

16541795
def _is_origin_detection_enabled(self, container_id, origin_detection_enabled):

datadog/dogstatsd/container.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,10 @@
66
import errno
77
import os
88
import re
9-
from typing import Optional
9+
import sys
10+
11+
if sys.version_info[:2] >= (3, 5):
12+
from typing import Optional # noqa: F401
1013

1114

1215
class UnresolvableContainerID(Exception):

datadog/dogstatsd/context.py

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,9 @@
33
# Copyright 2015-Present Datadog, Inc
44
# stdlib
55
from functools import wraps
6-
from typing import Any, Callable, List, Optional, Text, TYPE_CHECKING, Union
6+
import sys
7+
8+
79

810
try:
911
from time import monotonic # type: ignore[attr-defined]
@@ -14,8 +16,12 @@
1416
from datadog.dogstatsd.context_async import _get_wrapped_co
1517
from datadog.util.compat import iscoroutinefunction
1618

17-
if TYPE_CHECKING:
18-
from datadog.dogstatsd.base import DogStatsd
19+
20+
if sys.version_info[:2] >= (3, 5):
21+
from typing import Any, Callable, List, Optional, Text, TYPE_CHECKING, Union # noqa: F401
22+
23+
if TYPE_CHECKING:
24+
from datadog.dogstatsd.base import DogStatsd # noqa: F401
1925

2026

2127
class TimedContextManagerDecorator(object):

datadog/dogstatsd/context_async.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,9 @@
88
"""
99
# stdlib
1010
import sys
11-
from typing import Any, Callable
11+
12+
if sys.version_info[:2] >= (3, 5):
13+
from typing import Any, Callable # noqa: F401
1214

1315

1416
# Wrap the Python 3.5+ function in a docstring to avoid syntax errors when

datadog/dogstatsd/max_sample_metric.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
import random
2-
from typing import List, Optional, cast
2+
import sys
3+
4+
if sys.version_info[:2] >= (3, 5):
5+
from typing import List, Optional, cast # noqa: F401
6+
37

48
from datadog.dogstatsd.metric_types import MetricType
59
from datadog.dogstatsd.metrics import MetricAggregator

datadog/dogstatsd/max_sample_metric_context.py

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
11
from threading import Lock
22
import random
3-
from typing import Any, Dict, List, Optional, TYPE_CHECKING
3+
import sys
44

5-
if TYPE_CHECKING:
6-
from datadog.dogstatsd.max_sample_metric import MaxSampleMetric
7-
from datadog.dogstatsd.metrics import MetricAggregator
5+
if sys.version_info[:2] >= (3, 5):
6+
from typing import Any, Dict, List, Optional, TYPE_CHECKING # noqa: F401
7+
8+
if TYPE_CHECKING:
9+
from datadog.dogstatsd.max_sample_metric import MaxSampleMetric
10+
from datadog.dogstatsd.metrics import MetricAggregator
811

912

1013
class MaxSampleMetricContexts:

0 commit comments

Comments
 (0)