mongodb-haskell/mongodb

Connecting to MongoDB Atlas

marzhaev opened this issue · 9 comments

I have migrated from self hosted MongoDB to MongoDB Atlas hosted replica. There seems to be a problem authenticating my Yesod application which throws:

Error handler errored out: InternalError "ConnectionFailure <socket: 22>: hGetBuf: resource vanished (Connection reset by peer)"

Having migrated Ruby and NodeJS application, I believe the problem is the configuration needs to
include:

  • SSL flag to be set to truth.
  • AuthSource to be specified.

MongoDB's official manual at https://docs.mongodb.com/manual/reference/connection-string/ suggests to pass this along as URL's params. But this violates URL parser at readHostPortM in Database.MongoDB.Connection

Have you looked into this module: https://hackage.haskell.org/package/mongoDB-2.3.0.1/docs/Database-MongoDB-Transport-Tls.html
I'm not sure how persistent implements passing the connection information to mongodb driver.

Try using this module. I'll set up an account for Atlas and will take a look.

@marzhaev Did it work for you?

yesodweb/persistent#774

I'm trying to resolve the issue. I've managed to use Database.MongoDB.Transport.Tls but I'm running into the following error:

*** Exception: PersistMarshalError "User: could not find _id field:  for doc: [ ok: 0, errmsg: \"user is not allowed to do action [find] on [test.user]\", code: 8000, codeName: \"AtlasError\"]"

This user does have permission from my understanding, I can 'insert' a record using the mongo console successfully.

Hmm I'm guessing as it's not specifying the AuthSource anywhere? How can this be set?

testAccess :: DB.Pipe -> Database -> Maybe MongoAuth -> IO ()
testAccess _ _ mAuth = do
    pipe <- DBTLS.connect "cluster0-shard-00-00-gv0qj.mongodb.net" (PortNumber 27017)
    x <- case mAuth of
      Just (MongoAuth user pass) -> DB.access pipe DB.UnconfirmedWrites "admin" (DB.auth  user pass)
      Nothing -> error "no auth"
    print x
    return ()

I've narrowed it down to the above (the above seems to work). If I change the database from admin to test it fails (x becomes false)... (where admin is an existing database, and test is not).

A test database is currently not present on the server. I assumed it would be created automatically (it did do this on a local mongo server).

Maybe something to do with the following line? Is this meant to be a hardcoded admin vaule?

sd <- access p slaveOk "admin" retrieveServerData

Okay! I've finally got this to work! You can only authenticate against the admin table according to https://stackoverflow.com/questions/38237663/mongo-atlas-connection-authentication-failed-with-custom-databases.

So it seems the issue is somewhere in yesodweb/persistent.

In the event it is useful to someone, this works for me:

actions :: ReaderT MongoContext IO ()
actions = do
  i <- insert $ User "Dalalalala" $ Just 1234
  lift $ print i

testAccess :: DB.Pipe -> Database -> Maybe MongoAuth -> IO ()
testAccess _ _ mAuth = do
    pipe <- DBTLS.connect "cluster0-shard-00-00-gv0qj.mongodb.net" (PortNumber 27017)
    x <- case mAuth of
      Just (MongoAuth user pass) -> DB.access pipe DB.UnconfirmedWrites "admin" (DB.auth  user pass)
      Nothing -> error "no auth"
    print x
    DB.access pipe DB.UnconfirmedWrites "test" (actions)
    return ()

@chrissound did you have any luck with writing to a shard? I am able to connect to an Atlas DB the same way you were, and I am able to read from it, but my writes aren't going through, although it is generating mongo ids for each document to be inserted.

I figured out why the writing wasn't working. I was hitting a secondary node rather than the primary one.