Repository: prometheus/prometheus
File: scrape/manager_test.go (Go)

Code before:
	serverURL, err := url.Parse(server.URL)
	require.NoError(t, err)

	// Configuration with both convert_classic_histograms_to_nhcb enabled and CT zero ingestion enabled.
	testConfig := fmt.Sprintf(`
global:
  scrape_interval: 9999m
  scrape_timeout: 5s

scrape_configs:
- job_name: test
  convert_classic_histograms_to_nhcb: true
  static_configs:
  - targets: ['%s']
`, serverURL.Host)

	applyConfig(t, testConfig, scrapeManager, discoveryManager)

	// Wait for scrape to complete successfully.
	ctx, cancel = context.WithTimeout(ctx, 1*time.Minute)
	defer cancel()
	require.NoError(t, runutil.Retry(100*time.Millisecond, ctx.Done(), func() error {
		app.mtx.Lock()
		defer app.mtx.Unlock()

		if len(app.resultHistograms) > 0 {
			return nil
		}
		return errors.New("expected histogram samples, got none")
	}), "after 1 minute")

	// Verify that samples were ingested (proving both features work together).
	app.mtx.Lock()
	defer app.mtx.Unlock()

	var got []histogramSample
	for _, h := range app.resultHistograms {
		if h.metric.Get(model.MetricNameLabel) == mName {
			got = append(got, h)
		}
	}

	// With CT zero ingestion enabled and a created timestamp present, we expect 2 samples:
	// one zero sample and one actual sample.
	require.Len(t, got, 2, "expected 2 histogram samples (zero sample + actual sample)")
	require.Equal(t, histogram.Histogram{}, *got[0].h, "first sample should be zero sample")
	require.NotEqual(t, 0.0, got[1].h.Sum, "second sample should have non-zero sum")

	// The test successfully completes, proving that both ConvertClassicHistogramsToNHCBEnabled
	// and EnableCreatedTimestampZeroIngestion can work together without the error that was
	// previously thrown. The original issue #15137 about losing exemplars during CT parsing

Review comment (refactor):
This isn't future proof I think, let's add a helper function `getMatchingHistograms` to get the samples where `h.metric.Get(model.MetricNameLabel) == mName` and stop waiting when their number is > 1.

That will avoid race between exiting the loop and still appending results of the scrape.

Code after:
		}),
	)
	defer server.Close()

	serverURL, err := url.Parse(server.URL)
	require.NoError(t, err)

	// Configuration with both convert_classic_histograms_to_nhcb enabled and CT zero ingestion enabled.
	testConfig := fmt.Sprintf(`
global:
  # Use a very long scrape_interval to prevent automatic scraping during the test.
  scrape_interval: 9999m
  scrape_timeout: 5s

scrape_configs:
- job_name: test
  convert_classic_histograms_to_nhcb: true
  static_configs:
  - targets: ['%s']
`, serverURL.Host)

	applyConfig(t, testConfig, scrapeManager, discoveryManager)

	// Verify that the scrape pool was created (proves the blocking check was removed).
	require.Eventually(t, func() bool {
		scrapeManager.mtxScrape.Lock()
		defer scrapeManager.mtxScrape.Unlock()
		_, exists := scrapeManager.scrapePools["test"]
		return exists
	}, 5*time.Second, 100*time.Millisecond, "scrape pool should be created for job 'test'")

	// Helper function to get matching histograms to avoid race conditions.
	getMatchingHistograms := func() []histogramSample {
		app.mtx.Lock()
		defer app.mtx.Unlock()

		var got []histogramSample
		for _, h := range app.resultHistograms {
			if h.metric.Get(model.MetricNameLabel) == mName {
				got = append(got, h)
			}
		}
		return got
	}

	require.Eventually(t, func() bool {
		return len(getMatchingHistograms()) > 0
	}, 1*time.Minute, 100*time.Millisecond, "expected histogram samples, got none")

	// Verify that samples were ingested (proving both features work together).
	got := getMatchingHistograms()