go.etcd.io/etcd@v3.3.27+incompatible/clientv3/integration/watch_fragment_test.go (about)

     1  // Copyright 2018 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  // +build !cluster_proxy
    16  
    17  package integration
    18  
    19  import (
    20  	"context"
    21  	"fmt"
    22  	"strings"
    23  	"testing"
    24  	"time"
    25  
    26  	"github.com/coreos/etcd/clientv3"
    27  	"github.com/coreos/etcd/integration"
    28  	"github.com/coreos/etcd/pkg/testutil"
    29  )
    30  
    31  // TestWatchFragmentDisable ensures that large watch
    32  // response exceeding server-side request limit can
    33  // arrive even without watch response fragmentation.
    34  func TestWatchFragmentDisable(t *testing.T) {
    35  	testWatchFragment(t, false, false)
    36  }
    37  
    38  // TestWatchFragmentDisableWithGRPCLimit verifies
    39  // large watch response exceeding server-side request
    40  // limit and client-side gRPC response receive limit
    41  // cannot arrive without watch events fragmentation,
    42  // because multiple events exceed client-side gRPC
    43  // response receive limit.
    44  func TestWatchFragmentDisableWithGRPCLimit(t *testing.T) {
    45  	testWatchFragment(t, false, true)
    46  }
    47  
    48  // TestWatchFragmentEnable ensures that large watch
    49  // response exceeding server-side request limit arrive
    50  // with watch response fragmentation.
    51  func TestWatchFragmentEnable(t *testing.T) {
    52  	testWatchFragment(t, true, false)
    53  }
    54  
    55  // TestWatchFragmentEnableWithGRPCLimit verifies
    56  // large watch response exceeding server-side request
    57  // limit and client-side gRPC response receive limit
    58  // can arrive only when watch events are fragmented.
    59  func TestWatchFragmentEnableWithGRPCLimit(t *testing.T) {
    60  	testWatchFragment(t, true, true)
    61  }
    62  
    63  // testWatchFragment triggers watch response that spans over multiple
    64  // revisions exceeding server request limits when combined.
    65  func testWatchFragment(t *testing.T, fragment, exceedRecvLimit bool) {
    66  	cfg := &integration.ClusterConfig{
    67  		Size:            1,
    68  		MaxRequestBytes: 1.5 * 1024 * 1024,
    69  	}
    70  	if exceedRecvLimit {
    71  		cfg.ClientMaxCallRecvMsgSize = 1.5 * 1024 * 1024
    72  	}
    73  	clus := integration.NewClusterV3(t, cfg)
    74  	defer clus.Terminate(t)
    75  
    76  	cli := clus.Client(0)
    77  	errc := make(chan error)
    78  	for i := 0; i < 10; i++ {
    79  		go func(i int) {
    80  			_, err := cli.Put(context.TODO(),
    81  				fmt.Sprint("foo", i),
    82  				strings.Repeat("a", 1024*1024),
    83  			)
    84  			errc <- err
    85  		}(i)
    86  	}
    87  	for i := 0; i < 10; i++ {
    88  		if err := <-errc; err != nil {
    89  			t.Fatalf("failed to put: %v", err)
    90  		}
    91  	}
    92  
    93  	opts := []clientv3.OpOption{clientv3.WithPrefix(), clientv3.WithRev(1)}
    94  	if fragment {
    95  		opts = append(opts, clientv3.WithFragment())
    96  	}
    97  	wch := cli.Watch(context.TODO(), "foo", opts...)
    98  
    99  	// expect 10 MiB watch response
   100  	select {
   101  	case ws := <-wch:
   102  		// without fragment, should exceed gRPC client receive limit
   103  		if !fragment && exceedRecvLimit {
   104  			if len(ws.Events) != 0 {
   105  				t.Fatalf("expected 0 events with watch fragmentation, got %d", len(ws.Events))
   106  			}
   107  			exp := "code = ResourceExhausted desc = grpc: received message larger than max ("
   108  			if !strings.Contains(ws.Err().Error(), exp) {
   109  				t.Fatalf("expected 'ResourceExhausted' error, got %v", ws.Err())
   110  			}
   111  			return
   112  		}
   113  
   114  		// still expect merged watch events
   115  		if len(ws.Events) != 10 {
   116  			t.Fatalf("expected 10 events with watch fragmentation, got %d", len(ws.Events))
   117  		}
   118  		if ws.Err() != nil {
   119  			t.Fatalf("unexpected error %v", ws.Err())
   120  		}
   121  
   122  	case <-time.After(testutil.RequestTimeout):
   123  		t.Fatalf("took too long to receive events")
   124  	}
   125  }