Skip to content

Commit 2e5bbff

Browse files
chengyu-lliuchengyu
andauthored
feat: recover it when the goroutine caused some panic (#2349)
Co-authored-by: liuchengyu <liuchengyu@360.cn>
1 parent a885758 commit 2e5bbff

2 files changed

Lines changed: 97 additions & 12 deletions

File tree

codis/pkg/topom/topom.go

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import (
2222
"pika/codis/v2/pkg/utils/math2"
2323
"pika/codis/v2/pkg/utils/redis"
2424
"pika/codis/v2/pkg/utils/rpc"
25+
gxruntime "pika/codis/v2/pkg/utils/runtime"
2526
"pika/codis/v2/pkg/utils/sync2/atomic2"
2627
)
2728

@@ -197,7 +198,7 @@ func (s *Topom) Start(routines bool) error {
197198
}
198199

199200
// Check the status of all masters and slaves every 5 seconds
200-
go func() {
201+
gxruntime.GoUnterminated(func() {
201202
for !s.IsClosed() {
202203
if s.IsOnline() {
203204
w, _ := s.CheckMastersAndSlavesState(10 * time.Second)
@@ -207,11 +208,11 @@ func (s *Topom) Start(routines bool) error {
207208
}
208209
time.Sleep(s.Config().SentinelCheckServerStateInterval.Duration())
209210
}
210-
}()
211+
}, nil, true, 0)
211212

212213
// Check the status of the pre-offline master every 1 second
213214
// to determine whether to automatically switch master and slave
214-
go func() {
215+
gxruntime.GoUnterminated(func() {
215216
for !s.IsClosed() {
216217
if s.IsOnline() {
217218
w, _ := s.CheckPreOffineMastersState(5 * time.Second)
@@ -221,9 +222,9 @@ func (s *Topom) Start(routines bool) error {
221222
}
222223
time.Sleep(s.Config().SentinelCheckMasterFailoverInterval.Duration())
223224
}
224-
}()
225+
}, nil, true, 0)
225226

226-
go func() {
227+
gxruntime.GoUnterminated(func() {
227228
for !s.IsClosed() {
228229
if s.IsOnline() {
229230
w, _ := s.RefreshRedisStats(time.Second)
@@ -233,9 +234,9 @@ func (s *Topom) Start(routines bool) error {
233234
}
234235
time.Sleep(time.Second)
235236
}
236-
}()
237+
}, nil, true, 0)
237238

238-
go func() {
239+
gxruntime.GoUnterminated(func() {
239240
for !s.IsClosed() {
240241
if s.IsOnline() {
241242
w, _ := s.RefreshProxyStats(time.Second)
@@ -245,9 +246,9 @@ func (s *Topom) Start(routines bool) error {
245246
}
246247
time.Sleep(time.Second)
247248
}
248-
}()
249+
}, nil, true, 0)
249250

250-
go func() {
251+
gxruntime.GoUnterminated(func() {
251252
for !s.IsClosed() {
252253
if s.IsOnline() {
253254
if err := s.ProcessSlotAction(); err != nil {
@@ -257,9 +258,9 @@ func (s *Topom) Start(routines bool) error {
257258
}
258259
time.Sleep(time.Second)
259260
}
260-
}()
261+
}, nil, true, 0)
261262

262-
go func() {
263+
gxruntime.GoUnterminated(func() {
263264
for !s.IsClosed() {
264265
if s.IsOnline() {
265266
if err := s.ProcessSyncAction(); err != nil {
@@ -269,7 +270,7 @@ func (s *Topom) Start(routines bool) error {
269270
}
270271
time.Sleep(time.Second)
271272
}
272-
}()
273+
}, nil, true, 0)
273274

274275
return nil
275276
}
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one or more
3+
* contributor license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright ownership.
5+
* The ASF licenses this file to You under the Apache License, Version 2.0
6+
* (the "License"); you may not use this file except in compliance with
7+
* the License. You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
package gxruntime
19+
20+
import (
21+
"fmt"
22+
"os"
23+
"runtime/debug"
24+
"sync"
25+
"time"
26+
)
27+
28+
// GoSafely wraps a `go func()` with recover()
29+
func GoSafely(wg *sync.WaitGroup, ignoreRecover bool, handler func(), catchFunc func(r interface{})) {
30+
if wg != nil {
31+
wg.Add(1)
32+
}
33+
go func() {
34+
defer func() {
35+
if r := recover(); r != nil {
36+
if !ignoreRecover {
37+
fmt.Fprintf(os.Stderr, "%s goroutine panic: %v\n%s\n",
38+
time.Now(), r, string(debug.Stack()))
39+
}
40+
if catchFunc != nil {
41+
if wg != nil {
42+
wg.Add(1)
43+
}
44+
go func() {
45+
defer func() {
46+
if p := recover(); p != nil {
47+
if !ignoreRecover {
48+
fmt.Fprintf(os.Stderr, "recover goroutine panic:%v\n%s\n",
49+
p, string(debug.Stack()))
50+
}
51+
}
52+
53+
if wg != nil {
54+
wg.Done()
55+
}
56+
}()
57+
catchFunc(r)
58+
}()
59+
}
60+
}
61+
if wg != nil {
62+
wg.Done()
63+
}
64+
}()
65+
handler()
66+
}()
67+
}
68+
69+
// GoUnterminated is used for which goroutine wanna long live as its process.
70+
// @period: sleep time duration after panic to defeat @handle panic so frequently. if it is not positive,
71+
//
72+
// the @handle will be invoked asap after panic.
73+
func GoUnterminated(handle func(), wg *sync.WaitGroup, ignoreRecover bool, period time.Duration) {
74+
GoSafely(wg,
75+
ignoreRecover,
76+
handle,
77+
func(r interface{}) {
78+
if period > 0 {
79+
time.Sleep(period)
80+
}
81+
GoUnterminated(handle, wg, ignoreRecover, period)
82+
},
83+
)
84+
}

0 commit comments

Comments
 (0)