Repository: DataDog/datadog-agent
Pull request #36927: [EBPF] kmt: fix timeouts in complexity generation job
Author: login: gjulianm
Labels: name: changelog/no-changelog
description: No changelog entry needed
name: team/ebpf-platform
name: qa/done
description: QA done before merge and regressions are covered by tests
name: medium review
description: PR review might take time
name: ask-review
description: Ask required teams to review this PR
Created: 2025-05-13T15:37:21Z | Merged: 2025-05-21T16:38:04Z

<!--
* Contributors are encouraged to read our [CONTRIBUTING](/CONTRIBUTING.md) documentation.
* Both Contributor and Reviewer Checklists are available at https://datadoghq.dev/datadog-agent/guidelines/contributing/#pull-requests.
* The pull request:
  * Should only fix one issue or add one feature at a time.
  * Must update the test suite for the relevant functionality.
  * Should pass all status checks before being reviewed or merged.
* Commit titles should be prefixed with general area of pull request's change.
* Please fill the below sections if possible with relevant information or links.
-->
### What does this PR do?

This PR adds filter to the `download_complexity_data` KMT task so that it only downloads data from jobs with complexity collection enabled. This avoids an issue we've seen where downloading artifacts for files that don't exist take a long time (100 seconds) and cause job timeouts.

In order to avoid duplicating in the Python code the definition of which jobs have complexity collection enabled, the code parses the `.gitlab-ci.yml` configuration and grabs the requirements for the `notify_ebpf_complexity_changes` job.

This PR also adds timing data for each download, so that we can debug the issue if it happens again.

### Motivation

Avoid timeouts in complexity collection.

### Describe how you validated your changes
<!--
Validate your changes before merge, ensuring that:
* Your PR is tested by static / unit / integrations / e2e tests
* Your PR description details which e2e tests cover your changes, if any
* The PR description contains details of how you validated your changes. If you validated changes manually and not through automated tests, add context on why automated tests did not fit your changes validation.

If you want additional validation by a second person, you can ask reviewers to do it. Describe how to set up an environment for manual tests in the PR description. Manual validation is expected to happen on every commit before merge.

Any manual validation step should then map to an automated test. Manual validation should not substitute automation, minus exceptions not supported by test tooling yet.
-->

CI job passing and running below the timeout (8 minutes): https://gitlab.ddbuild.io/DataDog/datadog-agent/-/jobs/941537682

### Possible Drawbacks / Trade-offs

### Additional Notes
<!--
* Anything else we should know when reviewing?
* Include benchmarking information here whenever possible.
* Include info about alternatives that were considered and why the proposed
  version was chosen.
-->


Diff:
diff --git a/.gitlab/kernel_matrix_testing/common.yml b/.gitlab/kernel_matrix_testing/common.yml
index b2a1e17cc4ce..01f6a935e517 100644
--- a/.gitlab/kernel_matrix_testing/common.yml
+++ b/.gitlab/kernel_matrix_testing/common.yml
@@ -334,10 +334,10 @@ notify_ebpf_complexity_changes:
               - suse_12.5
               - fedora_38
             TEST_SET: no_usm
+    - job: compute_gitlab_ci_config
   before_script:
     - python3 -m dda self dep sync -f legacy-tasks
     - !reference [.setup_agent_github_app]
     - DD_API_KEY=$($CI_PROJECT_DIR/tools/ci/fetch_secret.sh $AGENT_API_KEY_ORG2 token) || exit $?; export DD_API_KEY
-    - GITLAB_TOKEN=$($CI_PROJECT_DIR/tools/ci/fetch_secret.sh $GITLAB_TOKEN read_api) || exit $?; export GITLAB_TOKEN
   script:
-    - dda inv -- -e ebpf.generate-complexity-summary-for-pr
+    - dda inv -- -e ebpf.generate-complexity-summary-for-pr --gitlab-config-file artifacts/after.gitlab-ci.yml
diff --git a/tasks/ebpf.py b/tasks/ebpf.py
index de1d72221a59..bc686920212c 100644
--- a/tasks/ebpf.py
+++ b/tasks/ebpf.py
@@ -694,10 +694,15 @@ def _add_measures_to_complexity_job(ctx: Context, measures: dict[str, int | floa
         "skip_github_comment": "Do not comment on the PR with the complexity summary",
         "branch_name": "Branch name to use for the complexity data. By default, the current branch is used",
         "base_branch": "Base branch to compare against. If not provided, we will try to find a PR for the current branch, and use the base branch from there. If that fails, the main branch will be used",
+        "gitlab_config_file": "Path to the fully parsed/resolved Gitlab CI configuration file. If not provided, we will try to resolve the current one using the API",
     }
 )
 def generate_complexity_summary_for_pr(
-    ctx: Context, skip_github_comment=False, branch_name: str | None = None, base_branch: str | None = None
+    ctx: Context,
+    skip_github_comment=False,
+    branch_name: str | None = None,
+    base_branch: str | None = None,
+    gitlab_config_file: str | None = None,
 ):
     """Task meant to run in CI. Generates a summary of the complexity data for the current PR"""
     if tabulate is None:
@@ -750,7 +755,7 @@ def _try_delete_github_comment(msg: str):
     common_ancestor = get_common_ancestor(ctx, commit_sha, f"origin/{base_branch}")
     main_branch_complexity_path = Path("/tmp/verifier-complexity-main")
     print(f"Downloading complexity data for {base_branch} branch (commit {common_ancestor})...")
-    download_complexity_data(ctx, common_ancestor, main_branch_complexity_path)
+    download_complexity_data(ctx, common_ancestor, main_branch_complexity_path, gitlab_config_file)
 
     main_complexity_files = list(main_branch_complexity_path.glob("verifier-complexity-*"))
     if len(main_complexity_files) == 0:
diff --git a/tasks/kmt.py b/tasks/kmt.py
index b75292351aa6..c40275869e82 100644
--- a/tasks/kmt.py
+++ b/tasks/kmt.py
@@ -1,5 +1,6 @@
 from __future__ import annotations
 
+import datetime
 import getpass
 import io
 import itertools
@@ -10,6 +11,7 @@
 import sys
 import tarfile
 import tempfile
+import time
 import xml.etree.ElementTree as ET
 from collections import defaultdict
 from collections.abc import Callable, Iterable
@@ -46,12 +48,17 @@
 from tasks.kernel_matrix_testing.tool import Exit, ask, error, get_binary_target_arch, info, warn
 from tasks.kernel_matrix_testing.vars import KMT_SUPPORTED_ARCHS, KMTPaths
 from tasks.libs.build.ninja import NinjaWriter
-from tasks.libs.ciproviders.gitlab_api import get_gitlab_repo
+from tasks.libs.ciproviders.gitlab_api import (
+    get_gitlab_ci_configuration,
+    get_gitlab_job_dependencies,
+    get_gitlab_repo,
+)
 from tasks.libs.common.git import get_current_branch
 from tasks.libs.common.utils import get_build_flags
 from tasks.libs.pipeline.tools import loop_status
 from tasks.libs.releasing.version import VERSION_RE, check_version
 from tasks.libs.types.arch import Arch, KMTArchName
+from tasks.libs.types.types import JobDependency
 from tasks.security_agent import build_functional_tests
 from tasks.system_probe import (
     BPF_TAG,
@@ -2276,11 +2283,38 @@ def install_ddagent(
         "commit": "The commit to download the complexity data for",
         "dest_path": "The path to save the complexity data to",
         "keep_compressed_archives": "Keep the compressed archives after extracting the data. Useful for testing, as it replicates the exact state of the artifacts in CI",
+        "download_all_jobs": "Download the complexity data for all jobs instead of just the ones that are marked as dependencies for the notify_ebpf_complexity_changes_job. This will make the download take far longer.",
+        "gitlab_config_file": "Path to the fully parsed/resolved Gitlab CI configuration file. If not provided, we will try to resolve the current one using the API",
     }
 )
-def download_complexity_data(ctx: Context, commit: str, dest_path: str | Path, keep_compressed_archives: bool = False):
+def download_complexity_data(
+    ctx: Context,
+    commit: str,
+    dest_path: str | Path,
+    keep_compressed_archives: bool = False,
+    download_all_jobs: bool = False,
+    gitlab_config_file: str | None = None,
+):
     gitlab = get_gitlab_repo()
     dest_path = Path(dest_path)
+    deps: list[JobDependency] = []
+
+    if not download_all_jobs:
+        print("Parsing .gitlab-ci.yml file to understand the dependencies for notify_ebpf_complexity_changes")
+
+        if gitlab_config_file is None:
+            gitlab_ci_file = os.fspath(Path(__file__).parent.parent / ".gitlab-ci.yml")
+            gitlab_config = get_gitlab_ci_configuration(ctx, gitlab_ci_file, job="notify_ebpf_complexity_changes")
+        else:
+            with open(gitlab_config_file) as f:
+                parsed_file = yaml.safe_load(f)
+
+            if ".gitlab-ci.yml" not in parsed_file:
+                raise Exit(f"Could not find .gitlab-ci.yml in {gitlab_config_file}")
+            gitlab_config = parsed_file[".gitlab-ci.yml"]
+
+        deps = get_gitlab_job_dependencies(gitlab_config, "notify_ebpf_complexity_changes")
+        print(f"Dependencies for notify_ebpf_complexity_changes: {deps}")
 
     # We can't get all the pipelines associated with a commit directly, so we get it by the commit statuses
     commit_statuses = gitlab.commits.get(commit, lazy=True).statuses.list(all=True)
@@ -2295,11 +2329,23 @@ def download_complexity_data(ctx: Context, commit: str, dest_path: str | Path, k
 
         _, test_jobs = get_all_jobs_for_pipeline(pipeline_id)
         for job in test_jobs:
+            if not download_all_jobs and not any(dep.matches(job.name) for dep in deps):
+                print(f"Ignoring job {job.name} because it is not a dependency of notify_ebpf_complexity_changes")
+                continue
+
             complexity_name = f"verifier-complexity-{job.arch}-{job.distro}-{job.component}"
             complexity_data_fname = f"test/new-e2e/tests/{complexity_name}.tar.gz"
+            download_start_time = time.time()
             data = job.artifact_file_binary(complexity_data_fname, ignore_not_found=True)
+            download_end_time = time.time()
+            download_end_tstamp = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
+            time_msg = (
+                f"Download took {download_end_time - download_start_time} seconds, finished at {download_end_tstamp}"
+            )
             if data is None:
-                print(f"Complexity data not found for {job.name} - filename {complexity_data_fname} not found")
+                print(
+                    f"Complexity data not found for {job.name} - filename {complexity_data_fname} not found ({time_msg})"
+                )
                 continue
 
             if keep_compressed_archives:
@@ -2310,7 +2356,10 @@ def download_complexity_data(ctx: Context, commit: str, dest_path: str | Path, k
             job_folder = dest_path / complexity_name
             job_folder.mkdir(parents=True, exist_ok=True)
             tar.extractall(dest_path / job_folder)
-            print(f"Extracted complexity data for {job.name} successfully, filename {complexity_data_fname}")
+            time_msg += f", extracted in {time.time() - download_end_time} seconds"
+            print(
+                f"Extracted complexity data for {job.name} successfully, filename {complexity_data_fname} ({time_msg})"
+            )
 
 
 @task
diff --git a/tasks/libs/ciproviders/gitlab_api.py b/tasks/libs/ciproviders/gitlab_api.py
index c5143e6b5b0c..ac48d6efe4fe 100644
--- a/tasks/libs/ciproviders/gitlab_api.py
+++ b/tasks/libs/ciproviders/gitlab_api.py
@@ -25,6 +25,7 @@
 from tasks.libs.common.color import Color, color_message
 from tasks.libs.common.git import get_common_ancestor, get_current_branch, get_default_branch
 from tasks.libs.common.utils import retry_function
+from tasks.libs.types.types import JobDependency
 
 BASE_URL = "https://gitlab.ddbuild.io"
 CONFIG_SPECIAL_OBJECTS = {
@@ -1342,3 +1343,26 @@ def update_image_tag(lines, tag, variables, test=True):
         else:
             output.append(line)
     return output
+
+
+def get_gitlab_job_dependencies(gitlab_cfg: dict, job_name: str) -> list[JobDependency]:
+    """
+    Get the dependencies of a job from the gitlab configuration.
+    """
+    job_dependencies = []
+    job_data = gitlab_cfg[job_name]
+    if "needs" in job_data:
+        for need in job_data["needs"]:
+            job_name = need["job"]
+            matrix_needs = need.get('parallel', {}).get('matrix', [])
+            tags = []
+            for matrix_need in matrix_needs:
+                need_tags = []
+                for tag_name, tag_values in matrix_need.items():
+                    if isinstance(tag_values, str):
+                        tag_values = [tag_values]
+                    need_tags.append((tag_name, set(tag_values)))
+                tags.append(need_tags)
+
+            job_dependencies.append(JobDependency(job_name, tags))
+    return job_dependencies
diff --git a/tasks/libs/types/types.py b/tasks/libs/types/types.py
index 108abdd40375..a9d0d9687ffd 100644
--- a/tasks/libs/types/types.py
+++ b/tasks/libs/types/types.py
@@ -1,4 +1,6 @@
+import re
 import subprocess
+from dataclasses import dataclass
 from enum import Enum
 
 from gitlab.v4.objects import ProjectJob
@@ -105,3 +107,52 @@ class PermissionCheck(Enum):
 
     REPO = 'repo'
     TEAM = 'team'
+
+
+@dataclass
+class JobDependency:
+    job_name: str  #: The name of the job
+
+    # For dependences that only require a subset of the tags of parallel jobs.
+    # The data is represent as a list of possible tag name + tag values pairs.
+    tags: list[list[tuple[str, set[str]]]]
+
+    def matches(self, job_name: str) -> bool:
+        job_name_parts = job_name.split(": ", maxsplit=1)
+        base_job_name = job_name_parts[0]
+
+        if base_job_name != self.job_name:
+            return False
+
+        # Check if we have to match tags
+        if len(self.tags) == 0:
+            return True
+
+        # We want tags but the job name doesn't have any, so it can't match
+        if len(job_name_parts) == 1:
+            return False
+
+        # Parse the job tags
+        match = re.search(r"\[([^\]]+)\]", job_name_parts[1])
+        if match is None:
+            raise RuntimeError(f"Invalid job name {job_name}, cannot parse parallel variables")
+
+        job_tags = [x.strip() for x in match.group(1).split(",")]
+
+        # Check if the job tags match any possible combination of the dependency tags
+        for tagset in self.tags:
+            if self._match_tagset(tagset, job_tags):
+                return True
+
+        return False
+
+    def _match_tagset(self, tagset: list[tuple[str, set[str]]], job_tags: list[str]) -> bool:
+        if len(job_tags) < len(tagset):
+            return False  # Not enough tags to match
+
+        for wanted, value in zip(tagset, job_tags, strict=False):
+            wanted_values = wanted[1]
+            if value not in wanted_values:
+                return False
+
+        return True
diff --git a/tasks/unit_tests/libs/types_tests.py b/tasks/unit_tests/libs/types_tests.py
new file mode 100644
index 000000000000..d90538a69299
--- /dev/null
+++ b/tasks/unit_tests/libs/types_tests.py
@@ -0,0 +1,49 @@
+import unittest
+
+from tasks.libs.types.types import JobDependency
+
+
+class TestJobDependency(unittest.TestCase):
+    def test_matches_simple_name(self):
+        dep = JobDependency(job_name="build", tags=[])
+        self.assertTrue(dep.matches("build"))
+        self.assertFalse(dep.matches("test"))
+
+    def test_matches_with_tags(self):
+        dep = JobDependency(job_name="test", tags=[[("os", {"linux"}), ("arch", {"amd64"})]])
+        # Correct job name and tags
+        self.assertTrue(dep.matches("test: [linux, amd64]"))
+        # Wrong job name
+        self.assertFalse(dep.matches("build: [linux, amd64]"))
+        # Wrong tag value
+        self.assertFalse(dep.matches("test: [windows, amd64]"))
+        # Missing tags
+        self.assertFalse(dep.matches("test"))
+
+    def test_matches_multiple_tagsets(self):
+        dep = JobDependency(
+            job_name="deploy",
+            tags=[
+                [("region", {"us-east-1"}), ("env", {"prod"})],
+                [("region", {"eu-west-1"}), ("env", {"staging"})],
+            ],
+        )
+        self.assertTrue(dep.matches("deploy: [us-east-1, prod]"))
+        self.assertTrue(dep.matches("deploy: [eu-west-1, staging]"))
+        self.assertFalse(dep.matches("deploy: [us-east-1, staging]"))
+
+    def test_matches_invalid_tag_format(self):
+        dep = JobDependency(job_name="foo", tags=[[("a", {"x"})]])
+        with self.assertRaises(RuntimeError):
+            dep.matches("foo: no_brackets")
+
+    def test__match_tagset(self):
+        dep = JobDependency(job_name="bar", tags=[])
+        tagset = [("os", {"linux", "windows"}), ("arch", {"amd64"})]
+        self.assertTrue(dep._match_tagset(tagset, ["linux", "amd64"]))
+        self.assertFalse(dep._match_tagset(tagset, ["darwin", "amd64"]))
+        self.assertFalse(dep._match_tagset(tagset, ["linux", "arm64"]))
+        # Fewer job_tags than tagset
+        self.assertFalse(dep._match_tagset(tagset, ["linux"]))
+        # More job_tags than tagset (should ignore extras)
+        self.assertTrue(dep._match_tagset(tagset, ["linux", "amd64", "extra"]))


Reviews:
user: login: brycekahle
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-16T15:56:02Z
user: login: gjulianm
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-16T15:58:18Z
user: login: gjulianm
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-16T15:59:10Z
user: login: gjulianm
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-16T16:12:57Z
user: login: brycekahle
state: APPROVED
author_association: MEMBER
submitted_at: 2025-05-16T16:53:45Z
user: login: CelianR
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-05-19T07:08:39Z
user: login: gjulianm
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-20T09:43:43Z

Review comments:
diff_hunk: @@ -338,6 +338,6 @@ notify_ebpf_complexity_changes:
     - python3 -m dda self dep sync -f legacy-tasks
     - !reference [.setup_agent_github_app]
     - DD_API_KEY=$($CI_PROJECT_DIR/tools/ci/fetch_secret.sh $AGENT_API_KEY_ORG2 token) || exit $?; export DD_API_KEY
-    - GITLAB_TOKEN=$($CI_PROJECT_DIR/tools/ci/fetch_secret.sh $GITLAB_TOKEN read_api) || exit $?; export GITLAB_TOKEN
+    - GITLAB_TOKEN=$($CI_PROJECT_DIR/tools/ci/fetch_secret.sh $GITLAB_TOKEN write_api) || exit $?; export GITLAB_TOKEN  # Require write_api to be able to interpret the .gitlab-ci configuration, we need access to the CI linter
path: .gitlab/kernel_matrix_testing/common.yml
user: login: brycekahle
body: We need API write access to parse the Gitlab config?
created_at: 2025-05-16T15:56:02Z
author_association: MEMBER
diff_hunk: @@ -338,6 +338,6 @@ notify_ebpf_complexity_changes:
     - python3 -m dda self dep sync -f legacy-tasks
     - !reference [.setup_agent_github_app]
     - DD_API_KEY=$($CI_PROJECT_DIR/tools/ci/fetch_secret.sh $AGENT_API_KEY_ORG2 token) || exit $?; export DD_API_KEY
-    - GITLAB_TOKEN=$($CI_PROJECT_DIR/tools/ci/fetch_secret.sh $GITLAB_TOKEN read_api) || exit $?; export GITLAB_TOKEN
+    - GITLAB_TOKEN=$($CI_PROJECT_DIR/tools/ci/fetch_secret.sh $GITLAB_TOKEN write_api) || exit $?; export GITLAB_TOKEN  # Require write_api to be able to interpret the .gitlab-ci configuration, we need access to the CI linter
path: .gitlab/kernel_matrix_testing/common.yml
user: login: gjulianm
body: Yep, the parsing is done via the Gitlab CI linter API, which requires write access. It seems to be the same in other jobs that use the same Python functions: https://github.com/DataDog/datadog-agent/blob/guillermo.julian/kmt-retry/.gitlab/.pre/ci_configuration.yml#L44
created_at: 2025-05-16T15:58:18Z
author_association: CONTRIBUTOR
diff_hunk: @@ -338,6 +338,6 @@ notify_ebpf_complexity_changes:
     - python3 -m dda self dep sync -f legacy-tasks
     - !reference [.setup_agent_github_app]
     - DD_API_KEY=$($CI_PROJECT_DIR/tools/ci/fetch_secret.sh $AGENT_API_KEY_ORG2 token) || exit $?; export DD_API_KEY
-    - GITLAB_TOKEN=$($CI_PROJECT_DIR/tools/ci/fetch_secret.sh $GITLAB_TOKEN read_api) || exit $?; export GITLAB_TOKEN
+    - GITLAB_TOKEN=$($CI_PROJECT_DIR/tools/ci/fetch_secret.sh $GITLAB_TOKEN write_api) || exit $?; export GITLAB_TOKEN  # Require write_api to be able to interpret the .gitlab-ci configuration, we need access to the CI linter
path: .gitlab/kernel_matrix_testing/common.yml
user: login: gjulianm
body: Although now that I look at that job, maybe we can parse the artifacts from that file.
created_at: 2025-05-16T15:59:10Z
author_association: CONTRIBUTOR
diff_hunk: @@ -338,6 +338,6 @@ notify_ebpf_complexity_changes:
     - python3 -m dda self dep sync -f legacy-tasks
     - !reference [.setup_agent_github_app]
     - DD_API_KEY=$($CI_PROJECT_DIR/tools/ci/fetch_secret.sh $AGENT_API_KEY_ORG2 token) || exit $?; export DD_API_KEY
-    - GITLAB_TOKEN=$($CI_PROJECT_DIR/tools/ci/fetch_secret.sh $GITLAB_TOKEN read_api) || exit $?; export GITLAB_TOKEN
+    - GITLAB_TOKEN=$($CI_PROJECT_DIR/tools/ci/fetch_secret.sh $GITLAB_TOKEN write_api) || exit $?; export GITLAB_TOKEN  # Require write_api to be able to interpret the .gitlab-ci configuration, we need access to the CI linter
path: .gitlab/kernel_matrix_testing/common.yml
user: login: gjulianm
body: Fixed, we don't need that token anymore :D 
created_at: 2025-05-16T16:12:57Z
author_association: CONTRIBUTOR
diff_hunk: @@ -105,3 +107,45 @@ class PermissionCheck(Enum):
 
     REPO = 'repo'
     TEAM = 'team'
+
+
+@dataclass
+class JobDependency:
+    job_name: str  #: The name of the job
+
+    # For dependences that only require a subset of the tags of parallel jobs.
+    # The data is represent as a list of possible tag name + tag values pairs.
+    tags: list[list[tuple[str, set[str]]]]
+
+    def matches(self, job_name: str) -> bool:
+        job_name_parts = job_name.split(": ", maxsplit=1)
+        base_job_name = job_name_parts[0]
+
+        if base_job_name != self.job_name:
+            return False
+
+        # Check if we have to match tags
+        if len(self.tags) == 0 or len(job_name_parts) == 1:
+            return True
+
+        # Parse the job tags
+        match = re.search(r"\[([^\]]+)\]", job_name_parts[1])
+        if match is None:
+            raise RuntimeError(f"Invalid job name {job_name}, cannot parse parallel variables")
+
+        job_tags = [x.strip() for x in match.group(1).split(",")]
+
+        # Check if the job tags match any possible combination of the dependency tags
+        for tagset in self.tags:
+            if self._match_tagset(tagset, job_tags):
+                return True
+
+        return False
+
+    def _match_tagset(self, tagset: list[tuple[str, set[str]]], job_tags: list[str]) -> bool:
+        for wanted, value in zip(tagset, job_tags, strict=False):
+            wanted_values = wanted[1]
+            if value not in wanted_values:
+                return False
+
+        return True
path: tasks/libs/types/types.py
user: login: CelianR
body: NIT: It would be nice to add some unit tests for that
created_at: 2025-05-19T07:08:15Z
author_association: CONTRIBUTOR
diff_hunk: @@ -105,3 +107,45 @@ class PermissionCheck(Enum):
 
     REPO = 'repo'
     TEAM = 'team'
+
+
+@dataclass
+class JobDependency:
+    job_name: str  #: The name of the job
+
+    # For dependences that only require a subset of the tags of parallel jobs.
+    # The data is represent as a list of possible tag name + tag values pairs.
+    tags: list[list[tuple[str, set[str]]]]
+
+    def matches(self, job_name: str) -> bool:
+        job_name_parts = job_name.split(": ", maxsplit=1)
+        base_job_name = job_name_parts[0]
+
+        if base_job_name != self.job_name:
+            return False
+
+        # Check if we have to match tags
+        if len(self.tags) == 0 or len(job_name_parts) == 1:
+            return True
+
+        # Parse the job tags
+        match = re.search(r"\[([^\]]+)\]", job_name_parts[1])
+        if match is None:
+            raise RuntimeError(f"Invalid job name {job_name}, cannot parse parallel variables")
+
+        job_tags = [x.strip() for x in match.group(1).split(",")]
+
+        # Check if the job tags match any possible combination of the dependency tags
+        for tagset in self.tags:
+            if self._match_tagset(tagset, job_tags):
+                return True
+
+        return False
+
+    def _match_tagset(self, tagset: list[tuple[str, set[str]]], job_tags: list[str]) -> bool:
+        for wanted, value in zip(tagset, job_tags, strict=False):
+            wanted_values = wanted[1]
+            if value not in wanted_values:
+                return False
+
+        return True
path: tasks/libs/types/types.py
user: login: gjulianm
body: Done!
created_at: 2025-05-20T09:43:43Z
author_association: CONTRIBUTOR

PR comments:
user: login: agent-platform-auto-pr[bot]
created_at: 2025-05-13T16:09:01Z
body: ## Uncompressed package size comparison

Comparison with [ancestor](https://github.com/DataDog/datadog-agent/commit/a82788ba0328da868f50664be77f945a8dba4817) `a82788ba0328da868f50664be77f945a8dba4817`


<details>
  <summary> Diff per package </summary>

|package|diff|status|size|ancestor|threshold|
|--|--|--|--|--|--|
|datadog-agent-amd64-deb|0.00MB|✅|773.30MB|773.30MB|0.50MB|
|datadog-agent-x86_64-rpm|0.00MB|✅|782.25MB|782.25MB|0.50MB|
|datadog-agent-x86_64-suse|0.00MB|✅|782.25MB|782.25MB|0.50MB|
|datadog-agent-arm64-deb|0.00MB|✅|759.26MB|759.26MB|0.50MB|
|datadog-agent-aarch64-rpm|0.00MB|✅|768.19MB|768.19MB|0.50MB|
|datadog-dogstatsd-amd64-deb|0.00MB|✅|31.99MB|31.99MB|0.50MB|
|datadog-dogstatsd-x86_64-rpm|0.00MB|✅|32.07MB|32.07MB|0.50MB|
|datadog-dogstatsd-x86_64-suse|0.00MB|✅|32.07MB|32.07MB|0.50MB|
|datadog-dogstatsd-arm64-deb|0.00MB|✅|30.47MB|30.47MB|0.50MB|
|datadog-heroku-agent-amd64-deb|0.00MB|✅|380.12MB|380.12MB|0.50MB|
|datadog-iot-agent-amd64-deb|0.00MB|✅|62.70MB|62.70MB|0.50MB|
|datadog-iot-agent-x86_64-rpm|0.00MB|✅|62.78MB|62.78MB|0.50MB|
|datadog-iot-agent-x86_64-suse|0.00MB|✅|62.78MB|62.78MB|0.50MB|
|datadog-iot-agent-arm64-deb|0.00MB|✅|59.27MB|59.27MB|0.50MB|
|datadog-iot-agent-aarch64-rpm|0.00MB|✅|59.35MB|59.35MB|0.50MB|

</details>

## Decision
✅ Passed



author_association: CONTRIBUTOR
user: login: cit-pr-commenter[bot]
created_at: 2025-05-13T16:27:18Z
body: ## Regression Detector

# Regression Detector Results

[Metrics dashboard](https://app.datadoghq.com/dashboard/ykh-ua8-vcu/SMP-Regression-Detector-Metrics?fromUser=true&refresh_mode=paused&tpl_var_run-id%5B0%5D=6afdc464-612c-4bcd-aaef-208199eebd42&view=spans&from_ts=1747837020000&to_ts=1747837620000&live=false)  
[Target profiles](https://app.datadoghq.com/profiling/explorer?query=env%3Asingle-machine-performance%20service%3Adatadog-agent%20job_id%3A6afdc464-612c-4bcd-aaef-208199eebd42&agg_m=count&agg_m_source=base&agg_t=count&fromUser=false&viz=stream&start=1747829820000&end=1747841220000&paused=true)  
Run ID: 6afdc464-612c-4bcd-aaef-208199eebd42

Baseline: a82788ba0328da868f50664be77f945a8dba4817
Comparison: 31044e242ea3cd3c3a02accbf7b0468f2518bd93
[Diff](a82788ba0328da868f50664be77f945a8dba4817...31044e242ea3cd3c3a02accbf7b0468f2518bd93)


## Optimization Goals: ✅ No significant changes detected


<details>
<summary><h2>
Fine details of change detection per experiment
</h2></summary>

| perf | experiment                                   | goal               | Δ mean % | Δ mean % CI    | trials | links                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               |
|------|----------------------------------------------|--------------------|----------|----------------|--------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| ➖   | quality_gate_logs                            | % cpu utilization  | +2.56    | [-0.07, +5.19] | 1      | [Logs](https://app.datadoghq.com/logs?query=experiment%3Aquality_gate_logs%20job_id%3A6afdc464-612c-4bcd-aaef-208199eebd42&agg_m=count&agg_m_source=base&agg_q=%40span.url&agg_q_source=base&agg_t=count&fromUser=true&index=single-machine-performance-target-logs&messageDisplay=inline&refresh_mode=paused&storage=hot&stream_sort=time%2Cdesc&top_n=100&top_o=top&viz=stream&x_missing=true&from_ts=1747829820000&to_ts=1747841220000&live=false) [bounds checks dashboard](https://app.datadoghq.com/dashboard/vz3-jd5-bdi?fromUser=true&refresh_mode=paused&tpl_var_experiment%5B0%5D=quality_gate_logs&tpl_var_job_id%5B0%5D=6afdc464-612c-4bcd-aaef-208199eebd42&view=spans&from_ts=1747837020000&to_ts=1747837620000&live=false)                           |
| ➖   | uds_dogstatsd_to_api_cpu                     | % cpu utilization  | +1.02    | [+0.11, +1.93] | 1      | [Logs](https://app.datadoghq.com/logs?query=experiment%3Auds_dogstatsd_to_api_cpu%20job_id%3A6afdc464-612c-4bcd-aaef-208199eebd42&agg_m=count&agg_m_source=base&agg_q=%40span.url&agg_q_source=base&agg_t=count&fromUser=true&index=single-machine-performance-target-logs&messageDisplay=inline&refresh_mode=paused&storage=hot&stream_sort=time%2Cdesc&top_n=100&top_o=top&viz=stream&x_missing=true&from_ts=1747829820000&to_ts=1747841220000&live=false)                                                                                                                                                                                                                                                                                                        |
| ➖   | tcp_syslog_to_blackhole                      | ingress throughput | +0.86    | [+0.80, +0.91] | 1      | [Logs](https://app.datadoghq.com/logs?query=experiment%3Atcp_syslog_to_blackhole%20job_id%3A6afdc464-612c-4bcd-aaef-208199eebd42&agg_m=count&agg_m_source=base&agg_q=%40span.url&agg_q_source=base&agg_t=count&fromUser=true&index=single-machine-performance-target-logs&messageDisplay=inline&refresh_mode=paused&storage=hot&stream_sort=time%2Cdesc&top_n=100&top_o=top&viz=stream&x_missing=true&from_ts=1747829820000&to_ts=1747841220000&live=false)                                                                                                                                                                                                                                                                                                         |
| ➖   | docker_containers_cpu                        | % cpu utilization  | +0.73    | [-3.08, +4.54] | 1      | [Logs](https://app.datadoghq.com/logs?query=experiment%3Adocker_containers_cpu%20job_id%3A6afdc464-612c-4bcd-aaef-208199eebd42&agg_m=count&agg_m_source=base&agg_q=%40span.url&agg_q_source=base&agg_t=count&fromUser=true&index=single-machine-performance-target-logs&messageDisplay=inline&refresh_mode=paused&storage=hot&stream_sort=time%2Cdesc&top_n=100&top_o=top&viz=stream&x_missing=true&from_ts=1747829820000&to_ts=1747841220000&live=false)                                                                                                                                                                                                                                                                                                           |
| ➖   | otlp_ingest_metrics                          | memory utilization | +0.29    | [+0.13, +0.45] | 1      | [Logs](https://app.datadoghq.com/logs?query=experiment%3Aotlp_ingest_metrics%20job_id%3A6afdc464-612c-4bcd-aaef-208199eebd42&agg_m=count&agg_m_source=base&agg_q=%40span.url&agg_q_source=base&agg_t=count&fromUser=true&index=single-machine-performance-target-logs&messageDisplay=inline&refresh_mode=paused&storage=hot&stream_sort=time%2Cdesc&top_n=100&top_o=top&viz=stream&x_missing=true&from_ts=1747829820000&to_ts=1747841220000&live=false)                                                                                                                                                                                                                                                                                                             |
| ➖   | docker_containers_memory                     | memory utilization | +0.16    | [+0.07, +0.25] | 1      | [Logs](https://app.datadoghq.com/logs?query=experiment%3Adocker_containers_memory%20job_id%3A6afdc464-612c-4bcd-aaef-208199eebd42&agg_m=count&agg_m_source=base&agg_q=%40span.url&agg_q_source=base&agg_t=count&fromUser=true&index=single-machine-performance-target-logs&messageDisplay=inline&refresh_mode=paused&storage=hot&stream_sort=time%2Cdesc&top_n=100&top_o=top&viz=stream&x_missing=true&from_ts=1747829820000&to_ts=1747841220000&live=false)                                                                                                                                                                                                                                                                                                        |
| ➖   | file_to_blackhole_0ms_latency_http1          | egress throughput  | +0.14    | [-0.48, +0.76] | 1      | [Logs](https://app.datadoghq.com/logs?query=experiment%3Afile_to_blackhole_0ms_latency_http1%20job_id%3A6afdc464-612c-4bcd-aaef-208199eebd42&agg_m=count&agg_m_source=base&agg_q=%40span.url&agg_q_source=base&agg_t=count&fromUser=true&index=single-machine-performance-target-logs&messageDisplay=inline&refresh_mode=paused&storage=hot&stream_sort=time%2Cdesc&top_n=100&top_o=top&viz=stream&x_missing=true&from_ts=1747829820000&to_ts=1747841220000&live=false)                                                                                                                                                                                                                                                                                             |
| ➖   | file_to_blackhole_100ms_latency              | egress throughput  | +0.12    | [-0.54, +0.78] | 1      | [Logs](https://app.datadoghq.com/logs?query=experiment%3Afile_to_blackhole_100ms_latency%20job_id%3A6afdc464-612c-4bcd-aaef-208199eebd42&agg_m=count&agg_m_source=base&agg_q=%40span.url&agg_q_source=base&agg_t=count&fromUser=true&index=single-machine-performance-target-logs&messageDisplay=inline&refresh_mode=paused&storage=hot&stream_sort=time%2Cdesc&top_n=100&top_o=top&viz=stream&x_missing=true&from_ts=1747829820000&to_ts=1747841220000&live=false)                                                                                                                                                                                                                                                                                                 |
| ➖   | ddot_metrics                                 | memory utilization | +0.11    | [+0.00, +0.22] | 1      | [Logs](https://app.datadoghq.com/logs?query=experiment%3Addot_metrics%20job_id%3A6afdc464-612c-4bcd-aaef-208199eebd42&agg_m=count&agg_m_source=base&agg_q=%40span.url&agg_q_source=base&agg_t=count&fromUser=true&index=single-machine-performance-target-logs&messageDisplay=inline&refresh_mode=paused&storage=hot&stream_sort=time%2Cdesc&top_n=100&top_o=top&viz=stream&x_missing=true&from_ts=1747829820000&to_ts=1747841220000&live=false)                                                                                                                                                                                                                                                                                                                    |
| ➖   | uds_dogstatsd_20mb_12k_contexts_20_senders   | memory utilization | +0.10    | [+0.03, +0.16] | 1      | [Logs](https://app.datadoghq.com/logs?query=experiment%3Auds_dogstatsd_20mb_12k_contexts_20_senders%20job_id%3A6afdc464-612c-4bcd-aaef-208199eebd42&agg_m=count&agg_m_source=base&agg_q=%40span.url&agg_q_source=base&agg_t=count&fromUser=true&index=single-machine-performance-target-logs&messageDisplay=inline&refresh_mode=paused&storage=hot&stream_sort=time%2Cdesc&top_n=100&top_o=top&viz=stream&x_missing=true&from_ts=1747829820000&to_ts=1747841220000&live=false)                                                                                                                                                                                                                                                                                      |
| ➖   | ddot_logs                                    | memory utilization | +0.08    | [-0.06, +0.22] | 1      | [Logs](https://app.datadoghq.com/logs?query=experiment%3Addot_logs%20job_id%3A6afdc464-612c-4bcd-aaef-208199eebd42&agg_m=count&agg_m_source=base&agg_q=%40span.url&agg_q_source=base&agg_t=count&fromUser=true&index=single-machine-performance-target-logs&messageDisplay=inline&refresh_mode=paused&storage=hot&stream_sort=time%2Cdesc&top_n=100&top_o=top&viz=stream&x_missing=true&from_ts=1747829820000&to_ts=1747841220000&live=false)                                                                                                                                                                                                                                                                                                                       |
| ➖   | file_to_blackhole_1000ms_latency             | egress throughput  | +0.06    | [-0.57, +0.69] | 1      | [Logs](https://app.datadoghq.com/logs?query=experiment%3Afile_to_blackhole_1000ms_latency%20job_id%3A6afdc464-612c-4bcd-aaef-208199eebd42&agg_m=count&agg_m_source=base&agg_q=%40span.url&agg_q_source=base&agg_t=count&fromUser=true&index=single-machine-performance-target-logs&messageDisplay=inline&refresh_mode=paused&storage=hot&stream_sort=time%2Cdesc&top_n=100&top_o=top&viz=stream&x_missing=true&from_ts=1747829820000&to_ts=1747841220000&live=false)                                                                                                                                                                                                                                                                                                |
| ➖   | file_to_blackhole_0ms_latency_http2          | egress throughput  | +0.04    | [-0.58, +0.65] | 1      | [Logs](https://app.datadoghq.com/logs?query=experiment%3Afile_to_blackhole_0ms_latency_http2%20job_id%3A6afdc464-612c-4bcd-aaef-208199eebd42&agg_m=count&agg_m_source=base&agg_q=%40span.url&agg_q_source=base&agg_t=count&fromUser=true&index=single-machine-performance-target-logs&messageDisplay=inline&refresh_mode=paused&storage=hot&stream_sort=time%2Cdesc&top_n=100&top_o=top&viz=stream&x_missing=true&from_ts=1747829820000&to_ts=1747841220000&live=false)                                                                                                                                                                                                                                                                                             |
| ➖   | file_to_blackhole_300ms_latency              | egress throughput  | +0.03    | [-0.61, +0.67] | 1      | [Logs](https://app.datadoghq.com/logs?query=experiment%3Afile_to_blackhole_300ms_latency%20job_id%3A6afdc464-612c-4bcd-aaef-208199eebd42&agg_m=count&agg_m_source=base&agg_q=%40span.url&agg_q_source=base&agg_t=count&fromUser=true&index=single-machine-performance-target-logs&messageDisplay=inline&refresh_mode=paused&storage=hot&stream_sort=time%2Cdesc&top_n=100&top_o=top&viz=stream&x_missing=true&from_ts=1747829820000&to_ts=1747841220000&live=false)                                                                                                                                                                                                                                                                                                 |
| ➖   | tcp_dd_logs_filter_exclude                   | ingress throughput | +0.00    | [-0.01, +0.02] | 1      | [Logs](https://app.datadoghq.com/logs?query=experiment%3Atcp_dd_logs_filter_exclude%20job_id%3A6afdc464-612c-4bcd-aaef-208199eebd42&agg_m=count&agg_m_source=base&agg_q=%40span.url&agg_q_source=base&agg_t=count&fromUser=true&index=single-machine-performance-target-logs&messageDisplay=inline&refresh_mode=paused&storage=hot&stream_sort=time%2Cdesc&top_n=100&top_o=top&viz=stream&x_missing=true&from_ts=1747829820000&to_ts=1747841220000&live=false)                                                                                                                                                                                                                                                                                                      |
| ➖   | file_to_blackhole_1000ms_latency_linear_load | egress throughput  | -0.01    | [-0.24, +0.23] | 1      | [Logs](https://app.datadoghq.com/logs?query=experiment%3Afile_to_blackhole_1000ms_latency_linear_load%20job_id%3A6afdc464-612c-4bcd-aaef-208199eebd42&agg_m=count&agg_m_source=base&agg_q=%40span.url&agg_q_source=base&agg_t=count&fromUser=true&index=single-machine-performance-target-logs&messageDisplay=inline&refresh_mode=paused&storage=hot&stream_sort=time%2Cdesc&top_n=100&top_o=top&viz=stream&x_missing=true&from_ts=1747829820000&to_ts=1747841220000&live=false)                                                                                                                                                                                                                                                                                    |
| ➖   | quality_gate_idle                            | memory utilization | -0.01    | [-0.06, +0.05] | 1      | [Logs](https://app.datadoghq.com/logs?query=experiment%3Aquality_gate_idle%20job_id%3A6afdc464-612c-4bcd-aaef-208199eebd42&agg_m=count&agg_m_source=base&agg_q=%40span.url&agg_q_source=base&agg_t=count&fromUser=true&index=single-machine-performance-target-logs&messageDisplay=inline&refresh_mode=paused&storage=hot&stream_sort=time%2Cdesc&top_n=100&top_o=top&viz=stream&x_missing=true&from_ts=1747829820000&to_ts=1747841220000&live=false) [bounds checks dashboard](https://app.datadoghq.com/dashboard/vz3-jd5-bdi?fromUser=true&refresh_mode=paused&tpl_var_experiment%5B0%5D=quality_gate_idle&tpl_var_job_id%5B0%5D=6afdc464-612c-4bcd-aaef-208199eebd42&view=spans&from_ts=1747837020000&to_ts=1747837620000&live=false)                           |
| ➖   | uds_dogstatsd_to_api                         | ingress throughput | -0.01    | [-0.26, +0.25] | 1      | [Logs](https://app.datadoghq.com/logs?query=experiment%3Auds_dogstatsd_to_api%20job_id%3A6afdc464-612c-4bcd-aaef-208199eebd42&agg_m=count&agg_m_source=base&agg_q=%40span.url&agg_q_source=base&agg_t=count&fromUser=true&index=single-machine-performance-target-logs&messageDisplay=inline&refresh_mode=paused&storage=hot&stream_sort=time%2Cdesc&top_n=100&top_o=top&viz=stream&x_missing=true&from_ts=1747829820000&to_ts=1747841220000&live=false)                                                                                                                                                                                                                                                                                                            |
| ➖   | file_to_blackhole_0ms_latency                | egress throughput  | -0.05    | [-0.66, +0.55] | 1      | [Logs](https://app.datadoghq.com/logs?query=experiment%3Afile_to_blackhole_0ms_latency%20job_id%3A6afdc464-612c-4bcd-aaef-208199eebd42&agg_m=count&agg_m_source=base&agg_q=%40span.url&agg_q_source=base&agg_t=count&fromUser=true&index=single-machine-performance-target-logs&messageDisplay=inline&refresh_mode=paused&storage=hot&stream_sort=time%2Cdesc&top_n=100&top_o=top&viz=stream&x_missing=true&from_ts=1747829820000&to_ts=1747841220000&live=false)                                                                                                                                                                                                                                                                                                   |
| ➖   | file_to_blackhole_500ms_latency              | egress throughput  | -0.07    | [-0.64, +0.51] | 1      | [Logs](https://app.datadoghq.com/logs?query=experiment%3Afile_to_blackhole_500ms_latency%20job_id%3A6afdc464-612c-4bcd-aaef-208199eebd42&agg_m=count&agg_m_source=base&agg_q=%40span.url&agg_q_source=base&agg_t=count&fromUser=true&index=single-machine-performance-target-logs&messageDisplay=inline&refresh_mode=paused&storage=hot&stream_sort=time%2Cdesc&top_n=100&top_o=top&viz=stream&x_missing=true&from_ts=1747829820000&to_ts=1747841220000&live=false)                                                                                                                                                                                                                                                                                                 |
| ➖   | quality_gate_idle_all_features               | memory utilization | -0.41    | [-0.49, -0.32] | 1      | [Logs](https://app.datadoghq.com/logs?query=experiment%3Aquality_gate_idle_all_features%20job_id%3A6afdc464-612c-4bcd-aaef-208199eebd42&agg_m=count&agg_m_source=base&agg_q=%40span.url&agg_q_source=base&agg_t=count&fromUser=true&index=single-machine-performance-target-logs&messageDisplay=inline&refresh_mode=paused&storage=hot&stream_sort=time%2Cdesc&top_n=100&top_o=top&viz=stream&x_missing=true&from_ts=1747829820000&to_ts=1747841220000&live=false) [bounds checks dashboard](https://app.datadoghq.com/dashboard/vz3-jd5-bdi?fromUser=true&refresh_mode=paused&tpl_var_experiment%5B0%5D=quality_gate_idle_all_features&tpl_var_job_id%5B0%5D=6afdc464-612c-4bcd-aaef-208199eebd42&view=spans&from_ts=1747837020000&to_ts=1747837620000&live=false) |
| ➖   | otlp_ingest_logs                             | memory utilization | -0.49    | [-0.61, -0.36] | 1      | [Logs](https://app.datadoghq.com/logs?query=experiment%3Aotlp_ingest_logs%20job_id%3A6afdc464-612c-4bcd-aaef-208199eebd42&agg_m=count&agg_m_source=base&agg_q=%40span.url&agg_q_source=base&agg_t=count&fromUser=true&index=single-machine-performance-target-logs&messageDisplay=inline&refresh_mode=paused&storage=hot&stream_sort=time%2Cdesc&top_n=100&top_o=top&viz=stream&x_missing=true&from_ts=1747829820000&to_ts=1747841220000&live=false)                                                                                                                                                                                                                                                                                                                |
| ➖   | file_tree                                    | memory utilization | -0.66    | [-0.80, -0.52] | 1      | [Logs](https://app.datadoghq.com/logs?query=experiment%3Afile_tree%20job_id%3A6afdc464-612c-4bcd-aaef-208199eebd42&agg_m=count&agg_m_source=base&agg_q=%40span.url&agg_q_source=base&agg_t=count&fromUser=true&index=single-machine-performance-target-logs&messageDisplay=inline&refresh_mode=paused&storage=hot&stream_sort=time%2Cdesc&top_n=100&top_o=top&viz=stream&x_missing=true&from_ts=1747829820000&to_ts=1747841220000&live=false)                                                                                                                                                                                                                                                                                                                       |
</details>


<details>
<summary><h2>
Bounds Checks: ✅ Passed
</h2></summary>

| perf | experiment                                   | bounds_check_name  | replicates_passed | links                                                                                                                                                                                                                                                                                            |
|------|----------------------------------------------|--------------------|-------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| ✅   | docker_containers_cpu                        | simple_check_run   | 10/10             |                                                                                                                                                                                                                                                                                                  |
| ✅   | docker_containers_memory                     | memory_usage       | 10/10             |                                                                                                                                                                                                                                                                                                  |
| ✅   | docker_containers_memory                     | simple_check_run   | 10/10             |                                                                                                                                                                                                                                                                                                  |
| ✅   | file_to_blackhole_0ms_latency                | lost_bytes         | 10/10             |                                                                                                                                                                                                                                                                                                  |
| ✅   | file_to_blackhole_0ms_latency                | memory_usage       | 10/10             |                                                                                                                                                                                                                                                                                                  |
| ✅   | file_to_blackhole_0ms_latency_http1          | lost_bytes         | 10/10             |                                                                                                                                                                                                                                                                                                  |
| ✅   | file_to_blackhole_0ms_latency_http1          | memory_usage       | 10/10             |                                                                                                                                                                                                                                                                                                  |
| ✅   | file_to_blackhole_0ms_latency_http2          | lost_bytes         | 10/10             |                                                                                                                                                                                                                                                                                                  |
| ✅   | file_to_blackhole_0ms_latency_http2          | memory_usage       | 10/10             |                                                                                                                                                                                                                                                                                                  |
| ✅   | file_to_blackhole_1000ms_latency             | memory_usage       | 10/10             |                                                                                                                                                                                                                                                                                                  |
| ✅   | file_to_blackhole_1000ms_latency_linear_load | memory_usage       | 10/10             |                                                                                                                                                                                                                                                                                                  |
| ✅   | file_to_blackhole_100ms_latency              | lost_bytes         | 10/10             |                                                                                                                                                                                                                                                                                                  |
| ✅   | file_to_blackhole_100ms_latency              | memory_usage       | 10/10             |                                                                                                                                                                                                                                                                                                  |
| ✅   | file_to_blackhole_300ms_latency              | lost_bytes         | 10/10             |                                                                                                                                                                                                                                                                                                  |
| ✅   | file_to_blackhole_300ms_latency              | memory_usage       | 10/10             |                                                                                                                                                                                                                                                                                                  |
| ✅   | file_to_blackhole_500ms_latency              | lost_bytes         | 10/10             |                                                                                                                                                                                                                                                                                                  |
| ✅   | file_to_blackhole_500ms_latency              | memory_usage       | 10/10             |                                                                                                                                                                                                                                                                                                  |
| ✅   | quality_gate_idle                            | intake_connections | 10/10             | [bounds checks dashboard](https://app.datadoghq.com/dashboard/vz3-jd5-bdi?fromUser=true&refresh_mode=paused&tpl_var_experiment%5B0%5D=quality_gate_idle&tpl_var_job_id%5B0%5D=6afdc464-612c-4bcd-aaef-208199eebd42&view=spans&from_ts=1747837020000&to_ts=1747837620000&live=false)              |
| ✅   | quality_gate_idle                            | memory_usage       | 10/10             | [bounds checks dashboard](https://app.datadoghq.com/dashboard/vz3-jd5-bdi?fromUser=true&refresh_mode=paused&tpl_var_experiment%5B0%5D=quality_gate_idle&tpl_var_job_id%5B0%5D=6afdc464-612c-4bcd-aaef-208199eebd42&view=spans&from_ts=1747837020000&to_ts=1747837620000&live=false)              |
| ✅   | quality_gate_idle_all_features               | intake_connections | 10/10             | [bounds checks dashboard](https://app.datadoghq.com/dashboard/vz3-jd5-bdi?fromUser=true&refresh_mode=paused&tpl_var_experiment%5B0%5D=quality_gate_idle_all_features&tpl_var_job_id%5B0%5D=6afdc464-612c-4bcd-aaef-208199eebd42&view=spans&from_ts=1747837020000&to_ts=1747837620000&live=false) |
| ✅   | quality_gate_idle_all_features               | memory_usage       | 10/10             | [bounds checks dashboard](https://app.datadoghq.com/dashboard/vz3-jd5-bdi?fromUser=true&refresh_mode=paused&tpl_var_experiment%5B0%5D=quality_gate_idle_all_features&tpl_var_job_id%5B0%5D=6afdc464-612c-4bcd-aaef-208199eebd42&view=spans&from_ts=1747837020000&to_ts=1747837620000&live=false) |
| ✅   | quality_gate_logs                            | intake_connections | 10/10             | [bounds checks dashboard](https://app.datadoghq.com/dashboard/vz3-jd5-bdi?fromUser=true&refresh_mode=paused&tpl_var_experiment%5B0%5D=quality_gate_logs&tpl_var_job_id%5B0%5D=6afdc464-612c-4bcd-aaef-208199eebd42&view=spans&from_ts=1747837020000&to_ts=1747837620000&live=false)              |
| ✅   | quality_gate_logs                            | lost_bytes         | 10/10             | [bounds checks dashboard](https://app.datadoghq.com/dashboard/vz3-jd5-bdi?fromUser=true&refresh_mode=paused&tpl_var_experiment%5B0%5D=quality_gate_logs&tpl_var_job_id%5B0%5D=6afdc464-612c-4bcd-aaef-208199eebd42&view=spans&from_ts=1747837020000&to_ts=1747837620000&live=false)              |
| ✅   | quality_gate_logs                            | memory_usage       | 10/10             | [bounds checks dashboard](https://app.datadoghq.com/dashboard/vz3-jd5-bdi?fromUser=true&refresh_mode=paused&tpl_var_experiment%5B0%5D=quality_gate_logs&tpl_var_job_id%5B0%5D=6afdc464-612c-4bcd-aaef-208199eebd42&view=spans&from_ts=1747837020000&to_ts=1747837620000&live=false)              |
</details>


<details>
<summary><h2>
Explanation
</h2></summary>

**Confidence level:** 90.00%
**Effect size tolerance:** |Δ mean %| ≥ 5.00%

Performance changes are noted in the **perf** column of each table:

* ✅ = significantly better comparison variant performance
* ❌ = significantly worse comparison variant performance
* ➖ = no significant change in performance

A regression test is an A/B test of target performance in a repeatable rig, where "performance" is measured as "comparison variant minus baseline variant" for an optimization goal (e.g., ingress throughput). Due to intrinsic variability in measuring that goal, we can only estimate its mean value for each experiment; we report uncertainty in that value as a 90.00% confidence interval denoted "Δ mean % CI".

For each experiment, we decide whether a change in performance is a "regression" -- a change worth investigating further -- if all of the following criteria are true:

1. Its estimated |Δ mean %| ≥ 5.00%, indicating the change is big enough to merit a closer look.

2. Its 90.00% confidence interval "Δ mean % CI" does not contain zero, indicating that *if our statistical model is accurate*, there is at least a 90.00% chance there is a difference in performance between baseline and comparison variants.

3. Its configuration does not mark it "erratic".
</details>

## CI Pass/Fail Decision

✅ **Passed.** All Quality Gates passed.

- **quality_gate_logs**, bounds check **intake_connections**: 10/10 replicas passed. Gate passed.
- **quality_gate_logs**, bounds check **memory_usage**: 10/10 replicas passed. Gate passed.
- **quality_gate_logs**, bounds check **lost_bytes**: 10/10 replicas passed. Gate passed.
- **quality_gate_idle_all_features**, bounds check **memory_usage**: 10/10 replicas passed. Gate passed.
- **quality_gate_idle_all_features**, bounds check **intake_connections**: 10/10 replicas passed. Gate passed.
- **quality_gate_idle**, bounds check **intake_connections**: 10/10 replicas passed. Gate passed.
- **quality_gate_idle**, bounds check **memory_usage**: 10/10 replicas passed. Gate passed.
author_association: NONE
user: login: agent-platform-auto-pr[bot]
created_at: 2025-05-14T14:07:11Z
body: ## Static quality checks

✅ Please find below the results from static quality gates


<details>
<summary>Successful checks</summary>

### Info

|Result|Quality gate|On disk size|On disk size limit|On wire size|On wire size limit|
|----|----|----|----|----|----|
|✅|static_quality_gate_agent_deb_amd64|746.02 MiB|752.99 MiB|184.71 MiB|187.44 MiB|
|✅|static_quality_gate_agent_deb_amd64_fips|744.39 MiB|751.36 MiB|184.17 MiB|187.06 MiB|
|✅|static_quality_gate_agent_heroku_amd64|368.5 MiB|369.68 MiB|98.53 MiB|99.55 MiB|
|✅|static_quality_gate_agent_msi|977.16 MiB|978.01 MiB|148.32 MiB|149.12 MiB|
|✅|static_quality_gate_agent_rpm_amd64|746.01 MiB|752.98 MiB|186.4 MiB|190.03 MiB|
|✅|static_quality_gate_agent_rpm_amd64_fips|744.38 MiB|751.35 MiB|186.19 MiB|189.81 MiB|
|✅|static_quality_gate_agent_rpm_arm64|732.6 MiB|739.42 MiB|168.41 MiB|171.23 MiB|
|✅|static_quality_gate_agent_rpm_arm64_fips|731.09 MiB|737.91 MiB|167.55 MiB|170.22 MiB|
|✅|static_quality_gate_agent_suse_amd64|746.01 MiB|752.98 MiB|186.4 MiB|190.03 MiB|
|✅|static_quality_gate_agent_suse_amd64_fips|744.38 MiB|751.35 MiB|186.19 MiB|189.81 MiB|
|✅|static_quality_gate_agent_suse_arm64|732.6 MiB|739.42 MiB|168.41 MiB|171.23 MiB|
|✅|static_quality_gate_agent_suse_arm64_fips|731.09 MiB|737.91 MiB|167.55 MiB|170.22 MiB|
|✅|static_quality_gate_docker_agent_amd64|829.79 MiB|849.39 MiB|281.76 MiB|288.34 MiB|
|✅|static_quality_gate_docker_agent_arm64|839.85 MiB|858.97 MiB|267.87 MiB|274.36 MiB|
|✅|static_quality_gate_docker_agent_jmx_amd64|829.79 MiB|849.39 MiB|281.76 MiB|288.34 MiB|
|✅|static_quality_gate_docker_agent_jmx_arm64|839.85 MiB|858.97 MiB|267.87 MiB|274.36 MiB|
|✅|static_quality_gate_docker_agent_windows1809|829.79 MiB|849.39 MiB|281.76 MiB|288.34 MiB|
|✅|static_quality_gate_docker_agent_windows1809_core|829.79 MiB|849.39 MiB|281.76 MiB|288.34 MiB|
|✅|static_quality_gate_docker_agent_windows1809_core_jmx|829.79 MiB|849.39 MiB|281.76 MiB|288.34 MiB|
|✅|static_quality_gate_docker_agent_windows1809_jmx|829.79 MiB|849.39 MiB|281.76 MiB|288.34 MiB|
|✅|static_quality_gate_docker_agent_windows2022|829.79 MiB|849.39 MiB|281.76 MiB|288.34 MiB|
|✅|static_quality_gate_docker_agent_windows2022_core|829.79 MiB|849.39 MiB|281.76 MiB|288.34 MiB|
|✅|static_quality_gate_docker_agent_windows2022_core_jmx|829.79 MiB|849.39 MiB|281.76 MiB|288.34 MiB|
|✅|static_quality_gate_docker_agent_windows2022_jmx|829.79 MiB|849.39 MiB|281.76 MiB|288.34 MiB|
|✅|static_quality_gate_docker_cluster_agent_amd64|258.9 MiB|259.73 MiB|102.78 MiB|103.68 MiB|
|✅|static_quality_gate_docker_cluster_agent_arm64|273.41 MiB|274.24 MiB|97.52 MiB|98.45 MiB|
|✅|static_quality_gate_docker_cws_instrumentation_amd64|7.08 MiB|7.12 MiB|2.95 MiB|3.29 MiB|
|✅|static_quality_gate_docker_cws_instrumentation_arm64|6.69 MiB|6.92 MiB|2.7 MiB|3.07 MiB|
|✅|static_quality_gate_docker_dogstatsd_amd64|38.63 MiB|39.57 MiB|14.81 MiB|15.76 MiB|
|✅|static_quality_gate_docker_dogstatsd_arm64|37.26 MiB|38.2 MiB|13.88 MiB|14.83 MiB|
|✅|static_quality_gate_dogstatsd_deb_amd64|30.58 MiB|31.52 MiB|8.03 MiB|8.97 MiB|
|✅|static_quality_gate_dogstatsd_deb_arm64|29.14 MiB|30.08 MiB|6.97 MiB|7.92 MiB|
|✅|static_quality_gate_dogstatsd_rpm_amd64|30.58 MiB|31.52 MiB|8.03 MiB|8.98 MiB|
|✅|static_quality_gate_dogstatsd_suse_amd64|30.58 MiB|31.52 MiB|8.03 MiB|8.98 MiB|
|✅|static_quality_gate_iot_agent_deb_amd64|59.87 MiB|60.17 MiB|14.97 MiB|15.82 MiB|
|✅|static_quality_gate_iot_agent_deb_arm64|56.6 MiB|56.94 MiB|12.84 MiB|13.86 MiB|
|✅|static_quality_gate_iot_agent_deb_armhf|56.05 MiB|56.41 MiB|12.98 MiB|13.86 MiB|
|✅|static_quality_gate_iot_agent_rpm_amd64|59.87 MiB|60.18 MiB|15.0 MiB|15.84 MiB|
|✅|static_quality_gate_iot_agent_rpm_arm64|56.6 MiB|56.94 MiB|12.86 MiB|13.76 MiB|
|✅|static_quality_gate_iot_agent_suse_amd64|59.87 MiB|60.18 MiB|15.0 MiB|15.84 MiB|

</details>

author_association: CONTRIBUTOR
user: login: agent-platform-auto-pr[bot]
created_at: 2025-05-16T12:21:46Z
body: ## Gitlab CI Configuration Changes

### Modified Jobs
<details>
<summary><b>notify_ebpf_complexity_changes</b></summary>

```diff
  notify_ebpf_complexity_changes:
    allow_failure: true
    before_script:
    - python3 -m dda self dep sync -f legacy-tasks
    - GITHUB_KEY_B64=$($CI_PROJECT_DIR/tools/ci/fetch_secret.sh $AGENT_GITHUB_APP key_b64)
      || exit $?; export GITHUB_KEY_B64
    - GITHUB_APP_ID=$($CI_PROJECT_DIR/tools/ci/fetch_secret.sh $AGENT_GITHUB_APP app_id)
      || exit $?; export GITHUB_APP_ID
    - GITHUB_INSTALLATION_ID=$($CI_PROJECT_DIR/tools/ci/fetch_secret.sh $AGENT_GITHUB_APP
      installation_id) || exit $?; export GITHUB_INSTALLATION_ID
    - echo "Using agent GitHub App"
    - DD_API_KEY=$($CI_PROJECT_DIR/tools/ci/fetch_secret.sh $AGENT_API_KEY_ORG2 token)
      || exit $?; export DD_API_KEY
-   - GITLAB_TOKEN=$($CI_PROJECT_DIR/tools/ci/fetch_secret.sh $GITLAB_TOKEN read_api)
-     || exit $?; export GITLAB_TOKEN
    image: registry.ddbuild.io/ci/datadog-agent-buildimages/linux-glibc-2-17-x64$CI_IMAGE_LINUX_GLIBC_2_17_X64_SUFFIX:$CI_IMAGE_LINUX_GLIBC_2_17_X64
    needs:
    - job: kmt_run_sysprobe_tests_x64
      optional: true
      parallel:
        matrix:
        - TAG:
          - debian_10
          - ubuntu_18.04
          - centos_8
          - opensuse_15.3
          - suse_12.5
          - fedora_38
          TEST_SET: no_usm
    - job: kmt_run_sysprobe_tests_arm64
      optional: true
      parallel:
        matrix:
        - TAG:
          - debian_10
          - ubuntu_18.04
          - centos_8
          - opensuse_15.3
          - suse_12.5
          - fedora_38
          TEST_SET: no_usm
+   - job: compute_gitlab_ci_config
    rules:
    - if: $CI_COMMIT_BRANCH == "main"
      when: never
    - if: $CI_COMMIT_BRANCH =~ /^[0-9]+\.[0-9]+\.x$/
      when: never
    - if: $CI_COMMIT_BRANCH =~ /^mq-working-branch-/
      when: never
    - allow_failure: true
      if: $DEPLOY_AGENT == "false" && $DDR_WORKFLOW_ID == null && $RUN_E2E_TESTS ==
        "off"
      when: manual
    - if: $DEPLOY_AGENT == "true" || $DDR_WORKFLOW_ID != null
      when: never
    - if: $CI_COMMIT_BRANCH == "main"
    - if: $CI_COMMIT_BRANCH =~ /^mq-working-branch-/
      when: never
    - if: $RUN_KMT_TESTS == 'on'
    - changes:
        compare_to: $COMPARE_TO_BRANCH
        paths:
        - cmd/system-probe/**/*
        - pkg/collector/corechecks/ebpf/**/*
        - pkg/collector/corechecks/servicediscovery/module/*
        - pkg/ebpf/**/*
        - pkg/network/**/*
        - pkg/process/monitor/*
        - pkg/util/kernel/**/*
        - pkg/dynamicinstrumentation/**/*
        - pkg/gpu/**/*
        - .gitlab/kernel_matrix_testing/system_probe.yml
        - .gitlab/kernel_matrix_testing/common.yml
        - .gitlab/source_test/ebpf.yml
        - test/new-e2e/system-probe/**/*
        - test/new-e2e/scenarios/system-probe/**/*
        - test/new-e2e/pkg/runner/**/*
        - test/new-e2e/pkg/utils/**/*
        - test/new-e2e/go.mod
        - tasks/system_probe.py
        - tasks/kmt.py
        - tasks/kernel_matrix_testing/*
    - allow_failure: true
      if: $CI_COMMIT_BRANCH == "main"
    - if: $CI_COMMIT_BRANCH =~ /^mq-working-branch-/
      when: never
    - if: $RUN_KMT_TESTS == 'on'
    - changes:
        compare_to: $COMPARE_TO_BRANCH
        paths:
        - pkg/ebpf/**/*
        - pkg/security/**/*
        - pkg/eventmonitor/**/*
        - .gitlab/kernel_matrix_testing/security_agent.yml
        - .gitlab/kernel_matrix_testing/common.yml
        - .gitlab/source_test/ebpf.yml
        - test/new-e2e/tests/cws/**/*
        - test/new-e2e/system-probe/**/*
        - test/new-e2e/scenarios/system-probe/**/*
        - test/new-e2e/pkg/runner/**/*
        - test/new-e2e/pkg/utils/**/*
        - test/new-e2e/go.mod
        - tasks/security_agent.py
        - tasks/kmt.py
        - tasks/kernel_matrix_testing/*
    - allow_failure: true
      when: manual
    script:
-   - dda inv -- -e ebpf.generate-complexity-summary-for-pr
+   - dda inv -- -e ebpf.generate-complexity-summary-for-pr --gitlab-config-file artifacts/after.gitlab-ci.yml
    stage: notify
    tags:
    - arch:amd64
    timeout: 15m
```

</details>

### Changes Summary
| Removed | Modified | Added | Renamed |
| ------- | -------- | ----- | ------- |
| 0 | 1 | 0 | 0 |

:information_source: *Diff available in the [job log](https://gitlab.ddbuild.io/DataDog/datadog-agent/-/jobs/948023768).*
author_association: CONTRIBUTOR
user: login: gjulianm
created_at: 2025-05-20T09:43:51Z
body: /merge
author_association: CONTRIBUTOR
user: login: dd-devflow[bot]
created_at: 2025-05-20T09:43:54Z
body: <!-- "FeedbackWorkflowId":"f4b91e9d-800d-49e7-90b8-2a6f7a95bb22_36" -->
[View all feedbacks in Devflow UI.](https://mosaic.us1.ddbuild.io/devflow/logs/f4b91e9d-800d-49e7-90b8-2a6f7a95bb22_36)

`2025-05-20 09:43:54 UTC` :information_source: Start processing command `/merge`

---
`2025-05-20 09:43:57 UTC` :x: MergeQueue

This PR is rejected because it was updated

author_association: NONE
user: login: gjulianm
created_at: 2025-05-20T09:45:06Z
body: /merge
author_association: CONTRIBUTOR
user: login: dd-devflow[bot]
created_at: 2025-05-20T09:45:11Z
body: <!-- "FeedbackWorkflowId":"f41d77b1-f4d4-463d-a648-de104cdfbfbf_36" -->
[View all feedbacks in Devflow UI.](https://mosaic.us1.ddbuild.io/devflow/logs/f41d77b1-f4d4-463d-a648-de104cdfbfbf_36)

`2025-05-20 09:45:10 UTC` :information_source: Start processing command `/merge`

---
`2025-05-20 09:45:18 UTC` :information_source: **MergeQueue:** waiting for PR to be ready

This merge request is not mergeable yet, because of pending checks/missing approvals. It will be added to the queue as soon as checks pass and/or get approvals.
**Note:** if you pushed new commits since the last approval, you may need additional approval.
You can remove it from the waiting list with `/remove` command.

---
`2025-05-20 13:46:05 UTC` :warning: **MergeQueue:** This merge request was unqueued

devflow unqueued this merge request: It did not become mergeable within the expected time

author_association: NONE
user: login: gjulianm
created_at: 2025-05-21T09:36:01Z
body: /merge
author_association: CONTRIBUTOR
user: login: dd-devflow[bot]
created_at: 2025-05-21T09:36:05Z
body: <!-- "FeedbackWorkflowId":"0fc6dc51-3bed-46aa-9e63-8deb05e6f896_36" -->
[View all feedbacks in Devflow UI.](https://mosaic.us1.ddbuild.io/devflow/logs/0fc6dc51-3bed-46aa-9e63-8deb05e6f896_36)

`2025-05-21 09:36:05 UTC` :information_source: Start processing command `/merge`

---
`2025-05-21 09:36:11 UTC` :information_source: **MergeQueue:** waiting for PR to be ready

This merge request is not mergeable yet, because of pending checks/missing approvals. It will be added to the queue as soon as checks pass and/or get approvals.
**Note:** if you pushed new commits since the last approval, you may need additional approval.
You can remove it from the waiting list with `/remove` command.

---
`2025-05-21 13:37:10 UTC` :warning: **MergeQueue:** This merge request was unqueued

devflow unqueued this merge request: It did not become mergeable within the expected time

author_association: NONE
user: login: gjulianm
created_at: 2025-05-21T13:59:26Z
body: /merge
author_association: CONTRIBUTOR
user: login: dd-devflow[bot]
created_at: 2025-05-21T13:59:35Z
body: <!-- "FeedbackWorkflowId":"0fdf47d9-3679-4bfd-b931-aad671625488_36" -->
[View all feedbacks in Devflow UI.](https://mosaic.us1.ddbuild.io/devflow/logs/0fdf47d9-3679-4bfd-b931-aad671625488_36)

`2025-05-21 13:59:34 UTC` :information_source: Start processing command `/merge`

---
`2025-05-21 13:59:49 UTC` :information_source: **MergeQueue:** waiting for PR to be ready

This merge request is not mergeable yet, because of pending checks/missing approvals. It will be added to the queue as soon as checks pass and/or get approvals.
**Note:** if you pushed new commits since the last approval, you may need additional approval.
You can remove it from the waiting list with `/remove` command.

---
`2025-05-21 15:58:16 UTC` :information_source: **MergeQueue:** merge request added to the queue

The expected merge time in `main` is approximately 1h (p90).

---
`2025-05-21 16:38:07 UTC` :information_source: **MergeQueue:** This merge request was merged

author_association: NONE