Skip to content

Conversation

eramongodb
Copy link
Contributor

@eramongodb eramongodb commented Oct 9, 2025

Summary

Resolves CXX-3236. Followup to #1462 and companion PR to #1469 and #1470. Contains cherry-pick of proposed changes in #1469 (the "cpx: cxx-abi-v1-instance" commit).

This is 5 out of an estimated 7 major PRs which in total are expected to resolve CXX-2745 and CXX-3320.

Related tickets affected by the changes in this PR (applicable only to the v1 API) include (not exhaustive, but tried my best):

  • CXX-849: "Audit API usage of int32_t"
  • CXX-1046: "Hide members of options classes behind private implementation"
  • CXX-1203: "Audit methods for noexcept and constexpr" (no constexpr)
  • CXX-1229: "Fix incorrect constness in several places"
  • CXX-1245 "Guarantee thread safety for const methods in mongocxx API"
  • CXX-1524: "Audit CXX usage of libmongoc APIs"
  • CXX-1546: "Return document::view instead of document::view_or_value from options getters"
  • CXX-1617: "Permit options::client::ssl_opts without "ssl=true" in URI"
  • CXX-1801: "stop deleting the rvalue overload of pool::entry::operator->()"
  • CXX-1826: "Reduce namespace nesting"
  • CXX-1836: "Nest options inside of their associated classes"
  • CXX-1827: "Reduce friction between view and value types"
  • CXX-1828: "Add constraints for templated functions"
  • CXX-1844: "Error hierarchy uncorrelated with error domains"
  • CXX-2038: "Change URI option getters to return optional views where possible"
  • CXX-2266: "Use bsoncxx::string::view_or_value in gridfs::bucket"
  • CXX-2270: "Audit string arguments"
  • CXX-2367: "Review #include hygiene in header files"
  • CXX-2376: "Revisit C++ client exceptions"
  • CXX-3257: "Remove deprecated mongocxx::v_noabi::options::index::storage_options"

Relative Changelog (v_noabi -> v1)

  • Added
    • is_open() to v1::gridfs::downloader and v1::gridfs::uploader to extend behavior of operator bool().
    • flush() to v1::gridfs::uploader (exposing v_noabi::gridfs::uploader::flush_chunks()) + documenting the existence of underlying buffers for better control over its behavior.
    • chunks_written() to v1::gridfs::uploader (useful information).
    • Add .upserted_ids() for v1::update_many_result for consistency with v1::bulk_write::result (vs. v1::update_one_result which uses the old .upserted_id()).
    • Add k_unknown to v1::write_concern for symmetry with v1::read_concern.
    • Add copyability to v1::indexes, v1::pipeline, v1::bulk_write::single, etc.
    • v1::bulk_write::result::raw() to allow access to the raw server response.
    • v1::bulk_write::insert_one::value is made public.
    • Allow rvalue invocation of v1::pool::entry::operator->, v1::client::database(), etc. (by removing deleted overloads and ref-qualifiers; premature pessimization).
  • Changed
    • v_noabi::*::view_or_value is replaced with either view or value (std::string for v_noabi::string::view_or_value).
      • For parameters:
        • If the value is (eventually) owned by mongocxx, value.
        • If the value is (eventually) passed to or owned by mongoc, view.
        • Some string parameters known to require null-terminated strings are defined in terms of a char const* to allow avoiding forced allocations. (We could really use a cstring_view...)
      • For return values:
        • Always view, with a few exceptions for "simple" classes wrapping the underlying value with no additional invariants (e.g. v1::bulk_write::insert_one).
        • Always replace Optional<view_or_value> -> Optional<View> (view_or_value implies internal ownership).
    • Do not return T const& or Optional<T> const&: return T or Optional<T> instead.
      • Avoid returning references to internal representation (e.g. Optional<T> const&) which severely restrict implementation freedom.
      • The only functions which return a reference are special member functions, error category functions, and setters (method chaining), plus the following exceptions:
        • v1::client_session::client(): return the associated client (must be a mongocxx reference).
        • v1::bulk_write::single::get_*(): straightforward union-like API.
    • Remove const qualifier from member functions and parameters which are not logically const.
      • v1::collection::list_indexes() uses mongoc_collection_t*.
      • v1::database parameters in v1::client_encryption use mongoc_database_t*.
      • v1::client database operations use mongoc_client_t*.
      • Several stray top-level const in return types are removed.
    • Consistently return T& for setters (method-chaining) where various functions used to return void.
    • v1::server_error
      • Invariant: a raw server error document is always available (not optional).
      • When a server error code is not accompanied by a raw server document (rare), throw as mongocxx::v1::exception with .code() == v1::source_errc::server.
      • Fix documentation for server vs. client error codes (.code() is always the server error code, .client_code() is the optional client error code, for consistency with the .raw() invariant).
    • v1::event::*
      • Use Ro0 instead of Ro5 for "view-only" trivially-copyable classes.
        • All void* are labeled with a comment indicating the underlying type.
        • v1::events::server_description is the sole exception due to v1::events::topology_description::servers(), see "Removed" entry below.
        • The "impl" constructors are made private and included in this PR to indicate the default constructor(s) are deliberately disabled.
      • server_heartbeat_failed::message() return type: string -> string_view.
    • v1::read_concern: ignore unsupported values instead of throwing an exception.
    • v1::read_preference: treat invalid read modes as k_primary instead of an error (same behavior as mongoc_read_prefs_get_mode()).
    • v1::write_concern
      • Treat .tag(nullptr) as k_default instead of an error.
      • Avoid .acknowledge_level() errors by converting unsupported values to k_unknown and document "not k_unknown" as a precondition for .to_document() (treat as if unset).
    • Ensure and document v1::gridfs::uploader flushes its buffer on destruction (it didn't already do this?!).
    • Unconditionally not-null T* parameters are made T& instead for input/output streams to v1::gridfs::bucket.
    • Return T* instead of Optional<T*> for .key_vault_*() in v1::auto_encryption and v1::client_encryption.
    • Change v1::search_indexes::create_index() return type to be consistent with v1::indexes::create_one().
    • Replace the deprecated .comment() with .comment_option() in v1::find_options for consistency with other option classes.
    • Basic template parameter constraints for v1::collection's bulk write API.
    • Int32 -> Int64 for bulk write API result values and some v1::pipeline fields.
    • id_map for v1::bulk_write::result and related API:
      • Change key type from Size -> Int64 for keys of id_map (e.g. v1::bulk_write::result) for better spec accuracy.
      • Change value type from document::element to types::view to avoid unnecessary proxy type (element key is always "_id"; useless info) + consistency with other "result" API.
    • Make v1::bulk_write::* operation single-argument constructors explicit.
    • Rename v1::client_session::options() -> .opts() to avoid conflict with ::options.
    • Change with_transaction_cb parameter type from T* (unconditional not-null) to T&.
  • Removed
    • v_noabi::events::topology_description::server_descriptions: move ownership of underlying mongoc_server_description_t objects to v1::events::server_description and avoid the awkward intermediate class. The array itself (not its elements) is deallocated after the std::vector<v1::events::server_description> is fully constructed.
    • v_noabi::gridfs::chunks_and_bytes_offset: implementation detail not used by any public API.
    • v_noabi::result::gridfs::upload ctor is made internal for v1::gridfs::upload_result.
    • ordered() for v1::insert_one_options (specific to v1::insert_many_options).
    • Removed deprecated v_noabi::options::index::base_storage_options and related API.
    • Removed deprecated v_noabi::read_preference::hedge().
    • Deprecated concern+preference accessors for v1::client.
    • Deprecated .ssl() API.
    • Remove deprecated v_noabi::options::index::haystack*() API.
    • Remove unused v_noabi::options::index::version(). (Was this meant to be "textIndexVersion"?)
  • Deferred/Rejected/Todo
    • API consistency across various "convertible to a BSON document" classes.
      • Required changes are far beyond the scope of this PR.
      • Conflicting design philosophies and goals over the years, e.g. CXX-763 vs. CXX-1359 vs. CXX-1833 (non-exhaustive list).
      • Inconsistent presence vs. absence of equality comparison operators is left unchanged.
    • Specification conformance for exception class types that should derive from v1::server_error (e.g. WriteConcernError, BulkWriteError, etc.).
      • Prior exception class types were not conforming either (did not provide necessary fields with error-specific information).
      • v1::server_error::raw() should continue to suffice as a workaround for obtaining error-specific fields manually.
    • Improving constraint validation of APM event handlers (e.g. noexcept) via template parameter constraints (e.g. like bsoncxx::v1::document::value's deleter API).
    • v1::events::*: .duration() and .round_trip_time(): changing the return type from std::int64_t to std::chrono::microseconds.
    • v1::events::server_heartbeat_failed: support returning or throwing a mongocxx::v1::exception for the bson_error_t?
    • v1::events::server_description does not conform to specification for ServerDescription.
    • v1::tls underspecification and lack of support for all mongoc_ssl_opt_t fields.
    • Many issues with v1::collection: missing access to raw server response (e.g. .rename()), missing support for additional command options (e.g. .rename()), missing session overloads (e.g. .estimated_document_count()), awkward range templates, use of bulk write operations instead of dedicated database commands (e.g. .insert_one() using bulk write "insertMany" instead of the insert database command?).
    • Many issues with indexes vs. search_indexes API symmetry and specification compliance (exacerbated by the model vs. command-specific options vs. common index options situation; v1::search_indexes::model should probably be split into SearchIndexModel and SearchIndexOptions).
    • Missing support for various fields, options, and commands.
    • Validation and error handling that should be handled by mongoc, but is not yet being done (e.g. the collation && !is_acknowledged condition for .find_and_modify() and etc.).
    • Fields which may be specified in multiple ways and override one another (underspecified).

General Notes

  • Minimize noisy, redundant, and superfluous documentation as much as possible.
    • Avoid silly \param or \returns when behavior is plainly obvious from the declaration (see: Slack). Only include when at least one parameter or the return value requires documentation of special values and behaviors that is not apparent from the types alone. Prefer \par Preconditions for partial parameter documentation.
      • e.g. Optional<T> return type for a "field" is "obviously" empty when "unset".
      • e.g. an argument of 0 being equivalent to "unset" for an Int field is not "obvious", thus it is documented.
      • e.g. v1::client::start_session documents: "The client session object MUST be passed as the first argument to all operations that are intended to be executed in the context of a session." -> avoid needing countless repetition of \param session paragraphs. The overload complexity should be revisited and addressed at a later time... (CXX-1835)
    • Use "Equivalent to ..." whenever able to avoid repetitive documention when \copydoc is not applicable.
      • Note: () is omitted given \ref func_name when more than one overload may apply.
      • Use \{ ... \} Doxygen groups when able (helped by absence of \param).
      • Take advantage of Markdown code blocks: "a code block is worth a thousand words".
    • Avoid documenting error codes coming from external libraries (e.g. mongoc) in detail.
      • e.g. "\throws mongocxx::v1::exception for all other runtime errors".
      • Most users only use ex.what(); encourage programmatic inspection via v1::source_errc and v1::type_errc instead.
      • Errors specific to mongocxx library behavior are defined using component-specific errc (as in bsoncxx), of which there are currently only 9 in total (all other errors are deferred to mongoc):
        • instance (multiple object detection: CXX-3320 migrate instance and logger to mongocxx::v1 #1469)
        • server_api (translate false returned by mongoc API for invalid versions into an exception)
        • uri (server_selection_try_once() setter throws instead of returning a boolean)
        • pool (wait queue timeout for .acquire())
        • collection (narrowing conversion from std::chrono::duration Int64 into a UInt32 for mongoc API compatibility)
        • indexes (index name "*" is not permitted for .drop_one(), no relevant mongoc API to defer to)
        • v1::gridfs::bucket (GridFS file metadata validation + database commands)
        • v1::gridfs::downloader (runtime GridFS file metadata validation and underlying stream state)
        • v1::gridfs::uploader (runtime GridFS file metadata validation and underlying stream state)
    • Define and document "supported fields" instead of "data members" with explicit reference to BSON document field names when applicable for reference and searchability.
      • Ensure parameter names and/or documented "supported fields" match external documentation and specification.
  • Defer validation and error handling to mongoc whenever possible.
    • Reduce mongocxx specific behaviors and redundant (re)implementation of behaviors handled by mongoc.
    • Avoid the need for many exception cases and isolate "may throw an exception" points to the actual point-of-use (i.e. executing a database command).
  • Aggressively use forward headers to minimize transitive dependencies as much as possible. (CXX-2367)
    • e.g. v1/database.hpp no longer includes v1/collection.hpp and all of its transitive dependencies. 🎉
    • Note: void fn(T) and T fn() do not require T to be a complete type (even when T = Optional<U>!).
    • Unable when default arguments are used. 😢 An effort is made to avoid default arguments, but not feasible at scale for some API (i.e. collection member functions)...
    • Somewhat forces Include What You Use (IWYU) in downstream code by providing the most minimal declarations necessary per component.
  • Clarify and fixup API for iterator types (similar to bsoncxx::v1::document::view).
    • Make end iterators equivalent to default-constructed iterator + iterator end() const even when iterator begin() (not-const).
    • Avoid confusing "exhausted" terminology for cursor iterators (not to be confused with "exhaust cursors").
  • Clarify and document discrepancies between specification and mongoc-specific behavior (e.g. bulk write result documents).
  • Identify and avoid unnecessary pointer semantics by changing T* -> T& when unconditionally not-null, or by changing Optional<T*> -> T* (double-null).

Review Focus Items

  • All classes and member functions should have a reference to documentation or specification describing the behavior and/or purpose of the given class or function.
    • This includes supported "fields" as parameters or data members (e.g. filter).
    • Documentation for member functions defer to the parent class (e.g. client_encryption) or the parameter (class) type (e.g. write_concern).
  • GridFS download/upload buffer behavior was previously undocumented/underspecified. Check that the new documentation is accurate.
  • Check that documented preconditions and postconditions are correct and consistent.

Questions for Review

  • Should std::unique_ptr<impl> _impl be made void* _impl instead (even when a class impl is used) to leave open the possibility of replacing impl with a mongoc equivalent or vise versa as an ABI compatible change? Or is this "premature optimization" and using std::unique_ptr<T> is sufficient?
  • Regardless of the question above, should classes which can currently be implemented entirely in terms of a mongoc_*_t type use void* _impl; // mongoc_*_t, e.g. to avoid double-indirection? (Initial changes propose that only the view-like v1::events::* classes do this.)
  • Is providing char const* + std::string + string_view set of overloads premature optimization? Should they be simplified to just a single string_view overload with an internal std::string allocation?
  • Is removing the deprecated v1::read_preference::hedge() premature? (CXX-3241)
  • Is removing the deprecated readConcern + writeConcern + readPreference accessors in v1::client premature? (CXX-1939)
  • Is removing the deprecated geoHaystack API for v1::indexes premature? (CXX-1978)
  • Should mongoc_cursor_clone() be implemented by mongocxx by making cursors copyable? Or should mongoc_cursor_clone() be deprecated given its confusing behavior (re-execute the underlying query)?
  • Should v1::change_stream::batch_size() support std::uint32_t for consistency with mongoc_cursor_set_batch_size()? Or does mongoc_cursor_set_batch_size() need to be updated to support std::int64_t instead?

@eramongodb eramongodb self-assigned this Oct 9, 2025
@eramongodb eramongodb requested a review from a team as a code owner October 9, 2025 16:24
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant