Skip to content

Unable to set readPreference #4831

Closed
Closed

Description

@rossdraper

Issue Description

When using parse-server 2.7.2 with a mongodb geographically seperated replicaset, using readPreference=nearest on the URI(or in databaseOptions) is not working. When setting mongoclient log level to debug I can see:

"readPreference":{"mode":"primary"}

showing in the logs for queries and they are going to the primary IP.

Steps to reproduce

Connect to any replicaset using the normal uri string:
mongodb://dbuser:dbpassword@10.0.0.1:27017,10.1.0.1:27017,10.2.0.1:27017/dbname?replicaSet=RS01&w=1&readPreference=nearest&connectTimeoutMS=30000&maxPoolSize=300',

Expected Results

Running mongostat I expect to see queries logged on the nearest db and in the debug logs for the parse server/mongo client I expect to see queries passed to the nearest server. This also happens for secondary or secondaryPreferred.

Actual Outcome

The readPreference is reset to "PRIMARY".

Environment Setup

  • Server

    • parse-server version (Be specific! Don't say 'latest'.) : 2.7.2
    • Operating System: Ubuntu 16.04
    • Hardware: T2 Large
    • Localhost or remote server? AWS
  • Database

    • MongoDB version: Percona Server for MongoDB shell version: 3.2.18-3.9
    • Storage engine: wiredtiget
    • Hardware: T2 Large
    • Localhost or remote server? AWS

Logs/Trace

I have added some console log statements to parse-server/lib/Adapters/Storage/Mongo/MongoStorageAdapter.js

I can see that the value "nearest" is correctly used in the constructor, however it is getting reset to "PRIMARY" because calls to _parseReadPreference are all having a undefined value for readPreference passed in. As such they hit lines 628-631 which is marked as being added due to testing and it gets reset to "PRIMARY'.

case undefined:
// this is to match existing tests, which were failing as mongodb@3.0 don't report readPreference anymore
readPreference = ReadPreference.PRIMARY;
break;

My knowledge of the source code is not good enough to trace this back further, but it seems that this is a option to support per query read preferences instead of using the global setting from the URI. If I set this to be:

readPreference = ReadPreference.NEAREST;

then things work as expected.

Activity

  1. flovilmart commented on Jun 13, 2018

    @flovilmart
    Contributor

    Ok, so basically, the readPreference parse forces PRIMARY when none is provided instead of using the one provided at database initialization level. Are you willing to provide a fix for that? It seems reasonable to change it so readPreference is 'undefined' if not provided.
    https://github.com/parse-community/parse-server/blob/master/src/Adapters/Storage/Mongo/MongoStorageAdapter.js#L630

  2. rossdraper commented on Jun 13, 2018

    @rossdraper
    Author

    Thanks for the quick response. It looks like this is a result of the change implemented here: #4449
    This was implemented to ensure your tests don't break, and not being familiar with your tests I'm not sure what your desired outcome would be(presumably anything other than undefined). I dont mind having a look, but its 1am here. If you are happy I'm not losing my mind and that this is a genuine bug, then I'll look where the appropriate value is and suggest a fix.

  3. flovilmart commented on Jun 13, 2018

    @flovilmart
    Contributor

    I believe the readPreference should not be passed on a query basis if it is not provided, as it overrides the default driver behavior. I can have a look too :)

  4. rossdraper commented on Jun 13, 2018

    @rossdraper
    Author

    Thanks for looking at this, I may have made a mistake here, but, I've manually put in your change(excluding the spec files) by commenting out the line) and it doesn't resolve my issue. Reads go to the Primary again. If I do the following:

    if (readPreference === undefined)       {
            readPreference = this._mongoOptions.readPreference.toString();
            readPreference = readPreference.toUpperCase();
            console.log('readpref now: ' + readPreference);
    }
    

    Then it works as I would expect.

  5. flovilmart commented on Jun 13, 2018

    @flovilmart
    Contributor

    Uhm... interesting, but what bothers me is that we should not touch this prop, the driver should do it’s job and use the provided readPreference unless specified.

    I perhaps suspect that passing readPreference: undefined, makes the driver use the primary.

    What if your ‘delete’ readPreference if undefined, jnstesd of you ur workaround?

  6. rossdraper commented on Jun 18, 2018

    @rossdraper
    Author

    Apologies for the delay in replying. Sorry, I'm not sure I understand what you want me to do? If you can point me to the section of code that parses and creates the structure for the mongodb URI, I can look deeper as my above solution only works if you add the databaseOptions parameter at the top level. Many thanks. I"m not sure why my URI readPreference at the URI level is not adhered to when using your submitted fix.

  7. flovilmart commented on Jun 18, 2018

    @flovilmart
    Contributor

    I believe the issue lies in passing undefined. And the driver sets internally as PRIMARY, not the connection parameter.

    My suggestion would be to not pass the readPreference if it’s un defined at all. Do you follow?

  8. rossdraper commented on Jun 18, 2018

    @rossdraper
    Author

    Understood. Thanks for clarification. Let me test again on my side and update you.

  9. rossdraper commented on Jun 18, 2018

    @rossdraper
    Author

    If I understand correctly it appears that if you omit the readPreference entirely it is defaulting to PRIMARY. I've tested this by simply commenting out in MongoStorageAdaptor as follows:

    find(className, schema, query, { skip, limit, sort, keys, readPreference }) {
    <snip>
    return this.createTextIndexesIfNeeded(className, query, schema).then(() => this._adaptiveCollection(className)).then(collection => collection.find(mongoWhere, {
    skip,
    limit,
    sort: mongoSort,
    keys: mongoKeys,
    maxTimeMS: this._maxTimeMS,
    //      readPreference
    

    with debug logging, I still see all requests going to the PRIMARY despite the database object clearly showing readPreference is nearest:

    Db {
    domain: null,
    _events: { error: [Function], close: [Function] },
    _eventsCount: 2,
    _maxListeners: undefined,
    s:
    { databaseName: 'mydb,
    dbCache: {},
    children: [],
    topology:
    ReplSet {
    domain: null,
    _events: [Object],
    _eventsCount: 22,
    _maxListeners: undefined,
    clientInfo: [Object],
    s: [Object] },
    options:
    { w: 1,
    readPreference: [Object],
    promiseLibrary: [Function: Promise] },
    logger: Logger { className: 'Db' },
    bson: BSON {},
    readPreference: ReadPreference { mode: 'nearest', tags: undefined, options: undefined },
    bufferMaxEntries: -1,
    parentDb: null,
    pkFactory: undefined,
    nativeParser: undefined,
    promiseLibrary: [Function: Promise],
    noListener: false,
    readConcern: undefined },
    serverConfig: [Getter],
    bufferMaxEntries: [Getter],
    databaseName: [Getter] }

  10. flovilmart commented on Jun 18, 2018

    @flovilmart
    Contributor

    That would mean that at large the driver option is not respected when initializing it. Which is odd..

  11. added a commit that references this issue on Jun 27, 2018
  12. flovilmart commented on Jun 27, 2018

    @flovilmart
    Contributor

    @rossdraper have a look at this particular commit 87a7c96 Everything looks fine now.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions