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 }