Change:
As Jordan pointed out in: watchcache treats RV=0 and RV="" the same way, even though it's not what we document: This should be fixed. /kind bug /sig api-machinery /priority important-soon
/assign
/triage accepted

Discussion:
// Watch implements storage.Interface. func (c *Cacher) Watch(ctx context.Context, key string, opts storage.ListOptions) (watch.Interface, error) { pred := opts.Predicate <ins> // If the resourceVersion is unset, ensure that the rv // from which the watch is being served, is the latest // one. "latest" is ensured by serving the watch from // the underlying storage. if opts.ResourceVersion == "" { return c.storage.Watch(ctx, key, opts) } </ins> watchRV, err := c.versioner.ParseResourceVersion(opts.ResourceVersion) if err != nil { return nil, err

} type dummyStorage struct { <ins> sync.RWMutex </ins> err error }

return fmt.Errorf("unimplemented") } func (d *dummyStorage) Watch(_ context.Context, _ string, _ storage.ListOptions) (watch.Interface, error) { <del> return newDummyWatch(), nil </del> <ins> d.RLock() defer d.RUnlock() return newDummyWatch(), d.err </ins> } func (d *dummyStorage) Get(_ context.Context, _ string, _ storage.GetOptions, _ runtime.Object) error { <ins> d.RLock() defer d.RUnlock() </ins> return d.err } func (d *dummyStorage) GetList(_ context.Context, _ string, _ storage.ListOptions, listObj runtime.Object) error { <ins> d.RLock() defer d.RUnlock() </ins> podList := listObj.(*example.PodList) podList.ListMeta = metav1.ListMeta{ResourceVersion: "100"} return d.err

func (d *dummyStorage) Count(_ string) (int64, error) { return 0, fmt.Errorf("unimplemented") } <ins> func (d *dummyStorage) injectError(err error) { d.Lock() defer d.Unlock() d.err = err } </ins> func TestGetListCacheBypass(t *testing.T) { backingStorage := &dummyStorage{}

} // Inject error to underlying layer and check if cacher is not bypassed. <del> backingStorage.err = errDummy </del> <ins> backingStorage.injectError(errDummy) </ins> err = cacher.GetList(context.TODO(), "pods/ns", storage.ListOptions{ ResourceVersion: "0", Predicate:       pred,

} // Inject error to underlying layer and check if cacher is not bypassed. <del> backingStorage.err = errDummy </del> <ins> backingStorage.injectError(errDummy) </ins> err = cacher.Get(context.TODO(), "pods/ns/pod-0", storage.GetOptions{ IgnoreNotFound:  true, ResourceVersion: "0",

} } <ins> func TestWatchCacheBypass(t *testing.T) { backingStorage := &dummyStorage{} cacher, _, err := newTestCacher(backingStorage) if err != nil { t.Fatalf("Couldn't create cacher: %v", err) } defer cacher.Stop() // Wait until cacher is initialized. if err := cacher.ready.wait(); err != nil { t.Fatalf("unexpected error waiting for the cache to be ready") } // Inject error to underlying layer and check if cacher is not bypassed. backingStorage.injectError(errDummy) _, err = cacher.Watch(context.TODO(), "pod/ns", storage.ListOptions{ ResourceVersion: "0", }) if err != nil { t.Errorf("Watch with RV=0 should be served from cache: %v", err) } // With unset RV, check if cacher is bypassed. _, err = cacher.Watch(context.TODO(), "pod/ns", storage.ListOptions{ ResourceVersion: "", }) if err != errDummy { t.Errorf("Watch with unset RV should bypass cacher: %v", err) } } </ins> func TestWatcherNotGoingBackInTime(t *testing.T) { backingStorage := &dummyStorage{} cacher, _, err := newTestCacher(backingStorage)