app/vlselect: expose the VL-Partial-Response HTTP header#1438
app/vlselect: expose the VL-Partial-Response HTTP header#1438vadimalekseev wants to merge 1 commit into
VL-Partial-Response HTTP header#1438Conversation
0e007d4 to
528bd4a
Compare
There was a problem hiding this comment.
1 issue found across 5 files
Reply with feedback, questions, or to request a fix.
Fix all with cubic | Re-trigger cubic
c0a7f4a to
8e03dd7
Compare
d9dc261 to
3622e0a
Compare
| if err == nil { | ||
| // At least a single vlstorage returned full response. | ||
| return nil | ||
| return true, nil | ||
| } |
There was a problem hiding this comment.
This may hide non-availability errors from the user when at least one vlstorage node succeeds, e.g.,[nil, nonAvailabilityError] returns no the first nil element here.
There was a problem hiding this comment.
Did I understand correctly that this issue also exists in the master branch?
There was a problem hiding this comment.
I was under the impression that the current behavior was correct, but it turns out this logic was correct before and was accidentally regressed by 348f103. So this bug already exists on master
| qc := qs.QueryCompleteness | ||
| h.Set("VL-Partial-Response", getPartialResponseHeaderValue(qc)) |
There was a problem hiding this comment.
This read may race with atomic writes to qs.QueryCompleteness. Adding a method such as QueryStats.GetQueryCompleteness(), which uses atomic.LoadUint32(), and use it here?
There was a problem hiding this comment.
Initially, there was such a method, but I removed it to simplify the code, because:
- Only one goroutine can call writeResponseHeaders.
- The other goroutines wait for the first one to finish calling
writeResponseHeadershere:
before updating the QueryCompleteness value here:
Should we explicitly sync this, do you think?
There was a problem hiding this comment.
Yeah, I double-checked the code, and the fact that it doesn't need synchronization is really counter-intuitive. I will add explicit synchronization
See the comment bellow.
There was a problem hiding this comment.
Even though this counter-intuitive behavior is basically the core of the whole PR: we cant let another goroutine write QueryCompletenessComplete, otherwise we'll set the partial response to 'false' for a streaming request.
There was a problem hiding this comment.
More explicit (but complex) implementation described here: #718 (comment)
There was a problem hiding this comment.
Sorry I don't understand, I don't mean waiting until all goroutines finish before writing the header. I mean using atomic load for reading QueryCompleteness, since the field is updated via atomic/CAS elsewhere. This doesn't change the timing. Could you explain more?
There was a problem hiding this comment.
Hey @func25, I've updated the runQuery function, so QueryStats updates only after the wg.Done(). Could you please check again?
Previously, there actually could have been a race condition if an empty response was received from vlstorage. In that case, we would never have called writeBlock, which used to act as a mutex due to writeResponseHeadersOnce.
This could lead to a situation where one vlstorage returned an empty response, while the second one was unavailable. In this case, we could potentially set QueryCompletenessComplete instead of Partial. The current code should avoid this situation.
4f71f8d to
da98e20
Compare
| noErrors := true | ||
| for _, err := range errs { | ||
| if err != nil { | ||
| noErrors = false | ||
| break | ||
| } | ||
| return nil | ||
| } | ||
| if noErrors { | ||
| // Not a partial response. | ||
| return false, nil | ||
| } |
There was a problem hiding this comment.
If you fix the issue in master, I think this snippet is not needed, it can be replaced with
var unavailableErr error
successes := 0
for _, err := range errs {
if err == nil {
successes++
continue
}
if !isUnavailableBackendError(err) {
...
}
unavailableErr = err
}
da98e20 to
8a7885c
Compare
The header value can be `true` or `false`, depending on whether the query was executed partially or completely. For streaming queries that do not require buffering the final result in memory, this header will contain the value `unknown`.
8a7885c to
8e0b506
Compare
The header value can be
trueorfalse, depending on whether the query was executed partially or completely. For streaming queries that do not require buffering the final result in memory, this header will contain the valueunknown.Closes #718
Updates #1208