Skip to content

Commit ba5f5a1

Browse files
authored
Merge pull request #79 from Random-Liu/change-resync-mechanism
Update NPD to only do forcibly sync every 1 minutes.
2 parents 4f964a1 + 60975f5 commit ba5f5a1

File tree

2 files changed

+63
-28
lines changed

2 files changed

+63
-28
lines changed

pkg/condition/manager.go

+35-23
Original file line numberDiff line numberDiff line change
@@ -32,12 +32,12 @@ import (
3232
)
3333

3434
const (
35-
// updatePeriod is the period which condition manager checks update.
35+
// updatePeriod is the period at which condition manager checks update.
3636
updatePeriod = 1 * time.Second
37-
// updateTimeout is the timeout of condition update operation.
38-
updateTimeout = 10 * time.Second
39-
// resyncPeriod is the period which condition manager does resync no matter whether these is any update.
40-
resyncPeriod = 30 * time.Second
37+
// resyncPeriod is the period at which condition manager does resync, only updates when needed.
38+
resyncPeriod = 10 * time.Second
39+
// heartbeatPeriod is the period at which condition manager does forcibly sync with apiserver.
40+
heartbeatPeriod = 1 * time.Minute
4141
)
4242

4343
// ConditionManager synchronizes node conditions with the apiserver with problem client.
@@ -52,17 +52,21 @@ const (
5252
type ConditionManager interface {
5353
// Start starts the condition manager.
5454
Start()
55-
// UpdateCondition update specific condition.
55+
// UpdateCondition updates a specific condition.
5656
UpdateCondition(types.Condition)
5757
}
5858

5959
type conditionManager struct {
60-
sync.Mutex
61-
clock clock.Clock
62-
latest time.Time
63-
client problemclient.Client
64-
updates map[string]types.Condition
65-
conditions map[string]types.Condition
60+
clock clock.Clock
61+
latestTry time.Time
62+
resyncNeeded bool
63+
client problemclient.Client
64+
// updatesLock is the lock protecting updates. Only the field `updates`
65+
// will be accessed by random caller and the sync routine, so only it
66+
// needs to be protected.
67+
updatesLock sync.Mutex
68+
updates map[string]types.Condition
69+
conditions map[string]types.Condition
6670
}
6771

6872
// NewConditionManager creates a condition manager.
@@ -80,8 +84,8 @@ func (c *conditionManager) Start() {
8084
}
8185

8286
func (c *conditionManager) UpdateCondition(condition types.Condition) {
83-
c.Lock()
84-
defer c.Unlock()
87+
c.updatesLock.Lock()
88+
defer c.updatesLock.Unlock()
8589
// New node condition will override the old condition, because we only need the newest
8690
// condition for each condition type.
8791
c.updates[condition.Type] = condition
@@ -92,17 +96,17 @@ func (c *conditionManager) syncLoop() {
9296
for {
9397
select {
9498
case <-updateCh:
95-
if c.checkUpdates() || c.checkResync() {
99+
if c.needUpdates() || c.needResync() || c.needHeartbeat() {
96100
c.sync()
97101
}
98102
}
99103
}
100104
}
101105

102-
// checkUpdates checks whether there are recent updates.
103-
func (c *conditionManager) checkUpdates() bool {
104-
c.Lock()
105-
defer c.Unlock()
106+
// needUpdates checks whether there are recent updates.
107+
func (c *conditionManager) needUpdates() bool {
108+
c.updatesLock.Lock()
109+
defer c.updatesLock.Unlock()
106110
needUpdate := false
107111
for t, update := range c.updates {
108112
if !reflect.DeepEqual(c.conditions[t], update) {
@@ -114,21 +118,29 @@ func (c *conditionManager) checkUpdates() bool {
114118
return needUpdate
115119
}
116120

117-
// checkResync checks whether a resync is needed.
118-
func (c *conditionManager) checkResync() bool {
119-
return c.clock.Now().Sub(c.latest) >= resyncPeriod
121+
// needResync checks whether a resync is needed.
122+
func (c *conditionManager) needResync() bool {
123+
// Only update when resync is needed.
124+
return c.clock.Now().Sub(c.latestTry) >= resyncPeriod && c.resyncNeeded
125+
}
126+
127+
// needHeartbeat checks whether a forcible heartbeat is needed.
128+
func (c *conditionManager) needHeartbeat() bool {
129+
return c.clock.Now().Sub(c.latestTry) >= heartbeatPeriod
120130
}
121131

122132
// sync synchronizes node conditions with the apiserver.
123133
func (c *conditionManager) sync() {
134+
c.latestTry = c.clock.Now()
135+
c.resyncNeeded = false
124136
conditions := []api.NodeCondition{}
125137
for i := range c.conditions {
126138
conditions = append(conditions, problemutil.ConvertToAPICondition(c.conditions[i]))
127139
}
128140
if err := c.client.SetConditions(conditions); err != nil {
129141
// The conditions will be updated again in future sync
130142
glog.Errorf("failed to update node conditions: %v", err)
143+
c.resyncNeeded = true
131144
return
132145
}
133-
c.latest = c.clock.Now()
134146
}

pkg/condition/manager_test.go

+28-5
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ limitations under the License.
1717
package condition
1818

1919
import (
20+
"fmt"
2021
"testing"
2122
"time"
2223

@@ -47,7 +48,7 @@ func newTestCondition(condition string) types.Condition {
4748
}
4849
}
4950

50-
func TestCheckUpdates(t *testing.T) {
51+
func TestNeedUpdates(t *testing.T) {
5152
m, _, _ := newTestManager()
5253
var c types.Condition
5354
for desc, test := range map[string]struct {
@@ -75,19 +76,41 @@ func TestCheckUpdates(t *testing.T) {
7576
c = newTestCondition(test.condition)
7677
}
7778
m.UpdateCondition(c)
78-
assert.Equal(t, test.update, m.checkUpdates(), desc)
79+
assert.Equal(t, test.update, m.needUpdates(), desc)
7980
assert.Equal(t, c, m.conditions[c.Type], desc)
8081
}
8182
}
8283

83-
func TestSync(t *testing.T) {
84+
func TestResync(t *testing.T) {
8485
m, fakeClient, fakeClock := newTestManager()
8586
condition := newTestCondition("TestCondition")
8687
m.conditions = map[string]types.Condition{condition.Type: condition}
8788
m.sync()
8889
expected := []api.NodeCondition{problemutil.ConvertToAPICondition(condition)}
8990
assert.Nil(t, fakeClient.AssertConditions(expected), "Condition should be updated via client")
90-
assert.False(t, m.checkResync(), "Should not resync before timeout exceeds")
91+
92+
assert.False(t, m.needResync(), "Should not resync before resync period")
93+
fakeClock.Step(resyncPeriod)
94+
assert.False(t, m.needResync(), "Should not resync after resync period without resync needed")
95+
96+
fakeClient.InjectError("SetConditions", fmt.Errorf("injected error"))
97+
m.sync()
98+
99+
assert.False(t, m.needResync(), "Should not resync before resync period")
91100
fakeClock.Step(resyncPeriod)
92-
assert.True(t, m.checkResync(), "Should resync after timeout exceeds")
101+
assert.True(t, m.needResync(), "Should resync after resync period and resync is needed")
102+
}
103+
104+
func TestHeartbeat(t *testing.T) {
105+
m, fakeClient, fakeClock := newTestManager()
106+
condition := newTestCondition("TestCondition")
107+
m.conditions = map[string]types.Condition{condition.Type: condition}
108+
m.sync()
109+
expected := []api.NodeCondition{problemutil.ConvertToAPICondition(condition)}
110+
assert.Nil(t, fakeClient.AssertConditions(expected), "Condition should be updated via client")
111+
112+
assert.False(t, m.needHeartbeat(), "Should not heartbeat before heartbeat period")
113+
114+
fakeClock.Step(heartbeatPeriod)
115+
assert.True(t, m.needHeartbeat(), "Should heartbeat after heartbeat period")
93116
}

0 commit comments

Comments
 (0)