Skip to content

Commit 0bd563b

Browse files
authored
Fix a leak in WS object browser (#3325)
``` goroutine 7399330769 [chan send, 70126 minutes]: github.com/minio/console/api.(*wsMinioClient).objectManager.func2.1() github.com/minio/[email protected]/api/ws_objects.go:135 +0x6f0 created by github.com/minio/console/api.(*wsMinioClient).objectManager.func2 in goroutine 7354918912 github.com/minio/[email protected]/api/ws_objects.go:95 +0x45e ```
1 parent 22fe915 commit 0bd563b

File tree

1 file changed

+48
-46
lines changed

1 file changed

+48
-46
lines changed

api/ws_objects.go

Lines changed: 48 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -43,13 +43,26 @@ func (wsc *wsMinioClient) objectManager(session *models.Principal) {
4343
writeChannel := make(chan WSResponse)
4444
done := make(chan interface{})
4545

46+
sendWSResponse := func(r WSResponse) {
47+
select {
48+
case writeChannel <- r:
49+
case <-done:
50+
}
51+
}
52+
4653
// Read goroutine
4754
go func() {
55+
defer close(writeChannel)
4856
for {
57+
select {
58+
case <-done:
59+
return
60+
default:
61+
}
62+
4963
mType, message, err := wsc.conn.readMessage()
5064
if err != nil {
5165
LogInfo("Error while reading objectManager message", err)
52-
close(done)
5366
return
5467
}
5568

@@ -60,8 +73,6 @@ func (wsc *wsMinioClient) objectManager(session *models.Principal) {
6073
err := json.Unmarshal(message, &messageRequest)
6174
if err != nil {
6275
LogInfo("Error on message request unmarshal")
63-
64-
close(done)
6576
return
6677
}
6778

@@ -74,7 +85,6 @@ func (wsc *wsMinioClient) objectManager(session *models.Principal) {
7485
const itemsPerBatch = 1000
7586
switch messageRequest.Mode {
7687
case "close":
77-
close(done)
7888
return
7989
case "cancel":
8090
// if we have that request id, cancel it
@@ -97,12 +107,12 @@ func (wsc *wsMinioClient) objectManager(session *models.Principal) {
97107
if err != nil {
98108
LogInfo(fmt.Sprintf("Error during Objects OptionsParse %s", err.Error()))
99109

100-
writeChannel <- WSResponse{
110+
sendWSResponse(WSResponse{
101111
RequestID: messageRequest.RequestID,
102112
Error: ErrorWithContext(ctx, err),
103113
Prefix: messageRequest.Prefix,
104114
BucketName: messageRequest.BucketName,
105-
}
115+
})
106116

107117
return
108118
}
@@ -112,12 +122,12 @@ func (wsc *wsMinioClient) objectManager(session *models.Principal) {
112122
return
113123
}
114124
if lsObj.Err != nil {
115-
writeChannel <- WSResponse{
125+
sendWSResponse(WSResponse{
116126
RequestID: messageRequest.RequestID,
117127
Error: ErrorWithContext(ctx, lsObj.Err),
118128
Prefix: messageRequest.Prefix,
119129
BucketName: messageRequest.BucketName,
120-
}
130+
})
121131

122132
continue
123133
}
@@ -132,24 +142,24 @@ func (wsc *wsMinioClient) objectManager(session *models.Principal) {
132142
buffer = append(buffer, objItem)
133143

134144
if len(buffer) >= itemsPerBatch {
135-
writeChannel <- WSResponse{
145+
sendWSResponse(WSResponse{
136146
RequestID: messageRequest.RequestID,
137147
Data: buffer,
138-
}
148+
})
139149
buffer = nil
140150
}
141151
}
142152
if len(buffer) > 0 {
143-
writeChannel <- WSResponse{
153+
sendWSResponse(WSResponse{
144154
RequestID: messageRequest.RequestID,
145155
Data: buffer,
146-
}
156+
})
147157
}
148158

149-
writeChannel <- WSResponse{
159+
sendWSResponse(WSResponse{
150160
RequestID: messageRequest.RequestID,
151161
RequestEnd: true,
152-
}
162+
})
153163

154164
// remove the cancellation context
155165
delete(cancelContexts, messageRequest.RequestID)
@@ -168,12 +178,12 @@ func (wsc *wsMinioClient) objectManager(session *models.Principal) {
168178
objectRqConfigs, err := getObjectsOptionsFromReq(messageRequest)
169179
if err != nil {
170180
LogInfo(fmt.Sprintf("Error during Objects OptionsParse %s", err.Error()))
171-
writeChannel <- WSResponse{
181+
sendWSResponse(WSResponse{
172182
RequestID: messageRequest.RequestID,
173183
Error: ErrorWithContext(ctx, err),
174184
Prefix: messageRequest.Prefix,
175185
BucketName: messageRequest.BucketName,
176-
}
186+
})
177187

178188
return
179189
}
@@ -182,12 +192,12 @@ func (wsc *wsMinioClient) objectManager(session *models.Principal) {
182192

183193
s3Client, err := newS3BucketClient(session, objectRqConfigs.BucketName, objectRqConfigs.Prefix, clientIP)
184194
if err != nil {
185-
writeChannel <- WSResponse{
195+
sendWSResponse(WSResponse{
186196
RequestID: messageRequest.RequestID,
187197
Error: ErrorWithContext(ctx, err),
188198
Prefix: messageRequest.Prefix,
189199
BucketName: messageRequest.BucketName,
190-
}
200+
})
191201

192202
cancel()
193203
return
@@ -199,12 +209,12 @@ func (wsc *wsMinioClient) objectManager(session *models.Principal) {
199209

200210
for lsObj := range startRewindListing(ctx, mcS3C, objectRqConfigs) {
201211
if lsObj.Err != nil {
202-
writeChannel <- WSResponse{
212+
sendWSResponse(WSResponse{
203213
RequestID: messageRequest.RequestID,
204214
Error: ErrorWithContext(ctx, lsObj.Err.ToGoError()),
205215
Prefix: messageRequest.Prefix,
206216
BucketName: messageRequest.BucketName,
207-
}
217+
})
208218

209219
continue
210220
}
@@ -222,25 +232,25 @@ func (wsc *wsMinioClient) objectManager(session *models.Principal) {
222232
buffer = append(buffer, objItem)
223233

224234
if len(buffer) >= itemsPerBatch {
225-
writeChannel <- WSResponse{
235+
sendWSResponse(WSResponse{
226236
RequestID: messageRequest.RequestID,
227237
Data: buffer,
228-
}
238+
})
229239
buffer = nil
230240
}
231241

232242
}
233243
if len(buffer) > 0 {
234-
writeChannel <- WSResponse{
244+
sendWSResponse(WSResponse{
235245
RequestID: messageRequest.RequestID,
236246
Data: buffer,
237-
}
247+
})
238248
}
239249

240-
writeChannel <- WSResponse{
250+
sendWSResponse(WSResponse{
241251
RequestID: messageRequest.RequestID,
242252
RequestEnd: true,
243-
}
253+
})
244254

245255
// remove the cancellation context
246256
delete(cancelContexts, messageRequest.RequestID)
@@ -250,27 +260,19 @@ func (wsc *wsMinioClient) objectManager(session *models.Principal) {
250260
}
251261
}()
252262

253-
// Write goroutine
254-
go func() {
255-
for {
256-
select {
257-
case <-done:
258-
return
259-
case writeM := <-writeChannel:
260-
jsonData, err := json.Marshal(writeM)
261-
if err != nil {
262-
LogInfo("Error while marshaling the response", err)
263-
return
264-
}
263+
defer close(done)
265264

266-
err = wsc.conn.writeMessage(websocket.TextMessage, jsonData)
267-
if err != nil {
268-
LogInfo("Error while writing the message", err)
269-
return
270-
}
271-
}
265+
for writeM := range writeChannel {
266+
jsonData, err := json.Marshal(writeM)
267+
if err != nil {
268+
LogInfo("Error while marshaling the response", err)
269+
return
272270
}
273-
}()
274271

275-
<-done
272+
err = wsc.conn.writeMessage(websocket.TextMessage, jsonData)
273+
if err != nil {
274+
LogInfo("Error while writing the message", err)
275+
return
276+
}
277+
}
276278
}

0 commit comments

Comments
 (0)