Repository: etcd-io/etcd
File: tests/integration/cache_test.go (Go)

Code before:
			t.Fatalf("cache never caught up to rev %d: %v", setupCompleteRev, err)
		}
	}

	allEventsCompleteRev := applyEvents(ctx, t, kv, followupEvents)
	if c, ok := reader.(*cache.Cache); ok {
		if err := c.WaitForRevision(ctx, allEventsCompleteRev); err != nil {
			t.Fatalf("cache never caught up to rev %d: %v", allEventsCompleteRev, err)
		}
	}

	t.Log("Validate")
	for _, tc := range getTestCases {
		tc := tc
		t.Run(tc.name, func(t *testing.T) {
			op := clientv3.OpGet(tc.key, tc.opts...)
			requestedRev := op.Rev()
			resp, err := reader.Get(ctx, tc.key, tc.opts...)
			if tc.expectErr {
				if !errors.Is(err, rpctypes.ErrFutureRev) {
					t.Fatalf("expected ErrFutureRev for Get %v; got %v", tc.expectErr, err)
				}
				return
			}
			if err != nil {
				if _, ok := reader.(*cache.Cache); ok && requestedRev > 0 && requestedRev < setupCompleteRev && errors.Is(err, rpctypes.ErrCompacted) {
					t.Logf("expected ErrCompacted: requestedRev=%d < setupCompleteRev=%d", requestedRev, setupCompleteRev)
					return
				}
				t.Fatalf("Get %q failed: %v", tc.key, err)
			}
			if diff := cmp.Diff(tc.wantKVs, resp.Kvs); diff != "" {
				t.Fatalf("unexpected KVs (-want +got):\n%s", diff)
			}
			if resp.Header.Revision != tc.wantRevision {
				t.Fatalf("revision: got %d, want %d", resp.Header.Revision, tc.wantRevision)
			}
		})
	}
}

var TestGetEvents = []*clientv3.Event{
	Rev2PutFooA, Rev3PutFooB, Rev4PutFooC, Rev5PutFooD, Rev6DeleteFooD, Rev7TxnPutFooA, Rev7TxnPutFooB, Rev8PutFooA,
}

var (
	Rev2PutFooA = &clientv3.Event{
		Type: clientv3.EventTypePut,
		Kv: &mvccpb.KeyValue{
			Key:            []byte("/foo/a"),
			Value:          []byte("a1"),

Review comment (performance):
I noticed that the test case names in `TestCacheWithoutPrefixGet` are a bit misleading. What the test really do is different setups for when the cache does the initial snapshot and starts watching -> how many events are available on that watch to be replayed into the store.

Maybe I should rename the cases to better reflect that:
`"empty cache"` -> `"watch-start-early (no pre-events)"`
`"partial load"` -> "`watch-start-mid (partial pre-events)"`
`"complete load"` -> `"watch-start-late (all pre-events)"`

Code after:
			t.Fatalf("cache never caught up to rev %d: %v", initialRev, err)
		}
	}

	followupRev := applyEvents(ctx, t, kv, followupEvents)
	if c, ok := reader.(*cache.Cache); ok {
		if err := c.WaitForRevision(ctx, followupRev); err != nil {
			t.Fatalf("cache never caught up to rev %d: %v", followupRev, err)
		}
	}

	t.Log("Validate")
	for _, tc := range getTestCases {
		tc := tc
		t.Run(tc.name, func(t *testing.T) {
			op := clientv3.OpGet(tc.key, tc.opts...)
			requestedRev := op.Rev()
			resp, err := reader.Get(ctx, tc.key, tc.opts...)
			if err != nil {
				if _, ok := reader.(*cache.Cache); ok && requestedRev > 0 && requestedRev < initialRev && errors.Is(err, rpctypes.ErrCompacted) {
					t.Logf("expected ErrCompacted: requestedRev=%d < initialCompleteRev=%d", requestedRev, initialRev)
					return
				}
				t.Fatalf("Get %q failed: %v", tc.key, err)
			}
			if diff := cmp.Diff(tc.wantKVs, resp.Kvs); diff != "" {
				t.Fatalf("unexpected KVs (-want +got):\n%s", diff)
			}
			if resp.Header.Revision != tc.wantRevision {
				t.Fatalf("revision: got %d, want %d", resp.Header.Revision, tc.wantRevision)
			}
		})
	}
}

var TestGetEvents = []*clientv3.Event{
	Rev2PutFooA, Rev3PutFooB, Rev4PutFooC, Rev5PutFooD, Rev6DeleteFooD, Rev7TxnPutFooA, Rev7TxnPutFooB, Rev8PutFooA,
}

var (
	Rev2PutFooA = &clientv3.Event{
		Type: clientv3.EventTypePut,
		Kv: &mvccpb.KeyValue{
			Key:            []byte("/foo/a"),
			Value:          []byte("a1"),
			CreateRevision: 2,
			ModRevision:    2,
			Version:        1,
		},
	}
	Rev3PutFooB = &clientv3.Event{