Connect to remote mongo server works fine but when connecting through Go, it gives err: context deadline exceeded

Viewed 1025

I'm connecting to remote mongo server using following ssh tunneling command:

ssh -i document-db-tun.pem -L 27017:docdb.cluster-cf.us-east-2.docdb.amazonaws.com:27017 ubuntu@ec2-111-111-111-111.us-east-2.compute.amazonaws.com -N

Also connecting to mongo server works fine using mongo command locally.

enter image description here

But when I'm trying to connect to it locally using golang code it throws Mongo connection ckeck failed. Err: context deadline exceeded error.

const (
    mongoTimeout = time.Second * 10
)

func initMongo() {
    mongoConf := common.Conf.ServiceConfig.Databases.Mongo

    ctx, _ := context.WithTimeout(context.Background(), mongoTimeout)
    uri := fmt.Sprintf("mongodb://127.0.0.1:27017")
    if common.IsSentryEnabled() {
        sentry.AddBreadcrumb(&sentry.Breadcrumb{
            Message:  fmt.Sprintf("Connecting to mongo server at: '%v'", uri),
            Category: common.SENTRY_CAT_REPO,
            Level:    sentry.LevelInfo,
        })
    }
    client := options.Client().
        SetReadPreference(readpref.SecondaryPreferred()).
        SetAppName("catalog").
        SetMaxConnIdleTime(time.Microsecond * 100000).
        SetAuth(options.Credential{
            Username: mongoConf.Username,
            Password: mongoConf.Password,
        }).
        ApplyURI(uri)

    pureMongoClient, err := mongo.Connect(ctx, client)
    if err != nil {
        if common.IsSentryEnabled() {
            sentry.AddBreadcrumb(&sentry.Breadcrumb{
                Message:  "Error connecting to mongo",
                Category: common.SENTRY_CAT_REPO,
                Level:    sentry.LevelFatal,
            })
            sentry.ConfigureScope(func(scope *sentry.Scope) {
                scope.SetTags(map[string]string{
                    common.SENTRY_SCOPE_KEY: common.SENTRY_SCOPE_MONGO,
                    common.SENTRY_TYPE_KEY:  common.SENTRY_TYPE_DB,
                })
            })
            sentry.CaptureException(err)
            sentry.Flush(time.Second * 5)
        }

        panic(fmt.Sprintf("Failed to establish mongo connection. Err: %v ", err))
    }

    err = pureMongoClient.Ping(ctx, readpref.Primary())
    if err != nil {
        if common.IsSentryEnabled() {
            sentry.AddBreadcrumb(&sentry.Breadcrumb{
                Message:  "Mongo connection ckeck failed",
                Category: common.SENTRY_CAT_REPO,
                Level:    sentry.LevelFatal,
            })
            sentry.ConfigureScope(func(scope *sentry.Scope) {
                scope.SetTags(map[string]string{
                    common.SENTRY_SCOPE_KEY: common.SENTRY_SCOPE_MONGO,
                    common.SENTRY_TYPE_KEY:  common.SENTRY_TYPE_DB,
                })
            })
            sentry.CaptureException(err)
            sentry.Flush(time.Second * 5)
        }

        panic(fmt.Sprintf("Mongo connection ckeck failed. Err: %v ", err))
    }

    mongoDB := pureMongoClient.Database(mongoConf.Dbname)

    dbManager.MongoDB = &MGODatabase{
        Database: mongoDB,
    }
}

Username and Password used in the code are those which are required to connect to the remote server. What might be going wrong while connecting to the remote mongo server using golang code?

3 Answers

From the golang mongo driver docs:

When creating an options.ClientOptions, the order the methods are called matters. Later Set* methods will overwrite the values from previous Set* method invocations. This includes the ApplyURI method. This allows callers to determine the order of precedence for option application. For instance, if ApplyURI is called before SetAuth, the Credential from SetAuth will overwrite the values from the connection string. If ApplyURI is called after SetAuth, then its values will overwrite those from SetAuth.

The opts parameter is processed using options.MergeClientOptions, which will overwrite entire option fields of previous options, there is no partial overwriting. For example, if Username is set in the Auth field for the first option, and Password is set for the second but with no Username, after the merge the Username field will be empty.

Your ApplyURI essentially overrides all previous options you have set for the options.ClientOptions instance.

Try changing the order like this:

client := options.Client().
    ApplyURI(uri).
    SetReadPreference(readpref.SecondaryPreferred()).
    SetAppName("catalog").
    SetMaxConnIdleTime(time.Microsecond * 100000).
    SetAuth(options.Credential{
        Username: mongoConf.Username,
        Password: mongoConf.Password,
    })

Hope it helps.

27017:docdb-2019-07-30-09-40-47.cluster-cffrhwfhhfof.us-east-2.docdb.amazonaws.com:27017

The URI shows that this is AWS DocumentDB, and not MongoDB. DocumentDB is not based on the MongoDB server. Rather it emulates the MongoDB API, and runs on top of Amazon’s Aurora backend platform.

If you intend to use DocumentDB, instead of using MongoDB official Go driver, please see SDK for Go: docdb as there are differences and compatibility issues between the two:

Depending on your use case, see MongoDB Atlas

Err: context deadline exceeded

This error message indicates that given the amount of time specified on context, it failed to connect. There are many reasons that could caused this, possibilities are:

  • The client machine is on different VPC than the DocumentDB cluster
  • Security group of your cluster does not permit inbound connections 27017
  • The client machine is in a different region than the DocumentDB cluster

See also Troubleshooting DocumentDB

Here are some possible problems

  • Port issue
    You need to check the ports are open and access is granted to connect from anywhere. If you are using Linux(Ubuntu), please check the firewall settings sudo ufw status verbose. If the firewall is disabled then it's fine.

  • VM port access
    Also allow the port at VM level. If you are using Amazon Lightsail then you will have to explicitly allow the port to give access to remote client to make the connection.

enter image description here

  • Docker Container
    If your application and MongoDB is deployed as Docker containers to the remote server and you are unable to establish connection with DB, then once possible solution is Docker Network.
    • Create your own Docker network
    • Create MongoDB container and name container, for example mongodb
    • Add MongoDB to your network
    • Add Web app to your network
    • Try to connect to MongoDB container using web app via container name.
// Set client options
    clientOptions := options.Client().ApplyURI("mongodb://mongodb:27017") 
    clientOptions = clientOptions.SetMaxPoolSize(100)
Related