mashingan / anonimongo

Another Nim pure Mongo DB driver
MIT License
43 stars 4 forks source link

How do I handle reconnections? #34

Open danbarbarito opened 5 months ago

danbarbarito commented 5 months ago

What is the best way to handle reconnections when the connection gets lost? I'm trying to use this library with Prologue. When the connection gets lost, I see an error message like this:

cannot read from stream
Async traceback:
  C:\Users\dan\Projects\myproject\src\myproject.nim(49) myproject
  C:\Users\dan\.nimble\pkgs2\prologue-0.6.4-60e959a0e99a1e8fccca9d05440080651b74bccd\prologue\core\application.nim(538) run
  C:\Users\dan\.nimble\pkgs2\prologue-0.6.4-60e959a0e99a1e8fccca9d05440080651b74bccd\prologue\core\application.nim(533) run
  C:\Users\dan\.nimble\pkgs2\prologue-0.6.4-60e959a0e99a1e8fccca9d05440080651b74bccd\prologue\core\naive\server.nim(30) serve
  C:\nim\lib\pure\asyncdispatch.nim(2022) waitFor
  C:\nim\lib\pure\asyncdispatch.nim(1711) poll
  C:\nim\lib\pure\asyncdispatch.nim(456) runOnce
  C:\nim\lib\system\threadimpl.nim(269) processPendingCallbacks
  C:\Users\dan\.nimble\pkgs2\anonimongo-0.7.0-9819c122ee2ff26affd50b27f245c1a65b27cef8\anonimongo\core\wire.nim(250) getReply (Async)
  C:\Users\dan\.nimble\pkgs2\anonimongo-0.7.0-9819c122ee2ff26affd50b27f245c1a65b27cef8\anonimongo\core\bson.nim(95) msgHeaderFetch
  C:\nim\lib\pure\streams.nim(635) readInt32
  C:\nim\lib\pure\streams.nim(426) read
Exception message: cannot read from stream

The connection does not appear to attempt to reconnect on its own, so I have to restart my application to get everything working again.

I am using MongoDB Atlas Cloud and to test this issue I am forcefully disconnecting my open connection by going to the Security > Network Access page and removing my IP address. Once the application starts throwing the above error, I add my IP address again. The error persists until I restart the application. If this library does not do reconnections, I assume I would need to manually re-run the connect and authenticate functions again in response to this exception, but I don't know where to add that logic in a typical Prologue application.

What is the best way to fix this? How should I catch and handle this exception in a typical Prologue application?

samsamros commented 5 months ago

If you are able to provide a snippet of your code, that would give us a better idea. You can make one as an example if you do not wish to share sensitive code. but just to understand where the problem is originating. Are you compiling with the debug or release flag defined? Compile it with debug to see a little more on the error, and see exactly what error it's raising. It's gonna be slower and the binary will be larger, but it will provide more info on the error.

You can create a template that spawns upon that error and asserts the application's connection to a socket. But that's just a guess. If you could provide a little more information I'd be happy to take a look when I get the chance.

danbarbarito commented 5 months ago

@samsamros Thanks for the quick response! I am compiling with the debug flag and this is the stacktrace I received.

I pushed up a repo to hopefully make it easy to reproduce the issue. You can find it here: https://github.com/danbarbarito/anonimongo-reconnection-issue

All you should need to do is create a new .env file in the project directory with the values found in sample.env, and then nimble run. At first, if the database is up, everything works great! But once I lose connection to Mongo, it starts throwing the error I posted above. Even if connection should be regained, the application will continue to throw errors until I restart the application.

Also, the implementation of the mongoClient proc in src\db.nim is there because I was getting gcsafe errors when referencing the global gMongoClient within the Prologue handlers. If there is a better way to fix that issue, please let me know!

samsamros commented 5 months ago

I'll take a look as soon as I can! For those global variables, off the top of my head, you can try the {.threadvar.} pragma. That will create a copy of the var within the thread's scope. As long as you don't redefine the variable (or object) you'll be able to call the original global values without the global variable warning going off.

something like this (but in your case the mongoClient):

var
  localcol* {.threadvar.}: Collection[AsyncSocket] 

you can initialize the object from the global variable, and it will take those values into the thread's scope. You can also redefine the variable, but only within that thread's scope. I will try to take a look at your code and see where the problem is arising.

danbarbarito commented 1 week ago

Just curious - any updates on this one?