[GODRIVER-1174] Topology will prefer a non-resolvable hostname over one known to resolve Created: 29/Jun/19  Updated: 27/Oct/23  Resolved: 29/Jul/19

Status: Closed
Project: Go Driver
Component/s: Server Discovery and Monitoring
Affects Version/s: None
Fix Version/s: None

Type: Bug Priority: Major - P3
Reporter: John Morales Assignee: Isabella Siu (Inactive)
Resolution: Gone away Votes: 0
Labels: None
Remaining Estimate: Not Specified
Time Spent: Not Specified
Original Estimate: Not Specified
Environment:

mongo-driver @ 60e65d0f2639c81f74a92904fcd10900e37b9be6 (tip of repo as of June 28)
macos


Issue Links:
Related

 Description   

I'm not entirely positive this is an expected behavior, however I do have reproducer programs in Java and Go where the Java program is successful but equivalent golang program is not.

Starting State (this can be setup locally on just a macbook, but I think is easier to illustrate in an environment with 2 servers):

  • Host A - server
    • hostname = "hostname.local"
    • single node replica set
    • rs.conf() is configured with members[0].host="hostname.local:27017"
  • Host B - client
    • Attempt to connect to the mongod on Host A using different hostname, such as one that's fully qualified: "hostname.acme.com:27017"
    • From Host B, "hostname.acme.com" is resolvable to address of Host A but "hostname.local" does not resolve

Expected: client communication is fine, because we found the server on Host A using the seed list entry "hostname.acme.com:27017" which is valid.
Actual: A Java program is successful but a golang program fails, appearing to initially connect and discover the configuration with hostname.local:27017, and then exclusively try and use hostname.local:27017 for all operations (which all fail because it's not resolvable from Host B).

Java Program (successfully prints ping command response):

  @Test
  public void testSDAM() throws Exception {
    try (final MongoClient client = new MongoClient(new MongoClientURI("mongodb://John-Morales-MacBook-Pro.local:10001"))) {
      System.out.println(client.getDatabase("admin").runCommand(new Document("ping", 1)));
    }
  }

go-driver program and output:

package main
 
import (
	"context"
	"flag"
	"fmt"
	"go.mongodb.org/mongo-driver/mongo"
	"go.mongodb.org/mongo-driver/mongo/options"
	"go.mongodb.org/mongo-driver/mongo/readpref"
	"time"
)
 
func ping(client *mongo.Client) error {
	ctx, cancel := context.WithTimeout(context.Background(), 5 * time.Second)
	defer cancel()
	return client.Ping(ctx, nil)
}
 
func main() {
	host := flag.String("host", "localhost", "Hostname to ping")
	port := flag.Int("port", 27017, "Port to ping")
	flag.Parse()
 
	var addrs string = fmt.Sprintf("%v:%v", *host, *port)
	hosts := []string{fmt.Sprintf("%v:%v", *host, *port)}
 
	clientOptions := options.Client()
	clientOptions.SetHosts(hosts).SetConnectTimeout(5 * time.Second).SetSocketTimeout(5 * time.Second).SetServerSelectionTimeout(2 * time.Second)
 
	rp, err := readpref.New(readpref.PrimaryMode)
	clientOptions.SetReadPreference(rp)
 
	client, err := mongo.NewClient(clientOptions.ApplyURI(fmt.Sprintf("mongodb://%v", addrs)))
	if err != nil {
		panic(nil)
	}
 
	if clientOptions.SocketTimeout != nil {
		ctx, cancel := context.WithTimeout(context.Background(), *clientOptions.SocketTimeout)
		defer cancel()
		err = client.Connect(ctx)
	} else {
		err = client.Connect(context.Background())
	}
 
	if err != nil {
		client.Disconnect(context.Background())
		panic(err)
	}
 
	err = ping(client)
	if err != nil {
		client.Disconnect(context.Background())
		panic(err)
	}
 
	fmt.Println(client.Disconnect(context.Background()))
}

Go program output:

$ go run -v godriver.go -host John-Morales-MacBook-Pro.local -port 10001
panic: server selection error: server selection timeout
current topology: Type: ReplicaSetNoPrimary
Servers:
Addr: does-not-exist:10001, Type: Unknown, State: Connected, Average RTT: 0, Last error: connection() : dial tcp: lookup does-not-exist: no such host
 
goroutine 1 [running]:
main.main()
	/Users/johnmorales/projects/mms-backup.git/goagent/src/10gen.com/backup-agent/play/godriver.go:54 +0x732
exit status 2

Additional note: if the connection URI is instead:
client, err := mongo.NewClient(clientOptions.ApplyURI(fmt.Sprintf("mongodb://%v/?connect=direct", addrs)))
... then the go program does work.



 Comments   
Comment by David Golden [ 03/Jul/19 ]

john.morales, in any split-horizon case (for any number of nodes), you'll need to use connect=direct to the public IP of one of the nodes. In 4.2, when split horizon support is released (PM-1289), you'll be able to do replica set connections to the public side of the split horizon (only if using TLS).

avbee, currently, a replica set must be configured to use the same IP addresses for member communications and client communications. If your client is inside the containerized environment, then the replica set configuration can use internal IPs. If your client is outside the containerized environment, then the replica set members must be configured with the same externally-visible IP addresses used by the clients. For more on MongoDB with Kubernetes, please see this article and if you have additional questions, I encourage you to post on the mongodb-user group or Stack Overflow with the mongodb tag, as this ticket isn't the right place for general Kubernetes+MongoDB questions.

Comment by bimo adi [ 03/Jul/19 ]

Hi @David Golden

I have similar issue with connection using go driver to my mongo HA in k8s cluster, so i nly have 1 Public IP to access. When i tried to curl, it returns . 

It looks like you are trying to access MongoDB over HTTP on the native driver port.

What is the correct way to connect?

Snippet: 

  client, err := mongo.NewClient(options.Client().ApplyURI("mongodb://root:password@12.45.68.78:27017"))
    if err != nil {
        fmt.Printf("1 %s", err)
    }
 
 
    ctx, cancel := context.WithCancel(context.Background())
    defer cancel()
 
 
    err = client.Connect(ctx)
    if err != nil {
        fmt.Printf("2 %s", err)
    }
 
 
    if err = client.Ping(ctx, nil); err != nil {
        fmt.Printf("3 %s", err)
    }

and it returns 

3   server selection error: server selection timeout
current topology: Type: ReplicaSetNoPrimary
Servers:
Addr: v1m1x-mongodb-primary-0.v1m1x-mongodb-headless.mongo.svc.cluster.local:27017, Type: Unknown, State: Connected, Average RTT: 0, Last error: connection() : dial tcp: lookup v1m1x-mongodb-primary-0.v1m1x-mongodb-headless.mongo.svc.cluster.local: no such host
Addr: v1m1x-mongodb-secondary-0.v1m1x-mongodb-headless.mongo.svc.cluster.local:27017, Type: Unknown, State: Connected, Average RTT: 0, Last error: connection() : dial tcp: lookup v1m1x-mongodb-secondary-0.v1m1x-mongodb-headless.mongo.svc.cluster.local: no such host
Addr: v1m1x-mongodb-secondary-1.v1m1x-mongodb-headless.mongo.svc.cluster.local:27017, Type: Unknown, State: Connected, Average RTT: 0, Last error: connection() : dial tcp: lookup v1m1x-mongodb-secondary-1.v1m1x-mongodb-headless.mongo.svc.cluster.local: no such host
Addr: v1m1x-mongodb-arbiter-0.v1m1x-mongodb-headless.mongo.svc.cluster.local:27017, Type: Unknown, State: Connected, Average RTT: 0, Last error: connection() : dial tcp: lookup v1m1x-mongodb-arbiter-0.v1m1x-mongodb-headless.mongo.svc.cluster.local: no such host

 
 

Comment by David Golden [ 02/Jul/19 ]

I think the Java driver example is doing the equivalent of "connect=direct" because you have a single host and no replica set name.  If you add the replica set name to the URI, I suspect you'll get the same result as the Go driver.  (Drivers are not consistent about how they interpret URIs for direct vs replica set mode. See SPEC-1248.)

SDAM says that in replica set mode, one should remove from the list of hosts anything that isn't in the member lists of the ismaster response, so the resolvable acme.com goes away and is replaced with the unresolvable .local.  (I.e. this is the "split horizon" problem.)  In direct connection mode, the server is treated as a standalone and the driver doesn't process the member lists of the ismaster response (and thus relies on the hostname provided in the seedlist).

Generated at Thu Feb 08 08:35:50 UTC 2024 using Jira 9.7.1#970001-sha1:2222b88b221c4928ef0de3161136cc90c8356a66.