Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
71 changes: 62 additions & 9 deletions datadog_sync/model/metric_percentiles.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,17 @@
)


# The bulk toggle endpoints always return 200, even for a metric they decline to
# touch (wrong summary_aggr source, unresolvable name, etc.) - those come back in
# the response body's "unsuccessful" list with no per-metric reason. This failure
# class distinguishes that case from FAILURE_CLASS_DESTINATION_METRIC_MISSING.
FAILURE_CLASS_DESTINATION_METRIC_NOT_CONFIGURABLE = "destination_metric_not_configurable"

# /api/v2/metrics window[seconds] and page[size] limits (governance app).
_WINDOW_SECONDS_14D = 14 * 86400
_PAGE_SIZE = 10000


def _error_body(error: CustomClientHTTPError) -> str:
return (error.response_body or "").lower()

Expand All @@ -29,17 +40,44 @@ class MetricPercentiles(BaseResource):
skip_resource_mapping=True,
)
# Additional MetricPercentiles specific attributes
metrics_summaries_get_path = "/metric/distribution/list_summaries"
metrics_list_path = "/api/v2/metrics"
enable_percentiles_path = "/metric/distribution/summary_aggr/percentiles/enable"
disable_percentiles_path = "/metric/distribution/summary_aggr/percentiles/disable"

async def get_resources(self, client: CustomClient) -> List[Dict]:
params = {
"window": 14 * 86400, # 14 days
}
resp = await client.get(self.metrics_summaries_get_path, params=params)

return resp
# The legacy /metric/distribution/list_summaries endpoint (mcnulty) leaks
# internal summary_aggr fields (e.g. summary_aggr.key) that we never used -
# include_percentiles is the only field this resource actually needs. The
# public governance API (/api/v2/metrics) doesn't attach include_percentiles
# to the response for metrics whose summary_aggr aggr_mode is still at its
# unconfigured default, but its filter[include_percentiles] facet queries the
# raw stored boolean directly and isn't affected by that gap. So we recover
# the same information via list membership: one call per boolean value.
metrics: Dict[str, Dict] = {}
for include_percentiles in (True, False):
cursor = None
while True:
params = {
"filter[metric_type]": "distribution",
"filter[include_percentiles]": "true" if include_percentiles else "false",
"window[seconds]": _WINDOW_SECONDS_14D,
"page[size]": _PAGE_SIZE,
}
if cursor is not None:
params["page[cursor]"] = cursor

resp = await client.get(self.metrics_list_path, params=params)
for item in resp["data"]:
metrics[item["id"]] = {
"metric_name": item["id"],
"include_percentiles": include_percentiles,
}

cursor = (resp.get("meta") or {}).get("pagination", {}).get("next_cursor")
if not cursor:
break

return list(metrics.values())

async def import_resource(self, _: Optional[str] = None, resource: Optional[Dict] = None) -> Tuple[str, Dict]:
# The bulk-toggle endpoints only accept metric_names; group_by, aggr_mode,
Expand Down Expand Up @@ -69,11 +107,11 @@ async def update_resource(self, _id: str, resource: Dict) -> Tuple[str, Dict]:
# returns 403 empty-body at the OBO auth layer.
destination_client = self.config.destination_client
path = self.enable_percentiles_path if resource.get("include_percentiles") else self.disable_percentiles_path
operation = "percentiles_enable" if resource.get("include_percentiles") else "percentiles_disable"
try:
await destination_client.patch(path, {"metric_names": [_id]})
resp = await destination_client.patch(path, {"metric_names": [_id]})
except CustomClientHTTPError as e:
if _is_metric_not_found_error(e):
operation = "percentiles_enable" if resource.get("include_percentiles") else "percentiles_disable"
raise SkipResource(
_id,
self.resource_type,
Expand All @@ -84,6 +122,21 @@ async def update_resource(self, _id: str, resource: Dict) -> Tuple[str, Dict]:
)
raise

# A 200 here doesn't mean the toggle actually applied - the destination
# silently declines metrics it can't configure (e.g. a summary_aggr source
# that isn't percentile-configurable) by putting them in "unsuccessful"
# instead of raising. Without this check that no-op reads as a success.
if _id in (resp or {}).get("unsuccessful", []):
raise SkipResource(
_id,
self.resource_type,
"Destination declined to toggle percentiles for this metric "
"(ineligible summary_aggr source or unresolved metric name).",
failure_class=FAILURE_CLASS_DESTINATION_METRIC_NOT_CONFIGURABLE,
reason=FAILURE_CLASS_DESTINATION_METRIC_NOT_CONFIGURABLE,
outcome_details={"metric_name": _id, "operation": operation},
)

return _id, resource

async def delete_resource(self, _id: str) -> None:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,51 +5,26 @@ interactions:
Content-Type:
- application/json
method: GET
uri: https://api.datadoghq.eu/metric/distribution/list_summaries?window=1209600
uri: https://api.datadoghq.eu/api/v2/metrics?filter%5Bmetric_type%5D=distribution&filter%5Binclude_percentiles%5D=true&window%5Bseconds%5D=1209600&page%5Bsize%5D=10000
response:
body:
string: '[{"metric_name": "runtime.go.metrics.sched_latencies.seconds", "group_by":
["app_type", "build_commit", "build_ts", "datacenter", "env", "framework_version",
"git_branch", "go_version", "gogc", "gomaxprocs", "gomemlimit", "goversion",
"lang", "lang_version", "language", "pgo", "protocol", "runtime-id", "service",
"team", "tracer_version", "type"], "include_percentiles": false, "aggr_mode":
"include_all", "summary_type": "default", "groups_negated": false, "key":
371903746, "source": "intake"}, {"metric_name": "runtime.go.metrics.gc_heap_frees_by_size.bytes",
"group_by": ["app_type", "build_commit", "build_ts", "datacenter", "env",
"framework_version", "git_branch", "go_version", "gogc", "gomaxprocs", "gomemlimit",
"goversion", "lang", "lang_version", "language", "pgo", "protocol", "runtime-id",
"service", "team", "tracer_version", "type"], "include_percentiles": false,
"aggr_mode": "include_all", "summary_type": "default", "groups_negated": false,
"key": 371903770, "source": "intake"}, {"metric_name": "runtime.go.metrics.gc_heap_allocs_by_size.bytes",
"group_by": ["app_type", "build_commit", "build_ts", "datacenter", "env",
"framework_version", "git_branch", "go_version", "gogc", "gomaxprocs", "gomemlimit",
"goversion", "lang", "lang_version", "language", "pgo", "protocol", "runtime-id",
"service", "team", "tracer_version", "type"], "include_percentiles": false,
"aggr_mode": "include_all", "summary_type": "default", "groups_negated": false,
"key": 371903729, "source": "intake"}, {"metric_name": "runtime.go.metrics.sched_pauses_total_gc.seconds",
"group_by": ["app_type", "build_commit", "build_ts", "datacenter", "env",
"framework_version", "git_branch", "go_version", "gogc", "gomaxprocs", "gomemlimit",
"goversion", "lang", "lang_version", "language", "pgo", "protocol", "runtime-id",
"service", "team", "tracer_version", "type"], "include_percentiles": false,
"aggr_mode": "include_all", "summary_type": "default", "groups_negated": false,
"key": 371903753, "source": "intake"}, {"metric_name": "runtime.go.metrics.sched_pauses_stopping_gc.seconds",
"group_by": ["app_type", "build_commit", "build_ts", "datacenter", "env",
"framework_version", "git_branch", "go_version", "gogc", "gomaxprocs", "gomemlimit",
"goversion", "lang", "lang_version", "language", "pgo", "protocol", "runtime-id",
"service", "team", "tracer_version", "type"], "include_percentiles": false,
"aggr_mode": "include_all", "summary_type": "default", "groups_negated": false,
"key": 371903756, "source": "intake"}, {"metric_name": "msr.other_distribution",
"group_by": [], "include_percentiles": true, "aggr_mode": "include_all", "summary_type":
"default", "groups_negated": false, "key": 285536557, "source": "intake"},
{"metric_name": "msr.distribution", "group_by": ["environment", "host"], "include_percentiles":
true, "aggr_mode": "use_groups", "summary_type": "default", "groups_negated":
false, "key": 285529188, "source": "intake"}, {"metric_name": "runtime.go.metrics.gc_pauses.seconds",
"group_by": ["app_type", "build_commit", "build_ts", "datacenter", "env",
"framework_version", "git_branch", "go_version", "gogc", "gomaxprocs", "gomemlimit",
"goversion", "lang", "lang_version", "language", "pgo", "protocol", "runtime-id",
"service", "team", "tracer_version", "type"], "include_percentiles": false,
"aggr_mode": "include_all", "summary_type": "default", "groups_negated": false,
"key": 371903757, "source": "intake"}]'
string: '{"data": [{"id": "msr.other_distribution", "type": "metric"}, {"id": "msr.distribution", "type": "metric"}], "meta": {"pagination": {}}}'
headers:
Content-Type:
- application/json
status:
code: 200
message: OK
- request:
body: null
headers:
Content-Type:
- application/json
method: GET
uri: https://api.datadoghq.eu/api/v2/metrics?filter%5Bmetric_type%5D=distribution&filter%5Binclude_percentiles%5D=false&window%5Bseconds%5D=1209600&page%5Bsize%5D=10000
response:
body:
string: '{"data": [{"id": "runtime.go.metrics.sched_latencies.seconds", "type": "metric"}, {"id": "runtime.go.metrics.gc_heap_frees_by_size.bytes", "type": "metric"}, {"id": "runtime.go.metrics.gc_heap_allocs_by_size.bytes", "type": "metric"}, {"id": "runtime.go.metrics.sched_pauses_total_gc.seconds", "type": "metric"}, {"id": "runtime.go.metrics.sched_pauses_stopping_gc.seconds", "type": "metric"}, {"id": "runtime.go.metrics.gc_pauses.seconds", "type": "metric"}], "meta": {"pagination": {}}}'
headers:
Content-Type:
- application/json
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,51 +25,26 @@ interactions:
Content-Type:
- application/json
method: GET
uri: https://api.datadoghq.eu/metric/distribution/list_summaries?window=1209600
uri: https://api.datadoghq.eu/api/v2/metrics?filter%5Bmetric_type%5D=distribution&filter%5Binclude_percentiles%5D=true&window%5Bseconds%5D=1209600&page%5Bsize%5D=10000
response:
body:
string: '[{"metric_name": "runtime.go.metrics.sched_latencies.seconds", "group_by":
["app_type", "build_commit", "build_ts", "datacenter", "env", "framework_version",
"git_branch", "go_version", "gogc", "gomaxprocs", "gomemlimit", "goversion",
"lang", "lang_version", "language", "pgo", "protocol", "runtime-id", "service",
"team", "tracer_version", "type"], "include_percentiles": false, "aggr_mode":
"include_all", "summary_type": "default", "groups_negated": false, "key":
371903746, "source": "intake"}, {"metric_name": "runtime.go.metrics.gc_heap_frees_by_size.bytes",
"group_by": ["app_type", "build_commit", "build_ts", "datacenter", "env",
"framework_version", "git_branch", "go_version", "gogc", "gomaxprocs", "gomemlimit",
"goversion", "lang", "lang_version", "language", "pgo", "protocol", "runtime-id",
"service", "team", "tracer_version", "type"], "include_percentiles": false,
"aggr_mode": "include_all", "summary_type": "default", "groups_negated": false,
"key": 371903770, "source": "intake"}, {"metric_name": "runtime.go.metrics.gc_heap_allocs_by_size.bytes",
"group_by": ["app_type", "build_commit", "build_ts", "datacenter", "env",
"framework_version", "git_branch", "go_version", "gogc", "gomaxprocs", "gomemlimit",
"goversion", "lang", "lang_version", "language", "pgo", "protocol", "runtime-id",
"service", "team", "tracer_version", "type"], "include_percentiles": false,
"aggr_mode": "include_all", "summary_type": "default", "groups_negated": false,
"key": 371903729, "source": "intake"}, {"metric_name": "runtime.go.metrics.sched_pauses_total_gc.seconds",
"group_by": ["app_type", "build_commit", "build_ts", "datacenter", "env",
"framework_version", "git_branch", "go_version", "gogc", "gomaxprocs", "gomemlimit",
"goversion", "lang", "lang_version", "language", "pgo", "protocol", "runtime-id",
"service", "team", "tracer_version", "type"], "include_percentiles": false,
"aggr_mode": "include_all", "summary_type": "default", "groups_negated": false,
"key": 371903753, "source": "intake"}, {"metric_name": "runtime.go.metrics.sched_pauses_stopping_gc.seconds",
"group_by": ["app_type", "build_commit", "build_ts", "datacenter", "env",
"framework_version", "git_branch", "go_version", "gogc", "gomaxprocs", "gomemlimit",
"goversion", "lang", "lang_version", "language", "pgo", "protocol", "runtime-id",
"service", "team", "tracer_version", "type"], "include_percentiles": false,
"aggr_mode": "include_all", "summary_type": "default", "groups_negated": false,
"key": 371903756, "source": "intake"}, {"metric_name": "msr.other_distribution",
"group_by": [], "include_percentiles": true, "aggr_mode": "include_all", "summary_type":
"default", "groups_negated": false, "key": 285536557, "source": "intake"},
{"metric_name": "msr.distribution", "group_by": ["environment", "host"], "include_percentiles":
true, "aggr_mode": "use_groups", "summary_type": "default", "groups_negated":
false, "key": 285529188, "source": "intake"}, {"metric_name": "runtime.go.metrics.gc_pauses.seconds",
"group_by": ["app_type", "build_commit", "build_ts", "datacenter", "env",
"framework_version", "git_branch", "go_version", "gogc", "gomaxprocs", "gomemlimit",
"goversion", "lang", "lang_version", "language", "pgo", "protocol", "runtime-id",
"service", "team", "tracer_version", "type"], "include_percentiles": false,
"aggr_mode": "include_all", "summary_type": "default", "groups_negated": false,
"key": 371903757, "source": "intake"}]'
string: '{"data": [{"id": "msr.other_distribution", "type": "metric"}, {"id": "msr.distribution", "type": "metric"}], "meta": {"pagination": {}}}'
headers:
Content-Type:
- application/json
status:
code: 200
message: OK
- request:
body: null
headers:
Content-Type:
- application/json
method: GET
uri: https://api.datadoghq.eu/api/v2/metrics?filter%5Bmetric_type%5D=distribution&filter%5Binclude_percentiles%5D=false&window%5Bseconds%5D=1209600&page%5Bsize%5D=10000
response:
body:
string: '{"data": [{"id": "runtime.go.metrics.sched_latencies.seconds", "type": "metric"}, {"id": "runtime.go.metrics.gc_heap_frees_by_size.bytes", "type": "metric"}, {"id": "runtime.go.metrics.gc_heap_allocs_by_size.bytes", "type": "metric"}, {"id": "runtime.go.metrics.sched_pauses_total_gc.seconds", "type": "metric"}, {"id": "runtime.go.metrics.sched_pauses_stopping_gc.seconds", "type": "metric"}, {"id": "runtime.go.metrics.gc_pauses.seconds", "type": "metric"}], "meta": {"pagination": {}}}'
headers:
Content-Type:
- application/json
Expand Down
Loading
Loading