mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-08-02 08:17:26 +00:00
Support multiple index functions, address feedback
This commit is contained in:
parent
7a2d63048d
commit
1cf69bdefc
52
pkg/client/cache/index.go
vendored
Normal file
52
pkg/client/cache/index.go
vendored
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2014 Google Inc. All rights reserved.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package cache
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/meta"
|
||||||
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Indexer is a storage interface that lets you list objects using multiple indexing functions
|
||||||
|
type Indexer interface {
|
||||||
|
Store
|
||||||
|
// Retrieve list of objects that match on the named indexing function
|
||||||
|
Index(indexName string, obj interface{}) ([]interface{}, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// IndexFunc knows how to provide an indexed value for an object.
|
||||||
|
type IndexFunc func(obj interface{}) (string, error)
|
||||||
|
|
||||||
|
// MetaNamespaceIndexFunc is a default index function that indexes based on an object's namespace
|
||||||
|
func MetaNamespaceIndexFunc(obj interface{}) (string, error) {
|
||||||
|
meta, err := meta.Accessor(obj)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("object has no meta: %v", err)
|
||||||
|
}
|
||||||
|
return meta.Namespace(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Index maps the indexed value to a set of keys in the store that match on that value
|
||||||
|
type Index map[string]util.StringSet
|
||||||
|
|
||||||
|
// Indexers maps a name to a IndexFunc
|
||||||
|
type Indexers map[string]IndexFunc
|
||||||
|
|
||||||
|
// Indices maps a name to an Index
|
||||||
|
type Indices map[string]Index
|
119
pkg/client/cache/store.go
vendored
119
pkg/client/cache/store.go
vendored
@ -60,35 +60,16 @@ func MetaNamespaceKeyFunc(obj interface{}) (string, error) {
|
|||||||
return meta.Namespace() + "/" + meta.Name(), nil
|
return meta.Namespace() + "/" + meta.Name(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Index is a generic object storage interface that lets you list objects by their Index
|
|
||||||
type Index interface {
|
|
||||||
Store
|
|
||||||
Index(obj interface{}) ([]interface{}, error)
|
|
||||||
}
|
|
||||||
|
|
||||||
// IndexFunc knows how to provide an indexed value for an object.
|
|
||||||
type IndexFunc func(obj interface{}) (string, error)
|
|
||||||
|
|
||||||
// MetaNamespaceIndexFunc is a convenient default IndexFun which knows how to index
|
|
||||||
// an object by its namespace.
|
|
||||||
func MetaNamespaceIndexFunc(obj interface{}) (string, error) {
|
|
||||||
meta, err := meta.Accessor(obj)
|
|
||||||
if err != nil {
|
|
||||||
return "", fmt.Errorf("object has no meta: %v", err)
|
|
||||||
}
|
|
||||||
return meta.Namespace(), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
type cache struct {
|
type cache struct {
|
||||||
lock sync.RWMutex
|
lock sync.RWMutex
|
||||||
items map[string]interface{}
|
items map[string]interface{}
|
||||||
// keyFunc is used to make the key for objects stored in and retrieved from items, and
|
// keyFunc is used to make the key for objects stored in and retrieved from items, and
|
||||||
// should be deterministic.
|
// should be deterministic.
|
||||||
keyFunc KeyFunc
|
keyFunc KeyFunc
|
||||||
// indexFunc is used to make the index value for objects stored in an retrieved from index
|
// indexers maps a name to an IndexFunc
|
||||||
indexFunc IndexFunc
|
indexers Indexers
|
||||||
// maps the indexFunc value for an object to a set whose keys are keys in items
|
// indices maps a name to an Index
|
||||||
index map[string]util.StringSet
|
indices Indices
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add inserts an item into the cache.
|
// Add inserts an item into the cache.
|
||||||
@ -100,53 +81,57 @@ func (c *cache) Add(obj interface{}) error {
|
|||||||
c.lock.Lock()
|
c.lock.Lock()
|
||||||
defer c.lock.Unlock()
|
defer c.lock.Unlock()
|
||||||
c.items[key] = obj
|
c.items[key] = obj
|
||||||
c.updateIndex(obj)
|
c.updateIndices(obj)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// updateIndex adds or modifies an object in the index
|
// updateIndices modifies the objects location in the managed indexes
|
||||||
// it is intended to be called from a function that already has a lock on the cache
|
// updateIndices must be called from a function that already has a lock on the cache
|
||||||
func (c *cache) updateIndex(obj interface{}) error {
|
func (c *cache) updateIndices(obj interface{}) error {
|
||||||
if c.indexFunc == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
key, err := c.keyFunc(obj)
|
key, err := c.keyFunc(obj)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
indexValue, err := c.indexFunc(obj)
|
for name, indexFunc := range c.indexers {
|
||||||
if err != nil {
|
indexValue, err := indexFunc(obj)
|
||||||
return err
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
index := c.indices[name]
|
||||||
|
if index == nil {
|
||||||
|
index = Index{}
|
||||||
|
c.indices[name] = index
|
||||||
|
}
|
||||||
|
set := index[indexValue]
|
||||||
|
if set == nil {
|
||||||
|
set = util.StringSet{}
|
||||||
|
index[indexValue] = set
|
||||||
|
}
|
||||||
|
set.Insert(key)
|
||||||
}
|
}
|
||||||
set := c.index[indexValue]
|
|
||||||
if set == nil {
|
|
||||||
set = util.StringSet{}
|
|
||||||
c.index[indexValue] = set
|
|
||||||
}
|
|
||||||
set.Insert(key)
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// deleteFromIndex removes an entry from the index
|
// deleteFromIndices removes the object from each of the managed indexes
|
||||||
// it is intended to be called from a function that already has a lock on the cache
|
// it is intended to be called from a function that already has a lock on the cache
|
||||||
func (c *cache) deleteFromIndex(obj interface{}) error {
|
func (c *cache) deleteFromIndices(obj interface{}) error {
|
||||||
if c.indexFunc == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
key, err := c.keyFunc(obj)
|
key, err := c.keyFunc(obj)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
indexValue, err := c.indexFunc(obj)
|
for name, indexFunc := range c.indexers {
|
||||||
if err != nil {
|
indexValue, err := indexFunc(obj)
|
||||||
return err
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
index := c.indices[name]
|
||||||
|
if index != nil {
|
||||||
|
set := index[indexValue]
|
||||||
|
if set != nil {
|
||||||
|
set.Delete(key)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
set := c.index[indexValue]
|
|
||||||
if set == nil {
|
|
||||||
set = util.StringSet{}
|
|
||||||
c.index[indexValue] = set
|
|
||||||
}
|
|
||||||
set.Delete(key)
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -159,7 +144,7 @@ func (c *cache) Update(obj interface{}) error {
|
|||||||
c.lock.Lock()
|
c.lock.Lock()
|
||||||
defer c.lock.Unlock()
|
defer c.lock.Unlock()
|
||||||
c.items[key] = obj
|
c.items[key] = obj
|
||||||
c.updateIndex(obj)
|
c.updateIndices(obj)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -172,7 +157,7 @@ func (c *cache) Delete(obj interface{}) error {
|
|||||||
c.lock.Lock()
|
c.lock.Lock()
|
||||||
defer c.lock.Unlock()
|
defer c.lock.Unlock()
|
||||||
delete(c.items, key)
|
delete(c.items, key)
|
||||||
c.deleteFromIndex(obj)
|
c.deleteFromIndices(obj)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -190,15 +175,21 @@ func (c *cache) List() []interface{} {
|
|||||||
|
|
||||||
// Index returns a list of items that match on the index function
|
// Index returns a list of items that match on the index function
|
||||||
// Index is thread-safe so long as you treat all items as immutable
|
// Index is thread-safe so long as you treat all items as immutable
|
||||||
func (c *cache) Index(obj interface{}) ([]interface{}, error) {
|
func (c *cache) Index(indexName string, obj interface{}) ([]interface{}, error) {
|
||||||
c.lock.RLock()
|
c.lock.RLock()
|
||||||
defer c.lock.RUnlock()
|
defer c.lock.RUnlock()
|
||||||
|
|
||||||
indexKey, err := c.indexFunc(obj)
|
indexFunc := c.indexers[indexName]
|
||||||
|
if indexFunc == nil {
|
||||||
|
return nil, fmt.Errorf("Index with name %s does not exist", indexName)
|
||||||
|
}
|
||||||
|
|
||||||
|
indexKey, err := indexFunc(obj)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
set := c.index[indexKey]
|
index := c.indices[indexName]
|
||||||
|
set := index[indexKey]
|
||||||
list := make([]interface{}, 0, set.Len())
|
list := make([]interface{}, 0, set.Len())
|
||||||
for _, key := range set.List() {
|
for _, key := range set.List() {
|
||||||
list = append(list, c.items[key])
|
list = append(list, c.items[key])
|
||||||
@ -243,9 +234,9 @@ func (c *cache) Replace(list []interface{}) error {
|
|||||||
c.items = items
|
c.items = items
|
||||||
|
|
||||||
// rebuild any index
|
// rebuild any index
|
||||||
c.index = map[string]util.StringSet{}
|
c.indices = Indices{}
|
||||||
for _, item := range c.items {
|
for _, item := range c.items {
|
||||||
c.updateIndex(item)
|
c.updateIndices(item)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
@ -253,10 +244,10 @@ func (c *cache) Replace(list []interface{}) error {
|
|||||||
|
|
||||||
// NewStore returns a Store implemented simply with a map and a lock.
|
// NewStore returns a Store implemented simply with a map and a lock.
|
||||||
func NewStore(keyFunc KeyFunc) Store {
|
func NewStore(keyFunc KeyFunc) Store {
|
||||||
return &cache{items: map[string]interface{}{}, keyFunc: keyFunc}
|
return &cache{items: map[string]interface{}{}, keyFunc: keyFunc, indexers: Indexers{}, indices: Indices{}}
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewIndex returns an Index implemented simply with a map and a lock.
|
// NewIndexer returns an Indexer implemented simply with a map and a lock.
|
||||||
func NewIndex(keyFunc KeyFunc, indexFunc IndexFunc) Index {
|
func NewIndexer(keyFunc KeyFunc, indexers Indexers) Indexer {
|
||||||
return &cache{items: map[string]interface{}{}, keyFunc: keyFunc, indexFunc: indexFunc, index: map[string]util.StringSet{}}
|
return &cache{items: map[string]interface{}{}, keyFunc: keyFunc, indexers: indexers, indices: Indices{}}
|
||||||
}
|
}
|
||||||
|
20
pkg/client/cache/store_test.go
vendored
20
pkg/client/cache/store_test.go
vendored
@ -87,7 +87,7 @@ func doTestStore(t *testing.T, store Store) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Test public interface
|
// Test public interface
|
||||||
func doTestIndex(t *testing.T, index Index) {
|
func doTestIndex(t *testing.T, indexer Indexer) {
|
||||||
mkObj := func(id string, val string) testStoreObject {
|
mkObj := func(id string, val string) testStoreObject {
|
||||||
return testStoreObject{id: id, val: val}
|
return testStoreObject{id: id, val: val}
|
||||||
}
|
}
|
||||||
@ -97,14 +97,14 @@ func doTestIndex(t *testing.T, index Index) {
|
|||||||
expected["b"] = util.NewStringSet("a", "c")
|
expected["b"] = util.NewStringSet("a", "c")
|
||||||
expected["f"] = util.NewStringSet("e")
|
expected["f"] = util.NewStringSet("e")
|
||||||
expected["h"] = util.NewStringSet("g")
|
expected["h"] = util.NewStringSet("g")
|
||||||
index.Add(mkObj("a", "b"))
|
indexer.Add(mkObj("a", "b"))
|
||||||
index.Add(mkObj("c", "b"))
|
indexer.Add(mkObj("c", "b"))
|
||||||
index.Add(mkObj("e", "f"))
|
indexer.Add(mkObj("e", "f"))
|
||||||
index.Add(mkObj("g", "h"))
|
indexer.Add(mkObj("g", "h"))
|
||||||
{
|
{
|
||||||
for k, v := range expected {
|
for k, v := range expected {
|
||||||
found := util.StringSet{}
|
found := util.StringSet{}
|
||||||
indexResults, err := index.Index(mkObj("", k))
|
indexResults, err := indexer.Index("by_val", mkObj("", k))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("Unexpected error %v", err)
|
t.Errorf("Unexpected error %v", err)
|
||||||
}
|
}
|
||||||
@ -127,6 +127,12 @@ func testStoreIndexFunc(obj interface{}) (string, error) {
|
|||||||
return obj.(testStoreObject).val, nil
|
return obj.(testStoreObject).val, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func testStoreIndexers() Indexers {
|
||||||
|
indexers := Indexers{}
|
||||||
|
indexers["by_val"] = testStoreIndexFunc
|
||||||
|
return indexers
|
||||||
|
}
|
||||||
|
|
||||||
type testStoreObject struct {
|
type testStoreObject struct {
|
||||||
id string
|
id string
|
||||||
val string
|
val string
|
||||||
@ -146,5 +152,5 @@ func TestUndeltaStore(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestIndex(t *testing.T) {
|
func TestIndex(t *testing.T) {
|
||||||
doTestIndex(t, NewIndex(testStoreKeyFunc, testStoreIndexFunc))
|
doTestIndex(t, NewIndexer(testStoreKeyFunc, testStoreIndexers()))
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user