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

Long running queries 2 #70

Merged
merged 3 commits into from Sep 23, 2022
Merged

Long running queries 2 #70

merged 3 commits into from Sep 23, 2022

Conversation

iwysiu
Copy link
Contributor

@iwysiu iwysiu commented Sep 20, 2022

Second take at this, to have the async code in aws-sdk

@sunker
Copy link
Collaborator

sunker commented Sep 21, 2022

@grafana/aws-plugins @andresmgot Adding my high level comments here this time. For more context, see
discussion in #69.

I think this looks like a great step forward! sqlds is now basically untouched and I appreciate that. Haven't reviewed the code in detail yet, but in general, I think this is something we could ship to production.

I wonder though if we could take one more step to make AsyncAWSDatasource more generic so that it's closer to be reusable by other plugins. Basically, I want the following interface

type AsyncDB interface {
	// DB generic methods
	driver.Conn
	Ping(ctx context.Context) error

	// Async flow
	StartQuery(ctx context.Context, query string, args ...interface{}) (string, error)
	GetQueryID(ctx context.Context, query string, args ...interface{}) (bool, string, error)
	QueryStatus(ctx context.Context, queryID string) (QueryStatus, error)
	CancelQuery(ctx context.Context, queryID string) error
	GetRows(ctx context.Context, queryID string) (driver.Rows, error)
}

...to be changed to:

type AsyncDatasource interface {
	StartQuery(ctx context.Context, query string, args ...interface{}) (string, error)
	GetQueryID(ctx context.Context, query string, args ...interface{}) (bool, string, error)
	QueryStatus(ctx context.Context, queryID string) (QueryStatus, error)
	CancelQuery(ctx context.Context, queryID string) error
	GetRows(ctx context.Context, req backend.DataQuery) (data.Frames, error) {
}

There might be things that I'm missing here that is preventing us from doing this refactoring now, but can you investigate if it would be possible for AsyncAWSDatasource to call the methods on AsyncDatasource directly instead of going through sqlds? That doesn't mean we have to completely stop using sqlds now - the redshift datasource could for example implement the GetRows(ctx context.Context, req backend.DataQuery) (data.Frames, error) method, and then just call handleQuery in sqlds (this method would have to be exported then).

If this is possible to do without a complete rewrite of the plugin, we should be able to remove any reference to sqlds in the AsyndAWSDatasource and rename it to AsyncDatasource.

@andresmgot
Copy link
Collaborator

andresmgot commented Sep 21, 2022

would be possible for AsyncAWSDatasource to call the methods on AsyncDatasource directly instead of going through sqlds? That doesn't mean we have to completely stop using sqlds

All this is starting to fade in my mind so disregard if this doesn't make sense. The problem I see with that is that basically you will end up with two backend data sources for the same plugin. In the case of an sync request, it would behave as a sqlds while for async requests it would be an AsyncDataSource. Having the abstraction at the database layer allows you to share some logic (db related) between the sync and the async flow. It has the disadvantage of not being able to follow the async flow for things that are not databases but afaik all the cases we are evaluating are databases.

@iwysiu iwysiu marked this pull request as ready for review September 21, 2022 13:42
@sunker
Copy link
Collaborator

sunker commented Sep 22, 2022

The problem I see with that is that basically you will end up with two backend data sources for the same plugin.

For me the long term goal is to move away from sqlds. sqlds does indeed do a few things for us (rows 2 frames conversion, macro interpolation etc), but it also locks us into a certain pattern where adding new features such as async queries becomes really painful. For AWS sql plugins, I think it's the fact that sqlds acts as a datasource instance that is preventing us for easily enhancing it and adding new features. I'd prefer to have a sqlds library instead that could provide for example utilities for macro interpolation, types for base sql queries and so on. Had we not had sqlds now, it would be fairly straightforward to implement async query support, and the async query data abstraction could live an a separate go package/npm package to could be reused by any plugin.

Talked to @iwysiu and we agreed to stick to the path taken in this PR (and its linked PRs). Once it's shipped to production, we can start looking into ways of moving away from sqlds. I'll take a closer look at the code during the day. Thanks everyone!

Copy link
Collaborator

@andresmgot andresmgot left a comment

Choose a reason for hiding this comment

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

LGTM! just a couple of minor naming suggestions

completion.go Outdated
@@ -49,7 +49,7 @@ func sendResourceResponse(rw http.ResponseWriter, res []string) {
}
}

func (ds *sqldatasource) getResources(rtype string) func(rw http.ResponseWriter, req *http.Request) {
func (ds *SqlDatasource) getResources(rtype string) func(rw http.ResponseWriter, req *http.Request) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
func (ds *SqlDatasource) getResources(rtype string) func(rw http.ResponseWriter, req *http.Request) {
func (ds *SQLDatasource) getResources(rtype string) func(rw http.ResponseWriter, req *http.Request) {

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done

// query sends the query to the connection and converts the rows to a dataframe.
func query(ctx context.Context, db Connection, converters []sqlutil.Converter, fillMode *data.FillMissing, query *Query) (data.Frames, error) {
// QueryDB sends the query to the connection and converts the rows to a dataframe.
func QueryDB(ctx context.Context, db Connection, converters []sqlutil.Converter, fillMode *data.FillMissing, query *Query, args ...interface{}) (data.Frames, error) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

why not just Query?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Unfortunately, Query is already the name of the query unmarshaling struct.

Copy link
Collaborator

@sunker sunker left a comment

Choose a reason for hiding this comment

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

Nice!

@iwysiu iwysiu merged commit 1f2fed4 into main Sep 23, 2022
@iwysiu iwysiu deleted the long-running-queries-2 branch September 23, 2022 12:44
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.

None yet

3 participants