Использование пакета client-go Kubernetes
Кэширование — одна из важных концепций, используемых в любой программе для быстрого доступа к часто используемым данным.
В этой статье давайте разберемся, насколько легко мы можем использовать методы кэширования, доступные в пакете client-go Kubernetes.
Для начала рассмотрим простой сценарий, в котором мы хотим сохранить ключ и значение в кеше. Полная версия программы доступна в моем репозитории Github.
// cacheTTL is the duration of time to hold the key in cache const cacheTTL = 20 * time.Second // keyValue contains the fields to store the key and value type keyValue struct { key string value string } func main() { cacheStore := cache.NewTTLStore(cacheKeyFunc, cacheTTL) } // cacheKeyFunc defines the key function required in TTLStore. func cacheKeyFunc(obj interface{}) (string, error) { return obj.(keyValue).key, nil }
В приведенном выше фрагменте кода в функции main мы инициализируем хранилище кеша следующим образом:
cacheKeyFunc
: указывает, что использовать в качестве ключа для хранения предоставленного пользователем объекта в кеше.
cacheTTL
: Время хранения ключа в кэше.
После завершения базовой инициализации мы можем сохранить значение в кеше с помощью следующих функций:
func addToCache(cacheStore cache.Store, object keyValue) error { err := cacheStore.Add(object) if err != nil { klog.Errorf("failed to add key value to cache error", err) return err } return nil } func fetchFromCache(cacheStore cache.Store, key string) (string, error) { obj, exists, err := cacheStore.GetByKey(key) if err != nil { klog.Errorf("failed to add key value to cache error", err) return "", err } if !exists { klog.Errorf("object does not exist in the cache") return "", nil } return obj.(keyValue).value, nil } func deleteFromCache(cacheStore cache.Store, object keyValue) error { return cacheStore.Delete(object) }
В приведенном выше фрагменте кода мы используем методы cachestore.
Добавитьдля сохранения значения в кеше, GetByKey
для извлечения значения из предоставленного ключа и "Удалить" для удаления значения из кеша.
При выборке нам нужно убедиться, что мы проверяем, существует ли значение для требуемого ключа в кеше, его может не быть в кеше в случае, если срок его действия истек или он никогда не добавлялся.
Помимо методов Add, GetByKey
и Delete
, в cache store есть множество других методов, таких как Get, Update, List и т. д. — все методы можно увидеть здесь.
Наконец, основная функция выглядит так
func main() { cacheStore := cache.NewTTLStore(cacheKeyFunc, cacheTTL) testKey := "myKey" key := keyValue{ key: testKey, value: "myValue", } klog.Infof("adding the key %v to cache", key) err := addToCache(cacheStore, key) if err != nil { klog.Fatalf("failed to add the key %v to cache error %v", key, err) } klog.Infof("fetching the value for key: %s from cache", testKey) value, err := fetchFromCache(cacheStore, "myKey") if err != nil { klog.Fatalf("failed to fetch value for key %S from cache error %v", testKey, err) } if value == "" { klog.Fatalf("the value for key %s is empty", testKey) } klog.Infof("successfully fetched the value for key %s from cache value: %s", testKey, value) klog.Infof("deleting the key %s from cache", testKey) err = deleteFromCache(cacheStore, key) if err != nil { klog.Fatalf("failed to delete key %s from cache error %v", testKey, err) } }
Наша работа не будет полностью завершена, пока мы не добавим модульный тест для приведенного выше кода. Давайте добавим модульные тесты для функций addToCache
и fetchFromCache
.
func TestAddToCache(t *testing.T) { cacheStore := cache.NewTTLStore(cacheKeyFunc, cacheTTL) testKey := keyValue{ key: "testKey", value: "testValue", } err := addToCache(cacheStore, testKey) if err != nil { t.Fatalf("expecting error to be nil but got err %v", err) } } func TestFetchFromCache(t *testing.T) { defaultCacheStoreFunc := func() cache.Store { return cache.NewTTLStore(cacheKeyFunc, cacheTTL) } testKey := keyValue{ key: "testKey", value: "testValue", } testCases := []struct { name string expectedError error expectedValue string keyName string sleep bool cacheStore func() cache.Store }{ { name: "exists in cache", expectedValue: "testValue", keyName: "testKey", cacheStore: defaultCacheStoreFunc, }, { name: "not exists in cache", expectedValue: "", keyName: "notTestKey", cacheStore: defaultCacheStoreFunc, }, { name: "key in cache expired", expectedValue: "", keyName: "testKey", cacheStore: func() cache.Store { return cache.NewTTLStore(cacheKeyFunc, time.Millisecond) }, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { gs := NewWithT(t) cacheStore := tc.cacheStore() err := addToCache(cacheStore, testKey) if err != nil { t.Fatalf("failed to add key to cache error %v", err) } if tc.sleep { time.Sleep(time.Second) } value, err := fetchFromCache(cacheStore, keyValue{key: tc.keyName}) if err != nil { if tc.expectedError != nil { gs.Expect(err).To(HaveOccurred()) gs.Expect(err.Error()).To(Equal(tc.expectedError.Error())) } else { gs.Expect(err).ToNot(HaveOccurred()) } } gs.Expect(value).To(Equal(tc.expectedValue)) }) } }
Полную программу и тесты можно найти здесь, в моем репозитории GitHub.