Combine profiling start/stop APIs into one (#14662)

Take profile duration as a query parameter for profile API
This commit is contained in:
Poorna 2022-04-08 12:44:35 -07:00 committed by GitHub
parent 48594617b5
commit a1b01e6d5f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 85 additions and 3 deletions

View File

@ -27,6 +27,7 @@ import (
"fmt" "fmt"
"hash/crc32" "hash/crc32"
"io" "io"
"io/ioutil"
"math/rand" "math/rand"
"net/http" "net/http"
"net/url" "net/url"
@ -500,7 +501,7 @@ func (a adminAPIHandlers) TopLocksHandler(w http.ResponseWriter, r *http.Request
} }
// StartProfilingResult contains the status of the starting // StartProfilingResult contains the status of the starting
// profiling action in a given server // profiling action in a given server - deprecated API
type StartProfilingResult struct { type StartProfilingResult struct {
NodeName string `json:"nodeName"` NodeName string `json:"nodeName"`
Success bool `json:"success"` Success bool `json:"success"`
@ -594,6 +595,85 @@ func (a adminAPIHandlers) StartProfilingHandler(w http.ResponseWriter, r *http.R
writeSuccessResponseJSON(w, startProfilingResultInBytes) writeSuccessResponseJSON(w, startProfilingResultInBytes)
} }
// ProfileHandler - POST /minio/admin/v3/profile/?profilerType={profilerType}
// ----------
// Enable server profiling
func (a adminAPIHandlers) ProfileHandler(w http.ResponseWriter, r *http.Request) {
ctx := newContext(r, w, "Profile")
defer logger.AuditLog(ctx, w, r, mustGetClaimsFromToken(r))
// Validate request signature.
_, adminAPIErr := checkAdminRequestAuth(ctx, r, iampolicy.ProfilingAdminAction, "")
if adminAPIErr != ErrNone {
writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(adminAPIErr), r.URL)
return
}
if globalNotificationSys == nil {
writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrServerNotInitialized), r.URL)
return
}
profileStr := r.Form.Get("profilerType")
profiles := strings.Split(profileStr, ",")
duration := time.Minute
if dstr := r.Form.Get("duration"); dstr != "" {
var err error
duration, err = time.ParseDuration(dstr)
if err != nil {
writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrServerNotInitialized), r.URL)
return
}
}
// read request body
io.CopyN(ioutil.Discard, r.Body, 1)
globalProfilerMu.Lock()
if globalProfiler == nil {
globalProfiler = make(map[string]minioProfiler, 10)
}
// Stop profiler of all types if already running
for k, v := range globalProfiler {
v.Stop()
delete(globalProfiler, k)
}
// Start profiling on remote servers.
for _, profiler := range profiles {
globalNotificationSys.StartProfiling(profiler)
// Start profiling locally as well.
prof, err := startProfiler(profiler)
if err == nil {
globalProfiler[profiler] = prof
}
}
globalProfilerMu.Unlock()
timer := time.NewTimer(duration)
for {
select {
case <-ctx.Done():
if !timer.Stop() {
<-timer.C
}
for k, v := range globalProfiler {
v.Stop()
delete(globalProfiler, k)
}
return
case <-timer.C:
if !globalNotificationSys.DownloadProfilingData(ctx, w) {
writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrAdminProfilerNotEnabled), r.URL)
return
}
return
}
}
}
// dummyFileInfo represents a dummy representation of a profile data file // dummyFileInfo represents a dummy representation of a profile data file
// present only in memory, it helps to generate the zip stream. // present only in memory, it helps to generate the zip stream.
type dummyFileInfo struct { type dummyFileInfo struct {
@ -614,7 +694,7 @@ func (f dummyFileInfo) Sys() interface{} { return f.sys }
// DownloadProfilingHandler - POST /minio/admin/v3/profiling/download // DownloadProfilingHandler - POST /minio/admin/v3/profiling/download
// ---------- // ----------
// Download profiling information of all nodes in a zip format // Download profiling information of all nodes in a zip format - deprecated API
func (a adminAPIHandlers) DownloadProfilingHandler(w http.ResponseWriter, r *http.Request) { func (a adminAPIHandlers) DownloadProfilingHandler(w http.ResponseWriter, r *http.Request) {
ctx := newContext(r, w, "DownloadProfiling") ctx := newContext(r, w, "DownloadProfiling")

View File

@ -84,10 +84,12 @@ func registerAdminRouter(router *mux.Router, enableConfigOps bool) {
adminRouter.Methods(http.MethodPost).Path(adminVersion+"/pools/cancel").HandlerFunc(gz(httpTraceAll(adminAPI.CancelDecommission))).Queries("pool", "{pool:.*}") adminRouter.Methods(http.MethodPost).Path(adminVersion+"/pools/cancel").HandlerFunc(gz(httpTraceAll(adminAPI.CancelDecommission))).Queries("pool", "{pool:.*}")
} }
// Profiling operations // Profiling operations - deprecated API
adminRouter.Methods(http.MethodPost).Path(adminVersion+"/profiling/start").HandlerFunc(gz(httpTraceAll(adminAPI.StartProfilingHandler))). adminRouter.Methods(http.MethodPost).Path(adminVersion+"/profiling/start").HandlerFunc(gz(httpTraceAll(adminAPI.StartProfilingHandler))).
Queries("profilerType", "{profilerType:.*}") Queries("profilerType", "{profilerType:.*}")
adminRouter.Methods(http.MethodGet).Path(adminVersion + "/profiling/download").HandlerFunc(gz(httpTraceAll(adminAPI.DownloadProfilingHandler))) adminRouter.Methods(http.MethodGet).Path(adminVersion + "/profiling/download").HandlerFunc(gz(httpTraceAll(adminAPI.DownloadProfilingHandler)))
// Profiling operations
adminRouter.Methods(http.MethodPost).Path(adminVersion + "/profile").HandlerFunc(gz(httpTraceAll(adminAPI.ProfileHandler)))
// Config KV operations. // Config KV operations.
if enableConfigOps { if enableConfigOps {