Skip to content

Commit 5e53522

Browse files
author
Chao Xu
committed
In GuaranteedUpdate, retry on any error if we are working with stale data
1 parent 71bbabc commit 5e53522

File tree

2 files changed

+56
-11
lines changed

2 files changed

+56
-11
lines changed

staging/src/k8s.io/apiserver/pkg/registry/generic/registry/store_test.go

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1851,3 +1851,47 @@ func TestMarkAsDeleting(t *testing.T) {
18511851
})
18521852
}
18531853
}
1854+
1855+
type staleGuaranteedUpdateStorage struct {
1856+
storage.Interface
1857+
cachedObj runtime.Object
1858+
}
1859+
1860+
// GuaranteedUpdate overwrites the method with one that always suggests the cachedObj.
1861+
func (s *staleGuaranteedUpdateStorage) GuaranteedUpdate(
1862+
ctx context.Context, key string, ptrToType runtime.Object, ignoreNotFound bool,
1863+
preconditions *storage.Preconditions, tryUpdate storage.UpdateFunc, _ ...runtime.Object) error {
1864+
return s.Interface.GuaranteedUpdate(ctx, key, ptrToType, ignoreNotFound, preconditions, tryUpdate, s.cachedObj)
1865+
}
1866+
1867+
func TestDeleteWithCachedObject(t *testing.T) {
1868+
podName := "foo"
1869+
podWithFinalizer := &example.Pod{
1870+
ObjectMeta: metav1.ObjectMeta{Name: podName, Finalizers: []string{"foo.com/x"}},
1871+
Spec: example.PodSpec{NodeName: "machine"},
1872+
}
1873+
podWithNoFinalizer := &example.Pod{
1874+
ObjectMeta: metav1.ObjectMeta{Name: podName},
1875+
Spec: example.PodSpec{NodeName: "machine"},
1876+
}
1877+
ctx := genericapirequest.WithNamespace(genericapirequest.NewContext(), "test")
1878+
destroyFunc, registry := newTestGenericStoreRegistry(t, scheme, false)
1879+
defer destroyFunc()
1880+
// cached object does not have any finalizer.
1881+
registry.Storage.Storage = &staleGuaranteedUpdateStorage{Interface: registry.Storage.Storage, cachedObj: podWithNoFinalizer}
1882+
// created object with pending finalizer.
1883+
_, err := registry.Create(ctx, podWithFinalizer, rest.ValidateAllObjectFunc, &metav1.CreateOptions{})
1884+
if err != nil {
1885+
t.Fatal(err)
1886+
}
1887+
// The object shouldn't be deleted, because the persisted object has pending finalizers.
1888+
_, _, err = registry.Delete(ctx, podName, nil)
1889+
if err != nil {
1890+
t.Fatal(err)
1891+
}
1892+
// The object should still be there
1893+
_, err = registry.Get(ctx, podName, &metav1.GetOptions{})
1894+
if err != nil {
1895+
t.Fatal(err)
1896+
}
1897+
}

staging/src/k8s.io/apiserver/pkg/storage/etcd3/store.go

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -301,19 +301,20 @@ func (s *store) GuaranteedUpdate(
301301

302302
ret, ttl, err := s.updateState(origState, tryUpdate)
303303
if err != nil {
304-
// It's possible we were working with stale data
305-
if mustCheckData && apierrors.IsConflict(err) {
306-
// Actually fetch
307-
origState, err = getCurrentState()
308-
if err != nil {
309-
return err
310-
}
311-
mustCheckData = false
312-
// Retry
313-
continue
304+
// If our data is already up to date, return the error
305+
if !mustCheckData {
306+
return err
314307
}
315308

316-
return err
309+
// It's possible we were working with stale data
310+
// Actually fetch
311+
origState, err = getCurrentState()
312+
if err != nil {
313+
return err
314+
}
315+
mustCheckData = false
316+
// Retry
317+
continue
317318
}
318319

319320
data, err := runtime.Encode(s.codec, ret)

0 commit comments

Comments
 (0)