-
Notifications
You must be signed in to change notification settings - Fork 5.4k
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
Accept query id and slug in QueuedStatement #23407
Conversation
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.
Add a release note about the protocol change.
@rschlussel done |
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 can add some documentation here in statement.rst
.
String statement, | ||
@PathParam("queryId") QueryId queryId, | ||
@QueryParam("slug") String slug, | ||
@DefaultValue("false") @QueryParam("binaryResults") boolean binaryResults, |
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 was added for fuzzer testing, and no client presently uses this. I'd remove 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.
I'm not sure that no client is using it, and I kept it here since the POST supports it. If we want to remove support for this, we should probably raise an issue first and remove it as part of a separate PR.
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 asked around and it looks like we are actually using this flag for our client, so we should keep 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.
Can you share more details on the client that's using it? Is it an internal client?
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.
@amitkdutta do you have any details on this client?
tracerProviderManager.getTracerProvider(), | ||
Optional.of(sessionPropertyManager)); | ||
Query query = new Query(statement, sessionContext, dispatchManager, queryResultsProvider, 0, queryId, slug); | ||
queries.put(query.getQueryId(), 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.
The best thing to do, to preserve idempotency while also ensuring that retries work, would be to check if the query already exists. If it does, check if the slug matches. If it does, then ensure that the token should be 0
, otherwise, fail.
If the query doesn't already exist, then proceed as 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.
I just remembered that POST
is not an idempotent HTTP verb. Idempotency is a nice attribute, and I'd recommend using PUT
instead, which is. (The other endpoint doesn't care about idempotency, because you can call POST
multiple times for the same query, but the only one that matters is the first one whose nextUri
is followed. Not so here--creating multiple queries with the same identical query ID is impossible.)
I still recommend the improvement above, because I think the API should guard against adversarial usage, even if it's trivial or unlikely. For example, POST
ing an already executing query here would at best cause an error, at worst could be a security issue.
presto-main/src/main/java/com/facebook/presto/server/protocol/QueuedStatementResource.java
Show resolved
Hide resolved
* @return {@link javax.ws.rs.core.Response} HTTP response code | ||
*/ | ||
@POST | ||
@Path("/v1/statement/queued/{queryId}") |
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.
@Path("/v1/statement/queued/{queryId}") | |
@Path("/v1/statement/{queryId}") |
Can we make this consistent with the POST
? I don't think the queued
signifies any additional information.
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 would be great to have this added to the OpenAPI spec we're working on getting into the docs:
@ZacBlanco Is there a README for how to add and test this? |
We don't currently have a readme but it wouldn't hurt to have one. I'll try to submit a PR for that soon with instructions for adding new paths and components. For the time being, I would look at the existing endpoints in the spec and try mimicking them for your new endpoint. You can refer the official spec for the full set of options: https://spec.openapis.org/oas/latest.html Once you've added a new path to the YAML you can build the openapi module with . |
5d2c3f0
to
2697a94
Compare
@tdcmeehan Addressed all comments:
@ZacBlanco |
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.
LGTM! (docs)
Pull branch, local docs build, looks good. Thanks!
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 good % one more comment
if (queries.containsKey(queryId)) { | ||
Query query = queries.get(queryId); | ||
if (!query.getSlug().equals(slug) || query.getLastToken() != 0) { | ||
throw badRequest(CONFLICT, "Query already exists"); | ||
} | ||
} | ||
|
||
abortIfPrefixUrlInvalid(xPrestoPrefixUrl); |
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 (queries.containsKey(queryId)) { | |
Query query = queries.get(queryId); | |
if (!query.getSlug().equals(slug) || query.getLastToken() != 0) { | |
throw badRequest(CONFLICT, "Query already exists"); | |
} | |
} | |
abortIfPrefixUrlInvalid(xPrestoPrefixUrl); | |
abortIfPrefixUrlInvalid(xPrestoPrefixUrl); | |
if (queries.containsKey(queryId)) { | |
Query query = queries.get(queryId); | |
if (!query.getSlug().equals(slug) || query.getLastToken() != 0) { | |
throw badRequest(CONFLICT, "Query already exists"); | |
} | |
Query query = queries.get(queryId); | |
return withCompressionConfiguration(Response.ok(query.getInitialQueryResults(uriInfo, xForwardedProto, xPrestoPrefixUrl, binaryResults)), compressionEnabled).build(); | |
} |
I'd recommend returning the same query ID instead of proceeding and creating a new one, to guard against a race condition where we've retried the PUT, but meanwhile a GET has been made on the old copy of the 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.
@tdcmeehan Done, changed to return the same query ID.
2697a94
to
3e495d2
Compare
Query query = new Query(statement, sessionContext, dispatchManager, executingQueryResponseProvider, 0, queryId, slug); | ||
queries.put(query.getQueryId(), 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.
One more thing just to make it 100% foolproof:
Query query = new Query(statement, sessionContext, dispatchManager, executingQueryResponseProvider, 0, queryId, slug); | |
queries.put(query.getQueryId(), query); | |
Query query = queries.computeIfAbsent(queryId, unused -> new Query(statement, sessionContext, dispatchManager, executingQueryResponseProvider, 0, queryId, slug)); |
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 could combine them into one check, and remove the check you have above.
Query query = new Query(statement, sessionContext, dispatchManager, executingQueryResponseProvider, 0, queryId, slug); | |
queries.put(query.getQueryId(), query); | |
Query attemptedQuery = new Query(statement, sessionContext, dispatchManager, executingQueryResponseProvider, 0, queryId, slug); | |
Query query = queries.putIfAbsent(query.getQueryId(), unused -> attemptedQuery); | |
if (attemptedQuery != query && !attemptedQuery.getSlug().equals(query.getSlug()) || query.getLastToken() != 0) { | |
throw badRequest(CONFLICT, "Query already exists"); | |
} |
3e495d2
to
f6201c7
Compare
@ZacBlanco could I get a stamp? I've added the requested OpenAPI documentation. |
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.
Thanks for adding this! Sorry for the slow review. Was OOO last week.
Spec looks good, just need to shift the path component it is under.
f6201c7
to
02c294f
Compare
@tdcmeehan Could I get another stamp? |
Description
This change adds support for QueuedStatementResource to accept a pre-minted query id and slug.
Motivation and Context
This is required for RFC5 prestodb/rfcs#23
Impact
There is no user-facing or performance impact.
Test Plan
Added test in TestServer
Contributor checklist
Release Notes