zookzook / elixir-mongodb-driver

MongoDB driver for Elixir
Apache License 2.0
243 stars 60 forks source link

Passing :read_preference globally #206

Closed JohannesSoots closed 1 year ago

JohannesSoots commented 1 year ago

Hi! I'm trying to set read_preference globally, right now as far as I see you have to pass them to each query explicitly.

Looking through code, would it make sense if :read_preference was passed to Mongo.start_link/1 then right now it would already be stored in Mongo.Topology state in which case it could be used when :checkout_session happens?

Or allow passing this via URL params would also be an option.

zookzook commented 1 year ago

Sounds good. It seems that this is missing! The read-preferences are already in the topology process. I will take a look at the code.

JohannesSoots commented 1 year ago

Sounds good. It seems that this is missing! The read-preferences are already in the topology process. I will take a look at the code.

Awesome, thanks! We initially had the readPreference in the URL params and I noticed it was parsed and passed to Topology, but it's not parsed into the same format as the driver expects. Not sure if it will cause conflict as is, so might make sense either to exclude parsing these entirely or map them into expected format.

For example &readPreference=nearest&readPreferenceTags=dc:west would get parsed [read_preference: :nearest, read_preference_tags: "dc:west"]. These would be passed down with other options.

zookzook commented 1 year ago

You can try the PR https://github.com/zookzook/elixir-mongodb-driver/pull/207 to see if this is working.

JohannesSoots commented 1 year ago

If passing them from query params it seems to work parse and passes the read preference properly, but when given :read_preference to Mongo.start_link/1 then it blows up.

read_preference: %{
    mode: :secondary,
    max_staleness_ms: 120_000,
    tag_sets: [dc: "west", usage: "production"]
}
** (Mix) Could not start application my_app: Application.start(:normal, []) returned an error: shutdown: failed to start child: :mongo
    ** (EXIT) an exception was raised:
        ** (FunctionClauseError) no function clause matching in String.split/3
            (elixir 1.14.0) lib/string.ex:479: String.split([], ",", [])
            (mongodb_driver 1.2.0) lib/mongo/url_parser.ex:238: Mongo.UrlParser.parse_tags/1
            (mongodb_driver 1.2.0) lib/mongo/url_parser.ex:217: Mongo.UrlParser.extend_read_preference_tags/2
            (mongodb_driver 1.2.0) lib/mongo/url_parser.ex:207: Mongo.UrlParser.process_read_preferences/1
            (mongodb_driver 1.2.0) lib/mongo/url_parser.ex:186: Mongo.UrlParser.parse_url/1
            (mongodb_driver 1.2.0) lib/mongo.ex:140: Mongo.start_link/1
            (stdlib 4.2) supervisor.erl:414: :supervisor.do_start_child_i/3
            (stdlib 4.2) supervisor.erl:400: :supervisor.do_start_child/2
            (stdlib 4.2) supervisor.erl:384: anonymous fn/3 in :supervisor.start_children/2
            (stdlib 4.2) supervisor.erl:1250: :supervisor.children_map/4
            (stdlib 4.2) supervisor.erl:350: :supervisor.init_children/2
            (stdlib 4.2) gen_server.erl:851: :gen_server.init_it/2
            (stdlib 4.2) gen_server.erl:814: :gen_server.init_it/6
            (stdlib 4.2) proc_lib.erl:240: :proc_lib.init_p_do_apply/3
zookzook commented 1 year ago

Let me fix this issue.

zookzook commented 1 year ago

I pushed a new PR that fixes the issue.

JohannesSoots commented 1 year ago

Thanks a lot! Tested and so far passing read_preference works both ways either passing URL or as Keyword.

I don't know if this is intended or not, but when you set these read preferences, but you try to connect to single mongo instance not a cluster it timeouts in Topology :checkout_session. It's not issue for us really as we'll just configure it based on environment.

** (exit) exited in: GenServer.call(:mongo, {:checkout_session, :read, [batch_size: 1]}, 60000)
    ** (EXIT) time out
    (elixir 1.14.0) lib/gen_server.ex:1038: GenServer.call/3
    (mongodb_driver 1.2.0) lib/mongo/session.ex:146: Mongo.Session.start_session/3
    (mongodb_driver 1.2.0) lib/mongo/stream.ex:40: Mongo.Stream.checkout_session/2
    (mongodb_driver 1.2.0) lib/mongo/stream.ex:21: Mongo.Stream.new/3
    (mongodb_driver 1.2.0) lib/mongo.ex:776: Mongo.find/4
    (mongodb_driver 1.2.0) lib/mongo.ex:804: Mongo.find_one/4
    iex:218: (file)
zookzook commented 1 year ago

Yeah, this is correct, because of the tag_sets used there. With a singleton, it does not make sense. The topology process is not able to select a server and therefore the GenServer call will timeout.

zookzook commented 1 year ago

It is fixed so far, closing the issue.