Skip to content

Commit d7e5a1f

Browse files
Stephen-BaoStephen-BaoMark Kuhn
authored
Disallow duplicate dimension sets (#85)
* Disallow duplicate dimension sets * Adjusted the test * Ajusted naming of a test Co-authored-by: Stephen-Bao <[email protected]> Co-authored-by: Mark Kuhn <[email protected]>
1 parent add0ea9 commit d7e5a1f

File tree

2 files changed

+112
-5
lines changed

2 files changed

+112
-5
lines changed

aws_embedded_metrics/logger/metrics_context.py

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
from aws_embedded_metrics.constants import MAX_DIMENSION_SET_SIZE
1818
from aws_embedded_metrics.exceptions import DimensionSetExceededError
1919
from aws_embedded_metrics.logger.metric import Metric
20-
from typing import List, Dict, Any
20+
from typing import List, Dict, Any, Set
2121

2222

2323
class MetricsContext(object):
@@ -66,20 +66,26 @@ def validate_dimension_set(dimensions: Dict[str, str]) -> None:
6666
raise DimensionSetExceededError(
6767
f"Maximum number of dimensions per dimension set allowed are {MAX_DIMENSION_SET_SIZE}")
6868

69-
def put_dimensions(self, dimensions: Dict[str, str]) -> None:
69+
def put_dimensions(self, dimension_set: Dict[str, str]) -> None:
7070
"""
7171
Adds dimensions to the context.
7272
```
7373
context.put_dimensions({ "k1": "v1", "k2": "v2" })
7474
```
7575
"""
76-
if dimensions is None:
76+
if dimension_set is None:
7777
# TODO add ability to define failure strategy
7878
return
7979

80-
self.validate_dimension_set(dimensions)
80+
self.validate_dimension_set(dimension_set)
8181

82-
self.dimensions.append(dimensions)
82+
# Duplicate dimension sets are removed before being added to the end of the collection.
83+
# This ensures only latest dimension value is used as a target member on the root EMF node.
84+
# This operation is O(n^2), but acceptable given sets are capped at 30 dimensions
85+
incoming_keys: Set = set(dimension_set.keys())
86+
self.dimensions = list(filter(lambda dim: (set(dim.keys()) != incoming_keys), self.dimensions))
87+
88+
self.dimensions.append(dimension_set)
8389

8490
def set_dimensions(self, dimensionSets: List[Dict[str, str]]) -> None:
8591
"""

tests/logger/test_metrics_context.py

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,107 @@ def test_put_dimension_adds_to_dimensions():
5454
assert context.dimensions == [dimension_set]
5555

5656

57+
def test_put_dimensions_accept_multiple_unique_dimensions():
58+
# arrange
59+
context = MetricsContext()
60+
dimension1 = {fake.word(): fake.word()}
61+
dimension2 = {fake.word(): fake.word()}
62+
63+
# act
64+
context.put_dimensions(dimension1)
65+
context.put_dimensions(dimension2)
66+
67+
# assert
68+
assert len(context.get_dimensions()) == 2
69+
assert context.get_dimensions()[0] == dimension1
70+
assert context.get_dimensions()[1] == dimension2
71+
72+
73+
def test_put_dimensions_prevent_duplicate_dimensions():
74+
# arrange
75+
context = MetricsContext()
76+
pair1 = [fake.word(), fake.word()]
77+
pair2 = [fake.word(), fake.word()]
78+
79+
dimension1 = {pair1[0]: pair1[1]}
80+
dimension2 = {pair2[0]: pair2[1]}
81+
dimension3 = {pair1[0]: pair1[1], pair2[0]: pair2[1]}
82+
83+
# act
84+
context.put_dimensions(dimension1)
85+
context.put_dimensions(dimension2)
86+
context.put_dimensions(dimension1)
87+
context.put_dimensions(dimension3)
88+
context.put_dimensions(dimension2)
89+
context.put_dimensions(dimension3)
90+
91+
# assert
92+
assert len(context.get_dimensions()) == 3
93+
assert context.get_dimensions()[0] == dimension1
94+
assert context.get_dimensions()[1] == dimension2
95+
assert context.get_dimensions()[2] == dimension3
96+
97+
98+
def test_put_dimensions_use_most_recent_dimension_value():
99+
# arrange
100+
context = MetricsContext()
101+
key1 = fake.word()
102+
key2 = fake.word()
103+
val1 = fake.word()
104+
val2 = fake.word()
105+
106+
dimension1 = {key1: val1}
107+
dimension2 = {key2: val2}
108+
dimension3 = {key1: val2}
109+
dimension4 = {key2: val1}
110+
dimension5 = {key1: val1, key2: val2}
111+
dimension6 = {key1: val2, key2: val1}
112+
113+
# act
114+
context.put_dimensions(dimension1)
115+
context.put_dimensions(dimension2)
116+
context.put_dimensions(dimension5)
117+
context.put_dimensions(dimension3)
118+
context.put_dimensions(dimension4)
119+
context.put_dimensions(dimension6)
120+
121+
# assert
122+
assert len(context.get_dimensions()) == 3
123+
assert context.get_dimensions()[0] == dimension3
124+
assert context.get_dimensions()[1] == dimension4
125+
assert context.get_dimensions()[2] == dimension6
126+
127+
128+
def test_put_dimensions_with_set_dimensions():
129+
# arrange
130+
context = MetricsContext()
131+
key1 = fake.word()
132+
key2 = fake.word()
133+
val1 = fake.word()
134+
val2 = fake.word()
135+
136+
dimension1 = {key1: val1}
137+
dimension2 = {key2: val2}
138+
dimension3 = {key1: val2}
139+
dimension4 = {key2: val1}
140+
dimension5 = {key1: val1, key2: val2}
141+
dimension6 = {key1: val2, key2: val1}
142+
143+
# act
144+
context.put_dimensions(dimension1)
145+
context.put_dimensions(dimension2)
146+
context.set_dimensions([dimension3])
147+
context.put_dimensions(dimension4)
148+
context.put_dimensions(dimension5)
149+
context.put_dimensions(dimension6)
150+
151+
# assert
152+
assert len(context.get_dimensions()) == 3
153+
assert context.get_dimensions()[0] == dimension3
154+
assert context.get_dimensions()[1] == dimension4
155+
assert context.get_dimensions()[2] == dimension6
156+
157+
57158
def test_get_dimensions_returns_only_custom_dimensions_if_no_default_dimensions_not_set():
58159
# arrange
59160
context = MetricsContext()

0 commit comments

Comments
 (0)