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