github.com/m3db/m3@v1.5.0/src/cluster/services/services.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 services
    22  
    23  import (
    24  	"errors"
    25  	"fmt"
    26  	"sync"
    27  	"time"
    28  
    29  	"github.com/m3db/m3/src/cluster/generated/proto/metadatapb"
    30  	"github.com/m3db/m3/src/cluster/generated/proto/placementpb"
    31  	"github.com/m3db/m3/src/cluster/kv"
    32  	"github.com/m3db/m3/src/cluster/placement"
    33  	ps "github.com/m3db/m3/src/cluster/placement/service"
    34  	"github.com/m3db/m3/src/cluster/placement/storage"
    35  	"github.com/m3db/m3/src/cluster/shard"
    36  	xos "github.com/m3db/m3/src/x/os"
    37  	xwatch "github.com/m3db/m3/src/x/watch"
    38  
    39  	"github.com/uber-go/tally"
    40  	"go.uber.org/zap"
    41  )
    42  
    43  const (
    44  	defaultGaugeInterval = 10 * time.Second
    45  )
    46  
    47  var (
    48  	errNoServiceName             = errors.New("no service specified")
    49  	errNoServiceID               = errors.New("no service id specified")
    50  	errNoInstanceID              = errors.New("no instance id specified")
    51  	errAdPlacementMissing        = errors.New("advertisement is missing placement instance")
    52  	errInstanceNotFound          = errors.New("instance not found")
    53  	errNilPlacementProto         = errors.New("nil placement proto")
    54  	errNilPlacementInstanceProto = errors.New("nil placement instance proto")
    55  	errNilMetadataProto          = errors.New("nil metadata proto")
    56  )
    57  
    58  // NewServices returns a client of Services.
    59  func NewServices(opts Options) (Services, error) {
    60  	if err := opts.Validate(); err != nil {
    61  		return nil, err
    62  	}
    63  
    64  	return &client{
    65  		opts:           opts,
    66  		placementKeyFn: keyFnWithNamespace(placementNamespace(opts.NamespaceOptions().PlacementNamespace())),
    67  		metadataKeyFn:  keyFnWithNamespace(metadataNamespace(opts.NamespaceOptions().MetadataNamespace())),
    68  		kvManagers:     make(map[string]*kvManager),
    69  		hbStores:       make(map[string]HeartbeatService),
    70  		adDoneChs:      make(map[string]chan struct{}),
    71  		ldSvcs:         make(map[leaderKey]LeaderService),
    72  		logger:         opts.InstrumentsOptions().Logger(),
    73  		m:              opts.InstrumentsOptions().MetricsScope(),
    74  	}, nil
    75  }
    76  
    77  type client struct {
    78  	sync.RWMutex
    79  
    80  	opts           Options
    81  	placementKeyFn keyFn
    82  	metadataKeyFn  keyFn
    83  	kvManagers     map[string]*kvManager
    84  	hbStores       map[string]HeartbeatService
    85  	ldSvcs         map[leaderKey]LeaderService
    86  	adDoneChs      map[string]chan struct{}
    87  	logger         *zap.Logger
    88  	m              tally.Scope
    89  }
    90  
    91  func (c *client) Metadata(sid ServiceID) (Metadata, error) {
    92  	if err := validateServiceID(sid); err != nil {
    93  		return nil, err
    94  	}
    95  
    96  	m, err := c.getKVManager(sid.Zone())
    97  	if err != nil {
    98  		return nil, err
    99  	}
   100  
   101  	v, err := m.kv.Get(c.metadataKeyFn(sid))
   102  	if err != nil {
   103  		return nil, err
   104  	}
   105  
   106  	var mp metadatapb.Metadata
   107  	if err = v.Unmarshal(&mp); err != nil {
   108  		return nil, err
   109  	}
   110  
   111  	return NewMetadataFromProto(&mp)
   112  }
   113  
   114  func (c *client) SetMetadata(sid ServiceID, meta Metadata) error {
   115  	if err := validateServiceID(sid); err != nil {
   116  		return err
   117  	}
   118  
   119  	m, err := c.getKVManager(sid.Zone())
   120  	if err != nil {
   121  		return err
   122  	}
   123  
   124  	mp, err := meta.Proto()
   125  	if err != nil {
   126  		return err
   127  	}
   128  	_, err = m.kv.Set(c.metadataKeyFn(sid), mp)
   129  	return err
   130  }
   131  
   132  func (c *client) DeleteMetadata(sid ServiceID) error {
   133  	if err := validateServiceID(sid); err != nil {
   134  		return err
   135  	}
   136  
   137  	m, err := c.getKVManager(sid.Zone())
   138  	if err != nil {
   139  		return err
   140  	}
   141  
   142  	_, err = m.kv.Delete(c.metadataKeyFn(sid))
   143  	return err
   144  }
   145  
   146  func (c *client) PlacementService(sid ServiceID, opts placement.Options) (placement.Service, error) {
   147  	if err := validateServiceID(sid); err != nil {
   148  		return nil, err
   149  	}
   150  
   151  	store, err := c.opts.KVGen()(sid.Zone())
   152  	if err != nil {
   153  		return nil, err
   154  	}
   155  
   156  	return ps.NewPlacementService(
   157  		storage.NewPlacementStorage(store, c.placementKeyFn(sid), opts),
   158  		ps.WithPlacementOptions(opts),
   159  	), nil
   160  }
   161  
   162  func (c *client) Advertise(ad Advertisement) error {
   163  	pi := ad.PlacementInstance()
   164  	if pi == nil {
   165  		return errAdPlacementMissing
   166  	}
   167  
   168  	if err := validateAdvertisement(ad.ServiceID(), pi.ID()); err != nil {
   169  		return err
   170  	}
   171  
   172  	m, err := c.Metadata(ad.ServiceID())
   173  	if err != nil {
   174  		return err
   175  	}
   176  
   177  	hb, err := c.getHeartbeatService(ad.ServiceID())
   178  	if err != nil {
   179  		return err
   180  	}
   181  
   182  	key := adKey(ad.ServiceID(), pi.ID())
   183  	c.Lock()
   184  	ch, ok := c.adDoneChs[key]
   185  	if ok {
   186  		c.Unlock()
   187  		return fmt.Errorf("service %s, instance %s is already being advertised", ad.ServiceID(), pi.ID())
   188  	}
   189  	ch = make(chan struct{})
   190  	c.adDoneChs[key] = ch
   191  	c.Unlock()
   192  
   193  	go func() {
   194  		sid := ad.ServiceID()
   195  		errCounter := c.serviceTaggedScope(sid).Counter("heartbeat.error")
   196  
   197  		tickFn := func() {
   198  			if isHealthy(ad) {
   199  				if err := hb.Heartbeat(pi, m.LivenessInterval()); err != nil {
   200  					c.logger.Error("could not heartbeat service",
   201  						zap.String("service", sid.String()),
   202  						zap.Error(err))
   203  					errCounter.Inc(1)
   204  				}
   205  			}
   206  		}
   207  
   208  		tickFn()
   209  
   210  		ticker := time.NewTicker(m.HeartbeatInterval())
   211  		defer ticker.Stop()
   212  		for {
   213  			select {
   214  			case <-ticker.C:
   215  				tickFn()
   216  			case <-ch:
   217  				return
   218  			}
   219  		}
   220  	}()
   221  	return nil
   222  }
   223  
   224  func (c *client) Unadvertise(sid ServiceID, id string) error {
   225  	if err := validateAdvertisement(sid, id); err != nil {
   226  		return err
   227  	}
   228  
   229  	key := adKey(sid, id)
   230  
   231  	c.Lock()
   232  	if ch, ok := c.adDoneChs[key]; ok {
   233  		// If this client is advertising the instance, stop it.
   234  		close(ch)
   235  		delete(c.adDoneChs, key)
   236  	}
   237  	c.Unlock()
   238  
   239  	hbStore, err := c.getHeartbeatService(sid)
   240  	if err != nil {
   241  		return err
   242  	}
   243  
   244  	return hbStore.Delete(id)
   245  }
   246  
   247  func (c *client) Query(sid ServiceID, opts QueryOptions) (Service, error) {
   248  	if err := validateServiceID(sid); err != nil {
   249  		return nil, err
   250  	}
   251  
   252  	v, err := c.getPlacementValue(sid)
   253  	if err != nil {
   254  		return nil, err
   255  	}
   256  
   257  	service, err := getServiceFromValue(v, sid)
   258  	if err != nil {
   259  		return nil, err
   260  	}
   261  
   262  	if !opts.IncludeUnhealthy() {
   263  		hbStore, err := c.getHeartbeatService(sid)
   264  		if err != nil {
   265  			return nil, err
   266  		}
   267  
   268  		ids, err := hbStore.Get()
   269  		if err != nil {
   270  			return nil, err
   271  		}
   272  
   273  		service = filterInstances(service, ids)
   274  	}
   275  
   276  	return service, nil
   277  }
   278  
   279  func (c *client) Watch(sid ServiceID, opts QueryOptions) (Watch, error) {
   280  	if err := validateServiceID(sid); err != nil {
   281  		return nil, err
   282  	}
   283  
   284  	c.logger.Info("adding a watch",
   285  		zap.String("service", sid.Name()),
   286  		zap.String("env", sid.Environment()),
   287  		zap.String("zone", sid.Zone()),
   288  		zap.Bool("includeUnhealthy", opts.IncludeUnhealthy()),
   289  	)
   290  
   291  	kvm, err := c.getKVManager(sid.Zone())
   292  	if err != nil {
   293  		return nil, err
   294  	}
   295  
   296  	kvm.RLock()
   297  	watchable, exist := kvm.serviceWatchables[sid.String()]
   298  	kvm.RUnlock()
   299  	if exist {
   300  		_, w, err := watchable.watch()
   301  		return w, err
   302  	}
   303  
   304  	// Prepare the watch of placement outside of lock.
   305  	key := c.placementKeyFn(sid)
   306  	placementWatch, err := kvm.kv.Watch(key)
   307  	if err != nil {
   308  		return nil, err
   309  	}
   310  
   311  	initValue, err := c.waitForInitValue(kvm.kv, placementWatch, sid, c.opts.InitTimeout(), opts.InterruptedCh())
   312  	if err != nil {
   313  		return nil, fmt.Errorf("could not get init value for '%s',  err: %w", key, err)
   314  	}
   315  
   316  	initService, err := getServiceFromValue(initValue, sid)
   317  	if err != nil {
   318  		placementWatch.Close()
   319  		return nil, err
   320  	}
   321  
   322  	kvm.Lock()
   323  	defer kvm.Unlock()
   324  	watchable, exist = kvm.serviceWatchables[sid.String()]
   325  	if exist {
   326  		// If a watchable already exist now, we need to clean up the placement watch we just created.
   327  		placementWatch.Close()
   328  		_, w, err := watchable.watch()
   329  		return w, err
   330  	}
   331  
   332  	watchable = newServiceWatchable()
   333  	sdm := newServiceDiscoveryMetrics(c.serviceTaggedScope(sid))
   334  
   335  	if !opts.IncludeUnhealthy() {
   336  		hbStore, err := c.getHeartbeatService(sid)
   337  		if err != nil {
   338  			placementWatch.Close()
   339  			return nil, err
   340  		}
   341  		heartbeatWatch, err := hbStore.Watch()
   342  		if err != nil {
   343  			placementWatch.Close()
   344  			return nil, err
   345  		}
   346  		watchable.update(filterInstancesWithWatch(initService, heartbeatWatch))
   347  		go c.watchPlacementAndHeartbeat(watchable, placementWatch, heartbeatWatch, initValue, sid, initService, sdm.serviceUnmalshalErr)
   348  	} else {
   349  		watchable.update(initService)
   350  		go c.watchPlacement(watchable, placementWatch, initValue, sid, sdm.serviceUnmalshalErr)
   351  	}
   352  
   353  	kvm.serviceWatchables[sid.String()] = watchable
   354  
   355  	go updateVersionGauge(placementWatch, sdm.versionGauge)
   356  
   357  	_, w, err := watchable.watch()
   358  	return w, err
   359  }
   360  
   361  func (c *client) HeartbeatService(sid ServiceID) (HeartbeatService, error) {
   362  	if err := validateServiceID(sid); err != nil {
   363  		return nil, err
   364  	}
   365  
   366  	return c.getHeartbeatService(sid)
   367  }
   368  
   369  func (c *client) getPlacementValue(sid ServiceID) (kv.Value, error) {
   370  	kvm, err := c.getKVManager(sid.Zone())
   371  	if err != nil {
   372  		return nil, err
   373  	}
   374  
   375  	v, err := kvm.kv.Get(c.placementKeyFn(sid))
   376  	if err != nil {
   377  		return nil, err
   378  	}
   379  
   380  	return v, nil
   381  }
   382  
   383  func (c *client) getHeartbeatService(sid ServiceID) (HeartbeatService, error) {
   384  	c.Lock()
   385  	defer c.Unlock()
   386  	hb, ok := c.hbStores[sid.String()]
   387  	if ok {
   388  		return hb, nil
   389  	}
   390  
   391  	hb, err := c.opts.HeartbeatGen()(sid)
   392  	if err != nil {
   393  		return nil, err
   394  	}
   395  
   396  	c.hbStores[sid.String()] = hb
   397  	return hb, nil
   398  }
   399  
   400  func (c *client) LeaderService(sid ServiceID, opts ElectionOptions) (LeaderService, error) {
   401  	if sid == nil {
   402  		return nil, errNoServiceID
   403  	}
   404  
   405  	if opts == nil {
   406  		opts = NewElectionOptions()
   407  	}
   408  
   409  	key := leaderCacheKey(sid, opts)
   410  
   411  	c.RLock()
   412  	if ld, ok := c.ldSvcs[key]; ok {
   413  		c.RUnlock()
   414  		return ld, nil
   415  	}
   416  	c.RUnlock()
   417  
   418  	c.Lock()
   419  	defer c.Unlock()
   420  
   421  	if ld, ok := c.ldSvcs[key]; ok {
   422  		return ld, nil
   423  	}
   424  
   425  	ld, err := c.opts.LeaderGen()(sid, opts)
   426  	if err != nil {
   427  		return nil, err
   428  	}
   429  
   430  	c.ldSvcs[key] = ld
   431  	return ld, nil
   432  }
   433  
   434  func (c *client) getKVManager(zone string) (*kvManager, error) {
   435  	c.Lock()
   436  	defer c.Unlock()
   437  	m, ok := c.kvManagers[zone]
   438  	if ok {
   439  		return m, nil
   440  	}
   441  
   442  	kv, err := c.opts.KVGen()(zone)
   443  	if err != nil {
   444  		return nil, err
   445  	}
   446  
   447  	m = &kvManager{
   448  		kv:                kv,
   449  		serviceWatchables: map[string]serviceWatchable{},
   450  	}
   451  
   452  	c.kvManagers[zone] = m
   453  	return m, nil
   454  }
   455  
   456  func (c *client) watchPlacement(
   457  	w serviceWatchable,
   458  	vw kv.ValueWatch,
   459  	initValue kv.Value,
   460  	sid ServiceID,
   461  	errCounter tally.Counter,
   462  ) {
   463  	for range vw.C() {
   464  		newService := c.serviceFromUpdate(vw.Get(), initValue, sid, errCounter)
   465  		if newService == nil {
   466  			continue
   467  		}
   468  
   469  		w.update(newService)
   470  	}
   471  }
   472  
   473  func (c *client) watchPlacementAndHeartbeat(
   474  	w serviceWatchable,
   475  	vw kv.ValueWatch,
   476  	heartbeatWatch xwatch.Watch,
   477  	initValue kv.Value,
   478  	sid ServiceID,
   479  	service Service,
   480  	errCounter tally.Counter,
   481  ) {
   482  	for {
   483  		select {
   484  		case <-vw.C():
   485  			newService := c.serviceFromUpdate(vw.Get(), initValue, sid, errCounter)
   486  			if newService == nil {
   487  				continue
   488  			}
   489  
   490  			service = newService
   491  		case <-heartbeatWatch.C():
   492  			c.logger.Info("received heartbeat update")
   493  		}
   494  		w.update(filterInstancesWithWatch(service, heartbeatWatch))
   495  	}
   496  }
   497  
   498  func (c *client) serviceFromUpdate(
   499  	value kv.Value,
   500  	initValue kv.Value,
   501  	sid ServiceID,
   502  	errCounter tally.Counter,
   503  ) Service {
   504  	if value == nil {
   505  		// NB(cw) this can only happen when the placement has been deleted
   506  		// it is safer to let the user keep using the old topology
   507  		c.logger.Info("received placement update with nil value")
   508  		return nil
   509  	}
   510  
   511  	if initValue != nil && !value.IsNewer(initValue) {
   512  		// NB(cw) this can only happen when the init wait called a Get() itself
   513  		// so the init value did not come from the watch, when the watch gets created
   514  		// the first update from it may from the same version.
   515  		c.logger.Info("received stale placement update, skip",
   516  			zap.Int("version", value.Version()))
   517  		return nil
   518  	}
   519  
   520  	newService, err := getServiceFromValue(value, sid)
   521  	if err != nil {
   522  		c.logger.Error("could not unmarshal update from kv store for placement",
   523  			zap.Int("version", value.Version()),
   524  			zap.Error(err))
   525  		errCounter.Inc(1)
   526  		return nil
   527  	}
   528  
   529  	c.logger.Info("successfully parsed placement", zap.Int("version", value.Version()))
   530  	return newService
   531  }
   532  
   533  func (c *client) serviceTaggedScope(sid ServiceID) tally.Scope {
   534  	return c.m.Tagged(
   535  		map[string]string{
   536  			"sd_service": sid.Name(),
   537  			"sd_env":     sid.Environment(),
   538  			"sd_zone":    sid.Zone(),
   539  		},
   540  	)
   541  }
   542  
   543  func isHealthy(ad Advertisement) bool {
   544  	healthFn := ad.Health()
   545  	return healthFn == nil || healthFn() == nil
   546  }
   547  
   548  func filterInstances(s Service, ids []string) Service {
   549  	if s == nil {
   550  		return nil
   551  	}
   552  
   553  	instances := make([]ServiceInstance, 0, len(s.Instances()))
   554  	for _, id := range ids {
   555  		if instance, err := s.Instance(id); err == nil {
   556  			instances = append(instances, instance)
   557  		}
   558  	}
   559  
   560  	return NewService().
   561  		SetInstances(instances).
   562  		SetSharding(s.Sharding()).
   563  		SetReplication(s.Replication())
   564  }
   565  
   566  func filterInstancesWithWatch(s Service, hbw xwatch.Watch) Service {
   567  	if hbw.Get() == nil {
   568  		return s
   569  	}
   570  	return filterInstances(s, hbw.Get().([]string))
   571  }
   572  
   573  func updateVersionGauge(vw kv.ValueWatch, versionGauge tally.Gauge) {
   574  	for range time.Tick(defaultGaugeInterval) {
   575  		v := vw.Get()
   576  		if v != nil {
   577  			versionGauge.Update(float64(v.Version()))
   578  		}
   579  	}
   580  }
   581  
   582  func getServiceFromValue(value kv.Value, sid ServiceID) (Service, error) {
   583  	p, err := placementFromValue(value)
   584  	if err != nil {
   585  		return nil, err
   586  	}
   587  
   588  	return NewServiceFromPlacement(p, sid), nil
   589  }
   590  
   591  func (c *client) waitForInitValue(
   592  	kvStore kv.Store,
   593  	w kv.ValueWatch,
   594  	sid ServiceID,
   595  	timeout time.Duration,
   596  	interruptedCh <-chan struct{},
   597  ) (kv.Value, error) {
   598  	if interruptedCh == nil {
   599  		// NB(nate): if no interrupted channel is provided, then this wait is not
   600  		// gracefully interruptable.
   601  		interruptedCh = make(chan struct{})
   602  	}
   603  
   604  	if timeout < 0 {
   605  		timeout = defaultInitTimeout
   606  	} else if timeout == 0 {
   607  		// We want no timeout if specifically asking for none
   608  		select {
   609  		case <-w.C():
   610  			return w.Get(), nil
   611  		case <-interruptedCh:
   612  			return nil, xos.ErrInterrupted
   613  		}
   614  	}
   615  	select {
   616  	case <-w.C():
   617  		return w.Get(), nil
   618  	case <-time.After(timeout):
   619  		return kvStore.Get(c.placementKeyFn(sid))
   620  	case <-interruptedCh:
   621  		return nil, xos.ErrInterrupted
   622  	}
   623  }
   624  
   625  func validateAdvertisement(sid ServiceID, id string) error {
   626  	if sid == nil {
   627  		return errNoServiceID
   628  	}
   629  
   630  	if id == "" {
   631  		return errNoInstanceID
   632  	}
   633  
   634  	return nil
   635  }
   636  
   637  // cache key for leader service clients
   638  type leaderKey struct {
   639  	sid           string
   640  	leaderTimeout time.Duration
   641  	resignTimeout time.Duration
   642  	ttl           int
   643  }
   644  
   645  func leaderCacheKey(sid ServiceID, opts ElectionOptions) leaderKey {
   646  	return leaderKey{
   647  		sid:           sid.String(),
   648  		leaderTimeout: opts.LeaderTimeout(),
   649  		resignTimeout: opts.ResignTimeout(),
   650  		ttl:           opts.TTLSecs(),
   651  	}
   652  }
   653  
   654  type kvManager struct {
   655  	sync.RWMutex
   656  
   657  	kv                kv.Store
   658  	serviceWatchables map[string]serviceWatchable
   659  }
   660  
   661  func newServiceDiscoveryMetrics(m tally.Scope) serviceDiscoveryMetrics {
   662  	return serviceDiscoveryMetrics{
   663  		versionGauge:        m.Gauge("placement.version"),
   664  		serviceUnmalshalErr: m.Counter("placement.unmarshal.error"),
   665  	}
   666  }
   667  
   668  type serviceDiscoveryMetrics struct {
   669  	versionGauge        tally.Gauge
   670  	serviceUnmalshalErr tally.Counter
   671  }
   672  
   673  // NewService creates a new Service.
   674  func NewService() Service { return new(service) }
   675  
   676  // NewServiceFromProto takes the data from a placement and a service id and
   677  // returns the corresponding Service object.
   678  func NewServiceFromProto(
   679  	p *placementpb.Placement,
   680  	sid ServiceID,
   681  ) (Service, error) {
   682  	if p == nil {
   683  		return nil, errNilPlacementProto
   684  	}
   685  	r := make([]ServiceInstance, 0, len(p.Instances))
   686  	for _, instance := range p.Instances {
   687  		instance, err := NewServiceInstanceFromProto(instance, sid)
   688  		if err != nil {
   689  			return nil, err
   690  		}
   691  		r = append(r, instance)
   692  	}
   693  
   694  	return NewService().
   695  		SetReplication(NewServiceReplication().SetReplicas(int(p.ReplicaFactor))).
   696  		SetSharding(NewServiceSharding().SetNumShards(int(p.NumShards)).SetIsSharded(p.IsSharded)).
   697  		SetInstances(r), nil
   698  }
   699  
   700  // NewServiceFromPlacement creates a Service from the placement and service ID.
   701  func NewServiceFromPlacement(p placement.Placement, sid ServiceID) Service {
   702  	var (
   703  		placementInstances = p.Instances()
   704  		serviceInstances   = make([]ServiceInstance, len(placementInstances))
   705  	)
   706  
   707  	for i, placementInstance := range placementInstances {
   708  		serviceInstances[i] = NewServiceInstanceFromPlacementInstance(placementInstance, sid)
   709  	}
   710  
   711  	return NewService().
   712  		SetReplication(NewServiceReplication().SetReplicas(p.ReplicaFactor())).
   713  		SetSharding(NewServiceSharding().SetNumShards(p.NumShards()).SetIsSharded(p.IsSharded())).
   714  		SetInstances(serviceInstances)
   715  }
   716  
   717  type service struct {
   718  	instances   []ServiceInstance
   719  	replication ServiceReplication
   720  	sharding    ServiceSharding
   721  }
   722  
   723  func (s *service) Instance(instanceID string) (ServiceInstance, error) {
   724  	for _, instance := range s.instances {
   725  		if instance.InstanceID() == instanceID {
   726  			return instance, nil
   727  		}
   728  	}
   729  	return nil, errInstanceNotFound
   730  }
   731  func (s *service) Instances() []ServiceInstance                 { return s.instances }
   732  func (s *service) Replication() ServiceReplication              { return s.replication }
   733  func (s *service) Sharding() ServiceSharding                    { return s.sharding }
   734  func (s *service) SetInstances(insts []ServiceInstance) Service { s.instances = insts; return s }
   735  func (s *service) SetReplication(r ServiceReplication) Service  { s.replication = r; return s }
   736  func (s *service) SetSharding(ss ServiceSharding) Service       { s.sharding = ss; return s }
   737  
   738  // NewServiceReplication creates a new ServiceReplication.
   739  func NewServiceReplication() ServiceReplication { return new(serviceReplication) }
   740  
   741  type serviceReplication struct {
   742  	replicas int
   743  }
   744  
   745  func (r *serviceReplication) Replicas() int                          { return r.replicas }
   746  func (r *serviceReplication) SetReplicas(rep int) ServiceReplication { r.replicas = rep; return r }
   747  
   748  // NewServiceSharding creates a new ServiceSharding.
   749  func NewServiceSharding() ServiceSharding { return new(serviceSharding) }
   750  
   751  type serviceSharding struct {
   752  	isSharded bool
   753  	numShards int
   754  }
   755  
   756  func (s *serviceSharding) NumShards() int                      { return s.numShards }
   757  func (s *serviceSharding) IsSharded() bool                     { return s.isSharded }
   758  func (s *serviceSharding) SetNumShards(n int) ServiceSharding  { s.numShards = n; return s }
   759  func (s *serviceSharding) SetIsSharded(v bool) ServiceSharding { s.isSharded = v; return s }
   760  
   761  // NewServiceInstance creates a new ServiceInstance.
   762  func NewServiceInstance() ServiceInstance { return new(serviceInstance) }
   763  
   764  // NewServiceInstanceFromProto creates a new service instance from proto.
   765  func NewServiceInstanceFromProto(
   766  	instance *placementpb.Instance,
   767  	sid ServiceID,
   768  ) (ServiceInstance, error) {
   769  	if instance == nil {
   770  		return nil, errNilPlacementInstanceProto
   771  	}
   772  	shards, err := shard.NewShardsFromProto(instance.Shards)
   773  	if err != nil {
   774  		return nil, err
   775  	}
   776  	return NewServiceInstance().
   777  		SetServiceID(sid).
   778  		SetInstanceID(instance.Id).
   779  		SetEndpoint(instance.Endpoint).
   780  		SetShards(shards), nil
   781  }
   782  
   783  // NewServiceInstanceFromPlacementInstance creates a new service instance from placement instance.
   784  func NewServiceInstanceFromPlacementInstance(
   785  	instance placement.Instance,
   786  	sid ServiceID,
   787  ) ServiceInstance {
   788  	return NewServiceInstance().
   789  		SetServiceID(sid).
   790  		SetInstanceID(instance.ID()).
   791  		SetEndpoint(instance.Endpoint()).
   792  		SetShards(instance.Shards())
   793  }
   794  
   795  type serviceInstance struct {
   796  	service  ServiceID
   797  	id       string
   798  	endpoint string
   799  	shards   shard.Shards
   800  }
   801  
   802  func (i *serviceInstance) InstanceID() string                       { return i.id }
   803  func (i *serviceInstance) Endpoint() string                         { return i.endpoint }
   804  func (i *serviceInstance) Shards() shard.Shards                     { return i.shards }
   805  func (i *serviceInstance) ServiceID() ServiceID                     { return i.service }
   806  func (i *serviceInstance) SetInstanceID(id string) ServiceInstance  { i.id = id; return i }
   807  func (i *serviceInstance) SetEndpoint(e string) ServiceInstance     { i.endpoint = e; return i }
   808  func (i *serviceInstance) SetShards(s shard.Shards) ServiceInstance { i.shards = s; return i }
   809  
   810  func (i *serviceInstance) SetServiceID(service ServiceID) ServiceInstance {
   811  	i.service = service
   812  	return i
   813  }
   814  
   815  // NewAdvertisement creates a new Advertisement.
   816  func NewAdvertisement() Advertisement { return new(advertisement) }
   817  
   818  type advertisement struct {
   819  	instance placement.Instance
   820  	service  ServiceID
   821  	health   func() error
   822  }
   823  
   824  func (a *advertisement) ServiceID() ServiceID                   { return a.service }
   825  func (a *advertisement) Health() func() error                   { return a.health }
   826  func (a *advertisement) PlacementInstance() placement.Instance  { return a.instance }
   827  func (a *advertisement) SetServiceID(s ServiceID) Advertisement { a.service = s; return a }
   828  func (a *advertisement) SetHealth(h func() error) Advertisement { a.health = h; return a }
   829  func (a *advertisement) SetPlacementInstance(p placement.Instance) Advertisement {
   830  	a.instance = p
   831  	return a
   832  }
   833  
   834  // NewServiceID creates new ServiceID.
   835  func NewServiceID() ServiceID { return new(serviceID) }
   836  
   837  type serviceID struct {
   838  	name string
   839  	env  string
   840  	zone string
   841  }
   842  
   843  func (sid *serviceID) Name() string                      { return sid.name }
   844  func (sid *serviceID) Environment() string               { return sid.env }
   845  func (sid *serviceID) Zone() string                      { return sid.zone }
   846  func (sid *serviceID) SetName(n string) ServiceID        { sid.name = n; return sid }
   847  func (sid *serviceID) SetEnvironment(e string) ServiceID { sid.env = e; return sid }
   848  func (sid *serviceID) SetZone(z string) ServiceID        { sid.zone = z; return sid }
   849  
   850  func (sid *serviceID) Equal(other ServiceID) bool {
   851  	if other == nil {
   852  		return false
   853  	}
   854  	return sid.Name() == other.Name() &&
   855  		sid.Zone() == other.Zone() &&
   856  		sid.Environment() == other.Environment()
   857  }
   858  
   859  func (sid *serviceID) String() string {
   860  	return fmt.Sprintf("[name: %s, env: %s, zone: %s]", sid.name, sid.env, sid.zone)
   861  }
   862  
   863  // NewMetadata creates new Metadata.
   864  func NewMetadata() Metadata { return new(metadata) }
   865  
   866  // NewMetadataFromProto converts a Metadata proto message to an instance of
   867  // Metadata.
   868  func NewMetadataFromProto(m *metadatapb.Metadata) (Metadata, error) {
   869  	if m == nil {
   870  		return nil, errNilMetadataProto
   871  	}
   872  	return NewMetadata().
   873  		SetPort(m.Port).
   874  		SetLivenessInterval(time.Duration(m.LivenessInterval)).
   875  		SetHeartbeatInterval(time.Duration(m.HeartbeatInterval)), nil
   876  }
   877  
   878  type metadata struct {
   879  	port              uint32
   880  	livenessInterval  time.Duration
   881  	heartbeatInterval time.Duration
   882  }
   883  
   884  func (m *metadata) Port() uint32                     { return m.port }
   885  func (m *metadata) LivenessInterval() time.Duration  { return m.livenessInterval }
   886  func (m *metadata) HeartbeatInterval() time.Duration { return m.heartbeatInterval }
   887  func (m *metadata) SetPort(p uint32) Metadata        { m.port = p; return m }
   888  
   889  func (m *metadata) SetLivenessInterval(l time.Duration) Metadata {
   890  	m.livenessInterval = l
   891  	return m
   892  }
   893  
   894  func (m *metadata) SetHeartbeatInterval(l time.Duration) Metadata {
   895  	m.heartbeatInterval = l
   896  	return m
   897  }
   898  
   899  func (m *metadata) String() string {
   900  	return fmt.Sprintf("[port: %d, livenessInterval: %v, heartbeatInterval: %v]",
   901  		m.port,
   902  		m.livenessInterval,
   903  		m.heartbeatInterval,
   904  	)
   905  }
   906  
   907  func (m *metadata) Proto() (*metadatapb.Metadata, error) {
   908  	return &metadatapb.Metadata{
   909  		Port:              m.Port(),
   910  		LivenessInterval:  int64(m.LivenessInterval()),
   911  		HeartbeatInterval: int64(m.HeartbeatInterval()),
   912  	}, nil
   913  }