go.temporal.io/server@v1.23.0/common/namespace/registry_test.go (about)

     1  // The MIT License
     2  //
     3  // Copyright (c) 2020 Temporal Technologies Inc.  All rights reserved.
     4  //
     5  // Copyright (c) 2020 Uber Technologies, Inc.
     6  //
     7  // Permission is hereby granted, free of charge, to any person obtaining a copy
     8  // of this software and associated documentation files (the "Software"), to deal
     9  // in the Software without restriction, including without limitation the rights
    10  // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
    11  // copies of the Software, and to permit persons to whom the Software is
    12  // furnished to do so, subject to the following conditions:
    13  //
    14  // The above copyright notice and this permission notice shall be included in
    15  // all copies or substantial portions of the Software.
    16  //
    17  // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    18  // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    19  // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    20  // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    21  // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    22  // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
    23  // THE SOFTWARE.
    24  
    25  package namespace_test
    26  
    27  import (
    28  	"sync"
    29  	"testing"
    30  	"time"
    31  
    32  	"github.com/golang/mock/gomock"
    33  	"github.com/stretchr/testify/require"
    34  	"github.com/stretchr/testify/suite"
    35  	enumspb "go.temporal.io/api/enums/v1"
    36  	namespacepb "go.temporal.io/api/namespace/v1"
    37  	"go.temporal.io/api/serviceerror"
    38  
    39  	persistencespb "go.temporal.io/server/api/persistence/v1"
    40  	"go.temporal.io/server/common/cluster"
    41  	"go.temporal.io/server/common/dynamicconfig"
    42  	"go.temporal.io/server/common/log"
    43  	"go.temporal.io/server/common/metrics"
    44  	"go.temporal.io/server/common/namespace"
    45  	"go.temporal.io/server/common/persistence"
    46  	"go.temporal.io/server/common/primitives/timestamp"
    47  )
    48  
    49  type (
    50  	registrySuite struct {
    51  		suite.Suite
    52  		*require.Assertions
    53  
    54  		controller     *gomock.Controller
    55  		regPersistence *namespace.MockPersistence
    56  		registry       namespace.Registry
    57  	}
    58  )
    59  
    60  func TestRegistrySuite(t *testing.T) {
    61  	s := new(registrySuite)
    62  	suite.Run(t, s)
    63  }
    64  
    65  func (s *registrySuite) SetupSuite() {}
    66  
    67  func (s *registrySuite) TearDownSuite() {}
    68  
    69  func (s *registrySuite) SetupTest() {
    70  	s.Assertions = require.New(s.T())
    71  	s.controller = gomock.NewController(s.T())
    72  	s.regPersistence = namespace.NewMockPersistence(s.controller)
    73  	s.registry = namespace.NewRegistry(
    74  		s.regPersistence,
    75  		true,
    76  		dynamicconfig.GetDurationPropertyFn(time.Second),
    77  		dynamicconfig.GetBoolPropertyFn(false),
    78  		metrics.NoopMetricsHandler,
    79  		log.NewTestLogger())
    80  }
    81  
    82  func (s *registrySuite) TearDownTest() {
    83  	s.registry.Stop()
    84  	s.controller.Finish()
    85  }
    86  
    87  func (s *registrySuite) TestListNamespace() {
    88  	namespaceNotificationVersion := int64(0)
    89  	namespaceRecord1 := &persistence.GetNamespaceResponse{
    90  		Namespace: &persistencespb.NamespaceDetail{
    91  			Info: &persistencespb.NamespaceInfo{
    92  				Id:    namespace.NewID().String(),
    93  				Name:  "some random namespace name",
    94  				State: enumspb.NAMESPACE_STATE_REGISTERED,
    95  				Data:  make(map[string]string)},
    96  			Config: &persistencespb.NamespaceConfig{
    97  				Retention: timestamp.DurationFromDays(1),
    98  				BadBinaries: &namespacepb.BadBinaries{
    99  					Binaries: map[string]*namespacepb.BadBinaryInfo{},
   100  				}},
   101  			ReplicationConfig: &persistencespb.NamespaceReplicationConfig{
   102  				ActiveClusterName: cluster.TestCurrentClusterName,
   103  				Clusters: []string{
   104  					cluster.TestCurrentClusterName,
   105  					cluster.TestAlternativeClusterName,
   106  				},
   107  			},
   108  			FailoverNotificationVersion: 0,
   109  		},
   110  		NotificationVersion: namespaceNotificationVersion,
   111  	}
   112  	entry1 := namespace.FromPersistentState(namespaceRecord1)
   113  	namespaceNotificationVersion++
   114  
   115  	namespaceRecord2 := &persistence.GetNamespaceResponse{
   116  		Namespace: &persistencespb.NamespaceDetail{
   117  			Info: &persistencespb.NamespaceInfo{
   118  				Id:    namespace.NewID().String(),
   119  				Name:  "another random namespace name",
   120  				State: enumspb.NAMESPACE_STATE_DELETED, // Still must be included.
   121  				Data:  make(map[string]string)},
   122  			Config: &persistencespb.NamespaceConfig{
   123  				Retention: timestamp.DurationFromDays(2),
   124  				BadBinaries: &namespacepb.BadBinaries{
   125  					Binaries: map[string]*namespacepb.BadBinaryInfo{},
   126  				}},
   127  			ReplicationConfig: &persistencespb.NamespaceReplicationConfig{
   128  				ActiveClusterName: cluster.TestAlternativeClusterName,
   129  				Clusters: []string{
   130  					cluster.TestCurrentClusterName,
   131  					cluster.TestAlternativeClusterName,
   132  				},
   133  			},
   134  			FailoverNotificationVersion: 0,
   135  		},
   136  		NotificationVersion: namespaceNotificationVersion,
   137  	}
   138  	entry2 := namespace.FromPersistentState(namespaceRecord2)
   139  	namespaceNotificationVersion++
   140  
   141  	namespaceRecord3 := &persistence.GetNamespaceResponse{
   142  		Namespace: &persistencespb.NamespaceDetail{
   143  			Info: &persistencespb.NamespaceInfo{
   144  				Id:    namespace.NewID().String(),
   145  				Name:  "yet another random namespace name",
   146  				State: enumspb.NAMESPACE_STATE_DEPRECATED, // Still must be included.
   147  				Data:  make(map[string]string)},
   148  			Config: &persistencespb.NamespaceConfig{
   149  				Retention: timestamp.DurationFromDays(3),
   150  				BadBinaries: &namespacepb.BadBinaries{
   151  					Binaries: map[string]*namespacepb.BadBinaryInfo{},
   152  				}},
   153  			ReplicationConfig: &persistencespb.NamespaceReplicationConfig{
   154  				ActiveClusterName: cluster.TestAlternativeClusterName,
   155  				Clusters: []string{
   156  					cluster.TestCurrentClusterName,
   157  					cluster.TestAlternativeClusterName,
   158  				},
   159  			},
   160  			FailoverNotificationVersion: 0,
   161  		},
   162  		NotificationVersion: namespaceNotificationVersion,
   163  	}
   164  	// there is no namespaceNotificationVersion++ here
   165  	// this is to test that if new namespace change event happen during the pagination,
   166  	// new change will not be loaded to namespace cache
   167  
   168  	pageToken := []byte("some random page token")
   169  
   170  	s.regPersistence.EXPECT().ListNamespaces(gomock.Any(), &persistence.ListNamespacesRequest{
   171  		PageSize:       namespace.CacheRefreshPageSize,
   172  		IncludeDeleted: true,
   173  		NextPageToken:  nil,
   174  	}).Return(&persistence.ListNamespacesResponse{
   175  		Namespaces:    []*persistence.GetNamespaceResponse{namespaceRecord1},
   176  		NextPageToken: pageToken,
   177  	}, nil)
   178  
   179  	s.regPersistence.EXPECT().ListNamespaces(gomock.Any(), &persistence.ListNamespacesRequest{
   180  		PageSize:       namespace.CacheRefreshPageSize,
   181  		IncludeDeleted: true,
   182  		NextPageToken:  pageToken,
   183  	}).Return(&persistence.ListNamespacesResponse{
   184  		Namespaces: []*persistence.GetNamespaceResponse{
   185  			namespaceRecord2,
   186  			namespaceRecord3},
   187  		NextPageToken: nil,
   188  	}, nil)
   189  
   190  	// load namespaces
   191  	s.registry.Start()
   192  	defer s.registry.Stop()
   193  
   194  	entryByName1, err := s.registry.GetNamespace(namespace.Name(namespaceRecord1.Namespace.Info.Name))
   195  	s.Nil(err)
   196  	s.Equal(entry1, entryByName1)
   197  	entryByID1, err := s.registry.GetNamespaceByID(namespace.ID(namespaceRecord1.Namespace.Info.Id))
   198  	s.Nil(err)
   199  	s.Equal(entry1, entryByID1)
   200  
   201  	entryByName2, err := s.registry.GetNamespace(namespace.Name(namespaceRecord2.Namespace.Info.Name))
   202  	s.Nil(err)
   203  	s.Equal(entry2, entryByName2)
   204  	entryByID2, err := s.registry.GetNamespaceByID(namespace.ID(namespaceRecord2.Namespace.Info.Id))
   205  	s.Nil(err)
   206  	s.Equal(entry2, entryByID2)
   207  }
   208  
   209  func (s *registrySuite) TestRegisterStateChangeCallback_CatchUp() {
   210  	namespaceNotificationVersion := int64(0)
   211  	namespaceRecord1 := &persistence.GetNamespaceResponse{
   212  		Namespace: &persistencespb.NamespaceDetail{
   213  			Info: &persistencespb.NamespaceInfo{
   214  				Id:   namespace.NewID().String(),
   215  				Name: "some random namespace name",
   216  				Data: make(map[string]string)},
   217  			Config: &persistencespb.NamespaceConfig{
   218  				Retention: timestamp.DurationFromDays(1),
   219  				BadBinaries: &namespacepb.BadBinaries{
   220  					Binaries: map[string]*namespacepb.BadBinaryInfo{},
   221  				}},
   222  			ReplicationConfig: &persistencespb.NamespaceReplicationConfig{
   223  				ActiveClusterName: cluster.TestCurrentClusterName,
   224  				Clusters: []string{
   225  					cluster.TestCurrentClusterName,
   226  					cluster.TestAlternativeClusterName,
   227  				},
   228  			},
   229  			ConfigVersion:               10,
   230  			FailoverVersion:             11,
   231  			FailoverNotificationVersion: 0,
   232  		},
   233  		NotificationVersion: namespaceNotificationVersion,
   234  	}
   235  	entry1 := namespace.FromPersistentState(namespaceRecord1)
   236  	namespaceNotificationVersion++
   237  
   238  	namespaceRecord2 := &persistence.GetNamespaceResponse{
   239  		Namespace: &persistencespb.NamespaceDetail{
   240  			Info: &persistencespb.NamespaceInfo{
   241  				Id:   namespace.NewID().String(),
   242  				Name: "another random namespace name",
   243  				Data: make(map[string]string)},
   244  			Config: &persistencespb.NamespaceConfig{
   245  				Retention: timestamp.DurationFromDays(2),
   246  				BadBinaries: &namespacepb.BadBinaries{
   247  					Binaries: map[string]*namespacepb.BadBinaryInfo{},
   248  				}},
   249  			ReplicationConfig: &persistencespb.NamespaceReplicationConfig{
   250  				ActiveClusterName: cluster.TestAlternativeClusterName,
   251  				Clusters: []string{
   252  					cluster.TestCurrentClusterName,
   253  					cluster.TestAlternativeClusterName,
   254  				},
   255  			},
   256  			ConfigVersion:               20,
   257  			FailoverVersion:             21,
   258  			FailoverNotificationVersion: 0,
   259  		},
   260  		NotificationVersion: namespaceNotificationVersion,
   261  	}
   262  	entry2 := namespace.FromPersistentState(namespaceRecord2)
   263  	namespaceNotificationVersion++
   264  
   265  	s.regPersistence.EXPECT().ListNamespaces(gomock.Any(), &persistence.ListNamespacesRequest{
   266  		PageSize:       namespace.CacheRefreshPageSize,
   267  		IncludeDeleted: true,
   268  		NextPageToken:  nil,
   269  	}).Return(&persistence.ListNamespacesResponse{
   270  		Namespaces: []*persistence.GetNamespaceResponse{
   271  			namespaceRecord1,
   272  			namespaceRecord2},
   273  		NextPageToken: nil,
   274  	}, nil)
   275  
   276  	// load namespaces
   277  	s.registry.Start()
   278  	defer s.registry.Stop()
   279  
   280  	var entriesNotification []*namespace.Namespace
   281  	s.registry.RegisterStateChangeCallback(
   282  		"0",
   283  		func(ns *namespace.Namespace, deletedFromDb bool) {
   284  			s.False(deletedFromDb)
   285  			entriesNotification = append(entriesNotification, ns)
   286  		},
   287  	)
   288  
   289  	s.Len(entriesNotification, 2)
   290  	if entriesNotification[0].NotificationVersion() > entriesNotification[1].NotificationVersion() {
   291  		entriesNotification[0], entriesNotification[1] = entriesNotification[1], entriesNotification[0]
   292  	}
   293  	s.Equal([]*namespace.Namespace{entry1, entry2}, entriesNotification)
   294  }
   295  
   296  func (s *registrySuite) TestUpdateCache_TriggerCallBack() {
   297  	namespaceNotificationVersion := int64(0)
   298  	namespaceRecord1Old := &persistence.GetNamespaceResponse{
   299  		Namespace: &persistencespb.NamespaceDetail{
   300  			Info: &persistencespb.NamespaceInfo{
   301  				Id:   namespace.NewID().String(),
   302  				Name: "some random namespace name",
   303  				Data: make(map[string]string)},
   304  			Config: &persistencespb.NamespaceConfig{
   305  				Retention: timestamp.DurationFromDays(1),
   306  				BadBinaries: &namespacepb.BadBinaries{
   307  					Binaries: map[string]*namespacepb.BadBinaryInfo{},
   308  				}},
   309  			ReplicationConfig: &persistencespb.NamespaceReplicationConfig{
   310  				ActiveClusterName: cluster.TestCurrentClusterName,
   311  				Clusters: []string{
   312  					cluster.TestCurrentClusterName,
   313  					cluster.TestAlternativeClusterName,
   314  				},
   315  			},
   316  			ConfigVersion:               10,
   317  			FailoverVersion:             11,
   318  			FailoverNotificationVersion: 0,
   319  		},
   320  		NotificationVersion: namespaceNotificationVersion,
   321  	}
   322  	entry1Old := namespace.FromPersistentState(namespaceRecord1Old)
   323  	namespaceNotificationVersion++
   324  
   325  	namespaceRecord2Old := &persistence.GetNamespaceResponse{
   326  		Namespace: &persistencespb.NamespaceDetail{
   327  			Info: &persistencespb.NamespaceInfo{
   328  				Id:   namespace.NewID().String(),
   329  				Name: "another random namespace name",
   330  				Data: make(map[string]string)},
   331  			Config: &persistencespb.NamespaceConfig{
   332  				Retention: timestamp.DurationFromDays(2),
   333  				BadBinaries: &namespacepb.BadBinaries{
   334  					Binaries: map[string]*namespacepb.BadBinaryInfo{},
   335  				}},
   336  			ReplicationConfig: &persistencespb.NamespaceReplicationConfig{
   337  				ActiveClusterName: cluster.TestAlternativeClusterName,
   338  				Clusters: []string{
   339  					cluster.TestCurrentClusterName,
   340  					cluster.TestAlternativeClusterName,
   341  				},
   342  			},
   343  			ConfigVersion:               20,
   344  			FailoverVersion:             21,
   345  			FailoverNotificationVersion: 0,
   346  		},
   347  		NotificationVersion: namespaceNotificationVersion,
   348  	}
   349  	entry2Old := namespace.FromPersistentState(namespaceRecord2Old)
   350  	namespaceNotificationVersion++
   351  
   352  	s.regPersistence.EXPECT().ListNamespaces(gomock.Any(), &persistence.ListNamespacesRequest{
   353  		PageSize:       namespace.CacheRefreshPageSize,
   354  		IncludeDeleted: true,
   355  		NextPageToken:  nil,
   356  	}).Return(&persistence.ListNamespacesResponse{
   357  		Namespaces:    []*persistence.GetNamespaceResponse{namespaceRecord1Old, namespaceRecord2Old},
   358  		NextPageToken: nil,
   359  	}, nil)
   360  
   361  	namespaceRecord2New := &persistence.GetNamespaceResponse{
   362  		Namespace: &persistencespb.NamespaceDetail{
   363  			Info:   namespaceRecord2Old.Namespace.Info,
   364  			Config: namespaceRecord2Old.Namespace.Config,
   365  			ReplicationConfig: &persistencespb.NamespaceReplicationConfig{
   366  				ActiveClusterName: cluster.TestCurrentClusterName, // only this changed
   367  				Clusters: []string{
   368  					cluster.TestCurrentClusterName,
   369  					cluster.TestAlternativeClusterName,
   370  				},
   371  			},
   372  			ConfigVersion:               namespaceRecord2Old.Namespace.ConfigVersion,
   373  			FailoverVersion:             namespaceRecord2Old.Namespace.FailoverVersion + 1,
   374  			FailoverNotificationVersion: namespaceNotificationVersion,
   375  		},
   376  		NotificationVersion: namespaceNotificationVersion,
   377  	}
   378  	entry2New := namespace.FromPersistentState(namespaceRecord2New)
   379  	namespaceNotificationVersion++
   380  
   381  	namespaceRecord1New := &persistence.GetNamespaceResponse{ // only the description changed
   382  		Namespace: &persistencespb.NamespaceDetail{
   383  			Info: &persistencespb.NamespaceInfo{
   384  				Id:          namespaceRecord1Old.Namespace.Info.Id,
   385  				Name:        namespaceRecord1Old.Namespace.Info.Name,
   386  				Description: "updated description", Data: make(map[string]string)},
   387  			Config: namespaceRecord2Old.Namespace.Config,
   388  			ReplicationConfig: &persistencespb.NamespaceReplicationConfig{
   389  				ActiveClusterName: cluster.TestCurrentClusterName,
   390  				Clusters: []string{
   391  					cluster.TestCurrentClusterName,
   392  					cluster.TestAlternativeClusterName,
   393  				},
   394  			},
   395  			ConfigVersion:               namespaceRecord1Old.Namespace.ConfigVersion + 1,
   396  			FailoverVersion:             namespaceRecord1Old.Namespace.FailoverVersion,
   397  			FailoverNotificationVersion: namespaceRecord1Old.Namespace.FailoverNotificationVersion,
   398  		},
   399  		NotificationVersion: namespaceNotificationVersion,
   400  	}
   401  	namespaceNotificationVersion++
   402  
   403  	s.regPersistence.EXPECT().ListNamespaces(gomock.Any(), &persistence.ListNamespacesRequest{
   404  		PageSize:       namespace.CacheRefreshPageSize,
   405  		IncludeDeleted: true,
   406  		NextPageToken:  nil,
   407  	}).Return(&persistence.ListNamespacesResponse{
   408  		Namespaces: []*persistence.GetNamespaceResponse{
   409  			namespaceRecord1New,
   410  			namespaceRecord2New},
   411  		NextPageToken: nil,
   412  	}, nil)
   413  
   414  	// load namespaces
   415  	s.registry.Start()
   416  	defer s.registry.Stop()
   417  
   418  	var entries []*namespace.Namespace
   419  
   420  	wg := &sync.WaitGroup{}
   421  	wg.Add(2)
   422  	s.registry.RegisterStateChangeCallback(
   423  		"0",
   424  		func(ns *namespace.Namespace, deletedFromDb bool) {
   425  			defer wg.Done()
   426  			s.False(deletedFromDb)
   427  			entries = append(entries, ns)
   428  		},
   429  	)
   430  	wg.Wait()
   431  
   432  	s.Len(entries, 2)
   433  	if entries[0].NotificationVersion() > entries[1].NotificationVersion() {
   434  		entries[0], entries[1] = entries[1], entries[0]
   435  	}
   436  	s.Equal([]*namespace.Namespace{entry1Old, entry2Old}, entries)
   437  
   438  	wg.Add(1)
   439  	wg.Wait()
   440  
   441  	newEntries := entries[2:]
   442  
   443  	// entry1 only has descrption update, so won't trigger the state change callback
   444  	s.Len(newEntries, 1)
   445  	s.Equal([]*namespace.Namespace{entry2New}, newEntries)
   446  }
   447  
   448  func (s *registrySuite) TestGetTriggerListAndUpdateCache_ConcurrentAccess() {
   449  	id := namespace.NewID()
   450  	namespaceRecordOld := &persistence.GetNamespaceResponse{
   451  		Namespace: &persistencespb.NamespaceDetail{
   452  			Info: &persistencespb.NamespaceInfo{Id: id.String(), Name: "some random namespace name", Data: make(map[string]string)},
   453  			Config: &persistencespb.NamespaceConfig{
   454  				Retention: timestamp.DurationFromDays(1),
   455  				BadBinaries: &namespacepb.BadBinaries{
   456  					Binaries: map[string]*namespacepb.BadBinaryInfo{},
   457  				}},
   458  			ReplicationConfig: &persistencespb.NamespaceReplicationConfig{
   459  				ActiveClusterName: cluster.TestCurrentClusterName,
   460  				Clusters: []string{
   461  					cluster.TestCurrentClusterName,
   462  					cluster.TestAlternativeClusterName,
   463  				},
   464  			},
   465  			ConfigVersion:   0,
   466  			FailoverVersion: 0,
   467  		},
   468  	}
   469  	entryOld := namespace.FromPersistentState(namespaceRecordOld)
   470  
   471  	s.regPersistence.EXPECT().ListNamespaces(gomock.Any(), &persistence.ListNamespacesRequest{
   472  		PageSize:       namespace.CacheRefreshPageSize,
   473  		IncludeDeleted: true,
   474  		NextPageToken:  nil,
   475  	}).Return(&persistence.ListNamespacesResponse{
   476  		Namespaces:    []*persistence.GetNamespaceResponse{namespaceRecordOld},
   477  		NextPageToken: nil,
   478  	}, nil)
   479  
   480  	// load namespaces
   481  	s.registry.Start()
   482  	defer s.registry.Stop()
   483  
   484  	coroutineCountGet := 1000
   485  	waitGroup := &sync.WaitGroup{}
   486  	startChan := make(chan struct{})
   487  	testGetFn := func() {
   488  		<-startChan
   489  		entryNew, err := s.registry.GetNamespaceByID(id)
   490  		switch err.(type) {
   491  		case nil:
   492  			s.Equal(entryOld, entryNew)
   493  			waitGroup.Done()
   494  		case *serviceerror.NamespaceNotFound:
   495  			time.Sleep(4 * time.Second)
   496  			entryNew, err := s.registry.GetNamespaceByID(id)
   497  			s.NoError(err)
   498  			s.Equal(entryOld, entryNew)
   499  			waitGroup.Done()
   500  		default:
   501  			s.NoError(err)
   502  			waitGroup.Done()
   503  		}
   504  	}
   505  
   506  	for i := 0; i < coroutineCountGet; i++ {
   507  		waitGroup.Add(1)
   508  		go testGetFn()
   509  	}
   510  	close(startChan)
   511  	waitGroup.Wait()
   512  }
   513  
   514  func (s *registrySuite) TestRemoveDeletedNamespace() {
   515  	namespaceNotificationVersion := int64(0)
   516  	namespaceRecord1 := &persistence.GetNamespaceResponse{
   517  		Namespace: &persistencespb.NamespaceDetail{
   518  			Info: &persistencespb.NamespaceInfo{
   519  				Id:   namespace.NewID().String(),
   520  				Name: "some random namespace name",
   521  				Data: make(map[string]string)},
   522  			Config: &persistencespb.NamespaceConfig{
   523  				Retention: timestamp.DurationFromDays(1),
   524  				BadBinaries: &namespacepb.BadBinaries{
   525  					Binaries: map[string]*namespacepb.BadBinaryInfo{},
   526  				}},
   527  			ReplicationConfig: &persistencespb.NamespaceReplicationConfig{
   528  				ActiveClusterName: cluster.TestCurrentClusterName,
   529  				Clusters: []string{
   530  					cluster.TestCurrentClusterName,
   531  					cluster.TestAlternativeClusterName,
   532  				},
   533  			},
   534  			ConfigVersion:               10,
   535  			FailoverVersion:             11,
   536  			FailoverNotificationVersion: 0,
   537  		},
   538  		NotificationVersion: namespaceNotificationVersion,
   539  	}
   540  	namespaceNotificationVersion++
   541  
   542  	namespaceRecord2 := &persistence.GetNamespaceResponse{
   543  		Namespace: &persistencespb.NamespaceDetail{
   544  			Info: &persistencespb.NamespaceInfo{
   545  				Id:   namespace.NewID().String(),
   546  				Name: "another random namespace name",
   547  				Data: make(map[string]string)},
   548  			Config: &persistencespb.NamespaceConfig{
   549  				Retention: timestamp.DurationFromDays(2),
   550  				BadBinaries: &namespacepb.BadBinaries{
   551  					Binaries: map[string]*namespacepb.BadBinaryInfo{},
   552  				}},
   553  			ReplicationConfig: &persistencespb.NamespaceReplicationConfig{
   554  				ActiveClusterName: cluster.TestAlternativeClusterName,
   555  				Clusters: []string{
   556  					cluster.TestCurrentClusterName,
   557  					cluster.TestAlternativeClusterName,
   558  				},
   559  			},
   560  			ConfigVersion:               20,
   561  			FailoverVersion:             21,
   562  			FailoverNotificationVersion: 0,
   563  		},
   564  		NotificationVersion: namespaceNotificationVersion,
   565  	}
   566  	namespaceNotificationVersion++
   567  
   568  	s.regPersistence.EXPECT().ListNamespaces(gomock.Any(), &persistence.ListNamespacesRequest{
   569  		PageSize:       namespace.CacheRefreshPageSize,
   570  		IncludeDeleted: true,
   571  		NextPageToken:  nil,
   572  	}).Return(&persistence.ListNamespacesResponse{
   573  		Namespaces: []*persistence.GetNamespaceResponse{
   574  			namespaceRecord1,
   575  			namespaceRecord2},
   576  		NextPageToken: nil,
   577  	}, nil)
   578  
   579  	s.regPersistence.EXPECT().ListNamespaces(gomock.Any(), &persistence.ListNamespacesRequest{
   580  		PageSize:       namespace.CacheRefreshPageSize,
   581  		IncludeDeleted: true,
   582  		NextPageToken:  nil,
   583  	}).Return(&persistence.ListNamespacesResponse{
   584  		Namespaces: []*persistence.GetNamespaceResponse{
   585  			// namespaceRecord1 is removed
   586  			namespaceRecord2},
   587  		NextPageToken: nil,
   588  	}, nil)
   589  
   590  	// load namespaces
   591  	s.registry.Start()
   592  	defer s.registry.Stop()
   593  
   594  	// use WaitGroup and callback to wait until refresh loop picks up delete
   595  	wg := &sync.WaitGroup{}
   596  	wg.Add(1)
   597  	s.registry.RegisterStateChangeCallback(
   598  		"1",
   599  		func(ns *namespace.Namespace, deletedFromDb bool) {
   600  			if deletedFromDb {
   601  				wg.Done()
   602  			}
   603  		},
   604  	)
   605  	wg.Wait()
   606  
   607  	ns2FromRegistry, err := s.registry.GetNamespace(namespace.Name(namespaceRecord2.Namespace.Info.Name))
   608  	s.NotNil(ns2FromRegistry)
   609  	s.NoError(err)
   610  
   611  	// expect readthrough call for missing ns
   612  	s.regPersistence.EXPECT().GetNamespace(gomock.Any(), &persistence.GetNamespaceRequest{
   613  		Name: namespaceRecord1.Namespace.Info.Name,
   614  	}).Return(nil, serviceerror.NewNamespaceNotFound(namespaceRecord1.Namespace.Info.Name))
   615  
   616  	ns1FromRegistry, err := s.registry.GetNamespace(namespace.Name(namespaceRecord1.Namespace.Info.Name))
   617  	s.Nil(ns1FromRegistry)
   618  	s.Error(err)
   619  	var notFound *serviceerror.NamespaceNotFound
   620  	s.ErrorAs(err, &notFound)
   621  }
   622  
   623  func (s *registrySuite) TestCacheByName() {
   624  	nsrec := persistence.GetNamespaceResponse{
   625  		Namespace: &persistencespb.NamespaceDetail{
   626  			Info: &persistencespb.NamespaceInfo{
   627  				Id:   namespace.NewID().String(),
   628  				Name: "foo",
   629  			},
   630  			Config:            &persistencespb.NamespaceConfig{},
   631  			ReplicationConfig: &persistencespb.NamespaceReplicationConfig{},
   632  		},
   633  	}
   634  
   635  	s.regPersistence.EXPECT().ListNamespaces(gomock.Any(), gomock.Any()).Return(&persistence.ListNamespacesResponse{
   636  		Namespaces: []*persistence.GetNamespaceResponse{&nsrec},
   637  	}, nil)
   638  
   639  	s.registry.Start()
   640  	defer s.registry.Stop()
   641  	ns, err := s.registry.GetNamespace(namespace.Name("foo"))
   642  	s.NoError(err)
   643  	s.Equal(namespace.Name("foo"), ns.Name())
   644  }