-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathoom_selfdestruct.go
90 lines (80 loc) · 2.65 KB
/
oom_selfdestruct.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
package oom
import (
"net/http"
"os"
"sync/atomic"
"syscall"
"github.com/pressly/chi"
"golang.org/x/net/context"
)
var terminating int32
// Selfdestruct is a middleware that terminates current process if system
// memory use exceeds the threshold set.
//
// Most common use case is automatic restart of leaky *cough*GCO*cough service
// all you need is this middleware and something that will start the process
// again after it's been terminated (docker --restart=always for example )
//
// OOMSelfdestruct accepts one parameter - a float64 defining a fraction of the
// system memory that can be used before process self-terminates using system
// signal (SIG_TERM).
// As long as the app handles shutdown gracefully on SIG_TERM it should just work
//
// Right now it relies on memory usage data provided by /proc/meminfo so it is
// Linux specific. For convenience (dev envs) it will do nothing on other archs
func Selfdestruct(limit float64) func(chi.Handler) chi.Handler {
return func(next chi.Handler) chi.Handler {
fn := func(ctx context.Context, w http.ResponseWriter, r *http.Request) {
if MemoryUsage() > limit {
go selfdestruct(ctx, SignalSelfdestructGroup)
}
next.ServeHTTPC(ctx, w, r)
}
return chi.HandlerFunc(fn)
}
}
// SelfdestructFn is a version of Selfdestruct that accepts a custom
// termination function ( func(context.Context) bool ) that handles the process
// shutdown.
// Use it if your app requires extra steps to gracefully shut down.
func SelfdestructFn(limit float64, fn func(ctx context.Context) bool) func(chi.Handler) chi.Handler {
return func(next chi.Handler) chi.Handler {
fn := func(ctx context.Context, w http.ResponseWriter, r *http.Request) {
if MemoryUsage() > limit {
go selfdestruct(ctx, fn)
}
next.ServeHTTPC(ctx, w, r)
}
return chi.HandlerFunc(fn)
}
}
func selfdestruct(ctx context.Context, fn func(ctx context.Context) bool) {
// if it's already terminating do nothing
if !atomic.CompareAndSwapInt32(&terminating, 0, 1) {
return
}
if !fn(ctx) {
// selfdestruct failed, give future requests a chance to try again
atomic.SwapInt32(&terminating, 0)
}
}
// SignalSelfdestructGroup sends SIGTERM to whole process group
func SignalSelfdestructGroup(ctx context.Context) bool {
gpid, err := syscall.Getpgid(os.Getpid())
if err == nil {
if err = syscall.Kill(gpid, syscall.SIGTERM); err == nil {
return true
}
}
return false
}
// SignalSelfdestructProcess sends SIGTERM to current process
func SignalSelfdestructProcess(ctx context.Context) bool {
proc, err := os.FindProcess(os.Getpid())
if err == nil {
if err = proc.Signal(syscall.SIGTERM); err == nil {
return true
}
}
return false
}