-
Notifications
You must be signed in to change notification settings - Fork 234
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
[YUNIKORN-2249] Add compression option to getQueueApplication API #757
base: master
Are you sure you want to change the base?
Changes from 9 commits
9ef7968
85dd3de
b5e2651
e7ea5bc
7238626
ce40af6
995cb97
4cb1d9c
40b9649
4f57568
4e7228a
2205902
b811993
8a6f9cf
53c8827
1c4a0e1
41c0570
ca18e79
0a6c931
3f806ba
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -19,6 +19,8 @@ | |
package webservice | ||
|
||
import ( | ||
"bytes" | ||
"compress/gzip" | ||
"encoding/json" | ||
"fmt" | ||
"io" | ||
|
@@ -59,6 +61,7 @@ | |
GroupNameMissing = "Group name is missing" | ||
ApplicationDoesNotExists = "Application not found" | ||
NodeDoesNotExists = "Node not found" | ||
UnsupportedCompType = "Compression type not support" | ||
) | ||
|
||
var allowedActiveStatusMsg string | ||
|
@@ -153,6 +156,10 @@ | |
w.Header().Set("Access-Control-Allow-Headers", "X-Requested-With,Content-Type,Accept,Origin") | ||
} | ||
|
||
func writeHeader(w http.ResponseWriter, key, val string) { | ||
w.Header().Set(key, val) | ||
} | ||
|
||
func buildJSONErrorResponse(w http.ResponseWriter, detail string, code int) { | ||
w.WriteHeader(code) | ||
errorInfo := dao.NewYAPIError(nil, code, detail) | ||
|
@@ -455,7 +462,7 @@ | |
} | ||
|
||
if err := json.NewEncoder(w).Encode(result); err != nil { | ||
buildJSONErrorResponse(w, err.Error(), http.StatusInternalServerError) | ||
} | ||
} | ||
|
||
|
@@ -668,7 +675,7 @@ | |
} | ||
queueDao := queue.GetPartitionQueueDAOInfo(r.URL.Query().Has("subtree")) | ||
if err := json.NewEncoder(w).Encode(queueDao); err != nil { | ||
buildJSONErrorResponse(w, err.Error(), http.StatusInternalServerError) | ||
} | ||
} | ||
|
||
|
@@ -745,9 +752,13 @@ | |
for _, app := range queue.GetCopyOfApps() { | ||
appsDao = append(appsDao, getApplicationDAO(app)) | ||
} | ||
if checkHeader(r.Header, "Accept-Encoding", "gzip") { | ||
compress(w, appsDao) | ||
return | ||
} | ||
|
||
if err := json.NewEncoder(w).Encode(appsDao); err != nil { | ||
buildJSONErrorResponse(w, err.Error(), http.StatusInternalServerError) | ||
} | ||
} | ||
|
||
|
@@ -1175,8 +1186,8 @@ | |
if err := enc.Encode(dao.YunikornID{ | ||
InstanceUUID: schedulerContext.GetUUID(), | ||
}); err != nil { | ||
buildJSONErrorResponse(w, err.Error(), http.StatusInternalServerError) | ||
return | ||
} | ||
f.Flush() | ||
|
||
|
@@ -1207,12 +1218,62 @@ | |
} | ||
|
||
if err := enc.Encode(e); err != nil { | ||
log.Log(log.REST).Error("Marshalling error", | ||
zap.String("host", r.Host)) | ||
buildJSONErrorResponse(w, err.Error(), http.StatusOK) // status code is 200 at this point, cannot be changed | ||
return | ||
} | ||
f.Flush() | ||
} | ||
} | ||
} | ||
|
||
func checkHeader(h http.Header, key string, value string) bool { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should we return error if users use unsupported compression? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This may not be necessary. Skipping it is a solution when dealing with unsupported compression types in requests. Do you think it's essential for users to require this one? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please take a look https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/406 It seems to me following the standard error can avoid the misunderstanding in the future. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
NO. We should never return an error to the client if it request an encoding we don't understand. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
That is an acceptable way to me, but I'd like to have more discussion for my own education :) Should we support full representation of Or we ignore the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There's a common saying in network protocol design: Be liberal in what you accept, strict in what you produce. In other words, we can get away with just checking for the substring There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'd like to see a very simple function like: There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That will cause a leak as the gzip writer must be closed for it not to leak. Calling close on the http.ResponseWriter is not possible so we need more. Probably the easiest solution is to use the same solution as we have for the loggingHandler(). We wrap the compression choice in a handler function, which then gets wrapped in the logging handler. That means we have it all in one place and expand on it with compressor pooling or other things in the future. Example code, which is not complete but gives some idea on how we can close the compressor. That can be expanded to use a sync.Pool to not recreate the zip writer each time and just reset it before use.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thanks to everyone for all the advice. I have got some questions about what @wilfred-s said. |
||
values := h.Values(key) | ||
for _, v := range values { | ||
v2 := strings.Split(v, ",") | ||
for _, item := range v2 { | ||
item = strings.TrimSpace(item) | ||
if item == value { | ||
return true | ||
} | ||
} | ||
} | ||
return false | ||
} | ||
|
||
func compress(w http.ResponseWriter, data any) { | ||
response, err := json.Marshal(data) | ||
if err != nil { | ||
buildJSONErrorResponse(w, err.Error(), http.StatusInternalServerError) | ||
return | ||
} | ||
|
||
// don't compress the data if it is smaller than MTU size | ||
if len(response) < 1500 { | ||
targetoee marked this conversation as resolved.
Show resolved
Hide resolved
|
||
if err = json.NewEncoder(w).Encode(data); err != nil { | ||
buildJSONErrorResponse(w, err.Error(), http.StatusInternalServerError) | ||
} | ||
return | ||
} | ||
|
||
var compressedData bytes.Buffer | ||
writer := gzip.NewWriter(&compressedData) | ||
_, err = writer.Write(response) | ||
if err != nil { | ||
_ = writer.Close() | ||
buildJSONErrorResponse(w, err.Error(), http.StatusInternalServerError) | ||
targetoee marked this conversation as resolved.
Show resolved
Hide resolved
|
||
return | ||
} | ||
|
||
err = writer.Close() | ||
if err != nil { | ||
buildJSONErrorResponse(w, err.Error(), http.StatusInternalServerError) | ||
return | ||
} | ||
|
||
writeHeader(w, "Content-Encoding", "gzip") | ||
if _, err = w.Write(compressedData.Bytes()); err != nil { | ||
buildJSONErrorResponse(w, err.Error(), http.StatusInternalServerError) | ||
} | ||
} |
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.
nit: "not supported"
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 shouldn't be here at all. As I've repeatedly indicated we should never fail with an error, just fall back to no compression.