github.com/lfch/etcd-io/tests/v3@v3.0.0-20221004140520-eac99acd3e9d/integration/clientv3/maintenance_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 clientv3test 16 17 import ( 18 "bytes" 19 "context" 20 "fmt" 21 "io" 22 "math" 23 "path/filepath" 24 "testing" 25 "time" 26 27 "github.com/lfch/etcd-io/server/v3/storage/mvcc/testutil" 28 integration2 "github.com/lfch/etcd-io/tests/v3/framework/integration" 29 "go.uber.org/zap/zaptest" 30 "google.golang.org/grpc" 31 32 "github.com/lfch/etcd-io/api/v3/v3rpc/rpctypes" 33 "github.com/lfch/etcd-io/api/v3/version" 34 "github.com/lfch/etcd-io/client/v3" 35 "github.com/lfch/etcd-io/server/v3/lease" 36 "github.com/lfch/etcd-io/server/v3/storage/backend" 37 "github.com/lfch/etcd-io/server/v3/storage/mvcc" 38 ) 39 40 func TestMaintenanceHashKV(t *testing.T) { 41 integration2.BeforeTest(t) 42 43 clus := integration2.NewCluster(t, &integration2.ClusterConfig{Size: 3}) 44 defer clus.Terminate(t) 45 46 for i := 0; i < 3; i++ { 47 if _, err := clus.RandClient().Put(context.Background(), "foo", "bar"); err != nil { 48 t.Fatal(err) 49 } 50 } 51 52 var hv uint32 53 for i := 0; i < 3; i++ { 54 cli := clus.Client(i) 55 // ensure writes are replicated 56 if _, err := cli.Get(context.TODO(), "foo"); err != nil { 57 t.Fatal(err) 58 } 59 hresp, err := cli.HashKV(context.Background(), clus.Members[i].GRPCURL(), 0) 60 if err != nil { 61 t.Fatal(err) 62 } 63 if hv == 0 { 64 hv = hresp.Hash 65 continue 66 } 67 if hv != hresp.Hash { 68 t.Fatalf("#%d: hash expected %d, got %d", i, hv, hresp.Hash) 69 } 70 } 71 } 72 73 // TODO: Change this to fuzz test 74 func TestCompactionHash(t *testing.T) { 75 integration2.BeforeTest(t) 76 77 clus := integration2.NewCluster(t, &integration2.ClusterConfig{Size: 1}) 78 defer clus.Terminate(t) 79 80 cc, err := clus.ClusterClient(t) 81 if err != nil { 82 t.Fatal(err) 83 } 84 85 testutil.TestCompactionHash(context.Background(), t, hashTestCase{cc, clus.Members[0].GRPCURL()}, 1000) 86 } 87 88 type hashTestCase struct { 89 *clientv3.Client 90 url string 91 } 92 93 func (tc hashTestCase) Put(ctx context.Context, key, value string) error { 94 _, err := tc.Client.Put(ctx, key, value) 95 return err 96 } 97 98 func (tc hashTestCase) Delete(ctx context.Context, key string) error { 99 _, err := tc.Client.Delete(ctx, key) 100 return err 101 } 102 103 func (tc hashTestCase) HashByRev(ctx context.Context, rev int64) (testutil.KeyValueHash, error) { 104 resp, err := tc.Client.HashKV(ctx, tc.url, rev) 105 return testutil.KeyValueHash{Hash: resp.Hash, CompactRevision: resp.CompactRevision, Revision: resp.Header.Revision}, err 106 } 107 108 func (tc hashTestCase) Defrag(ctx context.Context) error { 109 _, err := tc.Client.Defragment(ctx, tc.url) 110 return err 111 } 112 113 func (tc hashTestCase) Compact(ctx context.Context, rev int64) error { 114 _, err := tc.Client.Compact(ctx, rev) 115 // Wait for compaction to be compacted 116 time.Sleep(50 * time.Millisecond) 117 return err 118 } 119 120 func TestMaintenanceMoveLeader(t *testing.T) { 121 integration2.BeforeTest(t) 122 123 clus := integration2.NewCluster(t, &integration2.ClusterConfig{Size: 3}) 124 defer clus.Terminate(t) 125 126 oldLeadIdx := clus.WaitLeader(t) 127 targetIdx := (oldLeadIdx + 1) % 3 128 target := uint64(clus.Members[targetIdx].ID()) 129 130 cli := clus.Client(targetIdx) 131 _, err := cli.MoveLeader(context.Background(), target) 132 if err != rpctypes.ErrNotLeader { 133 t.Fatalf("error expected %v, got %v", rpctypes.ErrNotLeader, err) 134 } 135 136 cli = clus.Client(oldLeadIdx) 137 _, err = cli.MoveLeader(context.Background(), target) 138 if err != nil { 139 t.Fatal(err) 140 } 141 142 leadIdx := clus.WaitLeader(t) 143 lead := uint64(clus.Members[leadIdx].ID()) 144 if target != lead { 145 t.Fatalf("new leader expected %d, got %d", target, lead) 146 } 147 } 148 149 // TestMaintenanceSnapshotCancel ensures that context cancel 150 // before snapshot reading returns corresponding context errors. 151 func TestMaintenanceSnapshotCancel(t *testing.T) { 152 integration2.BeforeTest(t) 153 154 clus := integration2.NewCluster(t, &integration2.ClusterConfig{Size: 1}) 155 defer clus.Terminate(t) 156 157 // reading snapshot with canceled context should error out 158 ctx, cancel := context.WithCancel(context.Background()) 159 rc1, err := clus.RandClient().Snapshot(ctx) 160 if err != nil { 161 t.Fatal(err) 162 } 163 defer rc1.Close() 164 165 cancel() 166 _, err = io.Copy(io.Discard, rc1) 167 if err != context.Canceled { 168 t.Errorf("expected %v, got %v", context.Canceled, err) 169 } 170 } 171 172 // TestMaintenanceSnapshotWithVersionTimeout ensures that SnapshotWithVersion function 173 // returns corresponding context errors when context timeout happened before snapshot reading 174 func TestMaintenanceSnapshotWithVersionTimeout(t *testing.T) { 175 testMaintenanceSnapshotTimeout(t, func(ctx context.Context, client *clientv3.Client) (io.ReadCloser, error) { 176 resp, err := client.SnapshotWithVersion(ctx) 177 if err != nil { 178 return nil, err 179 } 180 return resp.Snapshot, nil 181 }) 182 } 183 184 // TestMaintenanceSnapshotTimeout ensures that Snapshot function 185 // returns corresponding context errors when context timeout happened before snapshot reading 186 func TestMaintenanceSnapshotTimeout(t *testing.T) { 187 testMaintenanceSnapshotTimeout(t, func(ctx context.Context, client *clientv3.Client) (io.ReadCloser, error) { 188 return client.Snapshot(ctx) 189 }) 190 } 191 192 // testMaintenanceSnapshotTimeout given snapshot function ensures that it 193 // returns corresponding context errors when context timeout happened before snapshot reading 194 func testMaintenanceSnapshotTimeout(t *testing.T, snapshot func(context.Context, *clientv3.Client) (io.ReadCloser, error)) { 195 integration2.BeforeTest(t) 196 197 clus := integration2.NewCluster(t, &integration2.ClusterConfig{Size: 1}) 198 defer clus.Terminate(t) 199 200 // reading snapshot with deadline exceeded should error out 201 ctx, cancel := context.WithTimeout(context.Background(), time.Second) 202 defer cancel() 203 rc2, err := snapshot(ctx, clus.RandClient()) 204 if err != nil { 205 t.Fatal(err) 206 } 207 defer rc2.Close() 208 209 time.Sleep(2 * time.Second) 210 211 _, err = io.Copy(io.Discard, rc2) 212 if err != nil && !IsClientTimeout(err) { 213 t.Errorf("expected client timeout, got %v", err) 214 } 215 } 216 217 // TestMaintenanceSnapshotWithVersionErrorInflight ensures that ReaderCloser returned by SnapshotWithVersion function 218 // will fail to read with corresponding context errors on inflight context cancel timeout. 219 func TestMaintenanceSnapshotWithVersionErrorInflight(t *testing.T) { 220 testMaintenanceSnapshotErrorInflight(t, func(ctx context.Context, client *clientv3.Client) (io.ReadCloser, error) { 221 resp, err := client.SnapshotWithVersion(ctx) 222 if err != nil { 223 return nil, err 224 } 225 return resp.Snapshot, nil 226 }) 227 } 228 229 // TestMaintenanceSnapshotError ensures that ReaderCloser returned by Snapshot function 230 // will fail to read with corresponding context errors on inflight context cancel timeout. 231 func TestMaintenanceSnapshotErrorInflight(t *testing.T) { 232 testMaintenanceSnapshotErrorInflight(t, func(ctx context.Context, client *clientv3.Client) (io.ReadCloser, error) { 233 return client.Snapshot(ctx) 234 }) 235 } 236 237 // testMaintenanceSnapshotErrorInflight given snapshot function ensures that ReaderCloser returned by it 238 // will fail to read with corresponding context errors on inflight context cancel timeout. 239 func testMaintenanceSnapshotErrorInflight(t *testing.T, snapshot func(context.Context, *clientv3.Client) (io.ReadCloser, error)) { 240 integration2.BeforeTest(t) 241 lg := zaptest.NewLogger(t) 242 243 clus := integration2.NewCluster(t, &integration2.ClusterConfig{Size: 1, UseBridge: true}) 244 defer clus.Terminate(t) 245 246 // take about 1-second to read snapshot 247 clus.Members[0].Stop(t) 248 dpath := filepath.Join(clus.Members[0].DataDir, "member", "snap", "db") 249 b := backend.NewDefaultBackend(lg, dpath) 250 s := mvcc.NewStore(lg, b, &lease.FakeLessor{}, mvcc.StoreConfig{CompactionBatchLimit: math.MaxInt32}) 251 rev := 100000 252 for i := 2; i <= rev; i++ { 253 s.Put([]byte(fmt.Sprintf("%10d", i)), bytes.Repeat([]byte("a"), 1024), lease.NoLease) 254 } 255 s.Close() 256 b.Close() 257 clus.Members[0].Restart(t) 258 259 // reading snapshot with canceled context should error out 260 ctx, cancel := context.WithCancel(context.Background()) 261 rc1, err := snapshot(ctx, clus.RandClient()) 262 if err != nil { 263 t.Fatal(err) 264 } 265 defer rc1.Close() 266 267 donec := make(chan struct{}) 268 go func() { 269 time.Sleep(300 * time.Millisecond) 270 cancel() 271 close(donec) 272 }() 273 _, err = io.Copy(io.Discard, rc1) 274 if err != nil && err != context.Canceled { 275 t.Errorf("expected %v, got %v", context.Canceled, err) 276 } 277 <-donec 278 279 // reading snapshot with deadline exceeded should error out 280 ctx, cancel = context.WithTimeout(context.Background(), time.Second) 281 defer cancel() 282 rc2, err := snapshot(ctx, clus.RandClient()) 283 if err != nil { 284 t.Fatal(err) 285 } 286 defer rc2.Close() 287 288 // 300ms left and expect timeout while snapshot reading is in progress 289 time.Sleep(700 * time.Millisecond) 290 _, err = io.Copy(io.Discard, rc2) 291 if err != nil && !IsClientTimeout(err) { 292 t.Errorf("expected client timeout, got %v", err) 293 } 294 } 295 296 // TestMaintenanceSnapshotWithVersionVersion ensures that SnapshotWithVersion returns correct version value. 297 func TestMaintenanceSnapshotWithVersionVersion(t *testing.T) { 298 integration2.BeforeTest(t) 299 300 // Set SnapshotCount to 1 to force raft snapshot to ensure that storage version is set 301 clus := integration2.NewCluster(t, &integration2.ClusterConfig{Size: 1, SnapshotCount: 1}) 302 defer clus.Terminate(t) 303 304 // Put some keys to ensure that wal snapshot is triggered 305 for i := 0; i < 10; i++ { 306 clus.RandClient().Put(context.Background(), fmt.Sprintf("%d", i), "1") 307 } 308 309 // reading snapshot with canceled context should error out 310 resp, err := clus.RandClient().SnapshotWithVersion(context.Background()) 311 if err != nil { 312 t.Fatal(err) 313 } 314 defer resp.Snapshot.Close() 315 if resp.Version != "3.6.0" { 316 t.Errorf("unexpected version, expected %q, got %q", version.Version, resp.Version) 317 } 318 } 319 320 func TestMaintenanceStatus(t *testing.T) { 321 integration2.BeforeTest(t) 322 323 clus := integration2.NewCluster(t, &integration2.ClusterConfig{Size: 3}) 324 defer clus.Terminate(t) 325 326 t.Logf("Waiting for leader...") 327 clus.WaitLeader(t) 328 t.Logf("Leader established.") 329 330 eps := make([]string, 3) 331 for i := 0; i < 3; i++ { 332 eps[i] = clus.Members[i].GRPCURL() 333 } 334 335 t.Logf("Creating client...") 336 cli, err := integration2.NewClient(t, clientv3.Config{Endpoints: eps, DialOptions: []grpc.DialOption{grpc.WithBlock()}}) 337 if err != nil { 338 t.Fatal(err) 339 } 340 defer cli.Close() 341 t.Logf("Creating client [DONE]") 342 343 prevID, leaderFound := uint64(0), false 344 for i := 0; i < 3; i++ { 345 resp, err := cli.Status(context.TODO(), eps[i]) 346 if err != nil { 347 t.Fatal(err) 348 } 349 t.Logf("Response from %v: %v", i, resp) 350 if prevID == 0 { 351 prevID, leaderFound = resp.Header.MemberId, resp.Header.MemberId == resp.Leader 352 continue 353 } 354 if prevID == resp.Header.MemberId { 355 t.Errorf("#%d: status returned duplicate member ID with %016x", i, prevID) 356 } 357 if leaderFound && resp.Header.MemberId == resp.Leader { 358 t.Errorf("#%d: leader already found, but found another %016x", i, resp.Header.MemberId) 359 } 360 if !leaderFound { 361 leaderFound = resp.Header.MemberId == resp.Leader 362 } 363 } 364 if !leaderFound { 365 t.Fatal("no leader found") 366 } 367 }