Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
K
k3s
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Registry
Registry
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Jacklull
k3s
Commits
40247acb
Commit
40247acb
authored
Sep 23, 2015
by
Brendan Burns
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Fix a race in pod backoff.
parent
a518a273
Show whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
79 additions
and
35 deletions
+79
-35
factory.go
plugin/pkg/scheduler/factory/factory.go
+60
-23
factory_test.go
plugin/pkg/scheduler/factory/factory_test.go
+19
-12
No files found.
plugin/pkg/scheduler/factory/factory.go
View file @
40247acb
...
@@ -19,8 +19,10 @@ limitations under the License.
...
@@ -19,8 +19,10 @@ limitations under the License.
package
factory
package
factory
import
(
import
(
"fmt"
"math/rand"
"math/rand"
"sync"
"sync"
"sync/atomic"
"time"
"time"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api"
...
@@ -29,6 +31,7 @@ import (
...
@@ -29,6 +31,7 @@ import (
client
"k8s.io/kubernetes/pkg/client/unversioned"
client
"k8s.io/kubernetes/pkg/client/unversioned"
"k8s.io/kubernetes/pkg/controller/framework"
"k8s.io/kubernetes/pkg/controller/framework"
"k8s.io/kubernetes/pkg/fields"
"k8s.io/kubernetes/pkg/fields"
"k8s.io/kubernetes/pkg/types"
"k8s.io/kubernetes/pkg/util"
"k8s.io/kubernetes/pkg/util"
"k8s.io/kubernetes/pkg/util/sets"
"k8s.io/kubernetes/pkg/util/sets"
"k8s.io/kubernetes/plugin/pkg/scheduler"
"k8s.io/kubernetes/plugin/pkg/scheduler"
...
@@ -199,7 +202,7 @@ func (f *ConfigFactory) CreateFromKeys(predicateKeys, priorityKeys sets.String)
...
@@ -199,7 +202,7 @@ func (f *ConfigFactory) CreateFromKeys(predicateKeys, priorityKeys sets.String)
algo
:=
scheduler
.
NewGenericScheduler
(
predicateFuncs
,
priorityConfigs
,
f
.
PodLister
,
r
)
algo
:=
scheduler
.
NewGenericScheduler
(
predicateFuncs
,
priorityConfigs
,
f
.
PodLister
,
r
)
podBackoff
:=
podBackoff
{
podBackoff
:=
podBackoff
{
perPodBackoff
:
map
[
string
]
*
backoffEntry
{},
perPodBackoff
:
map
[
types
.
NamespacedName
]
*
backoffEntry
{},
clock
:
realClock
{},
clock
:
realClock
{},
defaultDuration
:
1
*
time
.
Second
,
defaultDuration
:
1
*
time
.
Second
,
...
@@ -274,12 +277,19 @@ func (factory *ConfigFactory) makeDefaultErrorFunc(backoff *podBackoff, podQueue
...
@@ -274,12 +277,19 @@ func (factory *ConfigFactory) makeDefaultErrorFunc(backoff *podBackoff, podQueue
// Note that this is extremely rudimentary and we need a more real error handling path.
// Note that this is extremely rudimentary and we need a more real error handling path.
go
func
()
{
go
func
()
{
defer
util
.
HandleCrash
()
defer
util
.
HandleCrash
()
podID
:=
pod
.
Name
podID
:=
types
.
NamespacedName
{
podNamespace
:=
pod
.
Namespace
Namespace
:
pod
.
Namespace
,
backoff
.
wait
(
podID
)
Name
:
pod
.
Name
,
}
entry
:=
backoff
.
getEntry
(
podID
)
if
!
entry
.
TryWait
(
backoff
.
maxDuration
)
{
glog
.
Warningf
(
"Request for pod %v already in flight, abandoning"
,
podID
)
return
}
// Get the pod again; it may have changed/been scheduled already.
// Get the pod again; it may have changed/been scheduled already.
pod
=
&
api
.
Pod
{}
pod
=
&
api
.
Pod
{}
err
:=
factory
.
Client
.
Get
()
.
Namespace
(
pod
Namespace
)
.
Resource
(
"pods"
)
.
Name
(
podID
)
.
Do
()
.
Into
(
pod
)
err
:=
factory
.
Client
.
Get
()
.
Namespace
(
pod
ID
.
Namespace
)
.
Resource
(
"pods"
)
.
Name
(
podID
.
Name
)
.
Do
()
.
Into
(
pod
)
if
err
!=
nil
{
if
err
!=
nil
{
if
!
errors
.
IsNotFound
(
err
)
{
if
!
errors
.
IsNotFound
(
err
)
{
glog
.
Errorf
(
"Error getting pod %v for retry: %v; abandoning"
,
podID
,
err
)
glog
.
Errorf
(
"Error getting pod %v for retry: %v; abandoning"
,
podID
,
err
)
...
@@ -287,7 +297,7 @@ func (factory *ConfigFactory) makeDefaultErrorFunc(backoff *podBackoff, podQueue
...
@@ -287,7 +297,7 @@ func (factory *ConfigFactory) makeDefaultErrorFunc(backoff *podBackoff, podQueue
return
return
}
}
if
pod
.
Spec
.
NodeName
==
""
{
if
pod
.
Spec
.
NodeName
==
""
{
podQueue
.
Add
(
pod
)
podQueue
.
Add
IfNotPresent
(
pod
)
}
}
}()
}()
}
}
...
@@ -334,20 +344,62 @@ func (realClock) Now() time.Time {
...
@@ -334,20 +344,62 @@ func (realClock) Now() time.Time {
return
time
.
Now
()
return
time
.
Now
()
}
}
// backoffEntry is single threaded. in particular, it only allows a single action to be waiting on backoff at a time.
// It is expected that all users will only use the public TryWait(...) method
// It is also not safe to copy this object.
type
backoffEntry
struct
{
type
backoffEntry
struct
{
backoff
time
.
Duration
backoff
time
.
Duration
lastUpdate
time
.
Time
lastUpdate
time
.
Time
reqInFlight
int32
}
// tryLock attempts to acquire a lock via atomic compare and swap.
// returns true if the lock was acquired, false otherwise
func
(
b
*
backoffEntry
)
tryLock
()
bool
{
return
atomic
.
CompareAndSwapInt32
(
&
b
.
reqInFlight
,
0
,
1
)
}
// unlock returns the lock. panics if the lock isn't held
func
(
b
*
backoffEntry
)
unlock
()
{
if
!
atomic
.
CompareAndSwapInt32
(
&
b
.
reqInFlight
,
1
,
0
)
{
panic
(
fmt
.
Sprintf
(
"unexpected state on unlocking: %v"
,
b
))
}
}
// TryWait tries to acquire the backoff lock, maxDuration is the maximum allowed period to wait for.
func
(
b
*
backoffEntry
)
TryWait
(
maxDuration
time
.
Duration
)
bool
{
if
!
b
.
tryLock
()
{
return
false
}
defer
b
.
unlock
()
b
.
wait
(
maxDuration
)
return
true
}
func
(
entry
*
backoffEntry
)
getBackoff
(
maxDuration
time
.
Duration
)
time
.
Duration
{
duration
:=
entry
.
backoff
newDuration
:=
time
.
Duration
(
duration
)
*
2
if
newDuration
>
maxDuration
{
newDuration
=
maxDuration
}
entry
.
backoff
=
newDuration
glog
.
V
(
4
)
.
Infof
(
"Backing off %s for pod %v"
,
duration
.
String
(),
entry
)
return
duration
}
func
(
entry
*
backoffEntry
)
wait
(
maxDuration
time
.
Duration
)
{
time
.
Sleep
(
entry
.
getBackoff
(
maxDuration
))
}
}
type
podBackoff
struct
{
type
podBackoff
struct
{
perPodBackoff
map
[
string
]
*
backoffEntry
perPodBackoff
map
[
types
.
NamespacedName
]
*
backoffEntry
lock
sync
.
Mutex
lock
sync
.
Mutex
clock
clock
clock
clock
defaultDuration
time
.
Duration
defaultDuration
time
.
Duration
maxDuration
time
.
Duration
maxDuration
time
.
Duration
}
}
func
(
p
*
podBackoff
)
getEntry
(
podID
string
)
*
backoffEntry
{
func
(
p
*
podBackoff
)
getEntry
(
podID
types
.
NamespacedName
)
*
backoffEntry
{
p
.
lock
.
Lock
()
p
.
lock
.
Lock
()
defer
p
.
lock
.
Unlock
()
defer
p
.
lock
.
Unlock
()
entry
,
ok
:=
p
.
perPodBackoff
[
podID
]
entry
,
ok
:=
p
.
perPodBackoff
[
podID
]
...
@@ -359,21 +411,6 @@ func (p *podBackoff) getEntry(podID string) *backoffEntry {
...
@@ -359,21 +411,6 @@ func (p *podBackoff) getEntry(podID string) *backoffEntry {
return
entry
return
entry
}
}
func
(
p
*
podBackoff
)
getBackoff
(
podID
string
)
time
.
Duration
{
entry
:=
p
.
getEntry
(
podID
)
duration
:=
entry
.
backoff
entry
.
backoff
*=
2
if
entry
.
backoff
>
p
.
maxDuration
{
entry
.
backoff
=
p
.
maxDuration
}
glog
.
V
(
4
)
.
Infof
(
"Backing off %s for pod %s"
,
duration
.
String
(),
podID
)
return
duration
}
func
(
p
*
podBackoff
)
wait
(
podID
string
)
{
time
.
Sleep
(
p
.
getBackoff
(
podID
))
}
func
(
p
*
podBackoff
)
gc
()
{
func
(
p
*
podBackoff
)
gc
()
{
p
.
lock
.
Lock
()
p
.
lock
.
Lock
()
defer
p
.
lock
.
Unlock
()
defer
p
.
lock
.
Unlock
()
...
...
plugin/pkg/scheduler/factory/factory_test.go
View file @
40247acb
...
@@ -28,6 +28,7 @@ import (
...
@@ -28,6 +28,7 @@ import (
"k8s.io/kubernetes/pkg/client/cache"
"k8s.io/kubernetes/pkg/client/cache"
client
"k8s.io/kubernetes/pkg/client/unversioned"
client
"k8s.io/kubernetes/pkg/client/unversioned"
"k8s.io/kubernetes/pkg/runtime"
"k8s.io/kubernetes/pkg/runtime"
"k8s.io/kubernetes/pkg/types"
"k8s.io/kubernetes/pkg/util"
"k8s.io/kubernetes/pkg/util"
"k8s.io/kubernetes/plugin/pkg/scheduler/algorithm"
"k8s.io/kubernetes/plugin/pkg/scheduler/algorithm"
schedulerapi
"k8s.io/kubernetes/plugin/pkg/scheduler/api"
schedulerapi
"k8s.io/kubernetes/plugin/pkg/scheduler/api"
...
@@ -154,7 +155,7 @@ func TestDefaultErrorFunc(t *testing.T) {
...
@@ -154,7 +155,7 @@ func TestDefaultErrorFunc(t *testing.T) {
factory
:=
NewConfigFactory
(
client
.
NewOrDie
(
&
client
.
Config
{
Host
:
server
.
URL
,
Version
:
testapi
.
Default
.
Version
()}),
nil
)
factory
:=
NewConfigFactory
(
client
.
NewOrDie
(
&
client
.
Config
{
Host
:
server
.
URL
,
Version
:
testapi
.
Default
.
Version
()}),
nil
)
queue
:=
cache
.
NewFIFO
(
cache
.
MetaNamespaceKeyFunc
)
queue
:=
cache
.
NewFIFO
(
cache
.
MetaNamespaceKeyFunc
)
podBackoff
:=
podBackoff
{
podBackoff
:=
podBackoff
{
perPodBackoff
:
map
[
string
]
*
backoffEntry
{},
perPodBackoff
:
map
[
types
.
NamespacedName
]
*
backoffEntry
{},
clock
:
&
fakeClock
{},
clock
:
&
fakeClock
{},
defaultDuration
:
1
*
time
.
Millisecond
,
defaultDuration
:
1
*
time
.
Millisecond
,
maxDuration
:
1
*
time
.
Second
,
maxDuration
:
1
*
time
.
Second
,
...
@@ -249,53 +250,59 @@ func TestBind(t *testing.T) {
...
@@ -249,53 +250,59 @@ func TestBind(t *testing.T) {
func
TestBackoff
(
t
*
testing
.
T
)
{
func
TestBackoff
(
t
*
testing
.
T
)
{
clock
:=
fakeClock
{}
clock
:=
fakeClock
{}
backoff
:=
podBackoff
{
backoff
:=
podBackoff
{
perPodBackoff
:
map
[
string
]
*
backoffEntry
{},
perPodBackoff
:
map
[
types
.
NamespacedName
]
*
backoffEntry
{},
clock
:
&
clock
,
clock
:
&
clock
,
defaultDuration
:
1
*
time
.
Second
,
defaultDuration
:
1
*
time
.
Second
,
maxDuration
:
60
*
time
.
Second
,
maxDuration
:
60
*
time
.
Second
,
}
}
tests
:=
[]
struct
{
tests
:=
[]
struct
{
podID
string
podID
types
.
NamespacedName
expectedDuration
time
.
Duration
expectedDuration
time
.
Duration
advanceClock
time
.
Duration
advanceClock
time
.
Duration
}{
}{
{
{
podID
:
"foo"
,
podID
:
types
.
NamespacedName
{
Namespace
:
"default"
,
Name
:
"foo"
}
,
expectedDuration
:
1
*
time
.
Second
,
expectedDuration
:
1
*
time
.
Second
,
},
},
{
{
podID
:
"foo"
,
podID
:
types
.
NamespacedName
{
Namespace
:
"default"
,
Name
:
"foo"
}
,
expectedDuration
:
2
*
time
.
Second
,
expectedDuration
:
2
*
time
.
Second
,
},
},
{
{
podID
:
"foo"
,
podID
:
types
.
NamespacedName
{
Namespace
:
"default"
,
Name
:
"foo"
}
,
expectedDuration
:
4
*
time
.
Second
,
expectedDuration
:
4
*
time
.
Second
,
},
},
{
{
podID
:
"bar"
,
podID
:
types
.
NamespacedName
{
Namespace
:
"default"
,
Name
:
"bar"
}
,
expectedDuration
:
1
*
time
.
Second
,
expectedDuration
:
1
*
time
.
Second
,
advanceClock
:
120
*
time
.
Second
,
advanceClock
:
120
*
time
.
Second
,
},
},
// 'foo' should have been gc'd here.
// 'foo' should have been gc'd here.
{
{
podID
:
"foo"
,
podID
:
types
.
NamespacedName
{
Namespace
:
"default"
,
Name
:
"foo"
}
,
expectedDuration
:
1
*
time
.
Second
,
expectedDuration
:
1
*
time
.
Second
,
},
},
}
}
for
_
,
test
:=
range
tests
{
for
_
,
test
:=
range
tests
{
duration
:=
backoff
.
get
Backoff
(
test
.
podID
)
duration
:=
backoff
.
get
Entry
(
test
.
podID
)
.
getBackoff
(
backoff
.
maxDuration
)
if
duration
!=
test
.
expectedDuration
{
if
duration
!=
test
.
expectedDuration
{
t
.
Errorf
(
"expected: %s, got %s for %s"
,
test
.
expectedDuration
.
String
(),
duration
.
String
(),
test
.
podID
)
t
.
Errorf
(
"expected: %s, got %s for %s"
,
test
.
expectedDuration
.
String
(),
duration
.
String
(),
test
.
podID
)
}
}
clock
.
t
=
clock
.
t
.
Add
(
test
.
advanceClock
)
clock
.
t
=
clock
.
t
.
Add
(
test
.
advanceClock
)
backoff
.
gc
()
backoff
.
gc
()
}
}
fooID
:=
types
.
NamespacedName
{
Namespace
:
"default"
,
Name
:
"foo"
}
backoff
.
perPodBackoff
[
"foo"
]
.
backoff
=
60
*
time
.
Second
backoff
.
perPodBackoff
[
fooID
]
.
backoff
=
60
*
time
.
Second
duration
:=
backoff
.
get
Backoff
(
"foo"
)
duration
:=
backoff
.
get
Entry
(
fooID
)
.
getBackoff
(
backoff
.
maxDuration
)
if
duration
!=
60
*
time
.
Second
{
if
duration
!=
60
*
time
.
Second
{
t
.
Errorf
(
"expected: 60, got %s"
,
duration
.
String
())
t
.
Errorf
(
"expected: 60, got %s"
,
duration
.
String
())
}
}
// Verify that we split on namespaces correctly, same name, different namespace
fooID
.
Namespace
=
"other"
duration
=
backoff
.
getEntry
(
fooID
)
.
getBackoff
(
backoff
.
maxDuration
)
if
duration
!=
1
*
time
.
Second
{
t
.
Errorf
(
"expected: 1, got %s"
,
duration
.
String
())
}
}
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment