github.com/m3db/m3@v1.5.1-0.20231129193456-75a402aa583b/src/msg/producer/writer/writer_test.go (about)

     1  // Copyright (c) 2018 Uber Technologies, Inc.
     2  //
     3  // Permission is hereby granted, free of charge, to any person obtaining a copy
     4  // of this software and associated documentation files (the "Software"), to deal
     5  // in the Software without restriction, including without limitation the rights
     6  // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     7  // copies of the Software, and to permit persons to whom the Software is
     8  // furnished to do so, subject to the following conditions:
     9  //
    10  // The above copyright notice and this permission notice shall be included in
    11  // all copies or substantial portions of the Software.
    12  //
    13  // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    14  // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    15  // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    16  // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    17  // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    18  // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
    19  // THE SOFTWARE.
    20  
    21  package writer
    22  
    23  import (
    24  	"errors"
    25  	"net"
    26  	"sync"
    27  	"testing"
    28  	"time"
    29  
    30  	"github.com/m3db/m3/src/cluster/client"
    31  	"github.com/m3db/m3/src/cluster/kv"
    32  	"github.com/m3db/m3/src/cluster/kv/mem"
    33  	"github.com/m3db/m3/src/cluster/placement"
    34  	"github.com/m3db/m3/src/cluster/services"
    35  	"github.com/m3db/m3/src/cluster/shard"
    36  	"github.com/m3db/m3/src/msg/producer"
    37  	"github.com/m3db/m3/src/msg/topic"
    38  	xtest "github.com/m3db/m3/src/x/test"
    39  
    40  	"github.com/fortytw2/leaktest"
    41  	"github.com/golang/mock/gomock"
    42  	"github.com/stretchr/testify/require"
    43  	"go.uber.org/atomic"
    44  )
    45  
    46  func TestWriterInitErrorNoTopic(t *testing.T) {
    47  	defer leaktest.Check(t)()
    48  
    49  	ctrl := xtest.NewController(t)
    50  	defer ctrl.Finish()
    51  
    52  	store := mem.NewStore()
    53  	cs := client.NewMockClient(ctrl)
    54  	cs.EXPECT().Store(gomock.Any()).Return(store, nil)
    55  
    56  	ts, err := topic.NewService(topic.NewServiceOptions().SetConfigService(cs))
    57  	require.NoError(t, err)
    58  
    59  	opts := testOptions().SetTopicService(ts)
    60  	w := NewWriter(opts)
    61  	require.Error(t, w.Init())
    62  	w.Close()
    63  }
    64  
    65  func TestWriterWriteAfterClosed(t *testing.T) {
    66  	defer leaktest.Check(t)()
    67  
    68  	ctrl := xtest.NewController(t)
    69  	defer ctrl.Finish()
    70  
    71  	store := mem.NewStore()
    72  	cs := client.NewMockClient(ctrl)
    73  	cs.EXPECT().Store(gomock.Any()).Return(store, nil)
    74  
    75  	ts, err := topic.NewService(topic.NewServiceOptions().SetConfigService(cs))
    76  	require.NoError(t, err)
    77  
    78  	opts := testOptions().SetTopicService(ts)
    79  	w := NewWriter(opts)
    80  	w.Init()
    81  	w.Close()
    82  
    83  	mm := producer.NewMockMessage(ctrl)
    84  	mm.EXPECT().Finalize(producer.Dropped)
    85  	mm.EXPECT().Size().Return(3)
    86  	rm := producer.NewRefCountedMessage(mm, nil)
    87  	err = w.Write(rm)
    88  	require.Error(t, err)
    89  	require.Equal(t, errWriterClosed, err)
    90  }
    91  
    92  func TestWriterWriteWithInvalidShard(t *testing.T) {
    93  	defer leaktest.Check(t)()
    94  
    95  	ctrl := xtest.NewController(t)
    96  	defer ctrl.Finish()
    97  
    98  	store := mem.NewStore()
    99  	cs := client.NewMockClient(ctrl)
   100  	cs.EXPECT().Store(gomock.Any()).Return(store, nil)
   101  
   102  	ts, err := topic.NewService(topic.NewServiceOptions().SetConfigService(cs))
   103  	require.NoError(t, err)
   104  
   105  	opts := testOptions().SetTopicService(ts)
   106  	w := NewWriter(opts).(*writer)
   107  	w.numShards = 2
   108  
   109  	mm := producer.NewMockMessage(ctrl)
   110  	mm.EXPECT().Shard().Return(uint32(2))
   111  	mm.EXPECT().Finalize(producer.Dropped)
   112  	mm.EXPECT().Size().Return(3)
   113  	rm := producer.NewRefCountedMessage(mm, nil)
   114  	err = w.Write(rm)
   115  	require.Error(t, err)
   116  
   117  	mm.EXPECT().Shard().Return(uint32(100))
   118  	mm.EXPECT().Finalize(producer.Dropped)
   119  	mm.EXPECT().Size().Return(3)
   120  	rm = producer.NewRefCountedMessage(mm, nil)
   121  	err = w.Write(rm)
   122  	require.Error(t, err)
   123  }
   124  
   125  func TestWriterInvalidTopicUpdate(t *testing.T) {
   126  	defer leaktest.Check(t)()
   127  
   128  	ctrl := xtest.NewController(t)
   129  	defer ctrl.Finish()
   130  
   131  	store := mem.NewStore()
   132  	cs := client.NewMockClient(ctrl)
   133  	cs.EXPECT().Store(gomock.Any()).Return(store, nil)
   134  
   135  	ts, err := topic.NewService(topic.NewServiceOptions().SetConfigService(cs))
   136  	require.NoError(t, err)
   137  
   138  	opts := testOptions().SetTopicService(ts)
   139  	sid1 := services.NewServiceID().SetName("s1")
   140  	cs1 := topic.NewConsumerService().SetConsumptionType(topic.Replicated).SetServiceID(sid1)
   141  	testTopic := topic.NewTopic().
   142  		SetName(opts.TopicName()).
   143  		SetNumberOfShards(2).
   144  		SetConsumerServices([]topic.ConsumerService{cs1})
   145  	_, err = ts.CheckAndSet(testTopic, kv.UninitializedVersion)
   146  	require.NoError(t, err)
   147  
   148  	sd := services.NewMockServices(ctrl)
   149  	opts = opts.SetServiceDiscovery(sd)
   150  	ps1 := testPlacementService(store, sid1)
   151  	sd.EXPECT().PlacementService(sid1, gomock.Any()).Return(ps1, nil)
   152  
   153  	p1 := placement.NewPlacement().
   154  		SetInstances([]placement.Instance{
   155  			placement.NewInstance().
   156  				SetID("i1").
   157  				SetEndpoint("addr1").
   158  				SetShards(shard.NewShards([]shard.Shard{
   159  					shard.NewShard(0).SetState(shard.Available),
   160  					shard.NewShard(1).SetState(shard.Available),
   161  				})),
   162  		}).
   163  		SetShards([]uint32{0, 1}).
   164  		SetReplicaFactor(1).
   165  		SetIsSharded(true)
   166  	_, err = ps1.Set(p1)
   167  	require.NoError(t, err)
   168  
   169  	w := NewWriter(opts).(*writer)
   170  	var wg sync.WaitGroup
   171  	w.processFn = func(i interface{}) error {
   172  		defer wg.Done()
   173  		return w.process(i)
   174  	}
   175  	wg.Add(1)
   176  	require.NoError(t, w.Init())
   177  	wg.Wait()
   178  	defer w.Close()
   179  
   180  	require.Equal(t, 2, int(w.numShards))
   181  	testTopic = topic.NewTopic().
   182  		SetName(opts.TopicName()).
   183  		SetNumberOfShards(3).
   184  		SetConsumerServices([]topic.ConsumerService{cs1}).
   185  		SetVersion(1)
   186  	wg.Add(1)
   187  	_, err = ts.CheckAndSet(testTopic, 1)
   188  	require.NoError(t, err)
   189  	wg.Wait()
   190  
   191  	require.Equal(t, 2, int(w.numShards))
   192  }
   193  
   194  func TestWriterRegisterFilter(t *testing.T) {
   195  	defer leaktest.Check(t)()
   196  
   197  	ctrl := xtest.NewController(t)
   198  	defer ctrl.Finish()
   199  
   200  	store := mem.NewStore()
   201  	cs := client.NewMockClient(ctrl)
   202  	cs.EXPECT().Store(gomock.Any()).Return(store, nil)
   203  
   204  	ts, err := topic.NewService(topic.NewServiceOptions().SetConfigService(cs))
   205  	require.NoError(t, err)
   206  
   207  	opts := testOptions().SetTopicService(ts)
   208  
   209  	sid1 := services.NewServiceID().SetName("s1")
   210  	cs1 := topic.NewConsumerService().SetConsumptionType(topic.Replicated).SetServiceID(sid1)
   211  	csw1 := NewMockconsumerServiceWriter(ctrl)
   212  
   213  	sid2 := services.NewServiceID().SetName("s2")
   214  	filter := func(producer.Message) bool { return false }
   215  	filter2 := func(producer.Message) bool { return true }
   216  
   217  	w := NewWriter(opts).(*writer)
   218  	w.consumerServiceWriters[cs1.ServiceID().String()] = csw1
   219  
   220  	csw1.EXPECT().UnregisterFilters()
   221  	w.UnregisterFilters(sid1)
   222  	_, ok := w.filterRegistry[sid1.String()]
   223  	require.True(t, !ok)
   224  
   225  	// Wrong service id triggers nothing.
   226  	w.RegisterFilter(sid2, filter)
   227  	_, ok = w.filterRegistry[sid2.String()]
   228  	require.True(t, ok)
   229  
   230  	csw1.EXPECT().RegisterFilter(gomock.Any())
   231  	w.RegisterFilter(sid1, filter)
   232  
   233  	csw1.EXPECT().UnregisterFilters()
   234  	w.UnregisterFilters(sid1)
   235  
   236  	csw1.EXPECT().RegisterFilter(gomock.Any())
   237  	w.RegisterFilter(sid1, filter)
   238  
   239  	csw1.EXPECT().RegisterFilter(gomock.Any())
   240  	csw1.EXPECT().SetMessageTTLNanos(int64(0))
   241  	testTopic := topic.NewTopic().
   242  		SetName(opts.TopicName()).
   243  		SetNumberOfShards(6).
   244  		SetConsumerServices([]topic.ConsumerService{cs1})
   245  	w.process(testTopic)
   246  
   247  	csw1.EXPECT().RegisterFilter(gomock.Any())
   248  	w.RegisterFilter(sid1, filter2)
   249  	require.True(t, len(w.filterRegistry[sid1.String()]) == 2)
   250  	csw1.EXPECT().UnregisterFilters()
   251  	w.UnregisterFilters(sid1)
   252  }
   253  
   254  func TestWriterTopicUpdate(t *testing.T) {
   255  	defer leaktest.Check(t)()
   256  
   257  	ctrl := xtest.NewController(t)
   258  	defer ctrl.Finish()
   259  
   260  	store := mem.NewStore()
   261  	cs := client.NewMockClient(ctrl)
   262  	cs.EXPECT().Store(gomock.Any()).Return(store, nil)
   263  
   264  	ts, err := topic.NewService(topic.NewServiceOptions().SetConfigService(cs))
   265  	require.NoError(t, err)
   266  
   267  	opts := testOptions().SetTopicService(ts)
   268  
   269  	sid1 := services.NewServiceID().SetName("s1")
   270  	cs1 := topic.NewConsumerService().SetConsumptionType(topic.Replicated).SetServiceID(sid1)
   271  	testTopic := topic.NewTopic().
   272  		SetName(opts.TopicName()).
   273  		SetNumberOfShards(2).
   274  		SetConsumerServices([]topic.ConsumerService{cs1})
   275  	_, err = ts.CheckAndSet(testTopic, kv.UninitializedVersion)
   276  	require.NoError(t, err)
   277  
   278  	sd := services.NewMockServices(ctrl)
   279  	opts = opts.SetServiceDiscovery(sd)
   280  	ps1 := testPlacementService(store, sid1)
   281  	sd.EXPECT().PlacementService(sid1, gomock.Any()).Return(ps1, nil)
   282  
   283  	p1 := placement.NewPlacement().
   284  		SetInstances([]placement.Instance{
   285  			placement.NewInstance().
   286  				SetID("i1").
   287  				SetEndpoint("addr1").
   288  				SetShards(shard.NewShards([]shard.Shard{
   289  					shard.NewShard(0).SetState(shard.Available),
   290  					shard.NewShard(1).SetState(shard.Available),
   291  				})),
   292  		}).
   293  		SetShards([]uint32{0, 1}).
   294  		SetReplicaFactor(1).
   295  		SetIsSharded(true)
   296  	_, err = ps1.Set(p1)
   297  	require.NoError(t, err)
   298  
   299  	w := NewWriter(opts).(*writer)
   300  	require.NoError(t, w.Init())
   301  	defer w.Close()
   302  
   303  	require.Equal(t, 1, len(w.consumerServiceWriters))
   304  
   305  	sid2 := services.NewServiceID().SetName("s2")
   306  	cs2 := topic.NewConsumerService().SetConsumptionType(topic.Shared).SetServiceID(sid2)
   307  	ps2 := testPlacementService(store, sid2)
   308  	sd.EXPECT().PlacementService(sid2, gomock.Any()).Return(ps2, nil)
   309  
   310  	sid3 := services.NewServiceID().SetName("s3")
   311  	cs3 := topic.NewConsumerService().SetConsumptionType(topic.Replicated).SetServiceID(sid3)
   312  	sd.EXPECT().PlacementService(sid3, gomock.Any()).Return(nil, errors.New("test error"))
   313  
   314  	sid4 := services.NewServiceID().SetName("s4")
   315  	cs4 := topic.NewConsumerService().SetConsumptionType(topic.Shared).SetServiceID(sid4)
   316  	ps4 := placement.NewMockService(ctrl)
   317  	sd.EXPECT().PlacementService(sid4, gomock.Any()).Return(ps4, nil)
   318  	ps4.EXPECT().Watch().Return(nil, errors.New("watch error"))
   319  
   320  	testTopic = testTopic.
   321  		SetConsumerServices([]topic.ConsumerService{
   322  			cs1, cs2,
   323  			cs3, // Could not create consumer service write for cs3.
   324  			cs4, // Could not init cs4.
   325  		}).
   326  		SetVersion(1)
   327  	_, err = ts.CheckAndSet(testTopic, 1)
   328  	require.NoError(t, err)
   329  
   330  	for {
   331  		w.RLock()
   332  		l := len(w.consumerServiceWriters)
   333  		w.RUnlock()
   334  		if l == 2 {
   335  			break
   336  		}
   337  		time.Sleep(100 * time.Millisecond)
   338  	}
   339  
   340  	store.Delete(opts.TopicName())
   341  	time.Sleep(100 * time.Millisecond)
   342  	require.Equal(t, 2, len(w.consumerServiceWriters))
   343  
   344  	testTopic = testTopic.
   345  		SetConsumerServices([]topic.ConsumerService{cs2}).
   346  		SetVersion(0)
   347  	_, err = ts.CheckAndSet(testTopic, kv.UninitializedVersion)
   348  	require.NoError(t, err)
   349  
   350  	for {
   351  		w.RLock()
   352  		l := len(w.consumerServiceWriters)
   353  		w.RUnlock()
   354  		if l == 1 {
   355  			break
   356  		}
   357  		time.Sleep(100 * time.Millisecond)
   358  	}
   359  	w.Close()
   360  
   361  	testTopic = testTopic.
   362  		SetConsumerServices([]topic.ConsumerService{cs1, cs2}).
   363  		SetVersion(1)
   364  	_, err = ts.CheckAndSet(testTopic, 1)
   365  	require.NoError(t, err)
   366  
   367  	// Not going to process topic update anymore.
   368  	time.Sleep(100 * time.Millisecond)
   369  	require.Equal(t, 1, len(w.consumerServiceWriters))
   370  }
   371  
   372  func TestTopicUpdateWithSameConsumerServicesButDifferentOrder(t *testing.T) {
   373  	defer leaktest.Check(t)()
   374  
   375  	ctrl := xtest.NewController(t)
   376  	defer ctrl.Finish()
   377  
   378  	store := mem.NewStore()
   379  	cs := client.NewMockClient(ctrl)
   380  	cs.EXPECT().Store(gomock.Any()).Return(store, nil)
   381  
   382  	ts, err := topic.NewService(topic.NewServiceOptions().SetConfigService(cs))
   383  	require.NoError(t, err)
   384  
   385  	opts := testOptions().SetTopicService(ts)
   386  
   387  	sid1 := services.NewServiceID().SetName("s1")
   388  	cs1 := topic.NewConsumerService().SetConsumptionType(topic.Replicated).SetServiceID(sid1)
   389  	sid2 := services.NewServiceID().SetName("s2")
   390  	cs2 := topic.NewConsumerService().SetConsumptionType(topic.Shared).SetServiceID(sid2).SetMessageTTLNanos(500)
   391  	testTopic := topic.NewTopic().
   392  		SetName(opts.TopicName()).
   393  		SetNumberOfShards(1).
   394  		SetConsumerServices([]topic.ConsumerService{cs1, cs2})
   395  	_, err = ts.CheckAndSet(testTopic, kv.UninitializedVersion)
   396  	require.NoError(t, err)
   397  
   398  	sd := services.NewMockServices(ctrl)
   399  	opts = opts.SetServiceDiscovery(sd)
   400  	ps1 := testPlacementService(store, sid1)
   401  	sd.EXPECT().PlacementService(sid1, gomock.Any()).Return(ps1, nil)
   402  	ps2 := testPlacementService(store, sid2)
   403  	sd.EXPECT().PlacementService(sid2, gomock.Any()).Return(ps2, nil)
   404  
   405  	p1 := placement.NewPlacement().
   406  		SetInstances([]placement.Instance{
   407  			placement.NewInstance().
   408  				SetID("i1").
   409  				SetEndpoint("i1").
   410  				SetShards(shard.NewShards([]shard.Shard{
   411  					shard.NewShard(0).SetState(shard.Available),
   412  				})),
   413  		}).
   414  		SetShards([]uint32{0}).
   415  		SetReplicaFactor(1).
   416  		SetIsSharded(true)
   417  	_, err = ps1.Set(p1)
   418  	require.NoError(t, err)
   419  
   420  	p2 := placement.NewPlacement().
   421  		SetInstances([]placement.Instance{
   422  			placement.NewInstance().
   423  				SetID("i2").
   424  				SetEndpoint("i2").
   425  				SetShards(shard.NewShards([]shard.Shard{
   426  					shard.NewShard(0).SetState(shard.Available),
   427  				})),
   428  		}).
   429  		SetShards([]uint32{0}).
   430  		SetReplicaFactor(1).
   431  		SetIsSharded(true)
   432  	_, err = ps2.Set(p2)
   433  	require.NoError(t, err)
   434  
   435  	w := NewWriter(opts).(*writer)
   436  
   437  	called := atomic.NewInt32(0)
   438  	w.processFn = func(update interface{}) error {
   439  		called.Inc()
   440  		return w.process(update)
   441  	}
   442  	require.NoError(t, w.Init())
   443  	require.Equal(t, 1, int(called.Load()))
   444  	require.Equal(t, 2, len(w.consumerServiceWriters))
   445  	csw, ok := w.consumerServiceWriters[cs1.ServiceID().String()]
   446  	require.True(t, ok)
   447  	cswMock1 := NewMockconsumerServiceWriter(ctrl)
   448  	w.consumerServiceWriters[cs1.ServiceID().String()] = cswMock1
   449  	defer csw.Close()
   450  
   451  	csw, ok = w.consumerServiceWriters[cs2.ServiceID().String()]
   452  	require.True(t, ok)
   453  	cswMock2 := NewMockconsumerServiceWriter(ctrl)
   454  	w.consumerServiceWriters[cs2.ServiceID().String()] = cswMock2
   455  	defer csw.Close()
   456  
   457  	cswMock1.EXPECT().SetMessageTTLNanos(int64(0))
   458  	cswMock2.EXPECT().SetMessageTTLNanos(int64(500))
   459  	testTopic = testTopic.
   460  		SetConsumerServices([]topic.ConsumerService{cs2, cs1}).
   461  		SetVersion(1)
   462  	_, err = ts.CheckAndSet(testTopic, 1)
   463  	require.NoError(t, err)
   464  
   465  	// The update will be processed, but nothing will be called on any of the mock writers.
   466  	for called.Load() != 2 {
   467  		time.Sleep(50 * time.Millisecond)
   468  	}
   469  	cswMock1.EXPECT().Close()
   470  	cswMock2.EXPECT().Close()
   471  	w.Close()
   472  }
   473  
   474  func TestWriterWrite(t *testing.T) {
   475  	defer leaktest.Check(t)()
   476  
   477  	ctrl := xtest.NewController(t)
   478  	defer ctrl.Finish()
   479  
   480  	store := mem.NewStore()
   481  	cs := client.NewMockClient(ctrl)
   482  	cs.EXPECT().Store(gomock.Any()).Return(store, nil)
   483  
   484  	ts, err := topic.NewService(topic.NewServiceOptions().SetConfigService(cs))
   485  	require.NoError(t, err)
   486  
   487  	opts := testOptions().SetTopicService(ts)
   488  
   489  	sid1 := services.NewServiceID().SetName("s1")
   490  	cs1 := topic.NewConsumerService().SetConsumptionType(topic.Replicated).SetServiceID(sid1)
   491  	sid2 := services.NewServiceID().SetName("s2")
   492  	cs2 := topic.NewConsumerService().SetConsumptionType(topic.Shared).SetServiceID(sid2)
   493  	testTopic := topic.NewTopic().
   494  		SetName(opts.TopicName()).
   495  		SetNumberOfShards(1).
   496  		SetConsumerServices([]topic.ConsumerService{cs1, cs2})
   497  	_, err = ts.CheckAndSet(testTopic, kv.UninitializedVersion)
   498  	require.NoError(t, err)
   499  
   500  	sd := services.NewMockServices(ctrl)
   501  	opts = opts.SetServiceDiscovery(sd)
   502  	ps1 := testPlacementService(store, sid1)
   503  	sd.EXPECT().PlacementService(sid1, gomock.Any()).Return(ps1, nil)
   504  	ps2 := testPlacementService(store, sid2)
   505  	sd.EXPECT().PlacementService(sid2, gomock.Any()).Return(ps2, nil)
   506  
   507  	lis1, err := net.Listen("tcp", "127.0.0.1:0")
   508  	require.NoError(t, err)
   509  	defer lis1.Close()
   510  
   511  	lis2, err := net.Listen("tcp", "127.0.0.1:0")
   512  	require.NoError(t, err)
   513  	defer lis2.Close()
   514  
   515  	lis3, err := net.Listen("tcp", "127.0.0.1:0")
   516  	require.NoError(t, err)
   517  	defer lis3.Close()
   518  
   519  	p1 := placement.NewPlacement().
   520  		SetInstances([]placement.Instance{
   521  			placement.NewInstance().
   522  				SetID("i1").
   523  				SetEndpoint(lis1.Addr().String()).
   524  				SetShards(shard.NewShards([]shard.Shard{
   525  					shard.NewShard(0).SetState(shard.Available),
   526  				})),
   527  			placement.NewInstance().
   528  				SetID("i2").
   529  				SetEndpoint(lis2.Addr().String()).
   530  				SetShards(shard.NewShards([]shard.Shard{
   531  					shard.NewShard(0).SetState(shard.Available),
   532  				})),
   533  		}).
   534  		SetShards([]uint32{0}).
   535  		SetReplicaFactor(2).
   536  		SetIsSharded(true)
   537  	_, err = ps1.Set(p1)
   538  	require.NoError(t, err)
   539  
   540  	p2 := placement.NewPlacement().
   541  		SetInstances([]placement.Instance{
   542  			placement.NewInstance().
   543  				SetID("i13").
   544  				SetEndpoint(lis3.Addr().String()).
   545  				SetShards(shard.NewShards([]shard.Shard{
   546  					shard.NewShard(0).SetState(shard.Available),
   547  				})),
   548  			placement.NewInstance().
   549  				SetID("i4").
   550  				SetEndpoint("addr4").
   551  				SetShards(shard.NewShards([]shard.Shard{
   552  					shard.NewShard(0).SetState(shard.Available),
   553  				})),
   554  		}).
   555  		SetShards([]uint32{0}).
   556  		SetReplicaFactor(2).
   557  		SetIsSharded(true)
   558  	_, err = ps2.Set(p2)
   559  	require.NoError(t, err)
   560  
   561  	w := NewWriter(opts).(*writer)
   562  	require.NoError(t, w.Init())
   563  	defer w.Close()
   564  
   565  	require.Equal(t, 2, len(w.consumerServiceWriters))
   566  
   567  	var wg sync.WaitGroup
   568  	mm := producer.NewMockMessage(ctrl)
   569  	mm.EXPECT().Shard().Return(uint32(0)).Times(3)
   570  	mm.EXPECT().Size().Return(3)
   571  	mm.EXPECT().Bytes().Return([]byte("foo")).Times(3)
   572  	mm.EXPECT().Finalize(producer.Consumed).Do(func(interface{}) { wg.Done() })
   573  	rm := producer.NewRefCountedMessage(mm, nil)
   574  	wg.Add(1)
   575  	require.NoError(t, w.Write(rm))
   576  
   577  	wg.Add(1)
   578  	go func() {
   579  		testConsumeAndAckOnConnectionListener(t, lis1, opts.EncoderOptions(), opts.DecoderOptions())
   580  		wg.Done()
   581  	}()
   582  
   583  	wg.Add(1)
   584  	go func() {
   585  		testConsumeAndAckOnConnectionListener(t, lis2, opts.EncoderOptions(), opts.DecoderOptions())
   586  		wg.Done()
   587  	}()
   588  
   589  	wg.Add(1)
   590  	go func() {
   591  		testConsumeAndAckOnConnectionListener(t, lis3, opts.EncoderOptions(), opts.DecoderOptions())
   592  		wg.Done()
   593  	}()
   594  
   595  	wg.Wait()
   596  	w.Close()
   597  }
   598  
   599  func TestWriterCloseBlocking(t *testing.T) {
   600  	defer leaktest.Check(t)()
   601  
   602  	ctrl := xtest.NewController(t)
   603  	defer ctrl.Finish()
   604  
   605  	store := mem.NewStore()
   606  	cs := client.NewMockClient(ctrl)
   607  	cs.EXPECT().Store(gomock.Any()).Return(store, nil)
   608  
   609  	ts, err := topic.NewService(topic.NewServiceOptions().SetConfigService(cs))
   610  	require.NoError(t, err)
   611  
   612  	opts := testOptions().SetTopicService(ts)
   613  
   614  	sid1 := services.NewServiceID().SetName("s1")
   615  	cs1 := topic.NewConsumerService().SetConsumptionType(topic.Shared).SetServiceID(sid1)
   616  	testTopic := topic.NewTopic().
   617  		SetName(opts.TopicName()).
   618  		SetNumberOfShards(1).
   619  		SetConsumerServices([]topic.ConsumerService{cs1})
   620  	_, err = ts.CheckAndSet(testTopic, kv.UninitializedVersion)
   621  	require.NoError(t, err)
   622  
   623  	sd := services.NewMockServices(ctrl)
   624  	opts = opts.SetServiceDiscovery(sd)
   625  	ps1 := testPlacementService(store, sid1)
   626  	sd.EXPECT().PlacementService(sid1, gomock.Any()).Return(ps1, nil)
   627  
   628  	p1 := placement.NewPlacement().
   629  		SetInstances([]placement.Instance{
   630  			placement.NewInstance().
   631  				SetID("i1").
   632  				SetEndpoint("addr1").
   633  				SetShards(shard.NewShards([]shard.Shard{
   634  					shard.NewShard(0).SetState(shard.Available),
   635  				})),
   636  		}).
   637  		SetShards([]uint32{0}).
   638  		SetReplicaFactor(1).
   639  		SetIsSharded(true)
   640  	_, err = ps1.Set(p1)
   641  	require.NoError(t, err)
   642  
   643  	w := NewWriter(opts).(*writer)
   644  	require.NoError(t, w.Init())
   645  	require.Equal(t, 1, len(w.consumerServiceWriters))
   646  
   647  	mm := producer.NewMockMessage(ctrl)
   648  	mm.EXPECT().Size().Return(3)
   649  	mm.EXPECT().Shard().Return(uint32(0)).Times(2)
   650  	mm.EXPECT().Bytes().Return([]byte("foo")).Times(1)
   651  	mm.EXPECT().Finalize(producer.Dropped)
   652  	rm := producer.NewRefCountedMessage(mm, nil)
   653  	require.NoError(t, w.Write(rm))
   654  
   655  	doneCh := make(chan struct{})
   656  	go func() {
   657  		w.Close()
   658  		close(doneCh)
   659  	}()
   660  
   661  	select {
   662  	case <-doneCh:
   663  		require.FailNow(t, "writer.Close() should block until all messages dropped or consumed")
   664  	default:
   665  	}
   666  
   667  	rm.Drop()
   668  	<-doneCh
   669  }
   670  
   671  func TestWriterSetMessageTTLNanosDropMetric(t *testing.T) {
   672  	defer leaktest.Check(t)()
   673  
   674  	ctrl := xtest.NewController(t)
   675  	defer ctrl.Finish()
   676  
   677  	store := mem.NewStore()
   678  	cs := client.NewMockClient(ctrl)
   679  	cs.EXPECT().Store(gomock.Any()).Return(store, nil)
   680  
   681  	ts, err := topic.NewService(topic.NewServiceOptions().SetConfigService(cs))
   682  	require.NoError(t, err)
   683  
   684  	opts := testOptions().SetTopicService(ts)
   685  
   686  	sid1 := services.NewServiceID().SetName("s1")
   687  	cs1 := topic.NewConsumerService().SetConsumptionType(topic.Replicated).SetServiceID(sid1)
   688  	sid2 := services.NewServiceID().SetName("s2")
   689  	cs2 := topic.NewConsumerService().SetConsumptionType(topic.Shared).SetServiceID(sid2)
   690  	testTopic := topic.NewTopic().
   691  		SetName(opts.TopicName()).
   692  		SetNumberOfShards(1).
   693  		SetConsumerServices([]topic.ConsumerService{cs1, cs2})
   694  	_, err = ts.CheckAndSet(testTopic, kv.UninitializedVersion)
   695  	require.NoError(t, err)
   696  
   697  	sd := services.NewMockServices(ctrl)
   698  	opts = opts.SetServiceDiscovery(sd)
   699  	ps1 := testPlacementService(store, sid1)
   700  	sd.EXPECT().PlacementService(sid1, gomock.Any()).Return(ps1, nil)
   701  	ps2 := testPlacementService(store, sid2)
   702  	sd.EXPECT().PlacementService(sid2, gomock.Any()).Return(ps2, nil)
   703  
   704  	lis1, err := net.Listen("tcp", "127.0.0.1:0")
   705  	require.NoError(t, err)
   706  	defer lis1.Close()
   707  
   708  	p1 := placement.NewPlacement().
   709  		SetInstances([]placement.Instance{
   710  			placement.NewInstance().
   711  				SetID("i1").
   712  				SetEndpoint(lis1.Addr().String()).
   713  				SetShards(shard.NewShards([]shard.Shard{
   714  					shard.NewShard(0).SetState(shard.Available),
   715  				})),
   716  		}).
   717  		SetShards([]uint32{0}).
   718  		SetReplicaFactor(1).
   719  		SetIsSharded(true)
   720  	_, err = ps1.Set(p1)
   721  	require.NoError(t, err)
   722  
   723  	p2 := placement.NewPlacement().
   724  		SetInstances([]placement.Instance{
   725  			placement.NewInstance().
   726  				SetID("i2").
   727  				SetEndpoint("i2").
   728  				SetShards(shard.NewShards([]shard.Shard{
   729  					shard.NewShard(0).SetState(shard.Available),
   730  				})),
   731  		}).
   732  		SetShards([]uint32{0}).
   733  		SetReplicaFactor(1).
   734  		SetIsSharded(true)
   735  	_, err = ps2.Set(p2)
   736  	require.NoError(t, err)
   737  
   738  	w := NewWriter(opts).(*writer)
   739  	require.NoError(t, w.Init())
   740  	defer w.Close()
   741  
   742  	require.Equal(t, 2, len(w.consumerServiceWriters))
   743  
   744  	var called int
   745  	var wg sync.WaitGroup
   746  	mm := producer.NewMockMessage(ctrl)
   747  	mm.EXPECT().Shard().Return(uint32(0)).AnyTimes()
   748  	mm.EXPECT().Size().Return(3).AnyTimes()
   749  	mm.EXPECT().Bytes().Return([]byte("foo")).AnyTimes()
   750  	mm.EXPECT().Finalize(producer.Consumed).Do(func(interface{}) { called++; wg.Done() })
   751  	require.NoError(t, w.Write(producer.NewRefCountedMessage(mm, nil)))
   752  
   753  	wg.Add(1)
   754  	go func() {
   755  		testConsumeAndAckOnConnectionListener(t, lis1, opts.EncoderOptions(), opts.DecoderOptions())
   756  		wg.Done()
   757  	}()
   758  	wg.Wait()
   759  	require.Equal(t, 0, called)
   760  
   761  	// Wait for the message ttl update to trigger finalize.
   762  	wg.Add(1)
   763  	testTopic = topic.NewTopic().
   764  		SetName(opts.TopicName()).
   765  		SetNumberOfShards(1).
   766  		SetConsumerServices([]topic.ConsumerService{cs1, cs2.SetMessageTTLNanos(int64(50 * time.Millisecond))})
   767  	_, err = ts.CheckAndSet(testTopic, 1)
   768  	require.NoError(t, err)
   769  	wg.Wait()
   770  	require.Equal(t, 1, called)
   771  
   772  	lis2, err := net.Listen("tcp", "127.0.0.1:0")
   773  	require.NoError(t, err)
   774  	defer lis2.Close()
   775  
   776  	p2 = placement.NewPlacement().
   777  		SetInstances([]placement.Instance{
   778  			placement.NewInstance().
   779  				SetID("i2").
   780  				SetEndpoint(lis2.Addr().String()).
   781  				SetShards(shard.NewShards([]shard.Shard{
   782  					shard.NewShard(0).SetState(shard.Available),
   783  				})),
   784  		}).
   785  		SetShards([]uint32{0}).
   786  		SetReplicaFactor(1).
   787  		SetIsSharded(true)
   788  	_, err = ps2.Set(p2)
   789  	require.NoError(t, err)
   790  
   791  	testTopic = topic.NewTopic().
   792  		SetName(opts.TopicName()).
   793  		SetNumberOfShards(1).
   794  		SetConsumerServices([]topic.ConsumerService{cs1, cs2.SetMessageTTLNanos(0)})
   795  	_, err = ts.CheckAndSet(testTopic, 2)
   796  	require.NoError(t, err)
   797  
   798  	require.NoError(t, w.Write(producer.NewRefCountedMessage(mm, nil)))
   799  
   800  	called = 0
   801  	mm.EXPECT().Finalize(producer.Consumed).Do(func(interface{}) { called++; wg.Done() })
   802  	wg.Add(1)
   803  	go func() {
   804  		testConsumeAndAckOnConnectionListener(t, lis1, opts.EncoderOptions(), opts.DecoderOptions())
   805  		wg.Done()
   806  	}()
   807  	wg.Wait()
   808  	require.Equal(t, 0, called)
   809  
   810  	time.Sleep(200 * time.Millisecond)
   811  	require.Equal(t, 0, called)
   812  
   813  	// Wait for the consumer to trigger finalize because there is no more message ttl.
   814  	wg.Add(1)
   815  	go func() {
   816  		testConsumeAndAckOnConnectionListener(t, lis2, opts.EncoderOptions(), opts.DecoderOptions())
   817  	}()
   818  	wg.Wait()
   819  	require.Equal(t, 1, called)
   820  
   821  	w.Close()
   822  }
   823  
   824  func TestWriterNumShards(t *testing.T) {
   825  	defer leaktest.Check(t)()
   826  
   827  	ctrl := xtest.NewController(t)
   828  	defer ctrl.Finish()
   829  
   830  	store := mem.NewStore()
   831  	cs := client.NewMockClient(ctrl)
   832  	cs.EXPECT().Store(gomock.Any()).Return(store, nil)
   833  
   834  	ts, err := topic.NewService(topic.NewServiceOptions().SetConfigService(cs))
   835  	require.NoError(t, err)
   836  
   837  	opts := testOptions().SetTopicService(ts)
   838  
   839  	testTopic := topic.NewTopic().
   840  		SetName(opts.TopicName()).
   841  		SetNumberOfShards(2)
   842  	_, err = ts.CheckAndSet(testTopic, kv.UninitializedVersion)
   843  	require.NoError(t, err)
   844  
   845  	w := NewWriter(opts).(*writer)
   846  	defer w.Close()
   847  
   848  	require.Equal(t, 0, int(w.NumShards()))
   849  
   850  	require.NoError(t, w.Init())
   851  	require.Equal(t, 2, int(w.NumShards()))
   852  }