// Attempt to resolve a hostname. This may return a database cached hostname or otherwise // it may resolve the hostname via CNAME func ResolveHostname(hostname string) (string, error) { hostname = strings.TrimSpace(hostname) if hostname == "" { return hostname, errors.New("Will not resolve empty hostname") } if strings.Contains(hostname, ",") { return hostname, fmt.Errorf("Will not resolve multi-hostname: %+v", hostname) } if (&InstanceKey{Hostname: hostname}).IsDetached() { return hostname, fmt.Errorf("Will not resolve detached hostname: %+v", hostname) } // First go to lightweight cache if resolvedHostname, found := hostnameResolvesLightweightCache.Get(hostname); found { return resolvedHostname.(string), nil } if !hostnameResolvesLightweightCacheLoadedOnceFromDB { // A continuous-discovery will first make sure to load all resolves from DB. // However cli does not do so. // Anyway, it seems like the cache was not loaded from DB. Before doing real resolves, // let's try and get the resolved hostname from database. if !HostnameResolveMethodIsNone() { if resolvedHostname, err := ReadResolvedHostname(hostname); err == nil && resolvedHostname != "" { hostnameResolvesLightweightCache.Set(hostname, resolvedHostname, 0) return resolvedHostname, nil } } } // Unfound: resolve! log.Debugf("Hostname unresolved yet: %s", hostname) resolvedHostname, err := resolveHostname(hostname) if config.Config.RejectHostnameResolvePattern != "" { // Reject, don't even cache if matched, _ := regexp.MatchString(config.Config.RejectHostnameResolvePattern, resolvedHostname); matched { log.Warningf("ResolveHostname: %+v resolved to %+v but rejected due to RejectHostnameResolvePattern '%+v'", hostname, resolvedHostname, config.Config.RejectHostnameResolvePattern) return hostname, nil } } if err != nil { // Problem. What we'll do is cache the hostname for just one minute, so as to avoid flooding requests // on one hand, yet make it refresh shortly on the other hand. Anyway do not write to database. hostnameResolvesLightweightCache.Set(hostname, resolvedHostname, time.Minute) return hostname, err } // Good result! Cache it, also to DB log.Debugf("Cache hostname resolve %s as %s", hostname, resolvedHostname) UpdateResolvedHostname(hostname, resolvedHostname) return resolvedHostname, nil }
// discoverInstance will attempt discovering an instance (unless it is already up to date) and will // list down its master and slaves (if any) for further discovery. func discoverInstance(instanceKey inst.InstanceKey) { instanceKey.Formalize() if !instanceKey.IsValid() { return } if existsInCacheError := recentDiscoveryOperationKeys.Add(instanceKey.DisplayString(), true, cache.DefaultExpiration); existsInCacheError != nil { // Just recently attempted return } instance, found, err := inst.ReadInstance(&instanceKey) if found && instance.IsUpToDate && instance.IsLastCheckValid { // we've already discovered this one. Skip! return } discoveriesCounter.Inc(1) // First we've ever heard of this instance. Continue investigation: instance, err = inst.ReadTopologyInstance(&instanceKey) // panic can occur (IO stuff). Therefore it may happen // that instance is nil. Check it. if instance == nil { failedDiscoveriesCounter.Inc(1) log.Warningf("instance is nil in discoverInstance. key=%+v, error=%+v", instanceKey, err) return } log.Debugf("Discovered host: %+v, master: %+v, version: %+v", instance.Key, instance.MasterKey, instance.Version) if !isElectedNode { // Maybe this node was elected before, but isn't elected anymore. // If not elected, stop drilling up/down the topology return } // Investigate slaves: for _, slaveKey := range instance.SlaveHosts.GetInstanceKeys() { slaveKey := slaveKey discoveryInstanceKeys <- slaveKey } // Investigate master: discoveryInstanceKeys <- instance.MasterKey }