go.temporal.io/server@v1.23.0/common/namespace/registry.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  //go:generate mockgen -copyright_file ../../LICENSE -package $GOPACKAGE -source $GOFILE -destination registry_mock.go
    26  
    27  package namespace
    28  
    29  import (
    30  	"context"
    31  	"sync"
    32  	"sync/atomic"
    33  	"time"
    34  
    35  	"golang.org/x/exp/maps"
    36  
    37  	"go.temporal.io/api/serviceerror"
    38  	"go.temporal.io/server/common"
    39  	"go.temporal.io/server/common/cache"
    40  	"go.temporal.io/server/common/clock"
    41  	"go.temporal.io/server/common/dynamicconfig"
    42  	"go.temporal.io/server/common/headers"
    43  	"go.temporal.io/server/common/log"
    44  	"go.temporal.io/server/common/log/tag"
    45  	"go.temporal.io/server/common/metrics"
    46  	"go.temporal.io/server/common/persistence"
    47  	"go.temporal.io/server/internal/goro"
    48  )
    49  
    50  // ReplicationPolicy is the namespace's replication policy,
    51  // derived from namespace's replication config
    52  type ReplicationPolicy int
    53  
    54  const (
    55  	// ReplicationPolicyOneCluster indicate that workflows does not need to be replicated
    56  	// applicable to local namespace & global namespace with one cluster
    57  	ReplicationPolicyOneCluster ReplicationPolicy = 0
    58  	// ReplicationPolicyMultiCluster indicate that workflows need to be replicated
    59  	ReplicationPolicyMultiCluster ReplicationPolicy = 1
    60  )
    61  
    62  const (
    63  	cacheMaxSize = 64 * 1024
    64  	cacheTTL     = 0 // 0 means infinity
    65  	// CacheRefreshFailureRetryInterval is the wait time
    66  	// if refreshment encounters error
    67  	CacheRefreshFailureRetryInterval = 1 * time.Second
    68  	CacheRefreshPageSize             = 1000
    69  	readthroughCacheTTL              = 1 * time.Second // represents minimum time to wait before trying to readthrough again
    70  	readthroughTimeout               = 3 * time.Second
    71  )
    72  
    73  const (
    74  	stopped int32 = iota
    75  	starting
    76  	running
    77  	stopping
    78  )
    79  
    80  var (
    81  	cacheOpts = cache.Options{
    82  		TTL: cacheTTL,
    83  	}
    84  	readthroughNotFoundCacheOpts = cache.Options{
    85  		TTL: readthroughCacheTTL,
    86  	}
    87  )
    88  
    89  type (
    90  	// Clock provides timestamping to Registry objects
    91  	Clock interface {
    92  		// Now returns the current time.
    93  		Now() time.Time
    94  	}
    95  
    96  	// Persistence describes the durable storage requirements for a Registry
    97  	// instance.
    98  	Persistence interface {
    99  
   100  		// GetNamespace reads the state for a single namespace by name or ID
   101  		// from persistent storage, returning an instance of
   102  		// serviceerror.NamespaceNotFound if there is no matching Namespace.
   103  		GetNamespace(
   104  			context.Context,
   105  			*persistence.GetNamespaceRequest,
   106  		) (*persistence.GetNamespaceResponse, error)
   107  
   108  		// ListNamespaces fetches a paged set of namespace persistent state
   109  		// instances.
   110  		ListNamespaces(
   111  			context.Context,
   112  			*persistence.ListNamespacesRequest,
   113  		) (*persistence.ListNamespacesResponse, error)
   114  
   115  		// GetMetadata fetches the notification version for Temporal namespaces.
   116  		GetMetadata(context.Context) (*persistence.GetMetadataResponse, error)
   117  	}
   118  
   119  	// PrepareCallbackFn is function to be called before CallbackFn is called,
   120  	// it is guaranteed that PrepareCallbackFn and CallbackFn pair will be both called or non will be called
   121  	PrepareCallbackFn func()
   122  
   123  	// CallbackFn is function to be called when the namespace cache entries are changed
   124  	// it is guaranteed that PrepareCallbackFn and CallbackFn pair will be both called or non will be called
   125  	CallbackFn func(oldNamespaces []*Namespace, newNamespaces []*Namespace)
   126  
   127  	// StateChangeCallbackFn can be registered to be called on any namespace state change or
   128  	// addition/removal from database, plus once for all namespaces after registration. There
   129  	// is no guarantee about when these are called.
   130  	StateChangeCallbackFn func(ns *Namespace, deletedFromDb bool)
   131  
   132  	// Registry provides access to Namespace objects by name or by ID.
   133  	Registry interface {
   134  		common.Pingable
   135  		GetNamespace(name Name) (*Namespace, error)
   136  		GetNamespaceByID(id ID) (*Namespace, error)
   137  		GetNamespaceID(name Name) (ID, error)
   138  		GetNamespaceName(id ID) (Name, error)
   139  		GetCacheSize() (sizeOfCacheByName int64, sizeOfCacheByID int64)
   140  		// Registers callback for namespace state changes.
   141  		// StateChangeCallbackFn will be invoked for a new/deleted namespace or namespace that has
   142  		// State, ReplicationState, ActiveCluster, or isGlobalNamespace config changed.
   143  		RegisterStateChangeCallback(key any, cb StateChangeCallbackFn)
   144  		UnregisterStateChangeCallback(key any)
   145  		// GetCustomSearchAttributesMapper is a temporary solution to be able to get search attributes
   146  		// with from persistence if forceSearchAttributesCacheRefreshOnRead is true.
   147  		GetCustomSearchAttributesMapper(name Name) (CustomSearchAttributesMapper, error)
   148  		Start()
   149  		Stop()
   150  	}
   151  
   152  	registry struct {
   153  		status                  int32
   154  		refresher               *goro.Handle
   155  		triggerRefreshCh        chan chan struct{}
   156  		persistence             Persistence
   157  		globalNamespacesEnabled bool
   158  		clock                   Clock
   159  		metricsHandler          metrics.Handler
   160  		logger                  log.Logger
   161  		refreshInterval         dynamicconfig.DurationPropertyFn
   162  
   163  		// cacheLock protects cachNameToID, cacheByID and stateChangeCallbacks.
   164  		cacheLock                     sync.RWMutex
   165  		cacheNameToID                 cache.Cache
   166  		cacheByID                     cache.Cache
   167  		stateChangeCallbacks          map[any]StateChangeCallbackFn
   168  		stateChangedDuringReadthrough []*Namespace
   169  
   170  		// readthroughLock protects readthroughNotFoundCache and requests to persistence
   171  		// it should be acquired before checking readthroughNotFoundCache, making a request
   172  		// to persistence, or updating readthroughNotFoundCache
   173  		// It should be acquired before cacheLock (above) if both are required
   174  		readthroughLock sync.Mutex
   175  		// readthroughNotFoundCache stores namespaces that missed the above caches
   176  		// AND was not found when reading through to the persistence layer
   177  		readthroughNotFoundCache cache.Cache
   178  
   179  		// Temporary solution to force read search attributes from persistence
   180  		forceSearchAttributesCacheRefreshOnRead dynamicconfig.BoolPropertyFn
   181  	}
   182  )
   183  
   184  // NewRegistry creates a new instance of Registry for accessing and caching
   185  // namespace information to reduce the load on persistence.
   186  func NewRegistry(
   187  	persistence Persistence,
   188  	enableGlobalNamespaces bool,
   189  	refreshInterval dynamicconfig.DurationPropertyFn,
   190  	forceSearchAttributesCacheRefreshOnRead dynamicconfig.BoolPropertyFn,
   191  	metricsHandler metrics.Handler,
   192  	logger log.Logger,
   193  ) Registry {
   194  	reg := &registry{
   195  		triggerRefreshCh:         make(chan chan struct{}, 1),
   196  		persistence:              persistence,
   197  		globalNamespacesEnabled:  enableGlobalNamespaces,
   198  		clock:                    clock.NewRealTimeSource(),
   199  		metricsHandler:           metricsHandler.WithTags(metrics.OperationTag(metrics.NamespaceCacheScope)),
   200  		logger:                   logger,
   201  		cacheNameToID:            cache.New(cacheMaxSize, &cacheOpts),
   202  		cacheByID:                cache.New(cacheMaxSize, &cacheOpts),
   203  		refreshInterval:          refreshInterval,
   204  		stateChangeCallbacks:     make(map[any]StateChangeCallbackFn),
   205  		readthroughNotFoundCache: cache.New(cacheMaxSize, &readthroughNotFoundCacheOpts),
   206  
   207  		forceSearchAttributesCacheRefreshOnRead: forceSearchAttributesCacheRefreshOnRead,
   208  	}
   209  	return reg
   210  }
   211  
   212  // GetCacheSize observes the size of the by-name and by-ID caches in number of
   213  // entries.
   214  func (r *registry) GetCacheSize() (sizeOfCacheByName int64, sizeOfCacheByID int64) {
   215  	r.cacheLock.RLock()
   216  	defer r.cacheLock.RUnlock()
   217  	return int64(r.cacheByID.Size()), int64(r.cacheNameToID.Size())
   218  }
   219  
   220  // Start the background refresh of Namespace data.
   221  func (r *registry) Start() {
   222  	if !atomic.CompareAndSwapInt32(&r.status, stopped, starting) {
   223  		return
   224  	}
   225  	defer atomic.StoreInt32(&r.status, running)
   226  
   227  	// initialize the cache by initial scan
   228  	ctx := headers.SetCallerInfo(
   229  		context.Background(),
   230  		headers.SystemBackgroundCallerInfo,
   231  	)
   232  
   233  	err := r.refreshNamespaces(ctx)
   234  	if err != nil {
   235  		r.logger.Fatal("Unable to initialize namespace cache", tag.Error(err))
   236  	}
   237  	r.refresher = goro.NewHandle(ctx).Go(r.refreshLoop)
   238  }
   239  
   240  // Stop the background refresh of Namespace data
   241  func (r *registry) Stop() {
   242  	if !atomic.CompareAndSwapInt32(&r.status, running, stopping) {
   243  		return
   244  	}
   245  	defer atomic.StoreInt32(&r.status, stopped)
   246  	r.refresher.Cancel()
   247  	<-r.refresher.Done()
   248  }
   249  
   250  func (r *registry) GetPingChecks() []common.PingCheck {
   251  	return []common.PingCheck{
   252  		{
   253  			Name: "namespace registry lock",
   254  			// we don't do any persistence ops, this shouldn't be blocked
   255  			Timeout: 10 * time.Second,
   256  			Ping: func() []common.Pingable {
   257  				r.cacheLock.Lock()
   258  				//lint:ignore SA2001 just checking if we can acquire the lock
   259  				r.cacheLock.Unlock()
   260  				return nil
   261  			},
   262  			MetricsName: metrics.DDNamespaceRegistryLockLatency.Name(),
   263  		},
   264  	}
   265  }
   266  
   267  func (r *registry) getAllNamespace() map[ID]*Namespace {
   268  	r.cacheLock.RLock()
   269  	defer r.cacheLock.RUnlock()
   270  	return r.getAllNamespaceLocked()
   271  }
   272  
   273  func (r *registry) getAllNamespaceLocked() map[ID]*Namespace {
   274  	result := make(map[ID]*Namespace)
   275  
   276  	ite := r.cacheByID.Iterator()
   277  	defer ite.Close()
   278  
   279  	for ite.HasNext() {
   280  		entry := ite.Next()
   281  		id := entry.Key().(ID)
   282  		result[id] = entry.Value().(*Namespace)
   283  	}
   284  	return result
   285  }
   286  
   287  func (r *registry) RegisterStateChangeCallback(key any, cb StateChangeCallbackFn) {
   288  	r.cacheLock.Lock()
   289  	r.stateChangeCallbacks[key] = cb
   290  	allNamespaces := r.getAllNamespaceLocked()
   291  	r.cacheLock.Unlock()
   292  
   293  	// call once for each namespace already in the registry
   294  	for _, ns := range allNamespaces {
   295  		cb(ns, false)
   296  	}
   297  }
   298  
   299  func (r *registry) UnregisterStateChangeCallback(key any) {
   300  	r.cacheLock.Lock()
   301  	defer r.cacheLock.Unlock()
   302  	delete(r.stateChangeCallbacks, key)
   303  }
   304  
   305  // GetNamespace retrieves the information from the cache if it exists, otherwise retrieves the information from metadata
   306  // store and writes it to the cache with an expiry before returning back
   307  func (r *registry) GetNamespace(name Name) (*Namespace, error) {
   308  	if name == "" {
   309  		return nil, serviceerror.NewInvalidArgument("Namespace is empty.")
   310  	}
   311  	return r.getOrReadthroughNamespace(name)
   312  }
   313  
   314  // GetNamespaceByID retrieves the information from the cache if it exists, otherwise retrieves the information from metadata
   315  // store and writes it to the cache with an expiry before returning back
   316  func (r *registry) GetNamespaceByID(id ID) (*Namespace, error) {
   317  	if id == "" {
   318  		return nil, serviceerror.NewInvalidArgument("NamespaceID is empty.")
   319  	}
   320  	return r.getOrReadthroughNamespaceByID(id)
   321  }
   322  
   323  // GetNamespaceID retrieves namespaceID by using GetNamespace
   324  func (r *registry) GetNamespaceID(
   325  	name Name,
   326  ) (ID, error) {
   327  
   328  	ns, err := r.GetNamespace(name)
   329  	if err != nil {
   330  		return "", err
   331  	}
   332  	return ns.ID(), nil
   333  }
   334  
   335  // GetNamespaceName returns namespace name given the namespace id
   336  func (r *registry) GetNamespaceName(
   337  	id ID,
   338  ) (Name, error) {
   339  
   340  	ns, err := r.getOrReadthroughNamespaceByID(id)
   341  	if err != nil {
   342  		return "", err
   343  	}
   344  	return ns.Name(), nil
   345  }
   346  
   347  // GetCustomSearchAttributesMapper is a temporary solution to be able to get search attributes
   348  // with from persistence if forceSearchAttributesCacheRefreshOnRead is true.
   349  func (r *registry) GetCustomSearchAttributesMapper(name Name) (CustomSearchAttributesMapper, error) {
   350  	var ns *Namespace
   351  	var err error
   352  	if r.forceSearchAttributesCacheRefreshOnRead() {
   353  		r.readthroughLock.Lock()
   354  		defer r.readthroughLock.Unlock()
   355  		ns, err = r.getNamespaceByNamePersistence(name)
   356  	} else {
   357  		ns, err = r.GetNamespace(name)
   358  	}
   359  	if err != nil {
   360  		return CustomSearchAttributesMapper{}, err
   361  	}
   362  	return ns.CustomSearchAttributesMapper(), nil
   363  }
   364  
   365  func (r *registry) refreshLoop(ctx context.Context) error {
   366  	// Put timer events on our channel so we can select on just one below.
   367  	go func() {
   368  		timer := time.NewTicker(r.refreshInterval())
   369  
   370  		for {
   371  			select {
   372  			case <-timer.C:
   373  				select {
   374  				case r.triggerRefreshCh <- nil:
   375  				default:
   376  				}
   377  				timer.Reset(r.refreshInterval())
   378  			case <-ctx.Done():
   379  				timer.Stop()
   380  				return
   381  			}
   382  		}
   383  	}()
   384  
   385  	for {
   386  		select {
   387  		case <-ctx.Done():
   388  			return nil
   389  		case replyCh := <-r.triggerRefreshCh:
   390  			for err := r.refreshNamespaces(ctx); err != nil; err = r.refreshNamespaces(ctx) {
   391  				select {
   392  				case <-ctx.Done():
   393  					return nil
   394  				default:
   395  					r.logger.Error("Error refreshing namespace cache", tag.Error(err))
   396  					timer := time.NewTimer(CacheRefreshFailureRetryInterval)
   397  					select {
   398  					case <-timer.C:
   399  					case <-ctx.Done():
   400  						timer.Stop()
   401  						return nil
   402  					}
   403  				}
   404  			}
   405  			if replyCh != nil {
   406  				replyCh <- struct{}{} // TODO: close replyCh?
   407  			}
   408  		}
   409  	}
   410  }
   411  
   412  func (r *registry) refreshNamespaces(ctx context.Context) error {
   413  	request := &persistence.ListNamespacesRequest{
   414  		PageSize:       CacheRefreshPageSize,
   415  		IncludeDeleted: true,
   416  	}
   417  	var namespacesDb Namespaces
   418  	namespaceIDsDb := make(map[ID]struct{})
   419  
   420  	for {
   421  		response, err := r.persistence.ListNamespaces(ctx, request)
   422  		if err != nil {
   423  			return err
   424  		}
   425  		for _, namespaceDb := range response.Namespaces {
   426  			namespacesDb = append(namespacesDb, FromPersistentState(namespaceDb))
   427  			namespaceIDsDb[ID(namespaceDb.Namespace.Info.Id)] = struct{}{}
   428  		}
   429  		if len(response.NextPageToken) == 0 {
   430  			break
   431  		}
   432  		request.NextPageToken = response.NextPageToken
   433  	}
   434  
   435  	// Make a copy of the existing namespace cache (excluding deleted), so we can calculate diff and do "compare and swap".
   436  	newCacheNameToID := cache.New(cacheMaxSize, &cacheOpts)
   437  	newCacheByID := cache.New(cacheMaxSize, &cacheOpts)
   438  	var deletedEntries []*Namespace
   439  	for _, namespace := range r.getAllNamespace() {
   440  		if _, namespaceExistsDb := namespaceIDsDb[namespace.ID()]; !namespaceExistsDb {
   441  			deletedEntries = append(deletedEntries, namespace)
   442  			continue
   443  		}
   444  		newCacheNameToID.Put(Name(namespace.info.Name), ID(namespace.info.Id))
   445  		newCacheByID.Put(ID(namespace.info.Id), namespace)
   446  	}
   447  
   448  	var stateChanged []*Namespace
   449  	for _, namespace := range namespacesDb {
   450  		oldNS := r.updateIDToNamespaceCache(newCacheByID, namespace.ID(), namespace)
   451  		newCacheNameToID.Put(namespace.Name(), namespace.ID())
   452  
   453  		if namespaceStateChanged(oldNS, namespace) {
   454  			stateChanged = append(stateChanged, namespace)
   455  		}
   456  	}
   457  
   458  	var stateChangeCallbacks []StateChangeCallbackFn
   459  
   460  	r.cacheLock.Lock()
   461  	r.cacheByID = newCacheByID
   462  	r.cacheNameToID = newCacheNameToID
   463  	stateChanged = append(stateChanged, r.stateChangedDuringReadthrough...)
   464  	r.stateChangedDuringReadthrough = nil
   465  	stateChangeCallbacks = maps.Values(r.stateChangeCallbacks)
   466  	r.cacheLock.Unlock()
   467  
   468  	// call state change callbacks
   469  	for _, cb := range stateChangeCallbacks {
   470  		for _, ns := range deletedEntries {
   471  			cb(ns, true)
   472  		}
   473  		for _, ns := range stateChanged {
   474  			cb(ns, false)
   475  		}
   476  	}
   477  
   478  	return nil
   479  }
   480  
   481  func (r *registry) updateIDToNamespaceCache(
   482  	cacheByID cache.Cache,
   483  	id ID,
   484  	newNS *Namespace,
   485  ) (oldNS *Namespace) {
   486  	oldCacheRec := cacheByID.Put(id, newNS)
   487  	if oldNS, ok := oldCacheRec.(*Namespace); ok {
   488  		return oldNS
   489  	}
   490  	return nil
   491  }
   492  
   493  // getNamespace retrieves the information from the cache if it exists
   494  func (r *registry) getNamespace(name Name) (*Namespace, error) {
   495  	r.cacheLock.RLock()
   496  	defer r.cacheLock.RUnlock()
   497  	if id, ok := r.cacheNameToID.Get(name).(ID); ok {
   498  		return r.getNamespaceByIDLocked(id)
   499  	}
   500  	return nil, serviceerror.NewNamespaceNotFound(name.String())
   501  }
   502  
   503  // getNamespaceByID retrieves the information from the cache if it exists.
   504  func (r *registry) getNamespaceByID(id ID) (*Namespace, error) {
   505  	r.cacheLock.RLock()
   506  	defer r.cacheLock.RUnlock()
   507  	return r.getNamespaceByIDLocked(id)
   508  }
   509  
   510  func (r *registry) getNamespaceByIDLocked(id ID) (*Namespace, error) {
   511  	if ns, ok := r.cacheByID.Get(id).(*Namespace); ok {
   512  		return ns, nil
   513  	}
   514  	return nil, serviceerror.NewNamespaceNotFound(id.String())
   515  }
   516  
   517  // getOrReadthroughNamespace retrieves the information from the cache if it exists or reads through
   518  // to the persistence layer and updates caches if it doesn't
   519  func (r *registry) getOrReadthroughNamespace(name Name) (*Namespace, error) {
   520  	// check main caches
   521  	cacheHit, cacheErr := r.getNamespace(name)
   522  	if cacheErr == nil {
   523  		return cacheHit, nil
   524  	}
   525  
   526  	r.readthroughLock.Lock()
   527  	defer r.readthroughLock.Unlock()
   528  
   529  	// check caches again in case there was an update while waiting
   530  	cacheHit, cacheErr = r.getNamespace(name)
   531  	if cacheErr == nil {
   532  		return cacheHit, nil
   533  	}
   534  
   535  	// check readthrough cache
   536  	if r.readthroughNotFoundCache.Get(name.String()) != nil {
   537  		return nil, serviceerror.NewNamespaceNotFound(name.String())
   538  	}
   539  
   540  	// readthrough to persistence layer and update readthrough cache if not found
   541  	ns, err := r.getNamespaceByNamePersistence(name)
   542  	if err != nil {
   543  		return nil, err
   544  	}
   545  
   546  	// update main caches if found
   547  	r.updateCachesSingleNamespace(ns)
   548  
   549  	return ns, nil
   550  }
   551  
   552  // getOrReadthroughNamespaceByID retrieves the information from the cache if it exists or reads through
   553  // to the persistence layer and updates caches if it doesn't
   554  func (r *registry) getOrReadthroughNamespaceByID(id ID) (*Namespace, error) {
   555  	// check main caches
   556  	cacheHit, cacheErr := r.getNamespaceByID(id)
   557  	if cacheErr == nil {
   558  		return cacheHit, nil
   559  	}
   560  
   561  	r.readthroughLock.Lock()
   562  	defer r.readthroughLock.Unlock()
   563  
   564  	// check caches again in case there was an update while waiting
   565  	cacheHit, cacheErr = r.getNamespaceByID(id)
   566  	if cacheErr == nil {
   567  		return cacheHit, nil
   568  	}
   569  
   570  	// check readthrough cache
   571  	if r.readthroughNotFoundCache.Get(id.String()) != nil {
   572  		return nil, serviceerror.NewNamespaceNotFound(id.String())
   573  	}
   574  
   575  	// readthrough to persistence layer and update readthrough cache if not found
   576  	ns, err := r.getNamespaceByIDPersistence(id)
   577  	if err != nil {
   578  		return nil, err
   579  	}
   580  
   581  	// update main caches if found
   582  	r.updateCachesSingleNamespace(ns)
   583  
   584  	return ns, nil
   585  }
   586  
   587  func (r *registry) updateCachesSingleNamespace(ns *Namespace) {
   588  	r.cacheLock.Lock()
   589  	defer r.cacheLock.Unlock()
   590  
   591  	if curEntry, ok := r.cacheByID.Get(ns.ID()).(*Namespace); ok {
   592  		if curEntry.NotificationVersion() >= ns.NotificationVersion() {
   593  			// More up to date version already put in cache by refresh
   594  			return
   595  		}
   596  	}
   597  
   598  	oldNS := r.updateIDToNamespaceCache(r.cacheByID, ns.ID(), ns)
   599  	r.cacheNameToID.Put(ns.Name(), ns.ID())
   600  	if namespaceStateChanged(oldNS, ns) {
   601  		r.stateChangedDuringReadthrough = append(r.stateChangedDuringReadthrough, ns)
   602  	}
   603  }
   604  
   605  func (r *registry) getNamespaceByNamePersistence(name Name) (*Namespace, error) {
   606  	request := &persistence.GetNamespaceRequest{
   607  		Name: name.String(),
   608  	}
   609  
   610  	ns, err := r.getNamespacePersistence(request)
   611  	if err != nil {
   612  		if _, ok := err.(*serviceerror.NamespaceNotFound); ok {
   613  			r.readthroughNotFoundCache.Put(name.String(), struct{}{})
   614  		}
   615  		// TODO: we should return the actual error we got (e.g. timeout)
   616  		return nil, serviceerror.NewNamespaceNotFound(name.String())
   617  	}
   618  	return ns, nil
   619  }
   620  
   621  func (r *registry) getNamespaceByIDPersistence(id ID) (*Namespace, error) {
   622  	request := &persistence.GetNamespaceRequest{
   623  		ID: id.String(),
   624  	}
   625  
   626  	ns, err := r.getNamespacePersistence(request)
   627  	if err != nil {
   628  		if _, ok := err.(*serviceerror.NamespaceNotFound); ok {
   629  			r.readthroughNotFoundCache.Put(id.String(), struct{}{})
   630  		}
   631  		// TODO: we should return the actual error we got (e.g. timeout)
   632  		return nil, serviceerror.NewNamespaceNotFound(id.String())
   633  	}
   634  	return ns, nil
   635  }
   636  
   637  func (r *registry) getNamespacePersistence(request *persistence.GetNamespaceRequest) (*Namespace, error) {
   638  	ctx, cancel := context.WithTimeout(context.Background(), readthroughTimeout)
   639  	defer cancel()
   640  	ctx = headers.SetCallerType(ctx, headers.CallerTypeAPI)
   641  	ctx = headers.SetCallerName(ctx, headers.CallerNameSystem)
   642  
   643  	response, err := r.persistence.GetNamespace(ctx, request)
   644  	if err != nil {
   645  		return nil, err
   646  	}
   647  
   648  	return FromPersistentState(response), nil
   649  }
   650  
   651  // this test should include anything that might affect whether a namespace is active on
   652  // this cluster.
   653  // returns true if the state was changed or false if not
   654  func namespaceStateChanged(old *Namespace, new *Namespace) bool {
   655  	return old == nil ||
   656  		old.State() != new.State() ||
   657  		old.IsGlobalNamespace() != new.IsGlobalNamespace() ||
   658  		old.ActiveClusterName() != new.ActiveClusterName() ||
   659  		old.ReplicationState() != new.ReplicationState()
   660  }