Topology Context
Topology (entities and associations) is a concept designed to organize MELT (Metrics, Events, Logs, and Traces) data. Most of the observation (MELT) data is assigned to some entity.
Entities also provide state information. For example, attributes or tags.
Entities are organized using associations - the oriented edge between two entities.
Entity types support inheritance. For example, k8s:cronjob
extends k8s:workload
. This is a unique differentiator from other AppD types.
The topology context of the query is defined in the FROM
block.
Note
Use uppercase or lowercase. Do not use mixed-case. All block keywords are valid in upper or lower case but not in mixed-case. Valid variants are
from
andFROM
.
Entities
List all entities of provided type(s):
entities(<entityType>[, <entityType>...])
List all entities of provided ID(s):
entities(<entityID>[, <entityID>...])
Example:
List IDs of all k8s clusters:
Copy
FETCH id
FROM entities(k8s:cluster)
Example:
List IDs of all k8s clusters and workloads:
Copy
FETCH id
FROM entities(k8s:cluster, k8s:workload)
Example:
List attributes of the specified entity:
Copy
FETCH attributes
FROM entities(k8s:cluster:9SsSYDbUPpeImCl8gTP0Iw)
Entity type inheritance
The entity type system supports inheritance. The entities(<type>)
function returns all entities of the given type and all subtypes.
fetch id from entities(apm:backend)
returns entities of apm:backend
, apm:custom_backend
, apm:database_backend
, and apm:http_backend
types.
Associations and Associated Entities
Associations are typed, oriented edges between entities. UQL can select associations only from entities.
Associations do not have assigned observations. You can use it to find entities on the opposite side. However, there is a potential to enrich associations with observations. Hence, they are supported in UQL.
<entity_function>.out([type1, type2, ...])
- Get outgoing association of specified type(s) from specified entity(ies). If type is not specified, then all outgoing associations are returned.<entity_function>.in([type1, type2, ...])
- Get incoming association of specified type(s) to specified entity(ies). If type is not specified, then all incoming associations are returned.<entity_function>.out([type1, type2, ...]).to([type1, type2, ...])
Get entities on the opposite side of the outgoing association(s). You can also specify requested entity types.
<entity_function>.in([type1, type2, ...]).from([type1, type2, ...])
Get entities on the opposite side of the incoming association(s).
You can also specify requested entity types.
Example 1:
Get Kubernetes namespaces of a given cluster:
Copy
fetch id
from
entities(k8s:cluster:8oWfRTQ9PBiOzEuRTleRug).out.to(k8s:namespace)
Example 2:
Get Kubernetes cluster for a given namespace:
Copy
fetch id
from
entities(k8s:namespace:7ghds179mdsGHb8s).in.from(k8s:cluster)
Example 3:
Get all entities (of any type) that are associated with a given cluster using common:has
association:
Copy
fetch id
from
entities(k8s:cluster:8oWfRTQ9PBiOzEuRTleRug).out(has).to
You can chain association queries.
Example 4:
Get Kubernetes pods of a given cluster passing all namespaces.
Copy
fetch id
from
entities(k8s:cluster:/MAVf/ftO4eYSaU/5v9Jiw)
.out.to(k8s:namespace)
.out.to(k8s:pod)
Alias
You can 'name' the result of the topology query (from
block) to reference it from other blocks. For example, the fetch
block. This name is called alias.
To add an alias:
<alias>: <topology expression>
The format of alias is: [a-zA-Z][a-zA-Z0-9]*
Example:
Use an alias in the from
block:
Copy
FETCH cls.id
FROM
cls: entities(k8s:cluster)
Joins
You can 'name' the result of the topology query (from
block) to reference it from other blocks, for example, the fetch
block. You can use the association between entities to traverse the topology. The associations are called joins.
For example, list nodes together with their pods (not just pods related to the node).
You can define multiple topology queries, each with its alias and with cross-referencing.
Syntax - Inner join:
Copy
<alias_1>: <topology query>,
<alias_2>: <alias_1>.<topology_query>,
...
<alias_N>: <alias_*>.<topology_query>
The result of the preceding query is an array of tuples data type containing all aliases.
Note
Above syntax defines inner join. So, all aliases in all tuples have non-null entities. So, if you list k8s nodes with their pods then nodes without any pod are not be shown in the result.
Example:
List nodes with their pods:
Copy
fetch
n.id,
p.id
from
n: entities(k8s:node),
p: n.out.to(k8s:pod)
The result is an array of pairs - node-ID, pod-ID
. So, each node appears in the result the same number of times as its pods.
Left Joins
Joins give you the ability to get nodes together with their pods. But nodes without pods are not included in the results until you use Left Joins.
Example:
List nodes with their (optional) pods:
Copy
fetch
n.id,
p.id
from
n: entities(k8s:node),
p?: n.out.to(k8s:pod)
The difference is the ?
mark at the p
alias. It indicates that pods are optional.
The query returns node-ID
and pod-ID
pairs for all nodes and their pods (the same as for Joins).
It also returns node-ID
and null
pairs for all nodes that do not have any pod. Hence, Left Join query returns all nodes while the Join query returns only nodes with at least one pod.
Entity Cross-References Filter
When filtering entities, you can use a defined alias to reference a different entity set.
Example 1:
Entity cross-reference filtering:
Copy
fetch
n.id,
p.id
from
n: entities(k8s:node),
p: n.out.to(k8s:pod)[n.attributes(node.name) = 'node123']
Example 2:
Entity cross-reference equivalent filtering:
Copy
fetch
n.id,
p.id
from
n: entities(k8s:node)[attributes(node.name) = 'node123'],
p: n.out.to(k8s:pod)
The results of examples 1 and 2 are the same. So the question is, what is the use of cross-references?
There is a use case for optional entities (left join). Let’s imagine a query with result:
Copy
fetch
n.attributes(node.name),
p.attributes(pod.name)
from
n: entities(k8s:node),
p?: n.out.to(k8s:pod)[attributes(pod.name) in ['pod123', 'pod456']]
n.attributes(node.name) | p.attributes(pod.name) |
---|---|
node123 |
pod123 |
node456 |
pod456 |
node789 |
null |
node789
has no pod with a specified name so the second column is null
. Let’s imagine that you don’t want to see rows with the pod pod456
.
The following approach doesn’t lead to the requested result. The reason is that p
is optional.
Copy
fetch
n.attributes(node.name),
p.attributes(pod.name)
from
n: entities(k8s:node),
p?: n.out.to(k8s:pod)[attributes(pod.name) in ['pod123']]
n.attributes(node.name) | p.attributes(pod.name) |
---|---|
node123 |
pod123 |
node456 |
null |
node789 |
null |
But this query leads to the requested result:
Copy
fetch
n.attributes(node.name),
p.attributes(pod.name)
from
n: entities(k8s:node),
p?: n.out.to(k8s:pod)[attributes(pod.name) in ['pod123', 'pod456']],
nFiltered: n[p.attributes(pod.name) in ['pod123']]
n.attributes(node.name) | p.attributes(pod.name) |
---|---|
node123 |
pod123 |
node789 |
null |
You can filter mandatory entity nodes by the optional entity nodes.
Limits
You can limit the maximum number of topology results returned by a query. To set the topology count limit:
CopyLIMITS topology.count(50)
The default limit on the number of topology is 1000.
Order
You can use the topology order function to specify the order of topology results returned by a query.
You can order topology by:
entity fields. For example,
attributes(location)
.functions. For example,
isActive
.metric observations. For example,
metrics(calls_min, 'sys:derived').value
.
Note You must use an alias in the FETCH clause to define what to order by.
The following examples illustrate how to order topology:
Example 1:
Ascending order by an entity field attribute:
CopyFETCH loc: attributes(location)
ORDER topology.asc(loc)
Example 2:
Descending order by entity an observation metric
CopyFETCH m: metrics(calls_min, apm).value
ORDER topology.desc(m)
Example 3:
Chain of order constructs:
CopyFETCH alias1: ..., alias2: ...
ORDER topology.asc(alias1).desc(alias2)
Order by Metric Observations to Get Top N Entities
You can order entities or entity groups by their metric value to get top N entities.
From a large entity set and wide time ranges, you can get the best performing entities based on a metric observation. Filter out the entities that are not reporting the metric. For example, you can get the top 10 slowest services by using the following query:
Copy
FETCH
id,
rt: metrics(response_time, apm).value
FROM
entities(apm:service)[
metrics(response_time, apm).value != null
]
ORDER topology.desc(rt)
LIMIT topology.count(10)