-
Notifications
You must be signed in to change notification settings - Fork 10
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: Add promql query converter and request handler #111
Conversation
...pl/src/main/java/org/hypertrace/core/query/service/promql/QueryRequestToPromqlConverter.java
Outdated
Show resolved
Hide resolved
...pl/src/main/java/org/hypertrace/core/query/service/promql/QueryRequestToPromqlConverter.java
Outdated
Show resolved
Hide resolved
...pl/src/main/java/org/hypertrace/core/query/service/promql/QueryRequestToPromqlConverter.java
Outdated
Show resolved
Hide resolved
Codecov Report
@@ Coverage Diff @@
## main #111 +/- ##
============================================
- Coverage 82.51% 78.60% -3.92%
- Complexity 456 508 +52
============================================
Files 41 50 +9
Lines 1716 2000 +284
Branches 180 216 +36
============================================
+ Hits 1416 1572 +156
- Misses 215 333 +118
- Partials 85 95 +10
Flags with carried forward coverage won't be shown. Click here to find out more.
Continue to review full report at Codecov.
|
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
query-service-impl/src/test/resources/prometheus_request_handler.conf
Outdated
Show resolved
Hide resolved
query-service-impl/src/test/resources/prometheus_request_handler.conf
Outdated
Show resolved
Hide resolved
tenantAttributeName = tenant_id | ||
prometheusViewDefinition { | ||
viewName = rawServiceView | ||
metricMap { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
each metric map entry, can we define something like
attribute: API.numCalls
metric : { metricName: "num_calls",
metricType: "GAUGE" }
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you log a ticket for this so we can change this structure to more readable and align with SERVICE.numCall
attribute pattern?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I thought the idea was to introduce a new API? Has that been done? Why did we choose to build from the bottom up - it makes it far easier to have to rework things when issues come up on integration at higher layers.
...main/java/org/hypertrace/core/query/service/prometheus/QueryRequestEligibilityValidator.java
Outdated
Show resolved
Hide resolved
...main/java/org/hypertrace/core/query/service/prometheus/QueryRequestEligibilityValidator.java
Show resolved
Hide resolved
...main/java/org/hypertrace/core/query/service/prometheus/QueryRequestEligibilityValidator.java
Show resolved
Hide resolved
return executionContext.getTimeSeriesPeriod().get(); | ||
} | ||
|
||
private String buildQuery( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we need this function as it is just returning the string from the arguments ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Some logic need to go in here (in the upcoming changes)
@Test | ||
void testCalculateCost_aggregationNotSupported() { | ||
Builder builder = QueryRequest.newBuilder(); | ||
builder.addAggregation( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use createCountByColumnSelection method ?
This comment has been minimized.
This comment has been minimized.
private QueryRequest buildMultipleGroupByMultipleAggQueryWithMultipleFiltersAndDateTime() { | ||
Builder builder = QueryRequest.newBuilder(); | ||
builder.addAggregation( | ||
createFunctionExpression("Count", createColumnExpression("SERVICE.errorCount").build())); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There is a separate function for count
{ | ||
name = raw-service-view-service-prometheus-handler | ||
type = prometheus | ||
clientConfig = "" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We need to hook host
and port
property for this in follow-up hook PR.
tenantAttributeName = tenant_id | ||
prometheusViewDefinition { | ||
viewName = rawServiceView | ||
metricMap { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you log a ticket for this so we can change this structure to more readable and align with SERVICE.numCall
attribute pattern?
metricName: "num_calls", | ||
metricType: "GAUGE" | ||
}, | ||
errorCount { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we only have num_calls emitter for now. So, can you remove this? We will add it later when we add error_count emitter.
"EVENT.protocolName": "protocol_name", | ||
"EVENT.status_code": "status_code", | ||
"API_TRACE.status_code": "status_code", | ||
"API.startTime": "start_time_millis", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we remove this "API.startTime": "start_time_millis", "API.endTime": "end_time_millis",
?
|
||
public class PrometheusBasedRequestHandler implements RequestHandler { | ||
|
||
public static final String VIEW_DEFINITION_CONFIG_KEY = "prometheusViewDefinition"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
public
-> private
private final String viewName; | ||
private final String tenantColumnName; | ||
private final Map<String, MetricConfig> metricMap; | ||
private final Map<String, String> columnMap; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we rename this to attributeMap?
Map<String, MetricConfig> metricMap, | ||
Map<String, String> columnMap) { | ||
this.viewName = viewName; | ||
this.tenantColumnName = tenantColumnName; // tenantAttributeName |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
tenantColumnName -> tenantAttributeName ?
@Value | ||
@AllArgsConstructor | ||
public static class MetricConfig { | ||
String name; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
name -> metricName ?
|
||
QueryCost calculateCost(QueryRequest queryRequest, ExecutionContext executionContext) { | ||
try { | ||
// orderBy to be supported later |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you create a ticket for this in same epic?
return true; | ||
} | ||
return prometheusViewDefinition.getMetricConfigForLogicalMetricName(attributeId) | ||
== null; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we put a comment as general (e.g only single arugment functions are supported on metrics columns only)?
} | ||
|
||
// filter lhs should be column or simple attribute | ||
if (!QueryRequestUtil.isSimpleColumnExpression(filter.getLhs())) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can e check this as leaf filter condition?
return true; | ||
} | ||
|
||
// todo check for valid operators here |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you remove this comment?
== null; | ||
}); | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Lets have a comment here that currently only AND
filters supported. Later this can be extended for OR filter on same column (attributeId) expression.
LinkedHashSet<Expression> allSelections, | ||
QueryTimeRange queryTimeRange, | ||
String timeFilterColumn) { | ||
List<String> groupByList = getGroupByList(request); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You will have to add tenant filter as default filter? Hopefully, tenant filter is already present in QueryReqeust?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yes added tenant filter, updated test
|
||
// iterate over all the functions in the query except for date time function (which is handled | ||
// separately and not a part of the query string) | ||
return allSelections.stream() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
allSelection = groupBy selection list + function selection list, right?
template, | ||
function, | ||
groupByList, | ||
function + "_over_time", // assuming gauge type of metric |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You have metricConfig with type (metricName, metricType), can we through the exception while preparing metricConfig itself that only supported type is Gauge for now?
"%s by (%s) (%s(%s{%s}[%sms]))"; // sum by (a1, a2) (sum_over_time(num_calls{a4="..", | ||
// a5=".."}[xms])) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you put this example // sum by (a1, a2) (sum_over_time(num_calls{a4="..", // a5=".."}[xms]))
above function?
} | ||
|
||
private MetricConfig getMetricConfigForFunction(Expression functionSelection) { | ||
return prometheusViewDefinition.getMetricConfigForLogicalMetricName( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If, you want here also we can check the type of metrics for gauge
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Checking the type in QueryRequestEligibilityValidator
*/ | ||
void convertFilterToString( | ||
Filter filter, | ||
List<String> filterList, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks like filterList is output? nit : can this be last argument? Also can we put comments for args?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yes, made it last argument
childFilter, filterList, timeFilterColumn, expressionToColumnConverter); | ||
} | ||
} else { | ||
if (QueryRequestUtil.isSimpleColumnExpression(filter.getLhs()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
isSimpleColumnExpression
do we need to check again? Isn't this already check in selection process?
StringBuilder builder = new StringBuilder(); | ||
builder.append(expressionToColumnConverter.apply(filter.getLhs())); | ||
builder.append(convertOperatorToString(filter.getOperator())); | ||
builder.append(convertLiteralToString(filter.getRhs().getLiteral())); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What will the null value convertLiteralToString
mean here? I haven't try such query? Instead of null, would it make to have ""
filter or no filter at all?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Throwing exception from convertLiteralToString
.build(); | ||
builder.setFilter(andFilter); | ||
|
||
builder.addGroupBy(createColumnExpression("SERVICE.name")); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shouldn't the selection expression required?
|
||
// time filter is removed from the query | ||
String query1 = | ||
"count by (service_name, api_name) (count_over_time(error_count{service_id=\"1|2|3\", service_name=~\"someregex\"}[100ms]))"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think, the value 100ms
of expression []
, we may have to change to value of time series period. we can talk on this.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
overall lgtm.
|
||
public enum MetricType { | ||
GAUGE, | ||
COUNTER |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In follow up remove the COUNTER for now.
This pr adds support for translating ht query request to promql, defines a prometheus view definition and request handler
Not every query can be served by prometheus, there are set of conditions under which a query maybe served by prometheus.
First a note on how data is stored in prometheus.
Currently
num_calls
metric time series is stored in prometheus with following attributesnum_calls(tenant_id, service_id, service_name, api_id, api_name)
This is a subset of the data from
rawServiceView
tableIn prometheus every metric will be an individual time series and there are some attributes on it for aggregations
The PrometheusViewDefinition (or PrometheusMetricDefinition) defines the attributeMapping and the metricMapping.
There are two types of prometheus query,
instant
andtimeseries
, any ht query request withdateTimeConvert
function is timeseries requestConditions for a query to be served by prometheus:
and
operator (prometheus only allows simple filter applied anded, https://prometheus.io/docs/prometheus/latest/querying/basics/#instant-vector-selectors)orderby
is not supported nowConversion example:
Rules for conversion:
by
clause of the queryhypertrace/hypertrace#324, hypertrace/hypertrace#320