Community Roadmap/Planning Meetings?

109 views
Skip to first unread message

Christopher Sterling

unread,
Nov 20, 2019, 1:50:54 PM11/20/19
to mongodb-go-driver
Are there presently any meetings where community members can come to discuss the upcoming roadmap for the mongo-go-driver project?
I've got a lot of issues with the driver coming from mgo, but we're required to use it by AWS.
I'm very open to submitting contributions to fix the issues I've been encountering, but a majority of the issues I'd like to fix seem to get shot down in JIRA.
I don't want to code MRs if the code will ultimately be thrown out, so my hope is I can align with the team so I can work the right things.

Divjot Arora

unread,
Nov 20, 2019, 11:21:50 PM11/20/19
to mongodb-go-driver
Hi Christopher,

The closest we currently have to community meetings are scheduled driver-specific meetups during the annual MongoDB World conference. During this meetup, developers attending the conference can ask questions to driver developers and provide suggestions/feedback.

Aside from this, we'd love to hear any feedback you have to offer. Is it possible for you to write up a list of issues you'd like us to address? For each issue, it would help us if you include the following:

1. The behavior you wanted
2. How the driver differs from mgo with respect to that behavior
3. Optionally, any Jira ticket(s) filed about the behavior that we closed or did not implement in a way that would help you.

We understand that this might be a lot of work for you to do, but it would help us tremendously to understand the needs of our users. Please understand that we can not guarantee that we will address each issue immediately, given that we cannot easily make backwards-breaking changes to our API and have a set of MongoDB driver specifications that we need to meet.

Christopher Sterling

unread,
Dec 19, 2019, 3:40:33 PM12/19/19
to mongodb-...@googlegroups.com

Sorry for the late reply Divjot -

Took me a while to get all this together. Sorry for the long post - I was trying to be thorough:

My general grievance with mongo-go-driver is how much harder it is to do normal day-to-day queries compared to mgo. I think the best way to understand that is to see it. Here are a few standard operations we end up doing implemented as best I can in mgo versus mongo-go-driver.

Update an existing object by it’s ID - one of the most common operations we perform in order to keep our performance optimal:

mgo:

func MgoUpdateID() error {

   coll := mgo.Collection{}

   var obj someStruct

   err := coll.UpdateId(obj.ID, obj)

   if err == mgo.ErrNotFound {

       logrus.Warn("Couldn't find that")

   } else if err != nil {

       logrus.Error("There was an internal issue with the DB")

   }

   return err

}

 


mongo-go-driver:

func MongoGoDriverUpsert() error {

   coll := mongo.Collection{}

   var obj someStruct

   info, err := coll.UpdateOne(

       context.Background(),

       bson.M{"_id": obj.ID}, obj)

   if err == nil {

       // err no longer is raised if not found - must compute from info result

       if info.MatchedCount == 0 &&

           info.ModifiedCount == 0 &&

           info.UpsertedCount == 0 {

           err = db.ErrNotFound

       }

   }

   if err == db.ErrNotFound {

       logrus.Warn("Couldn't find that")

   } else if err != nil {

       logrus.Error("There was an internal issue with the DB")

   }

   return err

}

Note that I now have to explicitly create a context, create the ID filter bson query, check the info result to generate a NotFound error (to make the error behavior consistent - NotFound is currently raised by FindOne but not by any other function). It’s more complicated to use now. It’s easy for a dev to forget the NotFound behavior is now inconsistent - it’s easier to introduce a bug. A majority of this code has to be copy+pasted over and over in each function. It makes me want to write a wrapper library for ease-of-use, but I feel like it makes more sense to just work on integrating the changes into mongo-go-driver itself.


Another example that I think shows the differences in usability comes in at Distinct. Here’s an example with a sort and a limit:

Mgo:

func MgoDistinctLimitSort(limit int) error {

   coll := mgo.Collection{}

   var adminNames []string

   err := coll.Find(bson.M{}).

       Sort("-adminName").

       Limit(limit).

       Distinct("adminName", &adminNames)

   if err == mgo.ErrNotFound {

       logrus.Warn("Couldn't find any distinct values")

   } else if err != nil {

       logrus.Error("There was an internal issue with the DB")

   }

   return err

}


Mongo-go-driver:

func MongoGoDriverDistinctLimitSort(limit int) error {

   coll := mongo.Collection{}

   adminNamesIface, err := coll.Distinct(

       context.Background(),

       "adminName",

       bson.M{})

 

   if err == nil && len(adminNamesIface) == 0 {

       // Pseudo-injection of not found error

       err = db.ErrNotFound

       logrus.Warn("Couldn't find any distinct values")

       return err

   } else if err != nil {

       logrus.Error("There was an internal issue with the DB")

       return err

   }

   adminNames := make([]string, len(adminNamesIface))

   for i, admin := range adminNamesIface {

       if adminName, ok := admin.(string); ok {

           adminNames[i] = adminName

       } else {

           err = fmt.Errorf("The adminName '%#v' could not be coerced to a string", admin)

           break

       }

   }

   // There is no ability to set Sort or Limit on Distinct options

   sort.Reverse(sort.Strings(adminNames)) // Sort by alpha

   if limit > 0 && len(adminNames) > limit {

       adminNames = adminNames[0:limit] // Limit

   }

   return err

}


Last example - Find using an iterator and timeout:

mgo:

func MgoFindWithIterTimeout(limit int) error {

   someProcessedString := ""

   var someObj someStruct

   var err error

   coll := mgo.Collection{}

   iter := coll.Find(bson.M{}).Limit(limit).Tail(

       time.Duration(2) * time.Second)

   defer iter.Close()

   for iter.Next(&someObj) {

       someProcessedString += someObj.ID.String()

   }

   if iter.Timeout() {

       logrus.Error("The DB timed out")

       return fmt.Errorf("db timeout")

   }

   err = iter.Err()

   if err == mgo.ErrNotFound {

       logrus.Warn("Couldn't find any distinct values")

   } else if err != nil {

       logrus.Error("There was an internal issue with the DB")

   }

   return err

}

mongo-go-driver:

func MongoGoDriverFindWithIterTimeout(limit int) error {

   someProcessedString := ""

   var someObj someStruct

   var err error

   coll := mongo.Collection{}

   ctx, cancel := context.WithTimeout(

       context.Background(),

       time.Duration(2)*time.Second)

   defer cancel()

   cursor, err := coll.Find(ctx, bson.M{})

   defer cursor.Close(context.Background())

   for cursor.Next(context.Background()) {

       if err = cursor.Decode(&someObj); err != nil {

           break

       }

       someProcessedString += someObj.ID.String()

   }

   if err == nil {

       err = cursor.Err()

   }

   if err == context.DeadlineExceeded {

       // A timeout occurred

       logrus.Error("There was a timeout")

       return err

   } else if err == nil && len(someProcessedString) == 0 {

       err = db.ErrNotFound

       logrus.Warn("Couldn't find any distinct values")

       return err

   } else if err != nil {

       logrus.Error("There was an internal issue with the DB")

       return err

   }

   return err

}

As a dev, I don’t really care about the context. Especially since mongo isn’t currently honoring it when it’s canceled. All I want to do is provide a time Duration and let the library handle the timeout internally. I don’t want to have to check for ErrNotFound.

Here is a goplay of my mongo-go-driver examples: https://goplay.space/#oYo-txFAmfC

And a goplay of the mgo examples: https://goplay.space/#v4QFP_ZrP_e


In addition to the ease-of-use issues, the migration path for us was insanely hard. We thoroughly read https://www.mongodb.com/blog/post/go-migration-guide but there were SO MANY gotchas that we had to account for that aren’t mentioned by any documentation. Most of these things required us to read the mongo-go-driver codebase directly to solve. e.g. 

  • The “truncate” tag is required with float32 for our pre-existing date mgo seemed to natively store them as float64. There was no documentation on the truncate tag. The error message the driver yields is unhelpful to actually know what to do to fix the issue.

  • Uninitialized arrays are handled very differently under the covers - mgo handles uninitialized slices as empty arrays, this makes things like $push operations really easy later. With mongo-go-driver, uninitialized arrays are set to null, which means that any subsequent $push or other array operations will fail. Making this a toggleable option, something like `SetHandleUninitializedSlicesAsArrays` would be helpful. The decoder/encoder are really hard to use directly, so adding some ease of use functions around these common options would be helpful.

  • Timestamps are handled differently under the covers - used to, if they were a string they were coerced into a time.Time. Now there is no coercion, if the timestamp isn’t a datetime in mongo, it errors out, even though golang natively supports parsing the string timestamp.

  • ErrNotFound is handled completely differently than mgo. It is inconsistent - only raised by FindOne and not raised by any other call. Adding something to writeconcern, such as `SetRaiseErrorOnNoDocuments(true)` could be used to make this behavior consistent. I realize this isn’t supported by the API, but it doesn’t seem like it would be difficult to implement natively in golang.

  • The use of bson.D was really confusing in the docs/examples. Why use it rather than bson.M and bson.A? They result in much cleaner looking code in general.

  • ObjectID is now on it’s own island in primitives - you have to import bson for the standard types and also import primitives just to pick up ObjectID.

For the 2nd and 3rd points it took a lot of work to get any kind of help. Your responses on the Google Group have been invaluable, but I’m still having issues implementing the solutions you suggested (e.g. using .Kind() to override default nil slice behavior and using the registry to modify default handling of datetimes).


There are not JIRA issues for all of my grievances, but here are some JIRA Issues that relate (some open/some closed):


Generally*, the closed/won’t work tickets seemed to read as “If mongo DB doesn’t do natively / can’t be supported by the API, it won’t be worked”. Is it frowned upon to have extra processing in the golang library in the mongo org? From a standardization perspective, I suppose I can understand this attitude, but it results in a bad experience for programmers that want to make DRY code without having to roll their own wrappers for each project.


I’m more than happy to take on writing an “easy-mongo” wrapper if the team is willing to consider allowing it to be part of the library. I’m not looking to fork and maintain compatibility - I think that’s why mgo died. I would like this to live in mongo-go-driver. It would include U/X things, like adding *ByID() functions, handling timeout/contexts all under the covers, add the ability to make ErrNotFound easy to raise consistently, improving ease of use with Distinct(), adding common encoders/decoders for nil slice handling/timestamps/float truncation/etc. Generally making the code as lean as possible to make it fast to implement new functionality.


Thoughts on all of this?

Rachelle Palmer

unread,
Jan 24, 2020, 12:10:01 PM1/24/20
to mongodb-go-driver
Hi Christopher,
Now we owe you an apology for our own delay on replying. Thank you for this thoughtful and thorough analysis of the differences and complexities of the go driver vs mgo.  We are currently working on a detailed response for each of the items that you raise below; some of them require internal discussion as well as research on the differences, and what improvements can be made. 

Philosophically we are in agreement with you in that we would also prefer that functionalities live in the mongo-go-driver and not in a wrapper. At the same time, we want to be thoughtful about introducing new methods in the API, reluctant to changes of the existing functionality. Further response will be forthcoming, though. In the meantime we are also re-examining the tickets you've linked above.

Thank you,
Rachelle

Christopher Sterling

unread,
Mar 2, 2020, 2:23:57 PM3/2/20
to mongodb-go-driver
has this been migrated to the new forum? I was having a hard time locating it, but maybe I'm just searching for it wrong.

Stennie Steneker

unread,
Mar 2, 2020, 5:21:03 PM3/2/20
to mongodb-go-driver
On Tuesday, 3 March 2020 06:23:57 UTC+11, Christopher Sterling wrote:
has this been migrated to the new forum? I was having a hard time locating it, but maybe I'm just searching for it wrong.

Hi Christopher,

We aren't moving existing content to the new community forum as that would add complications around attribution for individual posts. Past discussion will remain archived in this Google Group.

However, the equivalent for discussion on the new forum is the go-driver tag: https://community.mongodb.com/tag/go-driver.

If you click on the 🔔 icon at the top right of a category or tag page on the new forum you can choose your notification preferences.

Regards,
Stennie
Reply all
Reply to author
Forward
0 new messages