diff --git a/x-pack/plugin/core/template-resources/src/main/resources/profiling/component-template/profiling-events.json b/x-pack/plugin/core/template-resources/src/main/resources/profiling/component-template/profiling-events.json index bdd38876aea0d..2336461febcb1 100644 --- a/x-pack/plugin/core/template-resources/src/main/resources/profiling/component-template/profiling-events.json +++ b/x-pack/plugin/core/template-resources/src/main/resources/profiling/component-template/profiling-events.json @@ -76,6 +76,10 @@ "type": "short", "index": false }, + "Stacktrace.sampling_frequency": { + "type": "long", + "index": false + }, "agent.version": { "type": "keyword" }, diff --git a/x-pack/plugin/profiling/src/internalClusterTest/java/org/elasticsearch/xpack/profiling/action/GetStackTracesActionIT.java b/x-pack/plugin/profiling/src/internalClusterTest/java/org/elasticsearch/xpack/profiling/action/GetStackTracesActionIT.java index 7983543eece67..35b2f7a24d567 100644 --- a/x-pack/plugin/profiling/src/internalClusterTest/java/org/elasticsearch/xpack/profiling/action/GetStackTracesActionIT.java +++ b/x-pack/plugin/profiling/src/internalClusterTest/java/org/elasticsearch/xpack/profiling/action/GetStackTracesActionIT.java @@ -40,7 +40,13 @@ public void testGetStackTracesUnfiltered() throws Exception { Map traceEvents = response.getStackTraceEvents(); - TraceEventID traceEventID = new TraceEventID("", "497295213074376", "8457605156473051743", "L7kj7UvlKbT-vN73el4faQ"); + TraceEventID traceEventID = new TraceEventID( + "", + "497295213074376", + "8457605156473051743", + "L7kj7UvlKbT-vN73el4faQ", + TransportGetStackTracesAction.DEFAULT_SAMPLING_FREQUENCY + ); assertEquals(3L, response.getStackTraceEvents().get(traceEventID).count); assertNotNull(response.getStackTraces()); @@ -84,7 +90,13 @@ public void testGetStackTracesGroupedByServiceName() throws Exception { assertNotNull(response.getStackTraceEvents()); - TraceEventID traceEventID = new TraceEventID("", "497295213074376", "8457605156473051743", "L7kj7UvlKbT-vN73el4faQ"); + TraceEventID traceEventID = new TraceEventID( + "", + "497295213074376", + "8457605156473051743", + "L7kj7UvlKbT-vN73el4faQ", + TransportGetStackTracesAction.DEFAULT_SAMPLING_FREQUENCY + ); assertEquals(3L, response.getStackTraceEvents().get(traceEventID).count); assertEquals(Long.valueOf(2L), response.getStackTraceEvents().get(traceEventID).subGroups.getCount("basket")); @@ -131,11 +143,17 @@ public void testGetStackTracesFromAPMWithMatchNoDownsampling() throws Exception assertNotNull(response.getStackTraceEvents()); - TraceEventID traceEventID = new TraceEventID("", "", "", "Ce77w10WeIDow3kd1jowlA"); + TraceEventID traceEventID = new TraceEventID( + "", + "", + "", + "Ce77w10WeIDow3kd1jowlA", + TransportGetStackTracesAction.DEFAULT_SAMPLING_FREQUENCY + ); assertEquals(3L, response.getStackTraceEvents().get(traceEventID).count); assertEquals(Long.valueOf(3L), response.getStackTraceEvents().get(traceEventID).subGroups.getCount("encodeSha1")); - traceEventID = new TraceEventID("", "", "", "JvISdnJ47BQ01489cwF9DA"); + traceEventID = new TraceEventID("", "", "", "JvISdnJ47BQ01489cwF9DA", TransportGetStackTracesAction.DEFAULT_SAMPLING_FREQUENCY); assertEquals(2L, response.getStackTraceEvents().get(traceEventID).count); assertNotNull(response.getStackTraces()); @@ -182,10 +200,16 @@ public void testGetStackTracesFromAPMWithMatchAndDownsampling() throws Exception assertNotNull(response.getStackTraceEvents()); // as the sampling rate is 0.2, we see 5 times more samples (random sampler agg automatically adjusts sample count) - TraceEventID traceEventID = new TraceEventID("", "", "", "Ce77w10WeIDow3kd1jowlA"); + TraceEventID traceEventID = new TraceEventID( + "", + "", + "", + "Ce77w10WeIDow3kd1jowlA", + TransportGetStackTracesAction.DEFAULT_SAMPLING_FREQUENCY + ); assertEquals(5 * 3L, response.getStackTraceEvents().get(traceEventID).count); - traceEventID = new TraceEventID("", "", "", "JvISdnJ47BQ01489cwF9DA"); + traceEventID = new TraceEventID("", "", "", "JvISdnJ47BQ01489cwF9DA", TransportGetStackTracesAction.DEFAULT_SAMPLING_FREQUENCY); assertEquals(5 * 2L, response.getStackTraceEvents().get(traceEventID).count); assertNotNull(response.getStackTraces()); diff --git a/x-pack/plugin/profiling/src/main/java/org/elasticsearch/xpack/profiling/action/CO2Calculator.java b/x-pack/plugin/profiling/src/main/java/org/elasticsearch/xpack/profiling/action/CO2Calculator.java index 1155982fa810d..590e411e5e5a7 100644 --- a/x-pack/plugin/profiling/src/main/java/org/elasticsearch/xpack/profiling/action/CO2Calculator.java +++ b/x-pack/plugin/profiling/src/main/java/org/elasticsearch/xpack/profiling/action/CO2Calculator.java @@ -12,7 +12,6 @@ import java.util.Map; final class CO2Calculator { - private static final double DEFAULT_SAMPLING_FREQUENCY = 19.0d; private static final double DEFAULT_CO2_TONS_PER_KWH = 0.000379069d; // unit: metric tons / kWh private static final double DEFAULT_KILOWATTS_PER_CORE_X86 = 7.0d / 1000.0d; // unit: watt / core private static final double DEFAULT_KILOWATTS_PER_CORE_ARM64 = 2.8d / 1000.0d; // unit: watt / core @@ -43,8 +42,8 @@ final class CO2Calculator { : customPerCoreWattARM64 / 1000.0d; } - public double getAnnualCO2Tons(String hostID, long samples) { - double annualCoreHours = CostCalculator.annualCoreHours(samplingDurationInSeconds, samples, DEFAULT_SAMPLING_FREQUENCY); + public double getAnnualCO2Tons(String hostID, long samples, double samplingFrequency) { + double annualCoreHours = CostCalculator.annualCoreHours(samplingDurationInSeconds, samples, samplingFrequency); HostMetadata host = hostMetadata.get(hostID); if (host == null) { diff --git a/x-pack/plugin/profiling/src/main/java/org/elasticsearch/xpack/profiling/action/CostCalculator.java b/x-pack/plugin/profiling/src/main/java/org/elasticsearch/xpack/profiling/action/CostCalculator.java index 05b51adb6a52f..75f10b73f3c63 100644 --- a/x-pack/plugin/profiling/src/main/java/org/elasticsearch/xpack/profiling/action/CostCalculator.java +++ b/x-pack/plugin/profiling/src/main/java/org/elasticsearch/xpack/profiling/action/CostCalculator.java @@ -10,7 +10,6 @@ import java.util.Map; final class CostCalculator { - private static final double DEFAULT_SAMPLING_FREQUENCY = 19.0d; private static final double SECONDS_PER_HOUR = 60 * 60; private static final double SECONDS_PER_YEAR = SECONDS_PER_HOUR * 24 * 365.0d; // unit: seconds public static final double DEFAULT_COST_USD_PER_CORE_HOUR = 0.0425d; // unit: USD / (core * hour) @@ -40,8 +39,8 @@ final class CostCalculator { ); } - public double annualCostsUSD(String hostID, double samples) { - double annualCoreHours = annualCoreHours(samplingDurationInSeconds, samples, DEFAULT_SAMPLING_FREQUENCY); + public double annualCostsUSD(String hostID, double samples, double samplingFrequency) { + double annualCoreHours = annualCoreHours(samplingDurationInSeconds, samples, samplingFrequency); HostMetadata host = hostMetadata.get(hostID); if (host == null) { @@ -59,7 +58,6 @@ public double annualCostsUSD(String hostID, double samples) { } public static double annualCoreHours(double duration, double samples, double samplingFrequency) { - // samplingFrequency will a variable value when we start supporting probabilistic profiling (soon). return (SECONDS_PER_YEAR / duration * samples / samplingFrequency) / SECONDS_PER_HOUR; // unit: core * hour } } diff --git a/x-pack/plugin/profiling/src/main/java/org/elasticsearch/xpack/profiling/action/Resampler.java b/x-pack/plugin/profiling/src/main/java/org/elasticsearch/xpack/profiling/action/Resampler.java index 54401ce1d3a5a..c8533e37ef77f 100644 --- a/x-pack/plugin/profiling/src/main/java/org/elasticsearch/xpack/profiling/action/Resampler.java +++ b/x-pack/plugin/profiling/src/main/java/org/elasticsearch/xpack/profiling/action/Resampler.java @@ -14,7 +14,7 @@ class Resampler { private final boolean requiresResampling; private final RandomGenerator r; private final double adjustedSampleRate; - private final double p; + public final double p; Resampler(GetStackTracesRequest request, double sampleRate, long totalCount) { // Manually reduce sample count if totalCount exceeds sampleSize by 10%. @@ -50,7 +50,7 @@ public int adjustSampleCount(int originalCount) { } // Adjust the sample counts from down-sampled to fully sampled. // Be aware that downsampling drops entries from stackTraceEvents, so that - // the sum of the upscaled count values is less that totalCount. - return (int) Math.floor(rawCount / (p * adjustedSampleRate)); + // the sum of the upscaled count values is less than totalCount. + return (int) Math.round(rawCount / (p * adjustedSampleRate)); } } diff --git a/x-pack/plugin/profiling/src/main/java/org/elasticsearch/xpack/profiling/action/TraceEventID.java b/x-pack/plugin/profiling/src/main/java/org/elasticsearch/xpack/profiling/action/TraceEventID.java index c030d4f4561f1..562947a7b0d3b 100644 --- a/x-pack/plugin/profiling/src/main/java/org/elasticsearch/xpack/profiling/action/TraceEventID.java +++ b/x-pack/plugin/profiling/src/main/java/org/elasticsearch/xpack/profiling/action/TraceEventID.java @@ -7,4 +7,4 @@ package org.elasticsearch.xpack.profiling.action; -record TraceEventID(String executableName, String threadName, String hostID, String stacktraceID) {} +record TraceEventID(String executableName, String threadName, String hostID, String stacktraceID, double samplingFrequency) {} diff --git a/x-pack/plugin/profiling/src/main/java/org/elasticsearch/xpack/profiling/action/TransportGetStackTracesAction.java b/x-pack/plugin/profiling/src/main/java/org/elasticsearch/xpack/profiling/action/TransportGetStackTracesAction.java index a7c496b794f23..3ab30a9f3342d 100644 --- a/x-pack/plugin/profiling/src/main/java/org/elasticsearch/xpack/profiling/action/TransportGetStackTracesAction.java +++ b/x-pack/plugin/profiling/src/main/java/org/elasticsearch/xpack/profiling/action/TransportGetStackTracesAction.java @@ -115,6 +115,12 @@ public class TransportGetStackTracesAction extends TransportAction submitListener, GetStackTracesResponseBuilder responseBuilder ) { - CountedTermsAggregationBuilder groupByStackTraceId = new CountedTermsAggregationBuilder("group_by").size( MAX_TRACE_EVENTS_RESULT_SIZE ).field(request.getStackTraceIdsField()); @@ -286,7 +291,7 @@ private void searchGenericEventGroupedByStackTrace( String stackTraceID = stacktraceBucket.getKeyAsString(); - TraceEventID eventID = new TraceEventID("", "", "", stackTraceID); + TraceEventID eventID = new TraceEventID("", "", "", stackTraceID, DEFAULT_SAMPLING_FREQUENCY); TraceEvent event = stackTraceEvents.computeIfAbsent(eventID, k -> new TraceEvent()); event.count += count; subGroups.collectResults(stacktraceBucket, event); @@ -337,6 +342,16 @@ private void searchEventGroupedByStackTrace( // Especially with high cardinality fields, this makes aggregations really slow. .executionHint("map") .subAggregation(groupByHostId); + TermsAggregationBuilder groupByExecutableName = new TermsAggregationBuilder("group_by") + // 'size' specifies the max number of host ID we support per request. + .size(MAX_TRACE_EVENTS_RESULT_SIZE) + .field("process.executable.name") + // missing("") is used to include documents where the field is missing. + .missing("") + // 'execution_hint: map' skips the slow building of ordinals that we don't need. + // Especially with high cardinality fields, this makes aggregations really slow. + .executionHint("map") + .subAggregation(groupByThreadName); SubGroupCollector subGroups = SubGroupCollector.attach(groupByStackTraceId, request.getAggregationFields()); client.prepareSearch(eventsIndex.getName()) .setTrackTotalHits(false) @@ -351,17 +366,34 @@ private void searchEventGroupedByStackTrace( new TermsAggregationBuilder("group_by") // 'size' specifies the max number of host ID we support per request. .size(MAX_TRACE_EVENTS_RESULT_SIZE) - .field("process.executable.name") - // missing("") is used to include documents where the field is missing. - .missing("") + .field("Stacktrace.sampling_frequency") + // missing(DEFAULT_SAMPLING_RATE) is used to include documents where the field is missing. + .missing((long) DEFAULT_SAMPLING_FREQUENCY) // 'execution_hint: map' skips the slow building of ordinals that we don't need. // Especially with high cardinality fields, this makes aggregations really slow. .executionHint("map") - .subAggregation(groupByThreadName) + .subAggregation(groupByExecutableName) + .subAggregation(new SumAggregationBuilder("total_count").field("Stacktrace.count")) ) .addAggregation(new SumAggregationBuilder("total_count").field("Stacktrace.count")) .execute(handleEventsGroupedByStackTrace(submitTask, client, responseBuilder, submitListener, searchResponse -> { - long totalCount = getAggValueAsLong(searchResponse, "total_count"); + long maxSamplingFrequency = 0; + + Terms samplingFrequencies = searchResponse.getAggregations().get("group_by"); + for (Terms.Bucket samplingFrequencyBucket : samplingFrequencies.getBuckets()) { + final double samplingFrequency = samplingFrequencyBucket.getKeyAsNumber().doubleValue(); + if (samplingFrequency > maxSamplingFrequency) { + maxSamplingFrequency = (long) samplingFrequency; + } + } + + long totalCount = 0; + for (Terms.Bucket samplingFrequencyBucket : samplingFrequencies.getBuckets()) { + InternalNumericMetricsAggregation.SingleValue count = samplingFrequencyBucket.getAggregations().get("total_count"); + final double samplingFrequency = samplingFrequencyBucket.getKeyAsNumber().doubleValue(); + final double samplingFactor = maxSamplingFrequency / samplingFrequency; + totalCount += Math.round(count.value() * samplingFactor); + } Resampler resampler = new Resampler(request, responseBuilder.getSamplingRate(), totalCount); @@ -371,33 +403,49 @@ private void searchEventGroupedByStackTrace( long totalFinalCount = 0; Map stackTraceEvents = new HashMap<>(MAX_TRACE_EVENTS_RESULT_SIZE); - Terms executableNames = searchResponse.getAggregations().get("group_by"); - for (Terms.Bucket executableBucket : executableNames.getBuckets()) { - String executableName = executableBucket.getKeyAsString(); - - Terms threads = executableBucket.getAggregations().get("group_by"); - for (Terms.Bucket threadBucket : threads.getBuckets()) { - String threadName = threadBucket.getKeyAsString(); - - Terms hosts = threadBucket.getAggregations().get("group_by"); - for (Terms.Bucket hostBucket : hosts.getBuckets()) { - String hostID = hostBucket.getKeyAsString(); - - Terms stacktraces = hostBucket.getAggregations().get("group_by"); - for (Terms.Bucket stacktraceBucket : stacktraces.getBuckets()) { - Sum count = stacktraceBucket.getAggregations().get("count"); - int finalCount = resampler.adjustSampleCount((int) count.value()); - if (finalCount <= 0) { - continue; + for (Terms.Bucket samplingFrequencyBucket : samplingFrequencies.getBuckets()) { + log.debug( + "Using sampling frequency [{}] for [{}] stacktrace events.", + samplingFrequencyBucket.getKeyAsString(), + totalCount + ); + final double samplingFrequency = samplingFrequencyBucket.getKeyAsNumber().doubleValue(); + final double samplingFactor = maxSamplingFrequency / samplingFrequency; + + Terms executableNames = samplingFrequencyBucket.getAggregations().get("group_by"); + for (Terms.Bucket executableBucket : executableNames.getBuckets()) { + String executableName = executableBucket.getKeyAsString(); + + Terms threads = executableBucket.getAggregations().get("group_by"); + for (Terms.Bucket threadBucket : threads.getBuckets()) { + String threadName = threadBucket.getKeyAsString(); + + Terms hosts = threadBucket.getAggregations().get("group_by"); + for (Terms.Bucket hostBucket : hosts.getBuckets()) { + String hostID = hostBucket.getKeyAsString(); + + Terms stacktraces = hostBucket.getAggregations().get("group_by"); + for (Terms.Bucket stacktraceBucket : stacktraces.getBuckets()) { + Sum count = stacktraceBucket.getAggregations().get("count"); + int finalCount = resampler.adjustSampleCount((int) Math.round(count.value() * samplingFactor)); + if (finalCount <= 0) { + continue; + } + + totalFinalCount += finalCount; + + String stackTraceID = stacktraceBucket.getKeyAsString(); + TraceEventID eventID = new TraceEventID( + executableName, + threadName, + hostID, + stackTraceID, + maxSamplingFrequency + ); + TraceEvent event = stackTraceEvents.computeIfAbsent(eventID, k -> new TraceEvent()); + event.count += finalCount; + subGroups.collectResults(stacktraceBucket, event); } - totalFinalCount += finalCount; - - String stackTraceID = stacktraceBucket.getKeyAsString(); - - TraceEventID eventID = new TraceEventID(executableName, threadName, hostID, stackTraceID); - TraceEvent event = stackTraceEvents.computeIfAbsent(eventID, k -> new TraceEvent()); - event.count += finalCount; - subGroups.collectResults(stacktraceBucket, event); } } } @@ -629,8 +677,8 @@ public void calculateCO2AndCosts() { ); responseBuilder.getStackTraceEvents().forEach((eventId, event) -> { - event.annualCO2Tons += co2Calculator.getAnnualCO2Tons(eventId.hostID(), event.count); - event.annualCostsUSD += costCalculator.annualCostsUSD(eventId.hostID(), event.count); + event.annualCO2Tons += co2Calculator.getAnnualCO2Tons(eventId.hostID(), event.count, eventId.samplingFrequency()); + event.annualCostsUSD += costCalculator.annualCostsUSD(eventId.hostID(), event.count, eventId.samplingFrequency()); }); log.debug(watch::report); diff --git a/x-pack/plugin/profiling/src/test/java/org/elasticsearch/xpack/profiling/action/CO2CalculatorTests.java b/x-pack/plugin/profiling/src/test/java/org/elasticsearch/xpack/profiling/action/CO2CalculatorTests.java index 9be98fbe4f46b..1f9ee8348a647 100644 --- a/x-pack/plugin/profiling/src/test/java/org/elasticsearch/xpack/profiling/action/CO2CalculatorTests.java +++ b/x-pack/plugin/profiling/src/test/java/org/elasticsearch/xpack/profiling/action/CO2CalculatorTests.java @@ -73,13 +73,20 @@ public void testCreateFromRegularSource() { double samplingDurationInSeconds = 1_800.0d; // 30 minutes long samples = 100_000L; // 100k samples - double annualCoreHours = CostCalculator.annualCoreHours(samplingDurationInSeconds, samples, 19.0d); - CO2Calculator co2Calculator = new CO2Calculator(hostsTable, samplingDurationInSeconds, null, null, null, null); + double defaultFreq = TransportGetStackTracesAction.DEFAULT_SAMPLING_FREQUENCY; - checkCO2Calculation(co2Calculator.getAnnualCO2Tons(HOST_ID_A, samples), annualCoreHours, 1.135d, 0.0002786d, 7.0d); - checkCO2Calculation(co2Calculator.getAnnualCO2Tons(HOST_ID_B, samples), annualCoreHours, 1.1d, 0.0000198d, 7.0d); - checkCO2Calculation(co2Calculator.getAnnualCO2Tons(HOST_ID_C, samples), annualCoreHours, 1.185d, 0.000410608d, 2.8d); - checkCO2Calculation(co2Calculator.getAnnualCO2Tons(HOST_ID_D, samples), annualCoreHours, 1.7d, 0.000379069d, 2.8d); + // Plausibility check with different frequency factors. + // 10x higher frequency means 10x less annualCoreHours with the same number of samples, + // and also 10x lower co2 emission. + for (double freq : new double[] { defaultFreq, defaultFreq * 10 }) { + double annualCoreHours = CostCalculator.annualCoreHours(samplingDurationInSeconds, samples, freq); + CO2Calculator co2Calculator = new CO2Calculator(hostsTable, samplingDurationInSeconds, null, null, null, null); + + checkCO2Calculation(co2Calculator.getAnnualCO2Tons(HOST_ID_A, samples, freq), annualCoreHours, 1.135d, 0.0002786d, 7.0d); + checkCO2Calculation(co2Calculator.getAnnualCO2Tons(HOST_ID_B, samples, freq), annualCoreHours, 1.1d, 0.0000198d, 7.0d); + checkCO2Calculation(co2Calculator.getAnnualCO2Tons(HOST_ID_C, samples, freq), annualCoreHours, 1.185d, 0.000410608d, 2.8d); + checkCO2Calculation(co2Calculator.getAnnualCO2Tons(HOST_ID_D, samples, freq), annualCoreHours, 1.7d, 0.000379069d, 2.8d); + } } // Make sure that malformed data doesn't cause the CO2 calculation to fail. @@ -110,11 +117,18 @@ public void testCreateFromMalformedSource() { double samplingDurationInSeconds = 1_800.0d; // 30 minutes long samples = 100_000L; // 100k samples - double annualCoreHours = CostCalculator.annualCoreHours(samplingDurationInSeconds, samples, 19.0d); - CO2Calculator co2Calculator = new CO2Calculator(hostsTable, samplingDurationInSeconds, null, null, null, null); + double defaultFreq = TransportGetStackTracesAction.DEFAULT_SAMPLING_FREQUENCY; + + // Plausibility check with different frequency factors. + // 10x higher frequency means 10x less annualCoreHours with the same number of samples, + // and also 10x lower co2 emission. + for (double freq : new double[] { defaultFreq, defaultFreq * 10 }) { + double annualCoreHours = CostCalculator.annualCoreHours(samplingDurationInSeconds, samples, freq); + CO2Calculator co2Calculator = new CO2Calculator(hostsTable, samplingDurationInSeconds, null, null, null, null); - checkCO2Calculation(co2Calculator.getAnnualCO2Tons(HOST_ID_A, samples), annualCoreHours, 1.135d, 0.0002786d, 7.0d); - checkCO2Calculation(co2Calculator.getAnnualCO2Tons(HOST_ID_B, samples), annualCoreHours, 1.7d, 0.000379069d, 7.0d); + checkCO2Calculation(co2Calculator.getAnnualCO2Tons(HOST_ID_A, samples, freq), annualCoreHours, 1.135d, 0.0002786d, 7.0d); + checkCO2Calculation(co2Calculator.getAnnualCO2Tons(HOST_ID_B, samples, freq), annualCoreHours, 1.7d, 0.000379069d, 7.0d); + } } private void checkCO2Calculation( diff --git a/x-pack/plugin/profiling/src/test/java/org/elasticsearch/xpack/profiling/action/CostCalculatorTests.java b/x-pack/plugin/profiling/src/test/java/org/elasticsearch/xpack/profiling/action/CostCalculatorTests.java index 1c719c97164dc..7f9fa55475fed 100644 --- a/x-pack/plugin/profiling/src/test/java/org/elasticsearch/xpack/profiling/action/CostCalculatorTests.java +++ b/x-pack/plugin/profiling/src/test/java/org/elasticsearch/xpack/profiling/action/CostCalculatorTests.java @@ -63,24 +63,37 @@ public void testCreateFromRegularSource() { double samplingDurationInSeconds = 1_800.0d; // 30 minutes long samples = 100_000L; // 100k samples - double annualCoreHours = CostCalculator.annualCoreHours(samplingDurationInSeconds, samples, 19.0d); - CostCalculator costCalculator = new CostCalculator(hostsTable, samplingDurationInSeconds, null, null, null); + double defaultFreq = TransportGetStackTracesAction.DEFAULT_SAMPLING_FREQUENCY; - // Checks whether the cost calculation is based on the lookup data. - // The usd_per_hour value can be looked up from profiling-costs-aws.json.gz. - checkCostCalculation(costCalculator.annualCostsUSD(HOST_ID_AWS, samples), annualCoreHours, 0.244d, HOST_ID_A_NUM_CORES); + // Plausibility check with different frequency factors. + // 10x higher frequency means 10x less annualCoreHours with the same number of samples, + // and also 10x lower cost. + for (double freq : new double[] { defaultFreq, defaultFreq * 10 }) { + // Calculate the annual core hours based on the sampling duration, samples, and frequency. + double annualCoreHours = CostCalculator.annualCoreHours(samplingDurationInSeconds, samples, freq); - // Checks whether the cost calculation is based on the lookup data. - // The usd_per_hour value can be looked up from profiling-costs-azure.json.gz. - checkCostCalculation(costCalculator.annualCostsUSD(HOST_ID_AZURE, samples), annualCoreHours, 0.192d, HOST_ID_A_NUM_CORES); + // Create a new CostCalculator instance with the hosts table and sampling duration. + CostCalculator costCalculator = new CostCalculator(hostsTable, samplingDurationInSeconds, null, null, null); - // Checks whether the cost calculation is based on the default values. - checkCostCalculation( - costCalculator.annualCostsUSD(HOST_ID_UNKNOWN, samples), - annualCoreHours, - CostCalculator.DEFAULT_COST_USD_PER_CORE_HOUR * HostMetadata.DEFAULT_PROFILING_NUM_CORES, - HostMetadata.DEFAULT_PROFILING_NUM_CORES - ); + // Check the cost calculation for AWS host ID. + checkCostCalculation(costCalculator.annualCostsUSD(HOST_ID_AWS, samples, freq), annualCoreHours, 0.244d, HOST_ID_A_NUM_CORES); + + // Check the cost calculation for Azure host ID. + checkCostCalculation( + costCalculator.annualCostsUSD(HOST_ID_AZURE, samples, freq), + annualCoreHours, + 0.192d, + HOST_ID_AZURE_NUM_CORES + ); + + // Check the cost calculation for unknown host ID. + checkCostCalculation( + costCalculator.annualCostsUSD(HOST_ID_UNKNOWN, samples, freq), + annualCoreHours, + CostCalculator.DEFAULT_COST_USD_PER_CORE_HOUR * HostMetadata.DEFAULT_PROFILING_NUM_CORES, + HostMetadata.DEFAULT_PROFILING_NUM_CORES + ); + } } private void checkCostCalculation(double calculatedAnnualCostsUSD, double annualCoreHours, double usd_per_hour, int profilingNumCores) { diff --git a/x-pack/plugin/profiling/src/test/java/org/elasticsearch/xpack/profiling/action/GetStackTracesResponseTests.java b/x-pack/plugin/profiling/src/test/java/org/elasticsearch/xpack/profiling/action/GetStackTracesResponseTests.java index 1d71802a86f44..c8342f82edb11 100644 --- a/x-pack/plugin/profiling/src/test/java/org/elasticsearch/xpack/profiling/action/GetStackTracesResponseTests.java +++ b/x-pack/plugin/profiling/src/test/java/org/elasticsearch/xpack/profiling/action/GetStackTracesResponseTests.java @@ -47,7 +47,10 @@ private GetStackTracesResponse createTestInstance() { long totalSamples = randomLongBetween(1L, 200L); String stackTraceID = randomAlphaOfLength(12); Map stackTraceEvents = randomNullable( - Map.of(new TraceEventID("", "", "", stackTraceID), new TraceEvent(totalSamples)) + Map.of( + new TraceEventID("", "", "", stackTraceID, TransportGetStackTracesAction.DEFAULT_SAMPLING_FREQUENCY), + new TraceEvent(totalSamples) + ) ); return new GetStackTracesResponse(stackTraces, stackFrames, executables, stackTraceEvents, totalFrames, 1.0, totalSamples); diff --git a/x-pack/plugin/profiling/src/test/java/org/elasticsearch/xpack/profiling/action/TransportGetFlamegraphActionTests.java b/x-pack/plugin/profiling/src/test/java/org/elasticsearch/xpack/profiling/action/TransportGetFlamegraphActionTests.java index 8c55d8456160a..9b400f9669ad5 100644 --- a/x-pack/plugin/profiling/src/test/java/org/elasticsearch/xpack/profiling/action/TransportGetFlamegraphActionTests.java +++ b/x-pack/plugin/profiling/src/test/java/org/elasticsearch/xpack/profiling/action/TransportGetFlamegraphActionTests.java @@ -48,7 +48,10 @@ public void testCreateFlamegraph() { ), Map.of(), Map.of("fr28zxcZ2UDasxYuu6dV-w", "containerd"), - Map.of(new TraceEventID("", "", "", "2buqP1GpF-TXYmL4USW8gA"), traceEvent), + Map.of( + new TraceEventID("", "", "", "2buqP1GpF-TXYmL4USW8gA", TransportGetStackTracesAction.DEFAULT_SAMPLING_FREQUENCY), + traceEvent + ), 9, 1.0d, 1 diff --git a/x-pack/plugin/profiling/src/test/java/org/elasticsearch/xpack/profiling/action/TransportGetTopNFunctionsActionTests.java b/x-pack/plugin/profiling/src/test/java/org/elasticsearch/xpack/profiling/action/TransportGetTopNFunctionsActionTests.java index 49311c0719222..d0d72d05a1d1c 100644 --- a/x-pack/plugin/profiling/src/test/java/org/elasticsearch/xpack/profiling/action/TransportGetTopNFunctionsActionTests.java +++ b/x-pack/plugin/profiling/src/test/java/org/elasticsearch/xpack/profiling/action/TransportGetTopNFunctionsActionTests.java @@ -48,7 +48,10 @@ public void testCreateAllTopNFunctions() { ), Map.of(), Map.of("fr28zxcZ2UDasxYuu6dV-w", "containerd"), - Map.of(new TraceEventID("", "", "", "2buqP1GpF-TXYmL4USW8gA"), traceEvent), + Map.of( + new TraceEventID("", "", "", "2buqP1GpF-TXYmL4USW8gA", TransportGetStackTracesAction.DEFAULT_SAMPLING_FREQUENCY), + traceEvent + ), 9, 1.0d, 1 @@ -114,7 +117,10 @@ public void testCreateTopNFunctionsWithLimit() { ), Map.of(), Map.of("fr28zxcZ2UDasxYuu6dV-w", "containerd"), - Map.of(new TraceEventID("", "", "", "2buqP1GpF-TXYmL4USW8gA"), traceEvent), + Map.of( + new TraceEventID("", "", "", "2buqP1GpF-TXYmL4USW8gA", TransportGetStackTracesAction.DEFAULT_SAMPLING_FREQUENCY), + traceEvent + ), 9, 1.0d, 1