k8s.io/apiserver@v0.31.1/pkg/storage/etcd3/linearized_read_test.go (about)

     1  /*
     2  Copyright 2022 The Kubernetes Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package etcd3
    18  
    19  import (
    20  	"testing"
    21  
    22  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    23  	"k8s.io/apiserver/pkg/apis/example"
    24  	"k8s.io/apiserver/pkg/storage"
    25  )
    26  
    27  func TestLinearizedReadRevisionInvariant(t *testing.T) {
    28  	// The etcd documentation [1] states that "linearized requests must go through the Raft consensus process."
    29  	// A full round of Raft consensus adds a new item to the Raft log, some of which is surfaced by etcd as a
    30  	// higher store revision in the response header. Kubernetes exposes this header revision in e.g. List calls,
    31  	// so it is ultimately client-facing. By default, all the requests that our *etcd3.store{} issues are
    32  	// linearized. However, this also includes *read* requests, and we would not expect non-mutating requests
    33  	// against etcd to, by "go[ing] through the Raft consensus process," result in a higher resource version on
    34  	// List calls. Today, the mechanism etcd uses to increment the store revision ensures that linearized reads
    35  	// do *not* bump the key-value store revision. This test exists to ensure that we notice if this implementation
    36  	// detail ever changes.
    37  	// [1] https://etcd.io/docs/v3.5/learning/api_guarantees/#isolation-level-and-consistency-of-replicas
    38  	ctx, store, etcdClient := testSetup(t)
    39  
    40  	dir := "/testing"
    41  	key := dir + "/testkey"
    42  	out := &example.Pod{}
    43  	obj := &example.Pod{ObjectMeta: metav1.ObjectMeta{Name: "foo", SelfLink: "testlink"}}
    44  
    45  	if err := store.Create(ctx, key, obj, out, 0); err != nil {
    46  		t.Fatalf("Set failed: %v", err)
    47  	}
    48  	originalRevision := out.ResourceVersion
    49  
    50  	for i := 0; i < 5; i++ {
    51  		if _, err := etcdClient.KV.Get(ctx, key); err != nil { // this is by default linearizable, the only option the client library exposes is WithSerializable() to make it *not* a linearized read
    52  			t.Fatalf("failed to get key: %v", err)
    53  		}
    54  	}
    55  
    56  	list := &example.PodList{}
    57  	if err := store.GetList(ctx, dir, storage.ListOptions{Predicate: storage.Everything, Recursive: true}, list); err != nil {
    58  		t.Errorf("Unexpected List error: %v", err)
    59  	}
    60  	finalRevision := list.ResourceVersion
    61  
    62  	if originalRevision != finalRevision {
    63  		t.Fatalf("original revision (%s) did not match final revision after linearized reads (%s)", originalRevision, finalRevision)
    64  	}
    65  }