diff --git a/cmd/admin-handlers.go b/cmd/admin-handlers.go index 03964b27b..c62c1d0f7 100644 --- a/cmd/admin-handlers.go +++ b/cmd/admin-handlers.go @@ -1,5 +1,5 @@ /* - * MinIO Cloud Storage, (C) 2016-2019 MinIO, Inc. + * MinIO Cloud Storage, (C) 2016-2020 MinIO, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -328,7 +328,7 @@ func (a adminAPIHandlers) DataUsageInfoHandler(w http.ResponseWriter, r *http.Re writeSuccessResponseJSON(w, dataUsageInfoJSON) } -func newLockEntry(l lockRequesterInfo, resource, server string) *madmin.LockEntry { +func lriToLockEntry(l lockRequesterInfo, resource, server string) *madmin.LockEntry { entry := &madmin.LockEntry{ Timestamp: l.Timestamp, Resource: resource, @@ -337,14 +337,14 @@ func newLockEntry(l lockRequesterInfo, resource, server string) *madmin.LockEntr ID: l.UID, } if l.Writer { - entry.Type = "Write" + entry.Type = "WRITE" } else { - entry.Type = "Read" + entry.Type = "READ" } return entry } -func topLockEntries(peerLocks []*PeerLocks) madmin.LockEntries { +func topLockEntries(peerLocks []*PeerLocks, count int) madmin.LockEntries { entryMap := make(map[string]*madmin.LockEntry) for _, peerLock := range peerLocks { if peerLock == nil { @@ -356,20 +356,19 @@ func topLockEntries(peerLocks []*PeerLocks) madmin.LockEntries { if val, ok := entryMap[lockReqInfo.UID]; ok { val.ServerList = append(val.ServerList, peerLock.Addr) } else { - entryMap[lockReqInfo.UID] = newLockEntry(lockReqInfo, k, peerLock.Addr) + entryMap[lockReqInfo.UID] = lriToLockEntry(lockReqInfo, k, peerLock.Addr) } } } } } - var lockEntries = make(madmin.LockEntries, 0) + var lockEntries = make(madmin.LockEntries, 0, len(entryMap)) for _, v := range entryMap { lockEntries = append(lockEntries, *v) } sort.Sort(lockEntries) - const listCount int = 10 - if len(lockEntries) > listCount { - lockEntries = lockEntries[:listCount] + if len(lockEntries) > count { + lockEntries = lockEntries[:count] } return lockEntries } @@ -391,6 +390,16 @@ func (a adminAPIHandlers) TopLocksHandler(w http.ResponseWriter, r *http.Request return } + count := 10 // by default list only top 10 entries + if countStr := r.URL.Query().Get("count"); countStr != "" { + var err error + count, err = strconv.Atoi(countStr) + if err != nil { + writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL) + return + } + } + peerLocks := globalNotificationSys.GetLocks(ctx) // Once we have received all the locks currently used from peers // add the local peer locks list as well. @@ -398,12 +407,13 @@ func (a adminAPIHandlers) TopLocksHandler(w http.ResponseWriter, r *http.Request for _, llocker := range globalLockServers { getRespLocks = append(getRespLocks, llocker.DupLockMap()) } + peerLocks = append(peerLocks, &PeerLocks{ Addr: getHostName(r), Locks: getRespLocks, }) - topLocks := topLockEntries(peerLocks) + topLocks := topLockEntries(peerLocks, count) // Marshal API response jsonBytes, err := json.Marshal(topLocks) diff --git a/pkg/madmin/top-commands.go b/pkg/madmin/top-commands.go index 9469b69bc..c065533b5 100644 --- a/pkg/madmin/top-commands.go +++ b/pkg/madmin/top-commands.go @@ -22,6 +22,8 @@ import ( "encoding/json" "io/ioutil" "net/http" + "net/url" + "strconv" "time" ) @@ -29,12 +31,11 @@ import ( // servers holding the lock, source on the client machine, // ID, type(read or write) and time stamp. type LockEntry struct { - Timestamp time.Time `json:"time"` // Timestamp set at the time of initialization. - Resource string `json:"resource"` // Resource contains info like bucket, object etc - Type string `json:"type"` // Bool whether write or read lock. - Source string `json:"source"` // Source which created the lock - ServerList []string `json:"serverlist"` // RPC path of servers issuing the lock. - Owner string `json:"owner"` // RPC path of client claiming lock. + Timestamp time.Time `json:"time"` // When the lock was first granted + Resource string `json:"resource"` // Resource contains info like bucket+object + Type string `json:"type"` // Type indicates if 'Write' or 'Read' lock + Source string `json:"source"` // Source at which lock was granted + ServerList []string `json:"serverlist"` // List of servers participating in the lock. ID string `json:"id"` // UID to uniquely identify request of client. } @@ -53,13 +54,19 @@ func (l LockEntries) Swap(i, j int) { l[i], l[j] = l[j], l[i] } -// TopLocks - returns the oldest locks in a minio setup. -func (adm *AdminClient) TopLocks(ctx context.Context) (LockEntries, error) { - // Execute GET on /minio/admin/v3/top/locks - // to get the oldest locks in a minio setup. +// TopNLocks - returns the count number of oldest locks currently active on the server. +func (adm *AdminClient) TopNLocks(ctx context.Context, count int) (LockEntries, error) { + // Execute GET on /minio/admin/v3/top/locks?count=10 + // to get the 'count' number of oldest locks currently + // active on the server. + queryVals := make(url.Values) + queryVals.Set("count", strconv.Itoa(count)) resp, err := adm.executeMethod(ctx, http.MethodGet, - requestData{relPath: adminAPIPrefix + "/top/locks"}, + requestData{ + relPath: adminAPIPrefix + "/top/locks", + queryValues: queryVals, + }, ) defer closeResponse(resp) if err != nil { @@ -79,3 +86,8 @@ func (adm *AdminClient) TopLocks(ctx context.Context) (LockEntries, error) { err = json.Unmarshal(response, &lockEntries) return lockEntries, err } + +// TopLocks - returns top '10' oldest locks currently active on the server. +func (adm *AdminClient) TopLocks(ctx context.Context) (LockEntries, error) { + return adm.TopNLocks(ctx, 10) +}