|
5 | 5 | "encoding/binary"
|
6 | 6 | "fmt"
|
7 | 7 | "testing"
|
| 8 | + "unsafe" |
8 | 9 |
|
9 | 10 | "github.com/prometheus/prometheus/model/labels"
|
10 | 11 | "github.com/stretchr/testify/require"
|
@@ -175,3 +176,189 @@ func TestSymbolizer(t *testing.T) {
|
175 | 176 | }
|
176 | 177 | }
|
177 | 178 | }
|
| 179 | + |
| 180 | +func TestSymbolizerLabelNormalization(t *testing.T) { |
| 181 | + for _, tc := range []struct { |
| 182 | + name string |
| 183 | + labelsToAdd []labels.Labels |
| 184 | + expectedLabels []labels.Labels |
| 185 | + description string |
| 186 | + }{ |
| 187 | + { |
| 188 | + name: "basic label normalization", |
| 189 | + labelsToAdd: []labels.Labels{ |
| 190 | + { |
| 191 | + {Name: "foo-bar", Value: "value1"}, |
| 192 | + {Name: "fizz_buzz", Value: "value2"}, |
| 193 | + }, |
| 194 | + }, |
| 195 | + expectedLabels: []labels.Labels{ |
| 196 | + { |
| 197 | + {Name: "foo_bar", Value: "value1"}, |
| 198 | + {Name: "fizz_buzz", Value: "value2"}, |
| 199 | + }, |
| 200 | + }, |
| 201 | + description: "hyphens should be converted to underscores in label names", |
| 202 | + }, |
| 203 | + { |
| 204 | + name: "same string as name and value", |
| 205 | + labelsToAdd: []labels.Labels{ |
| 206 | + { |
| 207 | + {Name: "foo-bar", Value: "foo-bar"}, |
| 208 | + {Name: "fizz-buzz", Value: "fizz-buzz"}, |
| 209 | + }, |
| 210 | + }, |
| 211 | + expectedLabels: []labels.Labels{ |
| 212 | + { |
| 213 | + {Name: "foo_bar", Value: "foo-bar"}, |
| 214 | + {Name: "fizz_buzz", Value: "fizz-buzz"}, |
| 215 | + }, |
| 216 | + }, |
| 217 | + description: "only normalize when string is used as a name, not as a value", |
| 218 | + }, |
| 219 | + } { |
| 220 | + t.Run(tc.name, func(t *testing.T) { |
| 221 | + // Test direct addition |
| 222 | + s := newSymbolizer() |
| 223 | + for i, labels := range tc.labelsToAdd { |
| 224 | + symbols := s.Add(labels) |
| 225 | + result := s.Lookup(symbols, nil) |
| 226 | + require.Equal(t, tc.expectedLabels[i], result, "direct addition: %s", tc.description) |
| 227 | + } |
| 228 | + |
| 229 | + // Test serialization/deserialization via checkpoint |
| 230 | + buf := bytes.NewBuffer(nil) |
| 231 | + _, _, err := s.CheckpointTo(buf) |
| 232 | + require.NoError(t, err) |
| 233 | + |
| 234 | + loaded := symbolizerFromCheckpoint(buf.Bytes()) |
| 235 | + for i, labels := range tc.labelsToAdd { |
| 236 | + symbols := loaded.Add(labels) |
| 237 | + result := loaded.Lookup(symbols, nil) |
| 238 | + require.Equal(t, tc.expectedLabels[i], result, "after checkpoint: %s", tc.description) |
| 239 | + } |
| 240 | + |
| 241 | + // Test serialization/deserialization via compression |
| 242 | + buf.Reset() |
| 243 | + _, _, err = s.SerializeTo(buf, compression.GetWriterPool(compression.Snappy)) |
| 244 | + require.NoError(t, err) |
| 245 | + |
| 246 | + loaded, err = symbolizerFromEnc(buf.Bytes(), compression.GetReaderPool(compression.Snappy)) |
| 247 | + require.NoError(t, err) |
| 248 | + for i, labels := range tc.labelsToAdd { |
| 249 | + symbols := loaded.Add(labels) |
| 250 | + result := loaded.Lookup(symbols, nil) |
| 251 | + require.Equal(t, tc.expectedLabels[i], result, "after compression: %s", tc.description) |
| 252 | + } |
| 253 | + }) |
| 254 | + } |
| 255 | +} |
| 256 | + |
| 257 | +func TestSymbolizerNormalizationCache(t *testing.T) { |
| 258 | + s := newSymbolizer() |
| 259 | + |
| 260 | + // Add a label with a name that needs normalization |
| 261 | + labels1 := labels.Labels{{Name: "foo-bar", Value: "value1"}} |
| 262 | + symbols1 := s.Add(labels1) |
| 263 | + |
| 264 | + // Look up the label multiple times |
| 265 | + for i := 0; i < 3; i++ { |
| 266 | + result := s.Lookup(symbols1, nil) |
| 267 | + require.Equal(t, "foo_bar", result[0].Name, "normalized name should be consistent") |
| 268 | + require.Equal(t, "value1", result[0].Value, "value should remain unchanged") |
| 269 | + } |
| 270 | + |
| 271 | + // Add the same label name with a different value |
| 272 | + labels2 := labels.Labels{{Name: "foo-bar", Value: "value2"}} |
| 273 | + symbols2 := s.Add(labels2) |
| 274 | + |
| 275 | + // The normalized name should be reused |
| 276 | + result := s.Lookup(symbols1, nil) |
| 277 | + firstPtr := unsafe.StringData(result[0].Name) |
| 278 | + result = s.Lookup(symbols2, nil) |
| 279 | + secondPtr := unsafe.StringData(result[0].Name) |
| 280 | + require.Equal(t, firstPtr, secondPtr, "normalized name string data pointers should be identical") |
| 281 | + require.Equal(t, "value2", result[0].Value, "new value should be used") |
| 282 | + |
| 283 | + // Check that we have only one entry in normalizedNames for this label name |
| 284 | + require.Equal(t, 1, len(s.normalizedNames), "should have only one normalized name entry") |
| 285 | +} |
| 286 | + |
| 287 | +func TestSymbolizerLabelNormalizationAfterDeserialization(t *testing.T) { |
| 288 | + s := newSymbolizer() |
| 289 | + |
| 290 | + // Add some labels and serialize them |
| 291 | + originalLabels := labels.Labels{ |
| 292 | + {Name: "foo-bar", Value: "value1"}, |
| 293 | + {Name: "fizz-buzz", Value: "value2"}, |
| 294 | + } |
| 295 | + s.Add(originalLabels) |
| 296 | + |
| 297 | + buf := bytes.NewBuffer(nil) |
| 298 | + _, _, err := s.SerializeTo(buf, compression.GetWriterPool(compression.Snappy)) |
| 299 | + require.NoError(t, err) |
| 300 | + |
| 301 | + // Load the serialized data |
| 302 | + loaded, err := symbolizerFromEnc(buf.Bytes(), compression.GetReaderPool(compression.Snappy)) |
| 303 | + require.NoError(t, err) |
| 304 | + |
| 305 | + // Add new labels with the same names but different values |
| 306 | + newLabels := labels.Labels{ |
| 307 | + {Name: "foo-bar", Value: "new-value1"}, |
| 308 | + {Name: "fizz-buzz", Value: "new-value2"}, |
| 309 | + } |
| 310 | + symbols := loaded.Add(newLabels) |
| 311 | + |
| 312 | + // Check that the normalization is consistent |
| 313 | + result := loaded.Lookup(symbols, nil) |
| 314 | + require.Equal(t, "foo_bar", result[0].Name, "first label should be normalized") |
| 315 | + require.Equal(t, "new-value1", result[0].Value, "first value should be unchanged") |
| 316 | + require.Equal(t, "fizz_buzz", result[1].Name, "second label should be normalized") |
| 317 | + require.Equal(t, "new-value2", result[1].Value, "second value should be unchanged") |
| 318 | +} |
| 319 | + |
| 320 | +func TestSymbolizerLabelNormalizationSameNameValue(t *testing.T) { |
| 321 | + s := newSymbolizer() |
| 322 | + |
| 323 | + // Add labels where the name and value are the same string |
| 324 | + originalLabels := labels.Labels{ |
| 325 | + {Name: "foo-bar", Value: "foo-bar"}, |
| 326 | + {Name: "test-label", Value: "test-label"}, |
| 327 | + } |
| 328 | + originalSymbols := s.Add(originalLabels) |
| 329 | + |
| 330 | + // Verify initial state |
| 331 | + result := s.Lookup(originalSymbols, nil) |
| 332 | + require.Equal(t, "foo_bar", result[0].Name, "name should be normalized") |
| 333 | + require.Equal(t, "foo-bar", result[0].Value, "value should remain unchanged") |
| 334 | + require.Equal(t, "test_label", result[1].Name, "name should be normalized") |
| 335 | + require.Equal(t, "test-label", result[1].Value, "value should remain unchanged") |
| 336 | + |
| 337 | + // Serialize the symbolizer |
| 338 | + buf := bytes.NewBuffer(nil) |
| 339 | + _, _, err := s.SerializeTo(buf, compression.GetWriterPool(compression.Snappy)) |
| 340 | + require.NoError(t, err) |
| 341 | + |
| 342 | + // Load the serialized data |
| 343 | + loaded, err := symbolizerFromEnc(buf.Bytes(), compression.GetReaderPool(compression.Snappy)) |
| 344 | + require.NoError(t, err) |
| 345 | + |
| 346 | + // Look up using the original symbols without re-adding the labels |
| 347 | + result = loaded.Lookup(originalSymbols, nil) |
| 348 | + require.Equal(t, "foo_bar", result[0].Name, "name should be normalized after deserialization") |
| 349 | + require.Equal(t, "foo-bar", result[0].Value, "value should remain unchanged after deserialization") |
| 350 | + require.Equal(t, "test_label", result[1].Name, "name should be normalized after deserialization") |
| 351 | + require.Equal(t, "test-label", result[1].Value, "value should remain unchanged after deserialization") |
| 352 | + |
| 353 | + // Also test with checkpoint serialization |
| 354 | + buf.Reset() |
| 355 | + _, _, err = s.CheckpointTo(buf) |
| 356 | + require.NoError(t, err) |
| 357 | + |
| 358 | + loadedFromCheckpoint := symbolizerFromCheckpoint(buf.Bytes()) |
| 359 | + result = loadedFromCheckpoint.Lookup(originalSymbols, nil) |
| 360 | + require.Equal(t, "foo_bar", result[0].Name, "name should be normalized after checkpoint") |
| 361 | + require.Equal(t, "foo-bar", result[0].Value, "value should remain unchanged after checkpoint") |
| 362 | + require.Equal(t, "test_label", result[1].Name, "name should be normalized after checkpoint") |
| 363 | + require.Equal(t, "test-label", result[1].Value, "value should remain unchanged after checkpoint") |
| 364 | +} |
0 commit comments