4
4
"context"
5
5
"net"
6
6
"net/netip"
7
- "sync"
7
+ "sync/atomic "
8
8
"time"
9
9
10
10
"github.com/cybertec-postgresql/vip-manager/vipconfig"
@@ -25,10 +25,9 @@ var log *zap.SugaredLogger = zap.L().Sugar()
25
25
type IPManager struct {
26
26
configurer ipConfigurer
27
27
28
- states <- chan bool
29
- currentState bool
30
- stateLock sync.Mutex
31
- recheck * sync.Cond
28
+ states <- chan bool
29
+ shouldSetIPUp atomic.Bool
30
+ recheckChan chan struct {}
32
31
}
33
32
34
33
func getMask (vip netip.Addr , mask int ) net.IPMask {
@@ -69,7 +68,7 @@ func NewIPManager(conf *vipconfig.Config, states <-chan bool) (m *IPManager, err
69
68
states : states ,
70
69
}
71
70
log = conf .Logger .Sugar ()
72
- m .recheck = sync . NewCond ( & m . stateLock )
71
+ m .recheckChan = make ( chan struct {} )
73
72
switch conf .HostingType {
74
73
case "hetzner" :
75
74
m .configurer , err = newHetznerConfigurer (ipConf , conf .Verbose )
@@ -86,71 +85,45 @@ func NewIPManager(conf *vipconfig.Config, states <-chan bool) (m *IPManager, err
86
85
87
86
func (m * IPManager ) applyLoop (ctx context.Context ) {
88
87
strUpDown := map [bool ]string {true : "up" , false : "down" }
89
- timeout := 0
90
88
for {
91
- // Check if we should exit
89
+ isIPUp := m .configurer .queryAddress ()
90
+ shouldSetIPUp := m .shouldSetIPUp .Load ()
91
+ log .Infof ("IP address %s is %s, must be %s" ,
92
+ m .configurer .getCIDR (),
93
+ strUpDown [isIPUp ],
94
+ strUpDown [shouldSetIPUp ])
95
+ if isIPUp != shouldSetIPUp {
96
+ var isOk bool
97
+ if shouldSetIPUp {
98
+ isOk = m .configurer .configureAddress ()
99
+ } else {
100
+ isOk = m .configurer .deconfigureAddress ()
101
+ }
102
+ if ! isOk {
103
+ log .Error ("Failed to configure virtual ip for this machine" )
104
+ }
105
+ }
92
106
select {
93
107
case <- ctx .Done ():
94
- m .configurer .deconfigureAddress ()
95
108
return
96
- case <- time .After (time .Duration (timeout ) * time .Second ):
97
- actualState := m .configurer .queryAddress ()
98
- m .stateLock .Lock ()
99
- desiredState := m .currentState
100
- log .Infof ("IP address %s state is %s, must be %s" ,
101
- m .configurer .getCIDR (),
102
- strUpDown [actualState ],
103
- strUpDown [desiredState ])
104
- if actualState != desiredState {
105
- m .stateLock .Unlock ()
106
- var configureState bool
107
- if desiredState {
108
- configureState = m .configurer .configureAddress ()
109
- } else {
110
- configureState = m .configurer .deconfigureAddress ()
111
- }
112
- if ! configureState {
113
- log .Error ("Error while acquiring virtual ip for this machine" )
114
- //Sleep a little bit to avoid busy waiting due to the for loop.
115
- timeout = 10
116
- } else {
117
- timeout = 0
118
- }
119
- } else {
120
- // Wait for notification
121
- m .recheck .Wait ()
122
- // Want to query actual state anyway, so unlock
123
- m .stateLock .Unlock ()
124
- }
109
+ case <- m .recheckChan : // signal to recheck
110
+ case <- time .After (time .Duration (10 ) * time .Second ): // recheck every 10 seconds
125
111
}
126
112
}
127
113
}
128
114
129
115
// SyncStates implements states synchronization
130
116
func (m * IPManager ) SyncStates (ctx context.Context , states <- chan bool ) {
131
- ticker := time .NewTicker (10 * time .Second )
132
-
133
- var wg sync.WaitGroup
134
- wg .Add (1 )
135
- go func () {
136
- m .applyLoop (ctx )
137
- wg .Done ()
138
- }()
139
-
117
+ go m .applyLoop (ctx )
140
118
for {
141
119
select {
142
120
case newState := <- states :
143
- m .stateLock .Lock ()
144
- if m .currentState != newState {
145
- m .currentState = newState
146
- m .recheck .Broadcast ()
121
+ if m .shouldSetIPUp .Load () != newState {
122
+ m .shouldSetIPUp .Store (newState )
123
+ m .recheckChan <- struct {}{}
147
124
}
148
- m .stateLock .Unlock ()
149
- case <- ticker .C :
150
- m .recheck .Broadcast ()
151
125
case <- ctx .Done ():
152
- m .recheck .Broadcast ()
153
- wg .Wait ()
126
+ m .configurer .deconfigureAddress ()
154
127
m .configurer .cleanupArp ()
155
128
return
156
129
}
0 commit comments