Skip to content
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

Update chart to add metrics sql support #4277

Merged
merged 5 commits into from
Mar 8, 2024

Conversation

AdityaHegde
Copy link
Collaborator

@AdityaHegde AdityaHegde commented Mar 8, 2024

This PR replaces the old queryName and queryArgs with metrics sql support.

New format,

data:
  metrics_sql: |
    SELECT domain, count(*) as impression
    FROM AdBids_model
    GROUP BY domain
data:
  api: adbids_per_publisher

New API,

➜  rill git:(main) curl http://localhost:9009/v1/instances/default/charts/adbids_domain_chart/data
[{"domain":"sports.yahoo.com","impression":12889},{"domain":"news.google.com","impression":12883},{"domain":"facebook.com","impression":15609},{"domain":"instagram.com","impression":13104},{"domain":"news.yahoo.com","impression":14870},{"domain":"msn.com","impression":15526},{"domain":"google.com","impression":15119}]

Note: if any chart was created, the db file will have to be deleted.

chartName := req.PathValue("chart_name")
// TODO: is a separate auth needed?
if !auth.GetClaims(ctx).CanInstance(instanceId, auth.ReadMetrics) {
return ErrForbidden
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This returns grpc error which I think will map to a bad request. Should use httputil.Error.


ch, err := resolveChart(ctx, s.runtime, instanceId, chartName)
if err != nil {
return err
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as above, it will again map to http.StatusBadRequest.

@@ -0,0 +1,116 @@
package server
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

may be this should be charts_api.go >

@@ -208,6 +208,9 @@ func (s *Server) HTTPHandler(ctx context.Context, registerAdditionalHandlers fun
// custom API handler
observability.MuxHandle(httpMux, "/v1/instances/{instance_id}/api/{name...}", observability.Middleware("runtime", s.logger, auth.HTTPMiddleware(s.aud, httputil.Handler(s.apiForName))))

// custom Chart Data handler
observability.MuxHandle(httpMux, "/v1/instances/{instance_id}/charts/{chart_name...}", observability.Middleware("runtime", s.logger, auth.HTTPMiddleware(s.aud, httputil.Handler(s.GetChartData))))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Path also had /data suffix?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah yes will add that.

func resolveChart(ctx context.Context, rt *runtime.Runtime, instanceID, name string) (*runtimev1.ChartSpec, error) {
ctrl, err := rt.Controller(ctx, instanceID)
if err != nil {
return nil, status.Error(codes.InvalidArgument, err.Error())
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should not return grpc errors otherwise everything gets mapped to bad request (which although is correct in this case). See httputil.WriteError.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ya updating this method to just return the error as is. The caller will handle the not found and to use httputil.Errorf


res, err := ctrl.Get(ctx, &runtimev1.ResourceName{Kind: runtime.ResourceKindChart, Name: name}, false)
if err != nil {
return nil, status.Error(codes.InvalidArgument, err.Error())
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should handle notfound case separately since any other error apart from that is an internal error.

@AdityaHegde AdityaHegde force-pushed the adityahegde/chart-metrics-sql branch from e8cdf75 to 0b715c2 Compare March 8, 2024 12:07
Comment on lines 399 to 401
string metrics_sql = 2;
string api = 3;
map<string, string> args = 4;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of putting the YAML properties here, I would suggest having data_resolver and data_resolver_properties mirroring the APISpec. And then we should have one common YAML structure for parsing a data: clause (which we will want to use in all of APIs, charts, alerts, reports, etc.).

This would be similar to the parseScheduleYAML function (see here) in the compiler. We might have this:

type DataYAML struct {
  SQL string `...`
  MetricsSQL string `...`
  // Can add others. Like "api" for referencing another existing API.
}

func parseDataYAML(raw *DataYAML) (resolver string, resolverProps map[string]any, error) {
  ...
}

// In charts:
type ChartYaml struct {
  // ...
  Data *DataYAML `yaml:"data"`
}

// In alerts:
type AlertYAML struct {
  // ...
  Data *DataYAML `yaml:"data"` // This will eventually replace/deprecate the "query:" property
}

// In APIs:
type APIYAML struct {
  // ...
  Data *DataYAML `yaml:",inline"` // Inline since API's don't nest under "data":
}

// etc.

@AdityaHegde AdityaHegde force-pushed the adityahegde/chart-metrics-sql branch from 47841d8 to 8ea285b Compare March 8, 2024 12:41
if (tmp.Data.MetricsSQL == "" && tmp.Data.API == "") || (tmp.Data.MetricsSQL != "" && tmp.Data.API != "") {
return fmt.Errorf("exactly one of metrics_sql or api should be set")
if tmp.Data == nil {
return errors.New("data is mandatory")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
return errors.New("data is mandatory")
return errors.New(`missing required property "data"`)

Comment on lines 53 to 57
// Convert resolver properties to structpb.Struct before inserting the resource (since we can't return errors after that point)
resolverPropsPB, err := structpb.NewStruct(resolverProps)
if err != nil {
return fmt.Errorf("encountered invalid property type: %w", err)
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, can we also move this into the parseDataYAML function, and return a structpb value instead of a map? Since otherwise, we will need to duplicate it every place that has a data section.

Comment on lines 109 to 137
switch resolver {
case "Metrics":
resolverPropsPB, err := structpb.NewStruct(map[string]interface{}{
"sql": resolverProps["sql"],
})
if err != nil {
return nil, err
}
// TODO: are other fields needed?
api = &runtimev1.API{
Spec: &runtimev1.APISpec{
Resolver: "Metrics",
ResolverProperties: resolverPropsPB,
},
}

case "API":
apiName, ok := resolverProps["api"].(string)
if !ok {
return nil, errors.New("api name is missing")
}
api, err = rt.APIForName(ctx, instanceID, apiName)
if err != nil {
return nil, err
}

default:
return nil, errors.New("unknown resolver")
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would suggest refactoring runtime.APIResolverOptions to take Resolver and ResolverProperties as direct options instead of an API.

Since the point is to make the resolvers generic enough that all the code between the parser and the actual resolver doesn't need to know about the different available options.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To make that work, you probably also need to add a new resolver called "API" that proxies to another resolver (and maybe checks to avoid infinite recursion if you want to be fancy?)

@begelundmuller begelundmuller merged commit 00a4a18 into main Mar 8, 2024
7 checks passed
@begelundmuller begelundmuller deleted the adityahegde/chart-metrics-sql branch March 8, 2024 17:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants