github.com/mattermosttest/mattermost-server/v5@v5.0.0-20200917143240-9dfa12e121f9/app/busy.go (about) 1 // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. 2 // See LICENSE.txt for license information. 3 4 package app 5 6 import ( 7 "sync" 8 "sync/atomic" 9 "time" 10 11 "github.com/mattermost/mattermost-server/v5/einterfaces" 12 "github.com/mattermost/mattermost-server/v5/model" 13 ) 14 15 const ( 16 TIMESTAMP_FORMAT = "Mon Jan 2 15:04:05 -0700 MST 2006" 17 ) 18 19 // Busy represents the busy state of the server. A server marked busy 20 // will have non-critical services disabled. If a Cluster is provided 21 // any changes will be propagated to each node. 22 type Busy struct { 23 busy int32 // protected via atomic for fast IsBusy calls 24 mux sync.RWMutex 25 timer *time.Timer 26 expires time.Time 27 28 cluster einterfaces.ClusterInterface 29 } 30 31 // NewBusy creates a new Busy instance with optional cluster which will 32 // be notified of busy state changes. 33 func NewBusy(cluster einterfaces.ClusterInterface) *Busy { 34 return &Busy{cluster: cluster} 35 } 36 37 // IsBusy returns true if the server has been marked as busy. 38 func (b *Busy) IsBusy() bool { 39 if b == nil { 40 return false 41 } 42 return atomic.LoadInt32(&b.busy) != 0 43 } 44 45 // Set marks the server as busy for dur duration and notifies cluster nodes. 46 func (b *Busy) Set(dur time.Duration) { 47 b.mux.Lock() 48 defer b.mux.Unlock() 49 50 // minimum 1 second 51 if dur < (time.Second * 1) { 52 dur = time.Second * 1 53 } 54 55 b.setWithoutNotify(dur) 56 57 if b.cluster != nil { 58 sbs := &model.ServerBusyState{Busy: true, Expires: b.expires.Unix(), Expires_ts: b.expires.UTC().Format(TIMESTAMP_FORMAT)} 59 b.notifyServerBusyChange(sbs) 60 } 61 } 62 63 // must hold mutex 64 func (b *Busy) setWithoutNotify(dur time.Duration) { 65 b.clearWithoutNotify() 66 atomic.StoreInt32(&b.busy, 1) 67 b.expires = time.Now().Add(dur) 68 b.timer = time.AfterFunc(dur, b.clearWithoutNotify) 69 } 70 71 // ClearBusy marks the server as not busy and notifies cluster nodes. 72 func (b *Busy) Clear() { 73 b.mux.Lock() 74 defer b.mux.Unlock() 75 76 b.clearWithoutNotify() 77 78 if b.cluster != nil { 79 sbs := &model.ServerBusyState{Busy: false, Expires: time.Time{}.Unix(), Expires_ts: ""} 80 b.notifyServerBusyChange(sbs) 81 } 82 } 83 84 // must hold mutex 85 func (b *Busy) clearWithoutNotify() { 86 if b.timer != nil { 87 b.timer.Stop() // don't drain timer.C channel for AfterFunc timers. 88 } 89 b.timer = nil 90 b.expires = time.Time{} 91 atomic.StoreInt32(&b.busy, 0) 92 } 93 94 // Expires returns the expected time that the server 95 // will be marked not busy. This expiry can be extended 96 // via additional calls to SetBusy. 97 func (b *Busy) Expires() time.Time { 98 b.mux.RLock() 99 defer b.mux.RUnlock() 100 return b.expires 101 } 102 103 // notifyServerBusyChange informs all cluster members of a server busy state change. 104 func (b *Busy) notifyServerBusyChange(sbs *model.ServerBusyState) { 105 if b.cluster == nil { 106 return 107 } 108 msg := &model.ClusterMessage{ 109 Event: model.CLUSTER_EVENT_BUSY_STATE_CHANGED, 110 SendType: model.CLUSTER_SEND_RELIABLE, 111 WaitForAllToSend: true, 112 Data: sbs.ToJson(), 113 } 114 b.cluster.SendClusterMessage(msg) 115 } 116 117 // ClusterEventChanged is called when a CLUSTER_EVENT_BUSY_STATE_CHANGED is received. 118 func (b *Busy) ClusterEventChanged(sbs *model.ServerBusyState) { 119 b.mux.Lock() 120 defer b.mux.Unlock() 121 122 if sbs.Busy { 123 expires := time.Unix(sbs.Expires, 0) 124 dur := time.Until(expires) 125 if dur > 0 { 126 b.setWithoutNotify(dur) 127 } 128 } else { 129 b.clearWithoutNotify() 130 } 131 } 132 133 func (b *Busy) ToJson() string { 134 b.mux.RLock() 135 defer b.mux.RUnlock() 136 137 sbs := &model.ServerBusyState{ 138 Busy: atomic.LoadInt32(&b.busy) != 0, 139 Expires: b.expires.Unix(), 140 Expires_ts: b.expires.UTC().Format(TIMESTAMP_FORMAT), 141 } 142 return sbs.ToJson() 143 }