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
d1345a1f
Commit
d1345a1f
authored
Oct 30, 2015
by
k8s-merge-robot
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #16502 from wojtek-t/list_from_memory
Auto commit by PR queue bot
parents
c31a2222
652fb090
Show whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
60 additions
and
36 deletions
+60
-36
cacher.go
pkg/storage/cacher.go
+23
-14
cacher_test.go
pkg/storage/cacher_test.go
+4
-18
watch_cache.go
pkg/storage/watch_cache.go
+13
-2
watch_cache_test.go
pkg/storage/watch_cache_test.go
+20
-2
No files found.
pkg/storage/cacher.go
View file @
d1345a1f
...
@@ -45,6 +45,11 @@ type CacherConfig struct {
...
@@ -45,6 +45,11 @@ type CacherConfig struct {
// An underlying storage.Versioner.
// An underlying storage.Versioner.
Versioner
Versioner
Versioner
Versioner
// Whether to serve Lists from in-memory cache.
//
// NOTE: DO NOT SET TO TRUE IN PRODUCTION CODE!
ListFromCache
bool
// The Cache will be caching objects of a given Type and assumes that they
// The Cache will be caching objects of a given Type and assumes that they
// are all stored under ResourcePrefix directory in the underlying database.
// are all stored under ResourcePrefix directory in the underlying database.
Type
interface
{}
Type
interface
{}
...
@@ -99,6 +104,11 @@ type Cacher struct {
...
@@ -99,6 +104,11 @@ type Cacher struct {
// keyFunc is used to get a key in the underyling storage for a given object.
// keyFunc is used to get a key in the underyling storage for a given object.
keyFunc
func
(
runtime
.
Object
)
(
string
,
error
)
keyFunc
func
(
runtime
.
Object
)
(
string
,
error
)
// Whether to serve Lists from in-memory cache.
//
// NOTE: DO NOT SET TO TRUE IN PRODUCTION CODE!
ListFromCache
bool
}
}
// Create a new Cacher responsible from service WATCH and LIST requests from its
// Create a new Cacher responsible from service WATCH and LIST requests from its
...
@@ -117,6 +127,7 @@ func NewCacher(config CacherConfig) *Cacher {
...
@@ -117,6 +127,7 @@ func NewCacher(config CacherConfig) *Cacher {
watchers
:
make
(
map
[
int
]
*
cacheWatcher
),
watchers
:
make
(
map
[
int
]
*
cacheWatcher
),
versioner
:
config
.
Versioner
,
versioner
:
config
.
Versioner
,
keyFunc
:
config
.
KeyFunc
,
keyFunc
:
config
.
KeyFunc
,
ListFromCache
:
config
.
ListFromCache
,
}
}
cacher
.
usable
.
Lock
()
cacher
.
usable
.
Lock
()
// See startCaching method for why explanation on it.
// See startCaching method for why explanation on it.
...
@@ -220,21 +231,19 @@ func (c *Cacher) GetToList(ctx context.Context, key string, filter FilterFunc, l
...
@@ -220,21 +231,19 @@ func (c *Cacher) GetToList(ctx context.Context, key string, filter FilterFunc, l
// Implements storage.Interface.
// Implements storage.Interface.
func
(
c
*
Cacher
)
List
(
ctx
context
.
Context
,
key
string
,
resourceVersion
uint64
,
filter
FilterFunc
,
listObj
runtime
.
Object
)
error
{
func
(
c
*
Cacher
)
List
(
ctx
context
.
Context
,
key
string
,
resourceVersion
uint64
,
filter
FilterFunc
,
listObj
runtime
.
Object
)
error
{
if
!
c
.
ListFromCache
{
return
c
.
storage
.
List
(
ctx
,
key
,
resourceVersion
,
filter
,
listObj
)
return
c
.
storage
.
List
(
ctx
,
key
,
resourceVersion
,
filter
,
listObj
)
}
}
// ListFromMemory implements list operation (the same signature as List method)
// To avoid situation when List is proceesed before the underlying
// but it serves the contents from memory.
// watchCache is propagated for the first time, we acquire and immediately
// Current we cannot use ListFromMemory() instead of List(), because it only
// release the 'usable' lock.
// guarantees eventual consistency (e.g. it's possible for Get called right after
// We don't need to hold it all the time, because watchCache is thread-safe
// Create to return not-exist, before the change is propagate).
// and it would complicate already very difficult locking pattern.
// TODO: We may consider changing to use ListFromMemory in the future, but this
// requires wider discussion as an "api semantic change".
func
(
c
*
Cacher
)
ListFromMemory
(
key
string
,
listObj
runtime
.
Object
)
error
{
// Do NOT allow Watch to start when the underlying structures are not propagated.
c
.
usable
.
RLock
()
c
.
usable
.
RLock
()
defer
c
.
usable
.
RUnlock
()
c
.
usable
.
RUnlock
()
// List elements from cache, with at least 'resourceVersion'.
listPtr
,
err
:=
runtime
.
GetItemsPtr
(
listObj
)
listPtr
,
err
:=
runtime
.
GetItemsPtr
(
listObj
)
if
err
!=
nil
{
if
err
!=
nil
{
return
err
return
err
...
@@ -243,15 +252,15 @@ func (c *Cacher) ListFromMemory(key string, listObj runtime.Object) error {
...
@@ -243,15 +252,15 @@ func (c *Cacher) ListFromMemory(key string, listObj runtime.Object) error {
if
err
!=
nil
||
listVal
.
Kind
()
!=
reflect
.
Slice
{
if
err
!=
nil
||
listVal
.
Kind
()
!=
reflect
.
Slice
{
return
fmt
.
Errorf
(
"need a pointer to slice, got %v"
,
listVal
.
Kind
())
return
fmt
.
Errorf
(
"need a pointer to slice, got %v"
,
listVal
.
Kind
())
}
}
filter
:=
filterFunction
(
key
,
c
.
keyFunc
,
Everything
)
filter
Func
:=
filterFunction
(
key
,
c
.
keyFunc
,
filter
)
objs
,
resourceVersion
:=
c
.
watchCache
.
ListWithVersion
(
)
objs
,
resourceVersion
:=
c
.
watchCache
.
WaitUntilFreshAndList
(
resourceVersion
)
for
_
,
obj
:=
range
objs
{
for
_
,
obj
:=
range
objs
{
object
,
ok
:=
obj
.
(
runtime
.
Object
)
object
,
ok
:=
obj
.
(
runtime
.
Object
)
if
!
ok
{
if
!
ok
{
return
fmt
.
Errorf
(
"non runtime.Object returned from storage: %v"
,
obj
)
return
fmt
.
Errorf
(
"non runtime.Object returned from storage: %v"
,
obj
)
}
}
if
filter
(
object
)
{
if
filter
Func
(
object
)
{
listVal
.
Set
(
reflect
.
Append
(
listVal
,
reflect
.
ValueOf
(
object
)
.
Elem
()))
listVal
.
Set
(
reflect
.
Append
(
listVal
,
reflect
.
ValueOf
(
object
)
.
Elem
()))
}
}
}
}
...
...
pkg/storage/cacher_test.go
View file @
d1345a1f
...
@@ -37,7 +37,6 @@ import (
...
@@ -37,7 +37,6 @@ import (
"k8s.io/kubernetes/pkg/tools/etcdtest"
"k8s.io/kubernetes/pkg/tools/etcdtest"
"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/pkg/util/wait"
"k8s.io/kubernetes/pkg/watch"
"k8s.io/kubernetes/pkg/watch"
"golang.org/x/net/context"
"golang.org/x/net/context"
...
@@ -47,8 +46,9 @@ func newTestCacher(client tools.EtcdClient) *storage.Cacher {
...
@@ -47,8 +46,9 @@ func newTestCacher(client tools.EtcdClient) *storage.Cacher {
prefix
:=
"pods"
prefix
:=
"pods"
config
:=
storage
.
CacherConfig
{
config
:=
storage
.
CacherConfig
{
CacheCapacity
:
10
,
CacheCapacity
:
10
,
Versioner
:
etcdstorage
.
APIObjectVersioner
{},
Storage
:
etcdstorage
.
NewEtcdStorage
(
client
,
testapi
.
Default
.
Codec
(),
etcdtest
.
PathPrefix
()),
Storage
:
etcdstorage
.
NewEtcdStorage
(
client
,
testapi
.
Default
.
Codec
(),
etcdtest
.
PathPrefix
()),
Versioner
:
etcdstorage
.
APIObjectVersioner
{},
ListFromCache
:
true
,
Type
:
&
api
.
Pod
{},
Type
:
&
api
.
Pod
{},
ResourcePrefix
:
prefix
,
ResourcePrefix
:
prefix
,
KeyFunc
:
func
(
obj
runtime
.
Object
)
(
string
,
error
)
{
return
storage
.
NamespaceKeyFunc
(
prefix
,
obj
)
},
KeyFunc
:
func
(
obj
runtime
.
Object
)
(
string
,
error
)
{
return
storage
.
NamespaceKeyFunc
(
prefix
,
obj
)
},
...
@@ -65,18 +65,7 @@ func makeTestPod(name string) *api.Pod {
...
@@ -65,18 +65,7 @@ func makeTestPod(name string) *api.Pod {
}
}
}
}
func
waitForUpToDateCache
(
cacher
*
storage
.
Cacher
,
resourceVersion
uint64
)
error
{
func
TestList
(
t
*
testing
.
T
)
{
ready
:=
func
()
(
bool
,
error
)
{
result
,
err
:=
cacher
.
LastSyncResourceVersion
()
if
err
!=
nil
{
return
false
,
err
}
return
result
==
resourceVersion
,
nil
}
return
wait
.
Poll
(
10
*
time
.
Millisecond
,
util
.
ForeverTestTimeout
,
ready
)
}
func
TestListFromMemory
(
t
*
testing
.
T
)
{
fakeClient
:=
tools
.
NewFakeEtcdClient
(
t
)
fakeClient
:=
tools
.
NewFakeEtcdClient
(
t
)
prefixedKey
:=
etcdtest
.
AddPrefix
(
"pods"
)
prefixedKey
:=
etcdtest
.
AddPrefix
(
"pods"
)
fakeClient
.
ExpectNotFoundGet
(
prefixedKey
)
fakeClient
.
ExpectNotFoundGet
(
prefixedKey
)
...
@@ -146,12 +135,9 @@ func TestListFromMemory(t *testing.T) {
...
@@ -146,12 +135,9 @@ func TestListFromMemory(t *testing.T) {
for
_
,
test
:=
range
testCases
{
for
_
,
test
:=
range
testCases
{
fakeClient
.
WatchResponse
<-
test
fakeClient
.
WatchResponse
<-
test
}
}
if
err
:=
waitForUpToDateCache
(
cacher
,
5
);
err
!=
nil
{
t
.
Errorf
(
"watch cache didn't propagated correctly: %v"
,
err
)
}
result
:=
&
api
.
PodList
{}
result
:=
&
api
.
PodList
{}
if
err
:=
cacher
.
List
FromMemory
(
"pods/ns"
,
result
);
err
!=
nil
{
if
err
:=
cacher
.
List
(
context
.
TODO
(),
"pods/ns"
,
5
,
storage
.
Everything
,
result
);
err
!=
nil
{
t
.
Errorf
(
"unexpected error: %v"
,
err
)
t
.
Errorf
(
"unexpected error: %v"
,
err
)
}
}
if
result
.
ListMeta
.
ResourceVersion
!=
"5"
{
if
result
.
ListMeta
.
ResourceVersion
!=
"5"
{
...
...
pkg/storage/watch_cache.go
View file @
d1345a1f
...
@@ -55,6 +55,10 @@ type watchCacheElement struct {
...
@@ -55,6 +55,10 @@ type watchCacheElement struct {
type
watchCache
struct
{
type
watchCache
struct
{
sync
.
RWMutex
sync
.
RWMutex
// Condition on which lists are waiting for the fresh enough
// resource version.
cond
*
sync
.
Cond
// Maximum size of history window.
// Maximum size of history window.
capacity
int
capacity
int
...
@@ -84,7 +88,7 @@ type watchCache struct {
...
@@ -84,7 +88,7 @@ type watchCache struct {
}
}
func
newWatchCache
(
capacity
int
)
*
watchCache
{
func
newWatchCache
(
capacity
int
)
*
watchCache
{
return
&
watchCache
{
wc
:=
&
watchCache
{
capacity
:
capacity
,
capacity
:
capacity
,
cache
:
make
([]
watchCacheElement
,
capacity
),
cache
:
make
([]
watchCacheElement
,
capacity
),
startIndex
:
0
,
startIndex
:
0
,
...
@@ -92,6 +96,8 @@ func newWatchCache(capacity int) *watchCache {
...
@@ -92,6 +96,8 @@ func newWatchCache(capacity int) *watchCache {
store
:
cache
.
NewStore
(
cache
.
MetaNamespaceKeyFunc
),
store
:
cache
.
NewStore
(
cache
.
MetaNamespaceKeyFunc
),
resourceVersion
:
0
,
resourceVersion
:
0
,
}
}
wc
.
cond
=
sync
.
NewCond
(
wc
.
RLocker
())
return
wc
}
}
func
(
w
*
watchCache
)
Add
(
obj
interface
{})
error
{
func
(
w
*
watchCache
)
Add
(
obj
interface
{})
error
{
...
@@ -169,6 +175,7 @@ func (w *watchCache) processEvent(event watch.Event, resourceVersion uint64, upd
...
@@ -169,6 +175,7 @@ func (w *watchCache) processEvent(event watch.Event, resourceVersion uint64, upd
}
}
w
.
updateCache
(
resourceVersion
,
watchCacheEvent
)
w
.
updateCache
(
resourceVersion
,
watchCacheEvent
)
w
.
resourceVersion
=
resourceVersion
w
.
resourceVersion
=
resourceVersion
w
.
cond
.
Broadcast
()
return
updateFunc
(
event
.
Object
)
return
updateFunc
(
event
.
Object
)
}
}
...
@@ -188,8 +195,11 @@ func (w *watchCache) List() []interface{} {
...
@@ -188,8 +195,11 @@ func (w *watchCache) List() []interface{} {
return
w
.
store
.
List
()
return
w
.
store
.
List
()
}
}
func
(
w
*
watchCache
)
ListWithVersion
(
)
([]
interface
{},
uint64
)
{
func
(
w
*
watchCache
)
WaitUntilFreshAndList
(
resourceVersion
uint64
)
([]
interface
{},
uint64
)
{
w
.
RLock
()
w
.
RLock
()
for
w
.
resourceVersion
<
resourceVersion
{
w
.
cond
.
Wait
()
}
defer
w
.
RUnlock
()
defer
w
.
RUnlock
()
return
w
.
store
.
List
(),
w
.
resourceVersion
return
w
.
store
.
List
(),
w
.
resourceVersion
}
}
...
@@ -230,6 +240,7 @@ func (w *watchCache) Replace(objs []interface{}, resourceVersion string) error {
...
@@ -230,6 +240,7 @@ func (w *watchCache) Replace(objs []interface{}, resourceVersion string) error {
if
w
.
onReplace
!=
nil
{
if
w
.
onReplace
!=
nil
{
w
.
onReplace
()
w
.
onReplace
()
}
}
w
.
cond
.
Broadcast
()
return
nil
return
nil
}
}
...
...
pkg/storage/watch_cache_test.go
View file @
d1345a1f
...
@@ -230,6 +230,24 @@ func TestEvents(t *testing.T) {
...
@@ -230,6 +230,24 @@ func TestEvents(t *testing.T) {
}
}
}
}
func
TestWaitUntilFreshAndList
(
t
*
testing
.
T
)
{
store
:=
newWatchCache
(
3
)
// In background, update the store.
go
func
()
{
store
.
Add
(
makeTestPod
(
"foo"
,
2
))
store
.
Add
(
makeTestPod
(
"bar"
,
5
))
}()
list
,
resourceVersion
:=
store
.
WaitUntilFreshAndList
(
4
)
if
resourceVersion
!=
5
{
t
.
Errorf
(
"unexpected resourceVersion: %v, expected: 5"
,
resourceVersion
)
}
if
len
(
list
)
!=
2
{
t
.
Errorf
(
"unexpected list returned: %#v"
,
list
)
}
}
type
testLW
struct
{
type
testLW
struct
{
ListFunc
func
()
(
runtime
.
Object
,
error
)
ListFunc
func
()
(
runtime
.
Object
,
error
)
WatchFunc
func
(
options
api
.
ListOptions
)
(
watch
.
Interface
,
error
)
WatchFunc
func
(
options
api
.
ListOptions
)
(
watch
.
Interface
,
error
)
...
@@ -244,7 +262,7 @@ func TestReflectorForWatchCache(t *testing.T) {
...
@@ -244,7 +262,7 @@ func TestReflectorForWatchCache(t *testing.T) {
store
:=
newWatchCache
(
5
)
store
:=
newWatchCache
(
5
)
{
{
_
,
version
:=
store
.
ListWithVersion
(
)
_
,
version
:=
store
.
WaitUntilFreshAndList
(
0
)
if
version
!=
0
{
if
version
!=
0
{
t
.
Errorf
(
"unexpected resource version: %d"
,
version
)
t
.
Errorf
(
"unexpected resource version: %d"
,
version
)
}
}
...
@@ -264,7 +282,7 @@ func TestReflectorForWatchCache(t *testing.T) {
...
@@ -264,7 +282,7 @@ func TestReflectorForWatchCache(t *testing.T) {
r
.
ListAndWatch
(
util
.
NeverStop
)
r
.
ListAndWatch
(
util
.
NeverStop
)
{
{
_
,
version
:=
store
.
ListWithVersion
(
)
_
,
version
:=
store
.
WaitUntilFreshAndList
(
10
)
if
version
!=
10
{
if
version
!=
10
{
t
.
Errorf
(
"unexpected resource version: %d"
,
version
)
t
.
Errorf
(
"unexpected resource version: %d"
,
version
)
}
}
...
...
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