-
Notifications
You must be signed in to change notification settings - Fork 543
CXX-3082 Add comprehensive examples (mongocxx) #1216
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
Synced with #1230 (rename |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM with minor suggestions. I like the database lock idea to safely parallelize.
EXPECT(!client); | ||
|
||
try { | ||
mongocxx::uri uri = client.uri(); // DO NOT DO THIS. Throws. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Strong language, is this worse than the other examples that throw exceptions (uninitialized read or something)? If so it should be stated, if not just the regular 'throws' comment is enough.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The pattern of an "invalid" or "empty" object (be it clients, iterators, elements, etc.) is explicitly discouraged in example code whose purpose is to demonstrate the recoverable error should the user violate the same principles behind !ptr
-> use(*ptr)
or !opt
-> opt->use()
. The purpose of these error handling examples is to demonstrate how to handle the error, not to encourage depending on it.
|
||
EXPECT(hold); | ||
|
||
mongocxx::pool::entry entry = pool.acquire(); // Throws. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Point out that this doesn't work because of maxPoolSize=1 in the URI. If you read a lot of these you start to mentally skip over the URIs.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The purpose of this error handling example is to demonstrate how to handle an exception thrown by a wait queue timeout. The explanation of the conditions used to trigger the timeout are documented by relevant reference documentation.
// Use mongocxx library interfaces at this point. | ||
use(mongocxx::client{}); | ||
|
||
// Cleanup the MongoDB C++ Driver. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
// Cleanup the MongoDB C++ Driver. | |
// Destroying the `instance` cleans up the MongoDB C++ Driver. |
Make explicit that it's the instance destructor as opposed to the client.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Updated the wording as suggested to make implicit-syntax behavior clearer.
|
||
// [Example] | ||
void example() { | ||
{ mongocxx::instance instance; } // Initialize and cleanup. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
{ mongocxx::instance instance; } // Initialize and cleanup. | |
{ mongocxx::instance instance; } // Initialize and cleanup. May be done only once per program lifetime. |
The example is clear but this rule should be explicitly stated.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The rule is explicitly stated in the reference documentation for the instance object. Per Diataxis, these example code blocks deliberately refrain from "digression, explanation, [or] teaching":
Typically, the temptations are to explain or to provide reference for completeness. Neither of these are part of guiding the user in their work. They get in the way of the action; if they’re important, link to them.
A how-to guide serves the work of the already-competent user, whom you can assume to know what they want to do, and to be able to follow your instructions correctly.
Clarifications by inline comments are reserved for highlighting essential behavior or unintuitive, easy-to-miss aspects of the example being demonstrated. For reference, see also the BSON document error handling examples added by #1208.
Summary
Followup to #1208. Partially resolves CXX-3082. Verified by this patch.
As before, API docs must be generated locally for review in their displayed state.
Adds comprehensive examples for most mongocxx interfaces. Due to the size of mongocxx (this PR is already the same size as the PR for bsoncxx despite being incomplete), this PR is posted as a Part 1? for mongocxx to allow for pipelining changes for a followup PR during review of this PR.
Examples for the following are planned but not yet ready for review (pipelined):
Examples for the following are not planned and are deliberately skipped:
See "Regarding Skipped Examples" below for more detail.
API Examples Runner
To accomodate the mongocxx library's use of
mongocxx::instance
for global initialization and cleanup, the API examples runner requires some additional features not present in #1208.There are now three different "kinds" of components:
mongocxx::instance
examples that create-and-destroy their own mongocxx instance objects."Normal" components are executed in the same way they were in #1208 (with some minor improvements to support reuse + handle
--jobs 1
better)."Forking" components execute each component in its own forked process. This permits creating and destroying a
mongocxx::instance
object in each forking component without affecting other components (since only one instance object is allowed per application). Whenfork()
is not available (specifically, with MSVC), these components are skipped entirely."With Instance" components execute each component just like normal components, but are executed after creating a single
mongocxx::instance
object. This instance object is shared across all subsequently executed components such that they do not require repeatedly invokingmongocxx::instance::current()
to handle indeterminate execution ordering (as is currently done in the test suite).Important
"With Instance" components must be executed after executing forking components. Otherwise, forking components will observe an instance object was already created before the fork takes place.
"For Server" components are a subset of "With Instance" components that are only executed if a live server is available. To maximize coverage, components that require a single server topology are also executed against replica and sharded topologies, and components that require a replica set are also executed against sharded topologies (coverage size: Single > Replica > Sharded). This behavior can be changed or removed if deemed unnecessary.
Note
Currently, all examples only require a single server topology. Examples which depend on a specific topology are likely unnecessary and are better left to the test suite.
DB Locker
The API examples are deliberately executed concurrently (for performance) and in a random order (for independence). To avoid examples that operate on a given database from conflicting with one another, the
db_lock
utility is used to guard access to a given database by name. This is implemented as adb_name -> db_mutex
map. The access of the map itself also requires a mutex that is held only for as long as is required to obtain adb_mutex
lock.Note
The high lock contention of the map mutex is not expected to be a significant problem for the purpose of executing API examples, as most time will likely be spent locking a
db_mutex
and running the example code block.db_mutex
locks for components using unique database names could be elided, but this was deliberately not done to keep things simple and easier to maintain. Performance is not the priority here.This permits examples to use common names in examples (i.e.
client["db"]
,db["coll"]
) without concern for concurrency database access problems. When not required by the example code block, the database name can be made unique to the component viaEXAMPLES_COMPONENT_NAME_STR
. This helps avoid unnecessarydb_mutex
lock contention.For convenience,
db_lock
drops the database on lock and unlock to ensure every example component is given a clean slate to work with. This permits easily setting up collections and documents for use by the example code block.Read/Write Concern
To ensure examples behave correctly regardless of server topology, a convenience function
set_rw_concern_majority()
is used to set the read concern and write concern for databases and collections to "majority". This ensures any examples that expect writes to be reliably observed by subsequent reads will work even for replica sets and sharded clusters without requiring boilerplate in the example code block.Writing mongocxx Examples
Given the large scope of interfaces and overloads in mongocxx, some rough rules are used to avoid redundancy and verbosity of examples:
auto
are usually avoided in example code to aid in interface clarity, unless it is not directly related to the primary function/task itself. This is best observed in example code blocks that have both a "Basic Usage" block (little-to-no inline code) and a subsequent "With Options" block (inline code for already-demonstrated routines), as well as for bsoncxx interface usage in mongocxx example code blocks.Error handling for databases and collections for interfaces that may throw a
mongocxx::operation_exception
exception have been summarized in a dedicated "Operation Exceptions" page. The page also includes an important warning regardingmongocxx::server_error_category()
deficiencies (overloaded error codes: CXX-834) as well as a recommendation to usemongocxx::exception
instead for forward-compatibility (error handling redesign: CXX-2377).Regarding Skipped Examples
mongocxx/examples/clients/create/single/options/auto_encryption.cpp
). 😢These examples may be reconsidered for inclusion if their value to users is sufficiently high.
Miscellaneous
mongocxx::v_noabi::uri::k_default_uri
(was not referencable by Doxygen).--jobs 1
for simplicity and improved debugging experience.