go.etcd.io/etcd@v3.3.27+incompatible/clientv3/ordering/kv_test.go (about) 1 // Copyright 2017 The etcd Authors 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package ordering 16 17 import ( 18 "context" 19 gContext "context" 20 "errors" 21 "sync" 22 "testing" 23 "time" 24 25 "github.com/coreos/etcd/clientv3" 26 pb "github.com/coreos/etcd/etcdserver/etcdserverpb" 27 "github.com/coreos/etcd/integration" 28 "github.com/coreos/etcd/pkg/testutil" 29 ) 30 31 func TestDetectKvOrderViolation(t *testing.T) { 32 var errOrderViolation = errors.New("Detected Order Violation") 33 34 defer testutil.AfterTest(t) 35 clus := integration.NewClusterV3(t, &integration.ClusterConfig{Size: 3}) 36 defer clus.Terminate(t) 37 38 cfg := clientv3.Config{ 39 Endpoints: []string{ 40 clus.Members[0].GRPCAddr(), 41 clus.Members[1].GRPCAddr(), 42 clus.Members[2].GRPCAddr(), 43 }, 44 } 45 cli, err := clientv3.New(cfg) 46 if err != nil { 47 t.Fatal(err) 48 } 49 ctx := context.TODO() 50 51 if _, err = clus.Client(0).Put(ctx, "foo", "bar"); err != nil { 52 t.Fatal(err) 53 } 54 // ensure that the second member has the current revision for the key foo 55 if _, err = clus.Client(1).Get(ctx, "foo"); err != nil { 56 t.Fatal(err) 57 } 58 59 // stop third member in order to force the member to have an outdated revision 60 clus.Members[2].Stop(t) 61 time.Sleep(1 * time.Second) // give enough time for operation 62 _, err = cli.Put(ctx, "foo", "buzz") 63 if err != nil { 64 t.Fatal(err) 65 } 66 67 // perform get request against the first member, in order to 68 // set up kvOrdering to expect "foo" revisions greater than that of 69 // the third member. 70 orderingKv := NewKV(cli.KV, 71 func(op clientv3.Op, resp clientv3.OpResponse, prevRev int64) error { 72 return errOrderViolation 73 }) 74 _, err = orderingKv.Get(ctx, "foo") 75 if err != nil { 76 t.Fatal(err) 77 } 78 79 // ensure that only the third member is queried during requests 80 clus.Members[0].Stop(t) 81 clus.Members[1].Stop(t) 82 clus.Members[2].Restart(t) 83 // force OrderingKv to query the third member 84 cli.SetEndpoints(clus.Members[2].GRPCAddr()) 85 86 _, err = orderingKv.Get(ctx, "foo", clientv3.WithSerializable()) 87 if err != errOrderViolation { 88 t.Fatalf("expected %v, got %v", errOrderViolation, err) 89 } 90 } 91 92 func TestDetectTxnOrderViolation(t *testing.T) { 93 var errOrderViolation = errors.New("Detected Order Violation") 94 95 defer testutil.AfterTest(t) 96 clus := integration.NewClusterV3(t, &integration.ClusterConfig{Size: 3}) 97 defer clus.Terminate(t) 98 99 cfg := clientv3.Config{ 100 Endpoints: []string{ 101 clus.Members[0].GRPCAddr(), 102 clus.Members[1].GRPCAddr(), 103 clus.Members[2].GRPCAddr(), 104 }, 105 } 106 cli, err := clientv3.New(cfg) 107 if err != nil { 108 t.Fatal(err) 109 } 110 ctx := context.TODO() 111 112 if _, err = clus.Client(0).Put(ctx, "foo", "bar"); err != nil { 113 t.Fatal(err) 114 } 115 // ensure that the second member has the current revision for the key foo 116 if _, err = clus.Client(1).Get(ctx, "foo"); err != nil { 117 t.Fatal(err) 118 } 119 120 // stop third member in order to force the member to have an outdated revision 121 clus.Members[2].Stop(t) 122 time.Sleep(1 * time.Second) // give enough time for operation 123 if _, err = clus.Client(1).Put(ctx, "foo", "buzz"); err != nil { 124 t.Fatal(err) 125 } 126 127 // perform get request against the first member, in order to 128 // set up kvOrdering to expect "foo" revisions greater than that of 129 // the third member. 130 orderingKv := NewKV(cli.KV, 131 func(op clientv3.Op, resp clientv3.OpResponse, prevRev int64) error { 132 return errOrderViolation 133 }) 134 orderingTxn := orderingKv.Txn(ctx) 135 _, err = orderingTxn.If( 136 clientv3.Compare(clientv3.Value("b"), ">", "a"), 137 ).Then( 138 clientv3.OpGet("foo"), 139 ).Commit() 140 if err != nil { 141 t.Fatal(err) 142 } 143 144 // ensure that only the third member is queried during requests 145 clus.Members[0].Stop(t) 146 clus.Members[1].Stop(t) 147 clus.Members[2].Restart(t) 148 // force OrderingKv to query the third member 149 cli.SetEndpoints(clus.Members[2].GRPCAddr()) 150 151 _, err = orderingKv.Get(ctx, "foo", clientv3.WithSerializable()) 152 if err != errOrderViolation { 153 t.Fatalf("expected %v, got %v", errOrderViolation, err) 154 } 155 orderingTxn = orderingKv.Txn(ctx) 156 _, err = orderingTxn.If( 157 clientv3.Compare(clientv3.Value("b"), ">", "a"), 158 ).Then( 159 clientv3.OpGet("foo", clientv3.WithSerializable()), 160 ).Commit() 161 if err != errOrderViolation { 162 t.Fatalf("expected %v, got %v", errOrderViolation, err) 163 } 164 } 165 166 type mockKV struct { 167 clientv3.KV 168 response clientv3.OpResponse 169 } 170 171 func (kv *mockKV) Do(ctx gContext.Context, op clientv3.Op) (clientv3.OpResponse, error) { 172 return kv.response, nil 173 } 174 175 var rangeTests = []struct { 176 prevRev int64 177 response *clientv3.GetResponse 178 }{ 179 { 180 5, 181 &clientv3.GetResponse{ 182 Header: &pb.ResponseHeader{ 183 Revision: 5, 184 }, 185 }, 186 }, 187 { 188 5, 189 &clientv3.GetResponse{ 190 Header: &pb.ResponseHeader{ 191 Revision: 4, 192 }, 193 }, 194 }, 195 { 196 5, 197 &clientv3.GetResponse{ 198 Header: &pb.ResponseHeader{ 199 Revision: 6, 200 }, 201 }, 202 }, 203 } 204 205 func TestKvOrdering(t *testing.T) { 206 for i, tt := range rangeTests { 207 mKV := &mockKV{clientv3.NewKVFromKVClient(nil, nil), tt.response.OpResponse()} 208 kv := &kvOrdering{ 209 mKV, 210 func(r *clientv3.GetResponse) OrderViolationFunc { 211 return func(op clientv3.Op, resp clientv3.OpResponse, prevRev int64) error { 212 r.Header.Revision++ 213 return nil 214 } 215 }(tt.response), 216 tt.prevRev, 217 sync.RWMutex{}, 218 } 219 res, err := kv.Get(nil, "mockKey") 220 if err != nil { 221 t.Errorf("#%d: expected response %+v, got error %+v", i, tt.response, err) 222 } 223 if rev := res.Header.Revision; rev < tt.prevRev { 224 t.Errorf("#%d: expected revision %d, got %d", i, tt.prevRev, rev) 225 } 226 } 227 } 228 229 var txnTests = []struct { 230 prevRev int64 231 response *clientv3.TxnResponse 232 }{ 233 { 234 5, 235 &clientv3.TxnResponse{ 236 Header: &pb.ResponseHeader{ 237 Revision: 5, 238 }, 239 }, 240 }, 241 { 242 5, 243 &clientv3.TxnResponse{ 244 Header: &pb.ResponseHeader{ 245 Revision: 8, 246 }, 247 }, 248 }, 249 { 250 5, 251 &clientv3.TxnResponse{ 252 Header: &pb.ResponseHeader{ 253 Revision: 4, 254 }, 255 }, 256 }, 257 } 258 259 func TestTxnOrdering(t *testing.T) { 260 for i, tt := range txnTests { 261 mKV := &mockKV{clientv3.NewKVFromKVClient(nil, nil), tt.response.OpResponse()} 262 kv := &kvOrdering{ 263 mKV, 264 func(r *clientv3.TxnResponse) OrderViolationFunc { 265 return func(op clientv3.Op, resp clientv3.OpResponse, prevRev int64) error { 266 r.Header.Revision++ 267 return nil 268 } 269 }(tt.response), 270 tt.prevRev, 271 sync.RWMutex{}, 272 } 273 txn := &txnOrdering{ 274 kv.Txn(context.Background()), 275 kv, 276 context.Background(), 277 sync.Mutex{}, 278 []clientv3.Cmp{}, 279 []clientv3.Op{}, 280 []clientv3.Op{}, 281 } 282 res, err := txn.Commit() 283 if err != nil { 284 t.Errorf("#%d: expected response %+v, got error %+v", i, tt.response, err) 285 } 286 if rev := res.Header.Revision; rev < tt.prevRev { 287 t.Errorf("#%d: expected revision %d, got %d", i, tt.prevRev, rev) 288 } 289 } 290 }