go.etcd.io/etcd@v3.3.27+incompatible/clientv3/integration/txn_test.go (about) 1 // Copyright 2016 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 integration 16 17 import ( 18 "context" 19 "fmt" 20 "testing" 21 "time" 22 23 "github.com/coreos/etcd/clientv3" 24 "github.com/coreos/etcd/embed" 25 "github.com/coreos/etcd/etcdserver/api/v3rpc/rpctypes" 26 "github.com/coreos/etcd/integration" 27 "github.com/coreos/etcd/pkg/testutil" 28 ) 29 30 func TestTxnError(t *testing.T) { 31 defer testutil.AfterTest(t) 32 33 clus := integration.NewClusterV3(t, &integration.ClusterConfig{Size: 1}) 34 defer clus.Terminate(t) 35 36 kv := clus.RandClient() 37 ctx := context.TODO() 38 39 _, err := kv.Txn(ctx).Then(clientv3.OpPut("foo", "bar1"), clientv3.OpPut("foo", "bar2")).Commit() 40 if err != rpctypes.ErrDuplicateKey { 41 t.Fatalf("expected %v, got %v", rpctypes.ErrDuplicateKey, err) 42 } 43 44 ops := make([]clientv3.Op, int(embed.DefaultMaxTxnOps+10)) 45 for i := range ops { 46 ops[i] = clientv3.OpPut(fmt.Sprintf("foo%d", i), "") 47 } 48 _, err = kv.Txn(ctx).Then(ops...).Commit() 49 if err != rpctypes.ErrTooManyOps { 50 t.Fatalf("expected %v, got %v", rpctypes.ErrTooManyOps, err) 51 } 52 } 53 54 func TestTxnWriteFail(t *testing.T) { 55 defer testutil.AfterTest(t) 56 57 clus := integration.NewClusterV3(t, &integration.ClusterConfig{Size: 3}) 58 defer clus.Terminate(t) 59 60 kv := clus.Client(0) 61 62 clus.Members[0].Stop(t) 63 64 txnc, getc := make(chan struct{}), make(chan struct{}) 65 go func() { 66 ctx, cancel := context.WithTimeout(context.TODO(), time.Second) 67 defer cancel() 68 resp, err := kv.Txn(ctx).Then(clientv3.OpPut("foo", "bar")).Commit() 69 if err == nil { 70 t.Fatalf("expected error, got response %v", resp) 71 } 72 close(txnc) 73 }() 74 75 go func() { 76 defer close(getc) 77 select { 78 case <-time.After(5 * time.Second): 79 t.Fatalf("timed out waiting for txn fail") 80 case <-txnc: 81 } 82 // and ensure the put didn't take 83 gresp, gerr := clus.Client(1).Get(context.TODO(), "foo") 84 if gerr != nil { 85 t.Fatal(gerr) 86 } 87 if len(gresp.Kvs) != 0 { 88 t.Fatalf("expected no keys, got %v", gresp.Kvs) 89 } 90 }() 91 92 select { 93 case <-time.After(2 * clus.Members[1].ServerConfig.ReqTimeout()): 94 t.Fatalf("timed out waiting for get") 95 case <-getc: 96 } 97 98 // reconnect so terminate doesn't complain about double-close 99 clus.Members[0].Restart(t) 100 } 101 102 func TestTxnReadRetry(t *testing.T) { 103 t.Skipf("skipping txn read retry test: re-enable after we do retry on txn read request") 104 105 defer testutil.AfterTest(t) 106 107 clus := integration.NewClusterV3(t, &integration.ClusterConfig{Size: 3}) 108 defer clus.Terminate(t) 109 110 kv := clus.Client(0) 111 112 thenOps := [][]clientv3.Op{ 113 {clientv3.OpGet("foo")}, 114 {clientv3.OpTxn(nil, []clientv3.Op{clientv3.OpGet("foo")}, nil)}, 115 {clientv3.OpTxn(nil, nil, nil)}, 116 {}, 117 } 118 for i := range thenOps { 119 clus.Members[0].Stop(t) 120 <-clus.Members[0].StopNotify() 121 122 donec := make(chan struct{}) 123 go func() { 124 _, err := kv.Txn(context.TODO()).Then(thenOps[i]...).Commit() 125 if err != nil { 126 t.Fatalf("expected response, got error %v", err) 127 } 128 donec <- struct{}{} 129 }() 130 // wait for txn to fail on disconnect 131 time.Sleep(100 * time.Millisecond) 132 133 // restart node; client should resume 134 clus.Members[0].Restart(t) 135 select { 136 case <-donec: 137 case <-time.After(2 * clus.Members[1].ServerConfig.ReqTimeout()): 138 t.Fatalf("waited too long") 139 } 140 } 141 } 142 143 func TestTxnSuccess(t *testing.T) { 144 defer testutil.AfterTest(t) 145 146 clus := integration.NewClusterV3(t, &integration.ClusterConfig{Size: 3}) 147 defer clus.Terminate(t) 148 149 kv := clus.Client(0) 150 ctx := context.TODO() 151 152 _, err := kv.Txn(ctx).Then(clientv3.OpPut("foo", "bar")).Commit() 153 if err != nil { 154 t.Fatal(err) 155 } 156 157 resp, err := kv.Get(ctx, "foo") 158 if err != nil { 159 t.Fatal(err) 160 } 161 if len(resp.Kvs) != 1 || string(resp.Kvs[0].Key) != "foo" { 162 t.Fatalf("unexpected Get response %v", resp) 163 } 164 } 165 166 func TestTxnCompareRange(t *testing.T) { 167 defer testutil.AfterTest(t) 168 169 clus := integration.NewClusterV3(t, &integration.ClusterConfig{Size: 1}) 170 defer clus.Terminate(t) 171 172 kv := clus.Client(0) 173 fooResp, err := kv.Put(context.TODO(), "foo/", "bar") 174 if err != nil { 175 t.Fatal(err) 176 } 177 if _, err = kv.Put(context.TODO(), "foo/a", "baz"); err != nil { 178 t.Fatal(err) 179 } 180 tresp, terr := kv.Txn(context.TODO()).If( 181 clientv3.Compare( 182 clientv3.CreateRevision("foo/"), "=", fooResp.Header.Revision). 183 WithPrefix(), 184 ).Commit() 185 if terr != nil { 186 t.Fatal(terr) 187 } 188 if tresp.Succeeded { 189 t.Fatal("expected prefix compare to false, got compares as true") 190 } 191 } 192 193 func TestTxnNested(t *testing.T) { 194 defer testutil.AfterTest(t) 195 196 clus := integration.NewClusterV3(t, &integration.ClusterConfig{Size: 3}) 197 defer clus.Terminate(t) 198 199 kv := clus.Client(0) 200 201 tresp, err := kv.Txn(context.TODO()). 202 If(clientv3.Compare(clientv3.Version("foo"), "=", 0)). 203 Then( 204 clientv3.OpPut("foo", "bar"), 205 clientv3.OpTxn(nil, []clientv3.Op{clientv3.OpPut("abc", "123")}, nil)). 206 Else(clientv3.OpPut("foo", "baz")).Commit() 207 if err != nil { 208 t.Fatal(err) 209 } 210 if len(tresp.Responses) != 2 { 211 t.Errorf("expected 2 top-level txn responses, got %+v", tresp.Responses) 212 } 213 214 // check txn writes were applied 215 resp, err := kv.Get(context.TODO(), "foo") 216 if err != nil { 217 t.Fatal(err) 218 } 219 if len(resp.Kvs) != 1 || string(resp.Kvs[0].Value) != "bar" { 220 t.Errorf("unexpected Get response %+v", resp) 221 } 222 resp, err = kv.Get(context.TODO(), "abc") 223 if err != nil { 224 t.Fatal(err) 225 } 226 if len(resp.Kvs) != 1 || string(resp.Kvs[0].Value) != "123" { 227 t.Errorf("unexpected Get response %+v", resp) 228 } 229 }