[CXX-718] mongo::client::initialize() considered harmful Created: 22/Oct/15 Updated: 26/Oct/15 Resolved: 24/Oct/15 |
|
| Status: | Closed |
| Project: | C++ Driver |
| Component/s: | API, Implementation |
| Affects Version/s: | None |
| Fix Version/s: | None |
| Type: | Bug | Priority: | Major - P3 |
| Reporter: | Tim Niemueller | Assignee: | Andrew Morrow (Inactive) |
| Resolution: | Done | Votes: | 0 |
| Labels: | legacy-cxx | ||
| Remaining Estimate: | Not Specified | ||
| Time Spent: | Not Specified | ||
| Original Estimate: | Not Specified | ||
| Description |
|
Newer versions of mongo-cxx-driver require that mongo::client::initialize() is called at the beginning. I cannot remember having to do that for not much older versions. This causes major problems for us. In Fawkes, a robot software system, we have plugins loaded at run-time, some of which use MongoDB through mongo-cxx-driver. Now the initalization could require different options, where only the first would actually be accepted. Especially shutdown cannot be called lightly as it breaks the library for plugins still loaded. The question now is, why is it necessary to rely on global or static initialization at all? It would be much cleaner to have a handle which bundles everything related to a particular instance. Ideally, this would be per database connection so that they can be fully independent. The problem here is, that the library is assumed too much to be a platform that resides in a single application. The only benefit I see is that it avoids having to have a single place where the initialization of the many (more or less unrelated) components is performed. However, this comes at the cost of making the initialization trace largely non-obvious and cause problems as just described by relying on static initialization. The source separation comes at the cost of run-time obfuscation. Is there another reason overseen and can it be considered to re-factoring the library to avoid the use of a single static initializer? |
| Comments |
| Comment by Andrew Morrow (Inactive) [ 26/Oct/15 ] |
|
The driver that is built from the master branch shares no code with the legacy driver. It will install headers under a different path, has a completely different API, will use different namespaces, and the library will be named differently. So, it should be possible to have both the legacy driver and the new C++11 driver installed on the same system side by side. In fact, it should be fine to have both loaded in the same address space, if you really need to. So, the problematic part isn't so much making it available without conflicts, as that people will need to re-write their applications to use it. And of course, they need to be able to use C++11. |
| Comment by Tim Niemueller [ 26/Oct/15 ] |
|
I agree. While I'm still in favor of the implicit initialize() call on the creation of the first connection (the typical use case), I can see that you would rather put your effort in the new driver. However, consider that virtually everywhere the legacy driver is the mongo-cxx-driver (Fedora, FreeBSD, etc.). Hence the switch to the "actual" mongo-cxx-driver master branch may prove problematic. |
| Comment by Andrew Morrow (Inactive) [ 24/Oct/15 ] |
|
Hi timn - I'm closing this as works as designed, but please feel free to re-open or comment if you have further thoughts. While we certainly aren't happy about the situation with respect to initializing the driver either, we don't feel that investing the effort to repair this aspect of the legacy driver is worth the engineering effort and correctness risk that it would entail. |
| Comment by Andrew Morrow (Inactive) [ 22/Oct/15 ] |
|
Well, auto-initializing the driver might be achievable, but you would still be obligated to terminate it by calling client::terminate since there are background threads that must be joined before destructors run. How would we know when to run terminate if we auto-initialized? It would also require adding an "are we initialized check" into every code path that accessed any global state that is configured via "mongo initializers", and doing so in a thread safe fashion. Running the mongo initializers is one of the primary functions of calling the initialize function. The current plan for the C++11 driver is to ship it before the end of this year. |
| Comment by Tim Niemueller [ 22/Oct/15 ] |
|
I see, that is indeed pretty much a deadend then. The thing is now that you offer options you made the problem a bit worse because multiple plugins could pass in incompatible options and have different expectations. On our end we can deal with this gracefully (we have one central plugin managing connections for the others, but we have to leave MongoDB around when unloading the plugin as it might be loaded again, in which case we would not be able to initialize it again. I understand you do not want to put a lot of effort in the legacy driver and focus on the new code. I would still suggest to add implicitly initializing with default options when the first DBConnection (or other connection types) is created. Currently existing code that used the driver before initialize() was added just blows in your face with a non-intuitive error message. I had to debug into the driver to see what's going on. Also the wiki is non-consistent in its examples. One uses a "mongo::Instance inst{}" or so, for the same reason I'm assuming. I'll have a look at the upcoming version. When is it set to be released? We consider the library as part of the base system and therefore it will also take some time before it actually makes it to users... |
| Comment by Andrew Morrow (Inactive) [ 22/Oct/15 ] |
|
timn - It is important to understand that this has in fact always been the case with the C++ driver. All versions of this library have always had global static state, but before the introduction of mongo::client::initialize and associated functions there was no way at all to manage the lifetime of those objects or wait for clean termination. The reason for the unfortunate use of global state is that this "driver" was originally the MongoDB server's internal communications API, which was then extended in an effort to produce a client library. The server architecture at that time relied heavily on static global state, and that, unfortunately, is still reflected in the legacy C++ driver. Fixing it would require a major overhaul of the legacy C++ driver, which is not something we are currently considering. Instead, we are actively developing a wholesale replacement for the C++ driver on the master branch of the repository. The new library has far less global state (currently none other than that imposed by third party libraries like OpenSSL), uses C++11 extensively, provides an API that aligns with other MongoDB language drivers, and does not depend on boost. I would strongly encourage you to begin experimenting with the new C++11 driver. |