Skip to content

Expose HTTP Response headers from SqlResource - #13052

Merged
cheddar merged 4 commits into
apache:masterfrom
imply-cheddar:http-resp-headers-for-sql
Sep 12, 2022
Merged

cheddar merged 4 commits into
apache:masterfrom
imply-cheddar:http-resp-headers-for-sql

Conversation

@imply-cheddar

Copy link
Copy Markdown
Contributor

This change makes the SqlResource expose HTTP response headers in the same way that the QueryResource exposes them.

Fundamentally, the change is to pipe the QueryResponse object all the way through to the Resource so that it can populate response headers. There is also some code cleanup around DI, as there was a superfluous FactoryFactory class muddying things up.


This PR has:

  • been self-reviewed.
  • added comments explaining the "why" and the intent of the code wherever would not be obvious for an unfamiliar reader.
  • added unit tests or modified existing tests to cover new code paths, ensuring the threshold for code coverage is met.

@imply-cheddar
imply-cheddar force-pushed the http-resp-headers-for-sql branch from 177b179 to 3eee4ce Compare September 8, 2022 02:14
This change makes the SqlResource expose HTTP response
headers in the same way that the QueryResource exposes them.

Fundamentally, the change is to pipe the QueryResponse
object all the way through to the Resource so that it can
populate response headers.  There is also some code
cleanup around DI, as there was a superfluous FactoryFactory
class muddying things up.
@imply-cheddar
imply-cheddar force-pushed the http-resp-headers-for-sql branch from 3eee4ce to fe0fcfa Compare September 8, 2022 04:09
if (sqlQuery.includeHeader()) {
responseBuilder.header(SQL_HEADER_RESPONSE_HEADER, SQL_HEADER_VALUE);
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

should this be always added or only in case where includeHeader() is true

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

That's code that previously existed and I didn't change. It just looks like I changed it because of diff stuff. Look at lines 191-193 of the original.

@somu-imply

Copy link
Copy Markdown
Contributor

Overall looks good, the Sequence has been changed to a QueryResponse with a separate class and removing the old static QueryResponse class. Most of the places which deal with sequences now has an additional getResult as the return type of execute is now a QueryResponse object. Have one minor comment so far

@clintropolis clintropolis left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

seems like some legitimate failures

Comment thread core/src/main/java/org/apache/druid/guice/annotations/MSQ.java Outdated
Comment thread core/src/main/java/org/apache/druid/guice/annotations/NativeQ.java Outdated
Comment thread core/src/main/java/org/apache/druid/guice/annotations/MSQ.java
Comment thread core/src/main/java/org/apache/druid/guice/annotations/NativeQ.java Outdated
Comment thread sql/src/main/java/org/apache/druid/sql/calcite/parser/DruidSqlInsert.java Outdated
Comment thread sql/src/main/java/org/apache/druid/sql/calcite/schema/SegmentMetadataCache.java Outdated
Comment thread core/src/main/java/org/apache/druid/guice/annotations/NativeQ.java Outdated

@paul-rogers paul-rogers left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

@imply-cheddar, thanks for this improvement. A quick scan suggests the approach is sound. LGTM (non-binding)

Thanks for the cleanup of the SqlStatementFactory. I believe that awkward indirection was needed, in part, for some test hackery. If there is an issue, we'll fix it when it arises.

This change will conflict with an open PR to refactor the Calcite tests. However, I'll deal with that issue once this is committed.

Your approach to plumbing the QueryResponse up from the native engine parallels what I did in the "operator" branch for other reasons; the "operator" work can now build on your changes here.

// following bindings are overriding other bindings that end up needing a lot more dependencies.
// We replace the bindings with something that returns null to make things more brittle in case they
// actually are used somewhere in the test.
binder.bind(SqlStatementFactory.class).annotatedWith(MSQ.class).toProvider(Providers.of(null));

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This only works if the MSQ tests never use the SQL layer (at least via Guice.) Our Calcite tests are currently a muddle because they hand-build a set of objects, maintained in static variables. An open PR starts to fix this with the ultimate goal of using Guice to create the objects so we don't have to entirely different systems to maintain.

In the branch where the Calcite test refactoring is happening, I ran into issues with the new MSQ tests: they do seem to use Guice, but with objects created in that static bundle of objects. Still trying to sort out that bit of confusion.

The question is then, do the MSQ tests use hand-created objects for the SQL layer or objects from Guice? This change suggests that they are hand-created and the Guice-provided ones are ignored (or we have multiple Guice injectors in play.)

Perhaps the best way to proceed is to commit this, then I'll rebase the Calcite test cleanup on this PR and see what's what.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Yeah... when I was fixing up this test, it was definitely a bit of a mixed bag of strategies. What I saw is that some of the objects are coming from Guice, but some of the objects are being hand-created. I had to make this change because the place that I added the binding was used in the test, but the test also didn't have the other dependencies defined to make that "just work", so I had to give Guice just enough to let it make the injector

* It seems extremely weird that the below code is wrapping the Sequence in order to emitLogsAndMetrics.
* The Sequence was returned by the call to execute, it would be worthwile to figure out why this wrapping
* cannot be moved into execute(). We leave this as an exercise for the future, however as this oddity
* was discovered while just trying to expose HTTP response headers

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Native queries when running from SQL should probably not emit logs and metrics since there is code in the SQL layer to do that: assuming these are basically the same logs and metrics. Are we emitting SQL logs and metrics in SQL, but native ones here? Is that redundant? Should we figure this out?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Which code in the SQL layer handles it? IIRC, the call on QueryLifecycle that this comment is on is special built only for the SQL code to use and I didn't see other code doing the logging, so I guessed that the code I commented on here is actually the only way that the SQL layer handles its logs and metrics.

We should figure it out, but not in this code change :). I already went sideways doing Guice stuff, don't want to go and expand scope even more ;).

private final SqlToolbox lifecycleToolbox;

DirectStatement directStatement(SqlQueryPlus sqlRequest);
public SqlStatementFactory(SqlToolbox lifecycleToolbox)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Perhaps add a comment to point the reader to the SqlModule and SqlStatementFactoryModule to explain how these instances are created. It is, unfortunately, not intuitively obvious on the first read how things are now wired up.

@imply-cheddar imply-cheddar Sep 11, 2022 •

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

There is magic in DI platforms, to a certain degree, you must know the magic to know how things are wired up.

Generally speaking, there is one rule with using Guice that we should be following (I noticed that we have drifted from the rules in some of the code) to help know how things are wired up. Never, ever, ever, build on or expect implicit bindings (in the code I was looking at, I noticed a number of implicit bindings that were happening, I made explicit ones for everything that I found so far). We should always have an explicit binding in a module somewhere for everything that we expect to be bound. The reason for this is that without an explicit binding, it's impossible to do a usages search and figure out how things are setup. With an explicit binding, you can do a usages search on the class and then find the usages "as a .class" which tells you which Module it is bound in. Provider methods are kinda a gray area here and I generally try to keep away from them if possible. In this case, without revisiting the object structure in general, either a provider method or an actual provider would be required. For Provider methods, the .class usages don't show up, but if you also look for usages as a return value from a method, you can find the Provider methods as well. Additionally, usages of the constructor will show up here.

TBH, I would also just add the javadoc, but I'm scared of another multi-hour CI cycle blocking the PR. Maybe I'll do the javadoc and merge anyway.

* at time of writing was not conducive to reuse in test code, so, instead of fixing that we just take the easy
* way out of adding the test-reusable code to this module and reuse that.
*/
public static class SqlStatementFactoryModule implements Module

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Thanks for this. This is some fancy Guice wrangling that makes the setup a bit cleaner.

final ServerConfig serverConfig
final ServerConfig serverConfig,
ResponseContextConfig responseContextConfig,
@Self DruidNode selfNode

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Nit: add final to the above two items just to be consistent? I don't think the Java compiler cares: it can figure out that the items are essentially final. But, we seem to like including the statements as documentation.

@abhishekagarwal87 abhishekagarwal87 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

LGTM

@cheddar

cheddar commented Sep 12, 2022

Copy link
Copy Markdown
Contributor

I pushed up javadoc fixes and travis got into sad-land from flakey tests :(. Going to merge anyway, it was a clean build ahead of the javadoc push.

@cheddar
cheddar merged commit 5ba0075 into apache:master Sep 12, 2022
@kfaraz kfaraz added this to the 25.0 milestone Nov 22, 2022
riovic918data pushed a commit to riovic918data/druid that referenced this pull request Jun 12, 2026
* Expose HTTP Response headers from SqlResource

This change makes the SqlResource expose HTTP response
headers in the same way that the QueryResource exposes them.

Fundamentally, the change is to pipe the QueryResponse
object all the way through to the Resource so that it can
populate response headers.  There is also some code
cleanup around DI, as there was a superfluous FactoryFactory
class muddying things up.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants