-
Notifications
You must be signed in to change notification settings - Fork 96
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
Implement DruidPartialDataResponseProcessor #275
Conversation
7ea000c
to
6547e74
Compare
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.
Changelog and tests needed.
/** | ||
* Response processor for partial data V2 and cache data V3. | ||
* <p> | ||
* The full response processor also process header information about query response. |
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.
This description doesn't tell us very much. It's not clear what "partial data v2" or "cache data v3" are (or at least it won't be clear to us 6 months from now). This doc is also rather vague about what it does or is for, since "process header information" doesn't really indicate what it's doing or why this class is interesting.
* <p> | ||
* The full response processor also process header information about query response. | ||
*/ | ||
public interface FullResponseProcessor extends ResponseProcessor { |
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.
Why is this called "Full"? The doc should describe what "full" means.
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.
Explanation added
validateJsonResponse(json); | ||
|
||
int statusCode = json.get("status-code").asInt(); | ||
if (statusCode == 200) { |
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 should replace this status code number with one of the StatusCode enums. We do this elsewhere in the codebase I think
|
||
int statusCode = json.get("status-code").asInt(); | ||
if (statusCode == 200) { | ||
// implementation is blocked by https://github.com/yahoo/fili/pull/262 |
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 this is still WIP because it's not complete (which this comment seems to indicate), we should add the WIP label. Is this PR still in-progress?
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.
@cdeszaq Since this PR is blocked by another PR, my thought is that we could review what we have now to save the reviewing effort latter. As a result, we should be able to land this PR quicker.
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.
Since this PR does not really do anything, there's no point in landing it w/o what it's really supposed to do.
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.
Yeah. This PR won't merge even after review. I'll wait until the blocking PR is merged and every feature is implemented and reviewed in this PR. I'm giving out the first 50% to review, so people don't have to review large chunk all together latter.
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'm all for getting reviews on as much as possible as soon as possible, as long as what's getting reviewed is likely to be stable against the rest of the work.
* Validates JSON response object to make sure it contains all of the following information. | ||
* <ul> | ||
* <li>X-Druid-Response-Context</li> | ||
* <ol> |
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 don't think these list items are nesting the way you intend them to, so I don't think it will render correctly. I think the closing </li>
with the header name on it should go after the closing </ol>
for this list.
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.
Beautiful!
* <li>uncoveredIntervals</li> | ||
* <li>uncoveredIntervalsOverflowed</li> | ||
* </ol> | ||
* <li>status-code</li> |
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.
Why does the status code matter?
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 status code is 304, do nothing. It status code is 200, we do all logics of partial data V2
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.
Precisely. The javadoc should say that
logAndThrowRunTimeException("Response is missing X-Druid-Response-Context"); | ||
} | ||
if (!json.get("X-Druid-Response-Context").has("uncoveredIntervals")) { | ||
logAndThrowRunTimeException("Response is missing \"uncoveredIntervals\" X-Druid-Response-Context"); |
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 single quotes rather than escaping the double quotes.
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.
Also, all of these error messages really should go in the ErrorMessages
enum (or whatever it's called)
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.
And lastly, does this exist in the query if there are no uncovered intervals?
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.
It will always exist with 98% confidence, I can double check once I get my Http Requester authentication working again.
* @param message The error message passed to the logger and the exception. | ||
*/ | ||
private static void logAndThrowRunTimeException(String message) { | ||
LOG.error(message); |
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 this should just be a warning, and shouldn't thrown an exception. If, for some reason, Druid doesn't give us this info, I think this processor should warn, but not assume the result is bad.
@michael-mclawhorn or @garyluoex, do you have any thoughts on this?
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 my original intent is to return a 500 error response or similar stating that we are not confident that the data druid gave us match the api user's expectation, after talking with tom, I think we agree that fail the query instead of giving bad result is the way to go. If we dont fail the query, it defeat the whole purpose of PartialDataV2 right?
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.
That said, I don't know if there is a difference between throwing an exception directly here in comparison to preparing an actual error response by callinggetErrorCallback
? (Since throwing exception means the query failed anyways and the result will most likely be a error response anyways?) @cdeszaq
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 general, if a chunk of code can handle an exceptional case, it should handle it, rather than making someone else handle it. The reason for this is that, if you can handle the exception, you likely have more information about what's wrong than someone else does and can provide a better error response. (ie. "something bad happened" is far less useful than "druid indicated more data was missing than we expected" and including the additional missing intervals)
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.
@cdeszaq @garyluoex OK. I guess we agree to keep as it is now?
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.
No, this class needs to handle it's response. It's not useful for it to just throw
if it has a problem that should fail the 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.
As for the log message, it's OK to leave it error
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 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.
@garyluoex @cdeszaq Thanks!
|
||
@Override | ||
public void processResponse(JsonNode json, DruidAggregationQuery<?> query, LoggingContext metadata) { | ||
validateJsonResponse(json); |
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.
This doesn't handle the validator throwing an exception. If this response processor decides to fail the request, it needs to handle doing that failure, rather than letting someone else handle it. Likely it should call either the error or failure callback (depending on which makes more sense).
What is the thinking behind having an empty interface and leaving 'v2' in the concrete class name? Why make an empty interface? Why not just rename the response processor? |
313c265
to
6fb4b59
Compare
@cdeszaq @garyluoex @michael-mclawhorn Shall we avoid hardcoded string like Line 180 in 6fb4b59
|
98ab398
to
991983b
Compare
@QubitPi Lets just make them a static string on the top of the class for now, I do not see a good place to put then in at this point. |
CHANGELOG.md
Outdated
- [Implement PartialDataV2ResponseProcessor](https://github.com/yahoo/fili/pull/275) | ||
* Add `FullResponseProcessor` interface that extends `ResponseProcessor` | ||
* Add response status code to JSON response | ||
* Add `PartialDataV2ResponseProcessor` that checks for any missing data that's not being found |
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.
Update name to DruidPartialData
@@ -30,15 +30,20 @@ public HeaderNestingJsonBuilderStrategy(Function<Response, JsonNode> baseStrateg | |||
|
|||
@Override | |||
public JsonNode apply(Response response) { | |||
MappingJsonFactory mappingJsonFactory = new MappingJsonFactory(); | |||
ObjectNode objectNode = JsonNodeFactory.instance.objectNode(); | |||
objectNode.set("response", baseStrategy.apply(response)); |
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 should some how unify the naming convention of 'response', 'X-Druid-Response-Context', 'status-code' by using an enum. Maybe put the num in responseprocessors folder and name it something like FullResponseContentKeys
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.
Beautiful! I assume the static string you mentioned above is not needed anymore then
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 DruidJsonResponseContentKeys
* <p> | ||
* This is a "Full" response process in a sense that it extracts and incorporates header information. | ||
*/ | ||
public interface FullResponseProcessor extends ResponseProcessor { |
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.
Try if we can override processResponse
to instead of taking a JsonNode
, make it take a ObjectNode
in FullResponseProcessor
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.
@garyluoex processResponse
is not found to use ObjectNode
. Do we still need this change?
* missing interval list, we can send back an error response indicating the mismatch in data availability before the | ||
* response is cached. | ||
*/ | ||
public class PartialDataV2ResponseProcessor implements FullResponseProcessor { |
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.
Change name to DruidParitalData
* from Partial Data V1. If "uncoveredIntervals" contains any interval that is not present in our expected | ||
* missing interval list, we can send back an error response indicating the mismatch in data availability before the | ||
* response is cached. | ||
*/ |
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 slice this comment down a bit..? It feels like it is a bit too long, and if we don't simplify a little bit, it feels like we can add a phd thesis here... and it doesn't feel right to add too much detail here in the code. As long as the comment can provide a simple explanation and point to the correct place if the user wants more information, I think that is sufficient. But this is my personal preference before we come up with a rule for it, so feel free to leave it 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.
Simplified.
* @param metadata The LoggingContext to use | ||
*/ | ||
@Override | ||
public void processResponse(JsonNode json, DruidAggregationQuery<?> query, LoggingContext metadata) { |
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.
Hopefully we can make this take a ObjectNode
using the FullResponseProcessor
interface as mentioned above.
query.getDataSource().getPhysicalTable().getAvailableIntervals() | ||
); | ||
if (!overlap.isEmpty()) { | ||
logAndGetErrorCallback(ErrorMessageFormat.DATA_AVAILABILITY_MISMATCH.format(overlap), query); |
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 a test for this behavior.
* | ||
* @return uncovered intervals in a SimplifiedIntervalList. | ||
*/ | ||
private static SimplifiedIntervalList getUncoveredIntervalsFromResponse(JsonNode json) { |
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.
It should take a ObjectNode
*/ | ||
private void validateJsonResponse(JsonNode json, DruidAggregationQuery<?> query) { | ||
if (!json.has("X-Druid-Response-Context")) { | ||
logAndGetErrorCallback("Response is missing X-Druid-Response-Context", query); |
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 one more case that this statement will be executed, the case where an arrayNode is passed in instead of objectNod. The error message should note.
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.
@garyluoex I add a check, you can see it in the next commit
logAndGetErrorCallback("Response is missing X-Druid-Response-Context", query); | ||
} | ||
if (!json.get("X-Druid-Response-Context").has("uncoveredIntervals")) { | ||
logAndGetErrorCallback("Response is missing 'uncoveredIntervals' X-Druid-Response-Context", query); |
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.
Make the message clearer by adding 'uncoveredIntervals' from 'X-Druid-Response-Context' header
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 lot more testing needed here.
Also, the classes should be renamed around the non numbered naming scheme.
* <p> | ||
* This is a "Full" response process in a sense that it extracts and incorporates header information. | ||
*/ | ||
public interface FullResponseProcessor extends ResponseProcessor { |
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.
Since this thing is creating an explicit contract with the json, let's document the json contract in the class documentation. e.g. give some sample json describing the nesting strategy.
* Compare both SimplifiedIntervalLists above, if allAvailableIntervals has any overlap with | ||
* uncoveredIntervals, invoke error response indicating druid is missing some data that are we expects to | ||
* exists. | ||
* </li> |
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.
spelling: 'that we are expecting'
* @param query The query with the schema for processing this response | ||
*/ | ||
private void validateJsonResponse(JsonNode json, DruidAggregationQuery<?> query) { | ||
if (!json.has("X-Druid-Response-Context")) { |
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.
Extract as constant
if (json.get("X-Druid-Response-Context").get("uncoveredIntervalsOverflowed").asBoolean()) { | ||
int limit = query.getContext().getUncoveredIntervalsLimit(); | ||
logAndGetErrorCallback(ErrorMessageFormat.TOO_MUCH_INTERVAL_MISSING.format(limit, limit), query); | ||
} |
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.
grammar: either 'TOO_MANY_INTERVALS' or 'TOO_MUCH_TIME'
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.
Also, 'TOO_MUCH_INTERVAL_MISSING' really shouldn't need the same number twice.
It can probably be reworded as:
"Query is returning more than the configured limit of ‘%s’ missing intervals. There may be a problem with your data."
I don't think end users should ever be contemplating reconfiguring this parameter.
@@ -50,7 +51,8 @@ class AsyncDruidWebServiceImplWithHeaderNestingSpec extends Specification { | |||
"2016-12-25T00:00:00.000Z/2017-01-03T00:00:00.000Z" | |||
], | |||
"uncoveredIntervalsOverflowed": true | |||
} | |||
}, | |||
"status-code": 200 |
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.
it looks to me like this is a test of the HeaderNestingJsonBuilderStrategy, not of the webservice itself.
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.
Yeah. I agree. I think I should rename it to make it correspond to HeaderNestingJsonBuilderStrategy.java
1 * next.getResponseContext() >> responseContext | ||
1 * next.getFailureCallback(druidAggregationQuery) >> failureCallback | ||
1 * next.getErrorCallback(druidAggregationQuery) >> httpErrorCallback | ||
} |
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 is this even testing? I don't see the point of this.
"status-code": 200 | ||
} | ||
''' | ["2016-11-22T00:00:00.000Z/2016-12-18T00:00:00.000Z", "2016-12-25T00:00:00.000Z/2017-01-03T00:00:00.000Z"] | ||
} |
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.
This is a good test. However if we're only doing one value, there's not much point in doing a data table.
} | ||
''' | ["2016-11-22T00:00:00.000Z/2016-12-18T00:00:00.000Z", "2016-12-25T00:00:00.000Z/2017-01-03T00:00:00.000Z"] | ||
} | ||
} |
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 assume there are more tests coming here.
Specifically:
processResponse -> overflow message
processResponse -> success message
processResponse -> data availability mismatch
validateJsonResponse error paths
checkOverflow success and failure
getUncoveredIntervalFromResponse (optional, but might make for good low level testing)
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.
Minor stuff
@@ -176,19 +152,29 @@ public void processResponse(JsonNode json, DruidAggregationQuery<?> query, Loggi | |||
* @param query The query with the schema for processing this response | |||
*/ | |||
private void validateJsonResponse(JsonNode json, DruidAggregationQuery<?> query) { | |||
if (!json.has("X-Druid-Response-Context")) { | |||
if (json instanceof ArrayNode) { |
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.
@@ -176,19 +152,29 @@ public void processResponse(JsonNode json, DruidAggregationQuery<?> query, Loggi | |||
* @param query The query with the schema for processing this response | |||
*/ | |||
private void validateJsonResponse(JsonNode json, DruidAggregationQuery<?> query) { | |||
if (!json.has("X-Druid-Response-Context")) { | |||
if (json instanceof ArrayNode) { | |||
logAndGetErrorCallback("Response is missing X-Druid-Response-Context and status code", query); |
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 we should make the message more explicit such that Response
refers to json
being passed to ResponseHandler
so that people don't mistake it as being Response
from druid. Apply to all.
logAndGetErrorCallback("Response is missing X-Druid-Response-Context", query); | ||
} | ||
if (!json.get("X-Druid-Response-Context").has("uncoveredIntervals")) { | ||
logAndGetErrorCallback("Response is missing 'uncoveredIntervals' X-Druid-Response-Context", query); | ||
if (!json.get(FullResponseContentKeys.DRUID_RESPONSE_CONTEXT.getName()) |
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.
It is repeated four or more times, we should take it out as its own variable
/** | ||
* Enumerates the list of keys expected to be found in the FullResponseProcessor. | ||
*/ | ||
public enum FullResponseContentKeys { |
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 change it to DruidJsonResponseContentKeys
?
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.
@garyluoex Shall we change FullResponseProcessor
to DruidJsonResponseProcessor
?
@@ -39,4 +47,21 @@ | |||
* | |||
*/ | |||
public interface FullResponseProcessor extends ResponseProcessor { | |||
@Override | |||
default void processResponse(JsonNode json, DruidAggregationQuery<?> query, LoggingContext metadata) { |
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.
Why do we have a default?
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.
The only way to override is to implement processResponse
and default
is needed to implement this method
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.
An alternative is to tranform JSON node to Object node in DruidPartialDataResponseProcessor
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.
Are you sure? I think just overriding the method as an interface without default should be fine. Let me know the compilation error you get if possible.
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.
never mind, revert it
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.
ok
@@ -30,15 +30,20 @@ public HeaderNestingJsonBuilderStrategy(Function<Response, JsonNode> baseStrateg | |||
|
|||
@Override | |||
public JsonNode apply(Response response) { | |||
MappingJsonFactory mappingJsonFactory = new MappingJsonFactory(); | |||
ObjectNode objectNode = JsonNodeFactory.instance.objectNode(); | |||
objectNode.set("response", baseStrategy.apply(response)); | |||
try { | |||
objectNode.set( | |||
"X-Druid-Response-Context", |
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.
DruidJsonResponseContentKeys
.createParser(response.getHeader("X-Druid-Response-Context")) | ||
.readValueAsTree() | ||
); | ||
objectNode.set( | ||
"status-code", |
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.
DruidJsonResponseContentKeys
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.
Beautiful!
4a130e9
to
3b07f2a
Compare
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.
Minor comment
UNCOVERED_INTERVALS("uncoveredIntervals"), | ||
UNCOVERED_INTERVALS_OVERFLOWED("uncoveredIntervalsOverflowed"), | ||
STATUS_CODE("status-code"), | ||
RESPONSE("response"); |
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.
semi colon to next line
) | ||
dataSource.getPhysicalTable() >> constrainedTable | ||
druidAggregationQuery.getDataSource() >> dataSource | ||
|
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.
extra new line
JsonNode json = MAPPER.readTree( | ||
jsonInString | ||
.replace(" ", "") | ||
.replace("\n", "") |
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.
why are these two replace needed?
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.
Good catch! They are not needed after a refactor but I forget to delete them
} | ||
} | ||
|
||
next.processResponse(json, query, metadata); |
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 it is 304 and the next response processor is not fullResponseProcessor, we should throw error saying, received 304, but no etag cache response processor is available to process 304 response
"2016-11-22T00:00:00.000Z/2016-12-18T00:00:00.000Z", | ||
"2016-12-25T00:00:00.000Z/2017-01-03T00:00:00.000Z" | ||
], | ||
"uncoveredIntervalsOverflowed": true |
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.
Why are all over flow set to true????
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.
And.... this test only have a given: ????
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.
OK. Setting it to false makes more sense
json.get(DruidJsonResponseContentKeys.DRUID_RESPONSE_CONTEXT.getName()) | ||
.get(DruidJsonResponseContentKeys.UNCOVERED_INTERVALS.getName()) | ||
) { | ||
intervals.add(new Interval(jsonNode.asText())); |
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 add a test showing what this jsonNode
is? Or let me know if you know the answer, is this jsonNode a arraynode or objectnode and what is the content?
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.
According to Javadoc of this method
X-Druid-Response-Context: {
"uncoveredIntervals": [
"2016-11-22T00:00:00.000Z/2016-12-18T00:00:00.000Z","2016-12-25T00:00:00.000Z/2017-
01-03T00:00:00.000Z","2017-01-31T00:00:00.000Z/2017-02-01T00:00:00.000Z","2017-02-
08T00:00:00.000Z/2017-02-09T00:00:00.000Z","2017-02-10T00:00:00.000Z/2017-02-
13T00:00:00.000Z","2017-02-16T00:00:00.000Z/2017-02-20T00:00:00.000Z","2017-02-
22T00:00:00.000Z/2017-02-25T00:00:00.000Z","2017-02-26T00:00:00.000Z/2017-03-
01T00:00:00.000Z","2017-03-04T00:00:00.000Z/2017-03-05T00:00:00.000Z","2017-03-
08T00:00:00.000Z/2017-03-09T00:00:00.000Z"
],
"uncoveredIntervalsOverflowed": true
}
Each is a ObjectNode containing a String like "2016-11-22T00:00:00.000Z/2016-12-18T00:00:00.000Z"
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 it is a ObjectNode, it is some kind of key value mapping right? So what is the key for each individual string like "2016-11-22T00:00:00.000Z/2016-12-18T00:00:00.000Z"
?
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 I mean by ObjectNode is that it is a type of JsonNode that can be serialized to text in this way
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 show me some documentation or a test that confirms this behavior?
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 modified tests to make sure this method works as expected
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.
Isn't this a text node inside a list?
Re: @michael-mclawhorn Yes, which is why I call .asText()
. Any specific problem?
@@ -131,9 +132,14 @@ public void processResponse(JsonNode json, DruidAggregationQuery<?> query, Loggi | |||
} else { | |||
next.processResponse(json.get(DruidJsonResponseContentKeys.RESPONSE.getName()), query, metadata); | |||
} | |||
} else if (statusCode == Status.NOT_MODIFIED.getStatusCode() && next instanceof FullResponseProcessor) { |
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.
Are you sure this if statement is correct?
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.
Maybe adding a pair of parenthesis around latter?
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 304, and the next response Processor is FullResponseProcessor (which can be etagCacheResponseProcessor), we throw an error?
druidPartialDataResponseProcessor.next == next; | ||
} | ||
|
||
def "getOverlap returns intersection between Druid intervals and Fili intervals in case of #caseDescription"() { |
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.
This test only have a given: and where: which does not test anything right?
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.
Hmm...somehow a refactor removes it. Anyway it's back
@@ -50,7 +51,8 @@ class AsyncDruidWebServiceImplWithHeaderNestingSpec extends Specification { | |||
"2016-12-25T00:00:00.000Z/2017-01-03T00:00:00.000Z" | |||
], | |||
"uncoveredIntervalsOverflowed": true | |||
} | |||
}, | |||
"status-code": 200 | |||
} | |||
'''.replace(" ", "").replace("\n", "") |
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.
Not sure if this is needed.
@@ -28,6 +28,7 @@ class AsyncDruidWebServiceImplWithHeaderNestingSpec extends Specification { | |||
"uncoveredIntervalsOverflowed": true | |||
} | |||
'''.replace(" ", "").replace("\n", "") |
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.
Not sure if this is needed.
6a9c85d
to
6919466
Compare
6919466
to
5131965
Compare
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.
Readability of tests should be improved, otherwise this is pretty good. I'm going to 👍 this, but I'd like you to make the changes I've described.
json.get(DruidJsonResponseContentKeys.DRUID_RESPONSE_CONTEXT.getName()) | ||
.get(DruidJsonResponseContentKeys.UNCOVERED_INTERVALS.getName()) | ||
) { | ||
intervals.add(new Interval(jsonNode.asText())); |
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.
Isn't this a text node inside a list?
Re: @michael-mclawhorn Yes, which is why I call .asText()
. Any specific problem?
private void validateJsonResponse(JsonNode json, DruidAggregationQuery<?> query) { | ||
if (json.getNodeType() == JsonNodeType.ARRAY) { | ||
logAndGetErrorCallback("JSON response is missing X-Druid-Response-Context and status code", query); | ||
} |
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.
All of these error strings should be moved into constants and referenced by the constants in the tests so that they'll stay in sync.
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.
Beautiful!
} | ||
if (!json.has(DruidJsonResponseContentKeys.STATUS_CODE.getName())) { | ||
logAndGetErrorCallback("JSON response is missing response status code", query); | ||
} |
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.
All of these error strings should be moved into constants and referenced by the constants in the tests so that they'll stay in sync.
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.
Beautiful!
"Query is returning more than the configured limit of '10' missing intervals. " + | ||
"There may be a problem with your data.") | ||
} | ||
|
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 the constants for these errors rather than copying them into the tests.
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.
Beautiful!
], | ||
"uncoveredIntervalsOverflowed": false | ||
}, | ||
"status-code": 200 |
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.
It looks like your Json instring is the same across all the test cases. If so, lift it up out of the where clause and put it into the test.
9b34856
to
3a5e0e0
Compare
@cdeszaq Since merging is blocked, could you take a quick look at it and approve it? Thanks! |
@garyluoex Thanks Gary! |
3rd PR implementing #215