@@ -158,3 +158,67 @@ TEST(AttributesHashMap, HashConsistencyAcrossStringTypes)
158158 EXPECT_EQ (hash_c_str, hash_std_str_view);
159159#endif
160160}
161+
162+ TEST (AttributesHashMap, OverflowCardinalityLimitBehavior)
163+ {
164+ // Configure a very small limit to exercise overflow logic easily.
165+ const size_t limit = 4 ; // real attributes limit
166+ AttributesHashMapWithCustomHash<> map (limit);
167+
168+ // We expect to be able to insert exactly 'limit' distinct real attribute sets.
169+ // After that, further distinct attributes should route to the overflow bucket,
170+ // which should appear only once regardless of how many additional unique sets arrive.
171+
172+ // Insert distinct attributes up to the limit.
173+ for (size_t i = 0 ; i < limit; ++i)
174+ {
175+ MetricAttributes attr = {{" k" , std::to_string (i)}};
176+ map.GetOrSetDefault (attr, []() { return std::unique_ptr<Aggregation>(new DropAggregation ()); })
177+ ->Aggregate (static_cast <int64_t >(1 ));
178+ }
179+
180+ // Size should be exactly 'limit' (no overflow yet)
181+ EXPECT_EQ (map.Size (), limit);
182+
183+ // Insert one more distinct attribute; this should not increase the real attributes count
184+ MetricAttributes overflow_trigger = {{" k" , " overflow" }};
185+ map.GetOrSetDefault (overflow_trigger,
186+ []() { return std::unique_ptr<Aggregation>(new DropAggregation ()); })
187+ ->Aggregate (static_cast <int64_t >(1 ));
188+
189+ EXPECT_EQ (map.Size (), limit);
190+
191+ // Insert several more unique attributes - size must remain constant (limit)
192+ for (size_t i = 0 ; i < limit - 1 ; ++i)
193+ {
194+ MetricAttributes extra_attr = {{" k" , std::string (" extra" ) + std::to_string (i)}};
195+ map.GetOrSetDefault (extra_attr,
196+ []() { return std::unique_ptr<Aggregation>(new DropAggregation ()); })
197+ ->Aggregate (static_cast <int64_t >(1 ));
198+ }
199+ EXPECT_EQ (map.Size (), limit);
200+
201+ // Ensure overflow key was actually created and accessible via Get
202+ EXPECT_NE (map.Get (kOverflowAttributes ), nullptr );
203+
204+ // Ensure original real attributes still present
205+ for (size_t i = 0 ; i < limit - 1 ; ++i)
206+ {
207+ MetricAttributes attr = {{" k" , std::to_string (i)}};
208+ EXPECT_NE (map.Get (attr), nullptr );
209+ }
210+
211+ // Copy the hash map to a new map in non-determistic order and verify all entries are present
212+ AttributesHashMapWithCustomHash<> map_copy (limit);
213+ map.GetAllEnteries ([&map_copy](const MetricAttributes &attributes, Aggregation &) {
214+ map_copy.Set (attributes, std::unique_ptr<Aggregation>(new DropAggregation ()));
215+ return true ;
216+ });
217+ EXPECT_EQ (map_copy.Size (), map.Size ());
218+ EXPECT_NE (map_copy.Get (kOverflowAttributes ), nullptr );
219+ for (size_t i = 0 ; i < limit - 1 ; ++i)
220+ {
221+ MetricAttributes attr = {{" k" , std::to_string (i)}};
222+ EXPECT_NE (map_copy.Get (attr), nullptr );
223+ }
224+ }
0 commit comments