MONGOC_REFERENCE(3)
π MONGOC_REFERENCE(3) β libmongoc π
okay so bestie, this is the manual page for libmongoc, and it is literally the group chat for the MongoDB C Driver API. no cap.
β¨ LIBMONGOC - API β¨
A Cross Platform MongoDB Client Library for C
This site documents the API. For tutorials, guides, and explainers, see MongoDB C Driver.
π± Introduction π±
The MongoDB C Driver, also known as libmongoc, is a library for using MongoDB from C applications, and for writing MongoDB drivers in higher-level languages. it's giving βlow-level but make it usefulβ π
It depends on libbson to generate and parse BSON documents, the native data format of MongoDB. bson is the whole vibe, fr fr.
ποΈ API Reference ποΈ
π§Ό Initialization and cleanup π§Ό
π Synopsis π
Initialize the MongoDB C Driver by calling mongoc_init() exactly once at the beginning of your program. It is responsible for initializing global state such as process counters, SSL, and threading primitives. like, it sets the whole stage, okay? β¨
Exception to this is mongoc_log_set_handler(), which should be called before mongoc_init() or some log traces would not use your log handling function. See Custom Log Handlers for a detailed example.
Call mongoc_cleanup() exactly once at the end of your program to release all memory and other resources allocated by the driver. You must not call any other MongoDB C Driver functions after mongoc_cleanup(). Note that mongoc_init() does not reinitialize the driver after mongoc_cleanup(). that's a one-way exit, bestie πͺ
Changed in version 2.0.0: Versions prior to 2.0.0 supported a non-portable automatic initialization and cleanup with the CMake optionENABLE_AUTOMATIC_INIT_AND_CLEANUP. This was removed in 2.0.0. Ensure your application callmongoc_init()andmongoc_cleanup().
π Logging π
The MongoDB C driver has two different types of logging available:
- The original
mongoc_logfacility supports freeform string messages that originate from the driver itself or from application code. This has been retroactively termed βunstructured loggingβ. - A new
mongoc_structured_logfacility reports messages from the driver itself using a BSON format defined across driver implementations by the MongoDB Logging Specification.
These two systems are configured and used independently. they do not share a locker, periodt.
π§Έ Unstructured Logging π§Έ
This is the original logging facility that supports freeform string messages originating from the driver itself or from application code. This has been retroactively termed βunstructured loggingβ. See Structured Logging for the newer standardized logging facility.
typedef enum {
MONGOC_LOG_LEVEL_ERROR,
MONGOC_LOG_LEVEL_CRITICAL,
MONGOC_LOG_LEVEL_WARNING,
MONGOC_LOG_LEVEL_MESSAGE,
MONGOC_LOG_LEVEL_INFO,
MONGOC_LOG_LEVEL_DEBUG,
MONGOC_LOG_LEVEL_TRACE,
} mongoc_log_level_t;
#define MONGOC_ERROR(...)
#define MONGOC_CRITICAL(...)
#define MONGOC_WARNING(...)
#define MONGOC_MESSAGE(...)
#define MONGOC_INFO(...)
#define MONGOC_DEBUG(...)
typedef void (*mongoc_log_func_t) (mongoc_log_level_t log_level,
const char *log_domain,
const char *message,
void *user_data);
void
mongoc_log_set_handler (mongoc_log_func_t log_func, void *user_data);
void
mongoc_log (mongoc_log_level_t log_level,
const char *log_domain,
const char *format,
...);
const char *
mongoc_log_level_str (mongoc_log_level_t log_level);
void
mongoc_log_default_handler (mongoc_log_level_t log_level,
const char *log_domain,
const char *message,
void *user_data);
void
mongoc_log_trace_enable (void);
void
mongoc_log_trace_disable (void);
This abstraction can be used for logging in your application, or you can integrate the driver with an existing logging system. very βplug it into whatever you already haveβ energy π«Ά
πͺ Macros πͺ
To make logging a little less painful, various helper macros are provided. See the following example.
#undef MONGOC_LOG_DOMAIN
#define MONGOC_LOG_DOMAIN "my-custom-domain"
MONGOC_WARNING ("An error occurred: %s", strerror (errno));
ποΈ Custom Log Handlers ποΈ
The default log handler prints a timestamp and the log message to stdout, or to stderr for warnings, critical messages, and errors. You can override the handler with mongoc_log_set_handler(). Your handler function is called in a mutex for thread safety. safety first, bestie π
For example, you could register a custom handler to suppress messages at INFO level and below:
void
my_logger (mongoc_log_level_t log_level,
const char *log_domain,
const char *message,
void *user_data)
{
/* smaller values are more important */
if (log_level < MONGOC_LOG_LEVEL_INFO) {
mongoc_log_default_handler (log_level, log_domain, message, user_data);
}
}
int
main (int argc, char *argv[])
{
mongoc_log_set_handler (my_logger, NULL);
mongoc_init ();
/* ... your code ... */
mongoc_cleanup ();
return 0;
}
Note that in the example above mongoc_log_set_handler() is called before mongoc_init(). Otherwise, some log traces could not be processed by the log handler.
To restore the default handler:
mongoc_log_set_handler (mongoc_log_default_handler, NULL);
π Disable logging π
To disable all logging, including warnings, critical messages and errors, provide an empty log handler:
mongoc_log_set_handler (NULL, NULL);
π Tracing π
If compiling your own copy of the MongoDB C driver, consider configuring with -DENABLE_TRACING=ON to enable function tracing and hex dumps of network packets to STDERR and STDOUT during development and debugging. it's giving βx-ray vision for the driverβ π₯
This is especially useful when debugging what may be going on internally in the driver.
Trace messages can be enabled and disabled by calling mongoc_log_trace_enable() and mongoc_log_trace_disable()
NOTE: Compiling the driver with-DENABLE_TRACING=ONwill affect its performance. Disabling tracing withmongoc_log_trace_disable()significantly reduces the overhead, but cannot remove it completely.
𧬠Structured Logging π§¬
This document describes a newer βstructuredβ logging facility which reports messages from the driver itself using a BSON format defined across driver implementations by the MongoDB Logging Specification. See Unstructured Logging for the original freeform logging facility.
These two systems are configured and used independently.
Unstructured logging is global to the entire process, but structured logging is configured separately for each mongoc_client_t or mongoc_client_pool_t. See mongoc_client_set_structured_log_opts() and mongoc_client_pool_set_structured_log_opts(). per-client customization is the main character here π
βοΈ Options βοΈ
Structured log settings are tracked explicitly by a mongoc_structured_log_opts_t instance.
Like other drivers supporting structured logging, we take default settings from environment variables and offer additional optional programmatic configuration. Environment variables are captured during mongoc_structured_log_opts_new(), refer there for a full list of the supported variables.
Normally environment variables provide defaults that can be overridden programmatically. To request the opposite behavior, where your programmatic defaults can be overridden by the environment, see mongoc_structured_log_opts_set_max_levels_from_env().
Structured log messages may be filtered in arbitrary ways by the handler, but as both a performance optimization and a convenience, a built-in filter limits the maximum log level of reported messages with a per-component setting.
π§± mongoc_structured_log_opts_t π§±
π Synopsis π
typedef struct mongoc_structured_log_opts_t mongoc_structured_log_opts_t;
mongoc_structured_log_opts_t is an opaque type that contains options for the structured logging subsystem: per-component log levels, a maximum logged document length, and a handler function.
Create a mongoc_structured_log_opts_t with mongoc_structured_log_opts_new(), set options and a callback on it, then pass it to mongoc_client_set_structured_log_opts() or mongoc_client_pool_set_structured_log_opts(). Must be destroyed by calling mongoc_structured_log_opts_destroy(). don't ghost your options struct, bestie π
π οΈ Functions π οΈ
π mongoc_structured_log_opts_new() π
π Synopsis π
mongoc_structured_log_opts_t *
mongoc_structured_log_opts_new (void);
Creates a new mongoc_structured_log_opts_t, filled with defaults captured from the current environment.
Sets a default log handler which would write a text representation of each log message to stderr, stdout, or another file configurable using MONGODB_LOG_PATH. This setting has no effect if the default handler is replaced using mongoc_structured_log_opts_set_handler().
Environment variable errors are non-fatal, and result in one-time warnings delivered as an unstructured log.
Per-component maximum levels are initialized equivalently to:
mongoc_structured_log_opts_set_max_level_for_all_components(opts, MONGOC_STRUCTURED_LOG_LEVEL_WARNING);
mongoc_structured_log_opts_set_max_levels_from_env(opts);
π Environment Variables π
This is a full list of the captured environment variables.
MONGODB_LOG_MAX_DOCUMENT_LENGTH: Maximum length for JSON-serialized documents that appear within a log message. It may be a number, in bytes, orunlimited(case insensitive) to choose an implementation-specific value near the maximum representable length. By default, the limit is1000bytes. This limit affects interior documents like commands and replies, not the total length of a structured log message.MONGODB_LOG_PATH: A file path or one of the special stringsstderrorstdout(case insensitive) specifying the destination for structured logs seen by the default handler. By default, it writes tostderr. This path will be captured duringmongoc_structured_log_opts_new(), but it will not immediately be opened. If the file can't be opened, a warning is then written to the unstructured log and the handler writes structured logs tostderrinstead.
β οΈ WARNING: okay so if you giveMONGODB_LOG_PATHan actual file path, every single log instance (that's one stand-alone client or one pool, each one) opens that file separately for append. The results are operating system specific, babe. On UNIX-like platforms the output of each instance gets interleaved β in most cases without splitting up individual log messages. But notably on Windows? The first instance opens the file in exclusive mode and every instance after it just fails, falling back on the default ofstderr. So if your app rocks multiple processes or multiple client pools, you're gonna want to supply a log handler that annotates each message with which log instance it came from. iykyk π
MONGODB_LOG_COMMANDβ a log level name, set as the maximum forMONGOC_STRUCTURED_LOG_COMPONENT_COMMAND.MONGODB_LOG_TOPOLOGYβ a log level name, set as the maximum forMONGOC_STRUCTURED_LOG_COMPONENT_TOPOLOGY.MONGODB_LOG_SERVER_SELECTIONβ a log level name, set as the maximum forMONGOC_STRUCTURED_LOG_COMPONENT_SERVER_SELECTION.MONGODB_LOG_CONNECTIONβ a log level name, set as the maximum forMONGOC_STRUCTURED_LOG_COMPONENT_CONNECTION.MONGODB_LOG_ALLβ a log level name that gets applied to all components not otherwise specified.
Heads up bestie: log level names are always case insensitive. Here's the full list of recognized names, aliases included β¨:
emergency,offalertcriticalerrorwarning,warnnoticeinformational,infodebugtrace
Returns
A newly allocated mongoc_structured_log_opts_t. π
mongoc_structured_log_opts_destroy()
Synopsis
void
mongoc_structured_log_opts_destroy (mongoc_structured_log_opts_t *opts);
Parameters
- opts β pointer to a
mongoc_structured_log_opts_tthat you allocated withmongoc_structured_log_opts_new(), orNULL.
Description
This one frees every resource tied to a mongoc_structured_log_opts_t. And if opts is NULL? It simply does nothing. No drama, no crash. π«Ά
mongoc_structured_log_opts_set_handler()
Synopsis
void
mongoc_structured_log_opts_set_handler (mongoc_structured_log_opts_t *opts,
mongoc_structured_log_func_t log_func,
void *user_data);
Sets the function that gets called to handle structured log messages, as a mongoc_structured_log_func_t. Basically you're picking who answers the phone when a log shows up π
Your callback gets a mongoc_structured_log_entry_t, which is the handle for pulling extra info about the log message. But listen: that entry pointer is only valid during a callback, because it's a low-cost reference to temporary data. Do not go stashing it for later, bestie, it will not slay.
Structured log handlers must be thread-safe if they're gonna be used with mongoc_client_pool_t. And handlers must avoid unbounded recursion β the preferred way being to avoid using any libmongoc client or pool that uses the same handler. Recursive log girlies, this means you π
This function always replaces the default log handler from mongoc_structured_log_opts_new(), if it was still set. And if log_func is set to NULL, structured logging gets disabled entirely. Periodt.
Parameters
- opts β structured log options, allocated with
mongoc_structured_log_opts_new(). - log_func β the handler you're installing, a
mongoc_structured_log_func_t, orNULLto disable structured logging. - user_data β optional user data, passed on through to your handler.
SEE ALSO: Structured Logging
mongoc_structured_log_opts_set_max_level_for_component()
Synopsis
bool
mongoc_structured_log_opts_set_max_level_for_component (mongoc_structured_log_opts_t *opts,
mongoc_structured_log_component_t component,
mongoc_structured_log_level_t level);
Sets the maximum log level, per component. Only log messages at or below this severity level get passed to mongoc_structured_log_func_t. It's giving bouncer at the club π
By default, each component's log level may come from environment variables β see mongoc_structured_log_opts_set_max_levels_from_env().
Parameters
- opts β structured log options, allocated with
mongoc_structured_log_opts_new(). - component β the component you're setting a max log level for, as a
mongoc_structured_log_component_t. - level β the new max log level for this component, as a
mongoc_structured_log_level_t.
Returns
Returns true on success, or false if the parameters you supplied were incorrect. π§
SEE ALSO: Structured Logging
mongoc_structured_log_opts_set_max_level_for_all_components()
Synopsis
bool
mongoc_structured_log_opts_set_max_level_for_all_components (mongoc_structured_log_opts_t *opts,
mongoc_structured_log_level_t level);
Sets every per-component maximum log level to the same value. Only log messages at or below this severity level get passed to mongoc_structured_log_func_t. And the iconic part: this is effective even for logging components not known at compile-time. One level to rule them all π
Parameters
- opts β structured log options, allocated with
mongoc_structured_log_opts_new(). - level β the max log level for all components, as a
mongoc_structured_log_level_t.
Returns
Returns true on success, or false if the supplied parameters were incorrect.
SEE ALSO: Structured Logging
mongoc_structured_log_opts_set_max_levels_from_env()
Synopsis
bool
mongoc_structured_log_opts_set_max_levels_from_env (mongoc_structured_log_opts_t *opts);
Sets any maximum log levels that were requested by environment variables: MONGODB_LOG_ALL for all components, followed by the per-component log levels MONGODB_LOG_COMMAND, MONGODB_LOG_CONNECTION, MONGODB_LOG_TOPOLOGY, and MONGODB_LOG_SERVER_SELECTION. Order matters, bestie, that's the whole plot β¨
It expects the value to be recognizable by mongoc_structured_log_get_named_level(). Parse errors may cause a warning message, delivered via unstructured logging. (Side quest warning, basically.)
Component levels with no valid environment variable setting get left unmodified. Untouched. Cherished. π
This happens automatically when mongoc_structured_log_opts_new() establishes defaults. Any subsequent programmatic changes to the mongoc_structured_log_opts_t will override the environment variable settings. But for apps that want the opposite vibe β environment variables overriding programmatic settings β you can call mongoc_structured_log_opts_set_max_levels_from_env() after calling mongoc_structured_log_opts_set_max_level_for_component() and mongoc_structured_log_opts_set_max_level_for_all_components(). That processes the environment a second time, letting it override your customized defaults. Round two, fight π₯
Returns
Returns true on success. If warnings are encountered in the environment, returns false and may log additional information to the unstructured logging facility. And note β by design β these errors are by default non-fatal. When mongoc_structured_log_opts_new() internally calls this function, it just ignores the return value. Chill of it.
SEE ALSO: Structured Logging
mongoc_structured_log_opts_get_max_level_for_component()
Synopsis
mongoc_structured_log_level_t
mongoc_structured_log_opts_get_max_level_for_component (const mongoc_structured_log_opts_t *opts,
mongoc_structured_log_component_t component);
Parameters
- opts β structured log options, allocated with
mongoc_structured_log_opts_new(). - component β log component, as a
mongoc_structured_log_component_t.
Returns
Returns the configured maximum log level for one specific component, as a mongoc_structured_log_level_t. That could be the last value set with mongoc_structured_log_opts_set_max_level_for_component() or mongoc_structured_log_opts_set_max_level_for_all_components(), or it could be the default that came from environment variables. And if you hand it an invalid or unknown component enum? It returns the lowest log level. Lowkey dramatic but fair π
SEE ALSO: Structured Logging
mongoc_structured_log_opts_set_max_document_length()
Synopsis
bool
mongoc_structured_log_opts_set_max_document_length (mongoc_structured_log_opts_t *opts,
size_t max_document_length);
Sets a maximum length for BSON documents that show up serialized in JSON form as part of a structured log message. Snip snip, bestie βοΈ
Serialized JSON gets truncated at this limit, interpreted as a count of UTF-8 encoded bytes. Truncation is indicated with a ... suffix β and the length of that suffix is not included in the max document length, okay? Also, if truncating at the exact indicated length would split a valid UTF-8 sequence, we instead truncate the document earlier, at the nearest boundary between code points. We do not split characters in this house π
Parameters
- opts β structured log options, allocated with
mongoc_structured_log_opts_new(). - max_document_length β maximum length for each embedded JSON document, in bytes, not including an ellipsis (
...) added to indicate truncation. Values near or aboveINT_MAXwill be rejected.
Returns
Returns true on success, or false if the maximum length you supplied is too large.
SEE ALSO: Structured Logging, mongoc_structured_log_opts_set_max_document_length_from_env()
mongoc_structured_log_opts_set_max_document_length_from_env()
Synopsis
bool
mongoc_structured_log_opts_set_max_document_length_from_env (mongoc_structured_log_opts_t *opts);
Sets a maximum document length from the MONGODB_LOG_MAX_DOCUMENT_LENGTH environment variable, if a valid setting is found. (See mongoc_structured_log_opts_new() for the rundown on supported environment variable formats π)
Parse errors may cause a warning message, delivered via unstructured logging.
This happens automatically when mongoc_structured_log_opts_new() establishes defaults. Any subsequent programmatic modifications to the mongoc_structured_log_opts_t will override the environment variable settings. For applications that desire the opposite behavior β environment variables overriding programmatic settings, iykyk β they may call mongoc_structured_log_opts_set_max_document_length_from_env() after calling mongoc_structured_log_opts_set_max_document_length(). That processes the environment a second time, allowing it to override customized defaults. Second pass, better vibes πΈ
Returns
Returns true on success: either a valid environment setting was found, or the value is unset and opts will not be modified. If warnings are encountered in the environment, returns false and may log additional information to the unstructured logging facility. Note that, by design, these errors are by default non-fatal. When mongoc_structured_log_opts_new() internally calls this function, it ignores the return value.
SEE ALSO: Structured Logging
mongoc_structured_log_opts_get_max_document_length()
Synopsis
size_t
mongoc_structured_log_opts_get_max_document_length (const mongoc_structured_log_opts_t *opts);
Parameters
- opts: Structured log options, allocated with
mongoc_structured_log_opts_new().
Returns
Returns the current maximum document length that's set in opts, as a size_t. Just a lil peek at the setting, very low-key, very demure. π
SEE ALSO: Structured Logging
SEE ALSO: Structured Logging
yes bestie it literally says it twice π that's just how the page is, we don't question the classics π
ποΈ Levels and Components
Log levels and components are defined as the mongoc_structured_log_level_t and mongoc_structured_log_component_t enumerations. You get utility functions to convert between these values and their standard string representations. Oh and β the string values are case-insensitive, so you can be as chaotic with your capitalisation as you want. β¨
typedef enum {
MONGOC_STRUCTURED_LOG_LEVEL_EMERGENCY = 0, // "Emergency" ("off" also accepted)
MONGOC_STRUCTURED_LOG_LEVEL_ALERT = 1, // "Alert"
MONGOC_STRUCTURED_LOG_LEVEL_CRITICAL = 2, // "Critical"
MONGOC_STRUCTURED_LOG_LEVEL_ERROR = 3, // "Error"
MONGOC_STRUCTURED_LOG_LEVEL_WARNING = 4, // "Warning" ("warn" also accepted)
MONGOC_STRUCTURED_LOG_LEVEL_NOTICE = 5, // "Notice"
MONGOC_STRUCTURED_LOG_LEVEL_INFO = 6, // "Informational" ("info" also accepted)
MONGOC_STRUCTURED_LOG_LEVEL_DEBUG = 7, // "Debug"
MONGOC_STRUCTURED_LOG_LEVEL_TRACE = 8, // "Trace"
} mongoc_structured_log_level_t;
typedef enum {
MONGOC_STRUCTURED_LOG_COMPONENT_COMMAND = 0, // "command"
MONGOC_STRUCTURED_LOG_COMPONENT_TOPOLOGY = 1, // "topology"
MONGOC_STRUCTURED_LOG_COMPONENT_SERVER_SELECTION = 2, // "serverSelection"
MONGOC_STRUCTURED_LOG_COMPONENT_CONNECTION = 3, // "connection"
} mongoc_structured_log_component_t;
mongoc_structured_log_level_t
Synopsis
typedef enum {
MONGOC_STRUCTURED_LOG_LEVEL_EMERGENCY = 0,
MONGOC_STRUCTURED_LOG_LEVEL_ALERT = 1,
MONGOC_STRUCTURED_LOG_LEVEL_CRITICAL = 2,
MONGOC_STRUCTURED_LOG_LEVEL_ERROR = 3,
MONGOC_STRUCTURED_LOG_LEVEL_WARNING = 4,
MONGOC_STRUCTURED_LOG_LEVEL_NOTICE = 5,
MONGOC_STRUCTURED_LOG_LEVEL_INFO = 6,
MONGOC_STRUCTURED_LOG_LEVEL_DEBUG = 7,
MONGOC_STRUCTURED_LOG_LEVEL_TRACE = 8,
} mongoc_structured_log_level_t;
mongoc_structured_log_level_t enumerates all the available log levels you can use with structured logging. Emergency through Trace, the whole alphabet of "how much do you want to know" π
Functions π«Ά
mongoc_structured_log_get_level_name()
Synopsis
const char *
mongoc_structured_log_get_level_name (mongoc_structured_log_level_t level);
Parameters
- level: Log level as a
mongoc_structured_log_level_t.
Returns
If the level is known, it gives you back a pointer to a constant string β and listen, do not free it, it's not yours like that π«. If the level has no known name, it returns NULL.
SEE ALSO: Structured Logging
mongoc_structured_log_get_named_level()
Synopsis
bool
mongoc_structured_log_get_named_level (const char *name, mongoc_structured_log_level_t *out);
Look up a log level by name. Case insensitive, so go off with your INFO / info / InFo era π
Parameters
- name: A name to look up as a log level.
- out: On success, the corresponding
mongoc_structured_log_level_tis written here.
Returns
If the level name is known, it returns true and writes the level enum to *out. If the level name is not known, it returns false and does not write *out. Consistency queen π
SEE ALSO: Structured Logging
SEE ALSO: Structured Logging
mongoc_structured_log_component_t
Synopsis
typedef enum {
MONGOC_STRUCTURED_LOG_COMPONENT_COMMAND = 0,
MONGOC_STRUCTURED_LOG_COMPONENT_TOPOLOGY = 1,
MONGOC_STRUCTURED_LOG_COMPONENT_SERVER_SELECTION = 2,
MONGOC_STRUCTURED_LOG_COMPONENT_CONNECTION = 3,
} mongoc_structured_log_component_t;
mongoc_structured_log_component_t enumerates the structured logging components. Here's the important part, bestie, read it twice: applications should never rely on having an exhaustive list of all log components. Like, ever. Instead, use mongoc_structured_log_opts_set_max_level_for_all_components() to set a default level if you need one. Future-proofing is the main character here β¨
Functions π«Ά
mongoc_structured_log_get_component_name()
Synopsis
const char *
mongoc_structured_log_get_component_name (mongoc_structured_log_component_t component);
Parameters
- component: Log component as a
mongoc_structured_log_component_t.
Returns
If the component is known, returns a pointer to a constant string that should not be freed. If the component has no known name, returns NULL.
SEE ALSO: Structured Logging
mongoc_structured_log_get_named_component()
Synopsis
bool
mongoc_structured_log_get_named_component (const char *name, mongoc_structured_log_component_t *out);
Look up a component by name. Case insensitive, naturally π
Parameters
- name: A name to look up as a log component.
- out: On success, the corresponding
mongoc_structured_log_component_tis written here.
Returns
If the component name is known, it returns true and writes the component enum to *out. If the component name is not known, it returns false and does not write *out.
SEE ALSO: Structured Logging
SEE ALSO: Structured Logging
SEE ALSO:
mongoc_structured_log_get_level_namemongoc_structured_log_get_named_levelmongoc_structured_log_get_component_namemongoc_structured_log_get_named_component
π¬ Log Handlers
Each mongoc_client_pool_t or standalone mongoc_client_t has its own instance of the structured logging subsystem, with its own settings and handler. Independent icon behaviour, we love to see it π
When you're using a mongoc_client_pool_t, all the pooled clients share one common logging instance. Which means: handlers must be thread-safe. No exceptions, no vibes-based threading π§΅
The handler gets called for each log entry with a level no greater than its component's maximum. A mongoc_structured_log_entry_t pointer gives you access to further details β but only during the handler. Blink and it's gone π
Handlers must take care not to re-enter libmongoc with the samemongoc_client_tormongoc_client_pool_tthat the handler has been called by. That's the one rule, don't loop back in on yourself ππ
mongoc_structured_log_func_t
Synopsis
typedef void (*mongoc_structured_log_func_t)
(const mongoc_structured_log_entry_t *entry, void *user_data);
Callback function for mongoc_structured_log_opts_set_handler(). Structured log handlers must be thread-safe if they're going to be used with mongoc_client_pool_t. Handlers must also avoid unbounded recursion β preferably by avoiding the use of any libmongoc client or pool that uses the same handler. Basically: don't hand your handler a mirror and ask it to log itself πͺπ
Parameters
- entry: A
mongoc_structured_log_entry_tpointer, only valid during the handler invocation. - user_data: Optional user data from
mongoc_structured_log_opts_set_handler().
SEE ALSO: Structured Logging
π Log Entries
Each log entry is represented inside the handler by a short-lived mongoc_structured_log_entry_t pointer. During the handler, that pointer lets you access the individual properties of an entry: its level, component, and message. Three little facts, all yours for the duration of one callback π¦
The message gets assembled as a bson_t only when you explicitly ask for it with a call to mongoc_structured_log_entry_message_as_bson(). That gives you a standalone document you can keep for as long as you like β and which you must explicitly destroy. Your document, your responsibility, bestie π«‘
mongoc_structured_log_entry_t
Synopsis
typedef struct mongoc_structured_log_entry_t mongoc_structured_log_entry_t;
mongoc_structured_log_entry_t is an opaque structure representing the temporary state of an in-progress log entry. It can only be used during a mongoc_structured_log_func_t β it is not valid after the log handler returns. Use the functions below to query the individual aspects of the log entry. It's giving guest star, not series regular π«
Functions π«Ά
mongoc_structured_log_entry_get_component()
Synopsis
mongoc_structured_log_component_t
mongoc_structured_log_entry_get_component (const mongoc_structured_log_entry_t *entry);
Parameters
- entry: A
mongoc_structured_log_entry_tpointer.
Returns
The mongoc_structured_log_component_t associated with this log entry.
SEE ALSO: Structured Logging
mongoc_structured_log_entry_get_level()
Synopsis
mongoc_structured_log_level_t
mongoc_structured_log_entry_get_level (const mongoc_structured_log_entry_t *entry);
Parameters
- entry: A
mongoc_structured_log_entry_tpointer.
Returns
The mongoc_structured_log_level_t associated with this log entry.
SEE ALSO: Structured Logging
mongoc_structured_log_entry_get_message_string()
Synopsis
const char *
mongoc_structured_log_entry_get_message_string (const mongoc_structured_log_entry_t *entry);
Parameters
- entry: A
mongoc_structured_log_entry_tpointer.
Returns
A string, guaranteed to be valid only during the lifetime of the structured log handler. It should not be freed or modified. Hands off, it's not a souvenir π«π
Identical to the value of the message key in the document returned by mongoc_structured_log_entry_message_as_bson().
Heads up: this is not a complete string representation of the structured log. It's a standardized identifier for a particular log event. Like a name tag, not a whole biography π·οΈ
SEE ALSO: Structured Logging
mongoc_structured_log_entry_message_as_bson()
Synopsis
bson_t *
mongoc_structured_log_entry_message_as_bson (const mongoc_structured_log_entry_t *entry);
Make a new copy, as a bson_t, of the log entry's standardized BSON representation. When possible, a log handler should avoid serializing log messages that will be discarded β don't do the work just to throw it in the bin ποΈβ¨ Each call allocates an independent copy of the message that must be freed.
Parameters
- entry: A
mongoc_structured_log_entry_tpointer.
Returns
A new allocated bson_t that must be freed with a call to bson_destroy().
SEE ALSO: Structured Logging
SEE ALSO: Structured Logging
πΈ Example
example-structured-log.c β yes she's the demo, yes she ate π
/* gcc example-structured-log.c -o example-structured-log \
* $(pkg-config --cflags --libs libmongoc-1.0) */
#include <mongoc/mongoc.h>
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
static pthread_mutex_t handler_mutex;
static void
example_handler(const mongoc_structured_log_entry_t *entry, void *user_data)
{
(void)user_data;
mongoc_structured_log_component_t component = mongoc_structured_log_entry_get_component(entry);
mongoc_structured_log_level_t level = mongoc_structured_log_entry_get_level(entry);
const char *message_string = mongoc_structured_log_entry_get_message_string(entry);
/*
* With a single-threaded mongoc_client_t, handlers will always be called
* by the thread that owns the client. On a mongoc_client_pool_t, handlers
* are shared by multiple threads and must be reentrant.
*
* Note that unstructured logging includes a global mutex in the API,
* but structured logging allows applications to avoid lock contention
* even when multiple threads are issuing commands simultaneously.
*
* Simple apps like this example can achieve thread safety by adding their
* own global mutex. For other apps, this would be a performance bottleneck
* and it would be more appropriate for handlers to process their log
* messages concurrently.
*
* In this example, our mutex protects access to a global log counter.
* In a real application, you may need to protect access to a shared stream
* or queue.
*/
pthread_mutex_lock(&handler_mutex);
static unsigned log_serial_number = 0;
printf("%u. Log entry with component=%s level=%s message_string='%s'\n",
++log_serial_number,
mongoc_structured_log_get_component_name(component),
mongoc_structured_log_get_level_name(level),
message_string);
/*
* At this point, the handler might make additional filtering decisions
* before asking for a bson_t. As an example, let's log the component and
* level for all messages but only show contents for command logs.
*/
if (component == MONGOC_STRUCTURED_LOG_COMPONENT_COMMAND) {
bson_t *message = mongoc_structured_log_entry_message_as_bson(entry);
char *json = bson_as_relaxed_extended_json(message, NULL);
printf("Full log message, as json: %s\n", json);
bson_destroy(message);
bson_free(json);
}
pthread_mutex_unlock(&handler_mutex);
}
int
main(void)
{
const char *uri_string = "mongodb://localhost:27017";
int result = EXIT_FAILURE;
bson_error_t error;
mongoc_uri_t *uri = NULL;
mongoc_structured_log_opts_t *log_opts = NULL;
mongoc_client_t *client = NULL;
mongoc_client_pool_t *pool = NULL;
/*
* Note that structured logging only applies per-client or per-pool,
* and it won't be used during or before mongoc_init.
*/
mongoc_init();
/*
* Logging options are represented by a mongoc_structured_log_opts_t,
* which can be copied into a mongoc_client_t or mongoc_client_pool_t
* using mongoc_client_set_structured_log_opts() or
* mongoc_client_pool_set_structured_log_opts(), respectively.
*
* Default settings are captured from the environment into
* this structure when it's constructed.
*/
log_opts = mongoc_structured_log_opts_new();
/*
* For demonstration purposes, set up a handler that receives all possible log messages.
*/
pthread_mutex_init(&handler_mutex, NULL);
mongoc_structured_log_opts_set_max_level_for_all_components(log_opts, MONGOC_STRUCTURED_LOG_LEVEL_TRACE);
mongoc_structured_log_opts_set_handler(log_opts, example_handler, NULL);
/*
* By default libmongoc processes log options from the environment first,
* and then allows you to apply programmatic overrides. To request the
* opposite behavior, allowing the environment to override programmatic
* defaults, you can ask for the environment to be re-read after setting
* your own defaults.
*/
mongoc_structured_log_opts_set_max_levels_from_env(log_opts);
/*
* Create a MongoDB URI object. This example assumes a local server.
*/
uri = mongoc_uri_new_with_error(uri_string, &error);
if (!uri) {
fprintf(stderr, "URI parse error: %s\n", error.message);
goto done;
}
/*
* Create a new client pool.
*/
pool = mongoc_client_pool_new(uri);
if (!pool) {
goto done;
}
/*
* Set the client pool's log options.
* This must happen only once, and only before the first mongoc_client_pool_pop.
* There's no need to keep log_opts after this point.
*/
mongoc_client_pool_set_structured_log_opts(pool, log_opts);
/*
* Check out a client, and do some work that we'll see logs from.
* This example just sends a 'ping' command.
*/
client = mongoc_client_pool_pop(pool);
if (!client) {
goto done;
}
bson_t *command = BCON_NEW("ping", BCON_INT32(1));
bson_t reply;
bool command_ret = mongoc_client_command_simple(client, "admin", command, NULL, &reply, &error);
bson_destroy(command);
bson_destroy(&reply);
mongoc_client_pool_push(pool, client);
if (!command_ret) {
fprintf(stderr, "Command error: %s\n", error.message);
goto done;
}
result = EXIT_SUCCESS;
done:
mongoc_uri_destroy(uri);
mongoc_structured_log_opts_destroy(log_opts);
mongoc_client_pool_destroy(pool);
mongoc_cleanup();
return result;
}
π¦ SEE ALSO π¦
mongoc_structured_log_entry_get_componentmongoc_structured_log_entry_get_levelmongoc_structured_log_entry_message_as_bson
Β« libmongoc - API
π Error Reporting π
πΈ Description πΈ
ok so listen bestie β a lot of C Driver functions report errors the same way: they hand you back false or -1 and fill out a bson_error_t structure with three little gifts inside β an error domain, an error code, and a message. π
- Use
domainto figure out which subsystem actually generated the error. - Use
codefor the specific error. messageis a human-readable description of what went wrong. literally the driver explaining itself to you π«Ά
SEE ALSO: Handling Errors in libbson.
π The error codes π (aka the vibe check table)
MONGOC_ERROR_CLIENTMONGOC_ERROR_CLIENT_TOO_BIGβ bestie you tried to send a message that's larger than the server's max message size. it's giving too much πMONGOC_ERROR_CLIENT_AUTHENTICATEβ wrong credentials, or a failure while sending or receiving authentication messages.MONGOC_ERROR_CLIENT_NO_ACCEPTABLE_PEERβ you tried an TLS connection but the driver was not built with TLS. π no TLS, no peer.MONGOC_ERROR_CLIENT_IN_EXHAUSTβ you started iterating an exhaust cursor, then tried to begin another operation with the samemongoc_client_t. pick a lane, icon β¨MONGOC_ERROR_CLIENT_SESSION_FAILUREβ a failure related to creating or using a logical session.
MONGOC_ERROR_CLIENT_INVALID_ENCRYPTION_ARGβ bestie, you fumbled the arguments when you were initializing In-Use Encryption π failure related to arguments passed when initializing In-Use Encryption, that's the whole vibe.MONGOC_ERROR_CLIENT_INVALID_ENCRYPTION_STATEβ the encryption thing is in a bad state, plain and simple. Failure related to In-Use Encryption β¨MONGOC_ERROR_CLIENT_INVALID_LOAD_BALANCERβ you tried to connect to a MongoDB server behind a load balancer, but the server does not advertize load balanced support π that's it, that's the situation.MONGOC_ERROR_CLIENT_NOT_READYβ not me catching you trying to create a client without ever callingmongoc_init()first π you attempted to create a client but did not previously callmongoc_init(). Do the init. It's giving prerequisites.
MONGOC_ERROR_STREAMβ the stream domain, home of connection sadness π«ΆMONGOC_ERROR_STREAM_NAME_RESOLUTIONβ DNS failure. That's it. The name did not resolve. πMONGOC_ERROR_STREAM_SOCKETβ timeout communicating with server, or connection closed. One of the two, and neither is cute πMONGOC_ERROR_STREAM_CONNECTβ failed to connect to server. Straight up did not connect.
MONGOC_ERROR_PROTOCOLβ protocol domain, aka "the server and I are not on the same page" β¨MONGOC_ERROR_PROTOCOL_INVALID_REPLYβ corrupt response from server. The reply was giving nonsense.MONGOC_ERROR_PROTOCOL_BAD_WIRE_VERSIONβ the server version is too old or too new to communicate with the driver π₯ like, no compatible wire version, so it's a no from both of us.
MONGOC_ERROR_CURSORβ cursor domain, my beloved chaotic child π¦MONGOC_ERROR_CURSOR_INVALID_CURSORβ okay so this one has range π you passed bad arguments tomongoc_collection_find_with_opts(), or you calledmongoc_cursor_next()on a completed or failed cursor, or the cursor timed out on the server. Three iconic ways to end up here.
MONGOC_ERROR_CHANGE_STREAM_NO_RESUME_TOKENβ a resume token was not returned in a document found withmongoc_change_stream_next()π no token, no resume, that's the whole tragedy.MONGOC_ERROR_QUERYβ query domain time πMONGOC_ERROR_QUERY_FAILUREβ Error API Version 1: server error from command or query. The server error message is inmessageπ
MONGOC_ERROR_SERVERβ same code name, different era, do not get it twisted βMONGOC_ERROR_QUERY_FAILUREβ Error API Version 2: server error from command or query. The server error message is inmessage. fr fr, checkmessage.
MONGOC_ERROR_SASLβ a SASL error code. Runman sasl_errorsfor a list of codes π the literal value is the SASL code, iykyk.MONGOC_ERROR_BSONβ BSON domain, the document-validity police π¨MONGOC_ERROR_BSON_INVALIDβ you passed an invalid or oversized BSON document as a parameter, or the server reply was corrupt. Either way, something was not slaying π
MONGOC_ERROR_NAMESPACEβMONGOC_ERROR_NAMESPACE_INVALIDβ you tried to create a collection with an invalid name. like, the name was not namable. bestie the server said no πMONGOC_ERROR_COMMANDβMONGOC_ERROR_COMMAND_INVALID_ARGβ so many functions set this error code when you pass them bad parameters. print the error message for the deets, it literally tells you what's wrong π
okay so the next few rows leave the domain column blank β that's just the table being lazy, it means the domain is still MONGOC_ERROR_COMMAND carrying over from above π«Ά
MONGOC_ERROR_COMMANDβMONGOC_ERROR_PROTOCOL_BAD_WIRE_VERSIONβ you tried to use a command option the server does not support. wrong era of server, girly πMONGOC_ERROR_COMMANDβMONGOC_ERROR_DUPLICATE_KEYβ an insert or update failed because of a duplicate_idor other unique-index violation. someone already claimed that username πMONGOC_ERROR_COMMANDβMONGOC_ERROR_MAX_TIME_MS_EXPIREDβ the operation failed becausemaxTimeMSexpired. ran out of clock, iconic behaviour honestly β°MONGOC_ERROR_COMMANDβMONGOC_ERROR_SERVER_SELECTION_INVALID_IDβ theserverIdoption for an operation conflicts with the pinned server for that operation's client session (denoted by thesessionIdoption). two servers walked into a bar and could not agree π¦MONGOC_ERROR_COMMANDβ error code from server. β Error API Version 1: server error from a command. the server error message lives inmessageβ¨MONGOC_ERROR_SERVERβ error code from server. β Error API Version 2: server error from a command. the server error message lives inmessageβ¨ (yes it's the same vibe, different API version, that's the whole point π)
MONGOC_ERROR_COLLECTIONβMONGOC_ERROR_COLLECTION_INSERT_FAILED,MONGOC_ERROR_COLLECTION_UPDATE_FAILED,MONGOC_ERROR_COLLECTION_DELETE_FAILEDβ invalid or empty input tomongoc_collection_insert_one(),mongoc_collection_insert_many(),mongoc_collection_update_one(),mongoc_collection_update_many(),mongoc_collection_replace_one(),mongoc_collection_delete_one(), ormongoc_collection_delete_many()πMONGOC_ERROR_COLLECTIONβ error code from server. β Error API Version 1: server error frommongoc_collection_insert_one(),mongoc_collection_insert_many(),mongoc_collection_update_one(),mongoc_collection_update_many(),mongoc_collection_replace_one(),MONGOC_ERROR_SERVERβ error code from server. β Error API Version 2: server error frommongoc_collection_insert_one(),mongoc_collection_insert_many(),mongoc_collection_update_one(),mongoc_collection_update_many(),mongoc_collection_replace_one(),
ngl those last two lists trail off exactly like that in the original table β that's the whole row, not me getting distracted mid-sentence π
MONGOC_ERROR_GRIDFSβMONGOC_ERROR_GRIDFS_CHUNK_MISSINGβ the GridFS file is missing a document in itschunkscollection. girl where did the chunk go π
and again, blank domain cell = still MONGOC_ERROR_GRIDFS for these two π
MONGOC_ERROR_GRIDFSβMONGOC_ERROR_GRIDFS_CORRUPTβ a data inconsistency was detected in GridFS. something is Not Right in there πMONGOC_ERROR_GRIDFSβMONGOC_ERROR_GRIDFS_INVALID_FILENAMEβ you passed aNULLfilename tomongoc_gridfs_remove_by_filename(). can't delete a file with no name bestie, that's not a thing πMONGOC_ERROR_GRIDFS_PROTOCOL_ERRORβ bestie, this one means you calledmongoc_gridfs_file_set_id()aftermongoc_gridfs_file_save(), or you tried to write on a closed GridFS stream. order matters, okay?? πMONGOC_ERROR_GRIDFS_BUCKET_FILE_NOT_FOUNDβ a GridFS file is missing from the files collection. it's justβ¦ not there. gone. πMONGOC_ERROR_GRIDFS_BUCKET_STREAMβ an error occurred on a stream created from a GridFS operation likemongoc_gridfs_bucket_upload_from_stream(). stream said no π«Ά
MONGOC_ERROR_SCRAMπΈMONGOC_ERROR_SCRAM_PROTOCOL_ERRORβ failure in SCRAM-SHA-1 or SCRAM-SHA-256 authentication. the auth handshake did not slay, ngl
MONGOC_ERROR_SERVER_SELECTIONπMONGOC_ERROR_SERVER_SELECTION_FAILUREβ no replica set member or mongos is available, or none of them matches your read preference, or you supplied an invalidmongoc_read_prefs_t. three ways to end up here, all of them lowkey tragic
MONGOC_ERROR_WRITE_CONCERN- the code here is literally Error code from server.
- and the meaning: there was a write concern error or timeout from the server. the server said "hmm, no" π
MONGOC_ERROR_TRANSACTIONπ§MONGOC_ERROR_TRANSACTION_INVALIDβ you attempted to start a transaction when one is already in progress, or commit or abort when there is no transaction. bestie you can't start a vibe that's already started, and you can't end one that never began β¨
MONGOC_ERROR_CLIENT_SIDE_ENCRYPTIONπ- the code is Error code produced by libmongocrypt.
- and the meaning: an error occurred in the library responsible for In-Use Encryption
MONGOC_ERROR_AZUREβοΈMONGOC_ERROR_KMS_SERVER_HTTPβ an Azure HTTP service responded with an error statusMONGOC_ERROR_KMS_SERVER_BAD_JSONβ an Azure service responded with invalid JSON data. JSON said β¨nopeβ¨
MONGOC_ERROR_GCPπMONGOC_ERROR_KMS_SERVER_HTTPβ a GCP HTTP service responded with an error statusMONGOC_ERROR_KMS_SERVER_BAD_JSONβ a GCP service responded with invalid JSON data. same vibe, different cloud π
πΈ Error Labels πΈ
okay so sometimes your app literally <emphasis>has</emphasis> to make a decision based on what category of error the driver threw at you β but those categories don't map perfectly onto an error domain or an error code. that's the whole reason error labels exist, bestie. they're the reliable way to figure out how your app should react. no cap.
Any C Driver function that has a bson_t out-parameter named reply may include error labels in that reply, as a BSON field named "errorLabels" holding an array of strings:
{ "errorLabels": [ "TransientTransactionError" ] }
Use mongoc_error_has_label() to test whether a reply contains a specific label π
. For example code that shows error labels doing their thing in actual application logic, go peek at mongoc_client_session_start_transaction().
The following error labels are currently defined. Future versions of MongoDB may introduce new labels. π
π TransientTransactionError π
Inside a multi-document transaction, certain errors can leave the transaction in an unknown or aborted state π. these include write conflicts, primary stepdowns, and network errors. what should you do? abort the transaction and try the same sequence of operations again in a new transaction. that's it, that's the move. π«Ά
β UnknownTransactionCommitResult β
when mongoc_client_session_commit_transaction() hits a network error or certain server errors, nobody knows whether the transaction was actually committed. genuinely unknown. π applications should attempt to commit the transaction again until:
- the commit succeeds β¨
- the commit fails with an error that is <emphasis>not</emphasis> labeled
"UnknownTransactionCommitResult" - or your application just chooses to give up π
π§ Setting the Error API Version π§
ngl, the driver's error reporting started out with a design flaw: when the error domain is MONGOC_ERROR_COLLECTION, MONGOC_ERROR_QUERY, or MONGOC_ERROR_COMMAND, the error code might originate from the server or from the driver. an application can't always know where an error came from, so it literally cannot tell what the code even means. it's giving chaos.
for example: if mongoc_collection_update_one() sets the error's domain to MONGOC_ERROR_COLLECTION and its code to 24, your application has no idea whether that 24 is the generic driver error code MONGOC_ERROR_COLLECTION_UPDATE_FAILED or the specific server error code "LockTimeout". π
To fix this flaw while preserving backward compatibility, the C Driver 1.4 introduces "Error API Versions" π:
- Version 1 β the default Error API Version β maintains the flawed behavior.
- Version 2 β adds a new error domain,
MONGOC_ERROR_SERVER. in Version 2, error codes originating on the server always have error domainMONGOC_ERROR_SERVERorMONGOC_ERROR_WRITE_CONCERN. so when the driver uses Version 2, your application can always determine the origin and meaning of error codes. slay.
New applications should use Version 2, and existing applications should be updated to use Version 2 as well. periodt. π
here's the full glow-up breakdown, source by source:
mongoc_cursor_error()- API Version 1:
MONGOC_ERROR_QUERY - API Version 2:
MONGOC_ERROR_SERVER
- API Version 1:
mongoc_client_command_with_opts(),mongoc_database_command_with_opts(), and other command functions- API Version 1:
MONGOC_ERROR_QUERY - API Version 2:
MONGOC_ERROR_SERVER
- API Version 1:
mongoc_client_get_database_names_with_opts()and other command helper functions- API Version 1:
MONGOC_ERROR_QUERY - API Version 2:
MONGOC_ERROR_SERVER
- API Version 1:
mongoc_collection_insert_one(),mongoc_collection_insert_many(),mongoc_collection_update_one(),mongoc_collection_update_many(),mongoc_collection_replace_one(),mongoc_collection_delete_one(),mongoc_collection_delete_many()- API Version 1:
MONGOC_ERROR_COMMAND - API Version 2:
MONGOC_ERROR_SERVER
- API Version 1:
mongoc_bulk_operation_execute()- API Version 1:
MONGOC_ERROR_COMMAND - API Version 2:
MONGOC_ERROR_SERVER
- API Version 1:
- Write-concern timeout
- API Version 1:
MONGOC_ERROR_WRITE_CONCERN - API Version 2:
MONGOC_ERROR_WRITE_CONCERN
- API Version 1:
(yes, that last one is the same in both versions β write-concern timeouts stay iconically unchanged π β¨)
okay so before we get into the juicy stuff, a quick word on the Error API π The two Error API versions are called MONGOC_ERROR_API_VERSION_LEGACY and MONGOC_ERROR_API_VERSION_2 β those are your only two choices, bestie, no secret third one. You set whichever one you want with mongoc_client_set_error_api() or, if you're a pool girlie, mongoc_client_pool_set_error_api().
SEE ALSO:
- MongoDB Server Error Codes
π¦ Object Lifecycle
This page is literally a β¨mapβ¨ of the order you create and destroy libmongoc's main struct types. Get the order wrong and things get ugly, so pay attention bestie π«Ά
πΈ Clients and pools
Call mongoc_init() exactly once, before you call any other libmongoc function β like, any of them. Then call mongoc_cleanup() once before your program exits. That's the whole ritual, no cap.
If your program is using libmongoc from multiple threads, you should create a mongoc_client_pool_t with mongoc_client_pool_new(). Each thread then acquires a mongoc_client_t from the pool with mongoc_client_pool_pop(), and returns it with mongoc_client_pool_push() once that thread is done using it. To destroy the pool: first return all the clients, then call mongoc_client_pool_destroy(). In that order. Don't skip steps π
If your program only uses libmongoc from a single thread, you can just create a mongoc_client_t directly with mongoc_client_new() or mongoc_client_new_from_uri(), and destroy it with mongoc_client_destroy(). Simple, iconic, no pool needed π
π Databases, collections, and related objects
You can create a mongoc_database_t or a mongoc_collection_t from a mongoc_client_t, and you can create a mongoc_cursor_t or a mongoc_bulk_operation_t from a mongoc_collection_t. It's giving very nesting-doll energy πͺ
Every one of these objects must be destroyed before the client it was created from. Other than that one rule, their lifetimes are fully independent β do whatever, live your truth β¨
β GridFS objects
So here's the chain, and yes it's a whole chain: you can create a mongoc_gridfs_t from a mongoc_client_t, create a mongoc_gridfs_file_t or a mongoc_gridfs_file_list_t from a mongoc_gridfs_t, create a mongoc_gridfs_file_t from a mongoc_gridfs_file_list_t, and create a mongoc_stream_t from a mongoc_gridfs_file_t. Obsessed with the pipeline honestly π₯
Each of these objects depends on the object it was created from. So always destroy GridFS objects in the reverse of the order they were created β last one made is the first one gone, periodt.
The sole exception: a mongoc_gridfs_file_t does not need to be destroyed before the mongoc_gridfs_file_list_t it was created from. That one gets a pass π
π§ GridFS bucket objects
Create a mongoc_gridfs_bucket_t with a mongoc_database_t that's derived from a mongoc_client_t. The mongoc_database_t is independent from the mongoc_gridfs_bucket_t β they don't owe each other anything. But the mongoc_client_t must outlive the mongoc_gridfs_bucket_t. The client is the main character here, and it stays alive longest π
A mongoc_stream_t may be created from the mongoc_gridfs_bucket_t, and in that case the mongoc_gridfs_bucket_t must outlive the mongoc_stream_t.
π Sessions
Start a session with mongoc_client_start_session(), use it for a sequence of operations and multi-document transactions, then free it with mongoc_client_session_destroy(). Any mongoc_cursor_t or mongoc_change_stream_t using a session must be destroyed before the session, and a session must be destroyed before the mongoc_client_t it came from. Chain of command, bestie π«‘
By default, sessions are causally consistent. If you want to turn that off: before starting a session, create a mongoc_session_opt_t with mongoc_session_opts_new(), call mongoc_session_opts_set_causal_consistency(), then free the struct with mongoc_session_opts_destroy().
β οΈ Unacknowledged writes are prohibited with sessions. Not allowed. Not a vibe. Don't do it π
A mongoc_client_session_t must be used by only one thread at a time. And here's the sneaky part: because of session pooling, mongoc_client_start_session() may hand you back a session that's been idle for a while and is literally about to be closed after its idle timeout π So use the session within one minute of acquiring it to refresh it and dodge that timeout entirely. Clock's ticking, babe β°
π₯ Client Side Encryption
When you're configuring a mongoc_client_t for automatic encryption via mongoc_client_enable_auto_encryption(), if a separate key vault client is set in the options (that's via mongoc_auto_encryption_opts_set_keyvault_client()), then the key vault client must outlive the encrypted client. Like, it goes first, no exceptions π
When you're configuring a mongoc_client_pool_t for automatic encryption via mongoc_client_pool_enable_auto_encryption(), if a separate key vault client pool is set in the options (via mongoc_auto_encryption_opts_set_keyvault_client_pool()), then that key vault client pool must outlive the encrypted client pool.
And when you're creating a mongoc_client_encryption_t, the configured key vault client (set via mongoc_client_encryption_opts_set_keyvault_client()) must outlive the mongoc_client_encryption_t. Same energy, different struct π
π GridFS
Real talk: the C driver includes two APIs for GridFS, and they are not the same girlie.
The older API consists of mongoc_gridfs_t and its derivatives. It contains deprecated API, it does not support read preferences, and it is not recommended in new applications. Also β and this is the big one β it does not conform to the MongoDB GridFS specification. Yeah. π
The newer API consists of mongoc_gridfs_bucket_t and lets you upload/download through derived mongoc_stream_t objects. It does conform to the MongoDB GridFS specification. Slay β¨
There is not always a straightforward upgrade path from an application built with mongoc_gridfs_t to mongoc_gridfs_bucket_t (for example, a mongoc_gridfs_file_t provides functions to seek but a mongoc_stream_t does not π). But users are encouraged to upgrade when possible β future you will be so grateful π
π mongoc_auto_encryption_opts_t
Options for enabling automatic encryption and decryption for In-Use Encryption. That's it, that's the whole job.
Synopsis
typedef struct _mongoc_auto_encryption_opts_t mongoc_auto_encryption_opts_t;
SEE ALSO:
- In-Use Encryption
π mongoc_bulkwrite_t
Synopsis
typedef struct _mongoc_bulkwrite_t mongoc_bulkwrite_t;
Description
mongoc_bulkwrite_t provides an abstraction for submitting multiple write operations as a single batch. Group chat for writes, basically π¨
After adding all of the write operations to the mongoc_bulkwrite_t, call mongoc_bulkwrite_execute() to execute the operation.
β οΈ WARNING: It is only valid to callmongoc_bulkwrite_execute()once. Themongoc_bulkwrite_tmust be destroyed afterwards. One shot, one kill, no reruns π
π NOTE: If you're using MongoDB server 8.0+, prefermongoc_bulkwrite_tovermongoc_bulk_operation_tto reduce network round trips. Fewer trips = happier you π¦mongoc_bulkwrite_tuses thebulkWriteserver command introduced in MongoDB server 8.0. ThebulkWritecommand supports insert, update, and delete operations in the same payload.bulkWritesupports use of multiple collection namespaces in the same payload. Iconic behaviour honestly πmongoc_bulk_operation_tuses theinsert,updateanddeleteserver commands available in all current MongoDB server versions. Write operations are grouped by type (insert, update, delete) and sent in separate commands. Only one collection may be specified per bulk write.
β mongoc_bulkwriteopts_t
Synopsis
typedef struct _mongoc_bulkwriteopts_t mongoc_bulkwriteopts_t;
πΈ mongoc_bulkwriteresult_t
Synopsis
typedef struct _mongoc_bulkwriteresult_t mongoc_bulkwriteresult_t;
π mongoc_bulkwriteexception_t
Synopsis
typedef struct _mongoc_bulkwriteexception_t mongoc_bulkwriteexception_t;
π«Ά mongoc_bulkwrite_check_acknowledged_t
Synopsis
typedef struct {
bool is_ok; // true if no error
bool is_acknowledged; // true if the previous call to `mongoc_bulkwrite_execute` used an acknowledged write concern
} mongoc_bulkwrite_check_acknowledged_t;
Description
mongoc_bulkwrite_check_acknowledged_t is returned by mongoc_bulkwrite_check_acknowledged().
is_ok is false if there was no previous call to mongoc_bulkwrite_execute() or if execution failed before the write concern could be determined. So like, if it's false, something went sideways β either you never ran it or it broke before we could even tell π
is_acknowledged is true if the previous mongoc_bulkwrite_execute() call used an acknowledged write concern.
π mongoc_bulkwrite_serverid_t
Synopsis
typedef struct {
bool is_ok; // true if no error
uint32_t serverid; // the server ID last used in `mongoc_bulkwrite_execute`
} mongoc_bulkwrite_serverid_t;
Description
mongoc_bulkwrite_serverid_t is returned by mongoc_bulkwrite_serverid().
is_ok is false if there was no previous call to mongoc_bulkwrite_execute() or if execution failed.
serverid is the server ID that was last used in mongoc_bulkwrite_execute() π
π§± mongoc_bulk_operation_t β Bulk Write Operations π§±
Synopsis
typedef struct _mongoc_bulk_operation_t mongoc_bulk_operation_t;
Description
Okay bestie, so mongoc_bulk_operation_t gives you an abstraction for submitting multiple write operations as a single batch. It's basically the group-chat organizer of your writes β one batch, one vibe. β¨
Once you've added all the write operations to your mongoc_bulk_operation_t, you call mongoc_bulk_operation_execute() to actually run it.
β οΈ WARNING: it is only valid to callmongoc_bulk_operation_execute()once. Themongoc_bulk_operation_tmust be destroyed afterwards. No re-runs, no take-backs β one and done, periodt. π
SEE ALSO:
- Bulk Write Operations
mongoc_bulkwrite_t
π NOTE: if you're on MongoDB server 8.0+, prefermongoc_bulkwrite_tovermongoc_bulk_operation_tto reduce network round trips. Fewer round trips = fewer little delays = so much better for your vibe check. π₯
So like, here's the tea on the difference:
mongoc_bulkwrite_t uses the bulkWrite server command introduced in MongoDB server 8.0. The bulkWrite command supports insert, update, and delete operations in the same payload. bulkWrite supports use of multiple collection namespaces in the same payload. Iconic, honestly.
mongoc_bulk_operation_t uses the insert, update and delete server commands available in all current MongoDB server versions. Write operations are grouped by type (insert, update, delete) and sent in separate commands. Only one collection may be specified per bulk write. Old-school but reliable π«Ά
π mongoc_change_stream_t π
Synopsis
#include <mongoc/mongoc.h>
typedef struct _mongoc_change_stream_t mongoc_change_stream_t;
mongoc_change_stream_t is a handle to a change stream π A collection change stream can be obtained using mongoc_collection_watch().
It is recommended to use a mongoc_change_stream_t and its functions instead of a raw aggregation with a $changeStream stage. For more information see the MongoDB Manual Entry on Change Streams. Trust the process, it's giving convenience. β¨
Example
example-collection-watch.c
#include <mongoc/mongoc.h>
int
main(void)
{
bson_t empty = BSON_INITIALIZER;
const bson_t *doc;
bson_t *to_insert = BCON_NEW("x", BCON_INT32(1));
const bson_t *err_doc;
bson_error_t error;
const char *uri_string;
mongoc_uri_t *uri;
mongoc_client_t *client;
mongoc_collection_t *coll;
mongoc_change_stream_t *stream;
mongoc_write_concern_t *wc = mongoc_write_concern_new();
bson_t opts = BSON_INITIALIZER;
bool r;
mongoc_init();
uri_string = "mongodb://"
"localhost:27017,localhost:27018,localhost:"
"27019/db?replicaSet=rs0";
uri = mongoc_uri_new_with_error(uri_string, &error);
if (!uri) {
fprintf(stderr,
"failed to parse URI:\n"
"error message: %s\n",
error.message);
return EXIT_FAILURE;
}
client = mongoc_client_new_from_uri(uri);
if (!client) {
return EXIT_FAILURE;
}
coll = mongoc_client_get_collection(client, "db", "coll");
stream = mongoc_collection_watch(coll, &empty, NULL);
mongoc_write_concern_set_wmajority(wc, 10000);
mongoc_write_concern_append(wc, &opts);
r = mongoc_collection_insert_one(coll, to_insert, &opts, NULL, &error);
if (!r) {
fprintf(stderr, "Error: %s\n", error.message);
return EXIT_FAILURE;
}
while (mongoc_change_stream_next(stream, &doc)) {
char *as_json = bson_as_relaxed_extended_json(doc, NULL);
fprintf(stderr, "Got document: %s\n", as_json);
bson_free(as_json);
}
if (mongoc_change_stream_error_document(stream, &error, &err_doc)) {
if (!bson_empty(err_doc)) {
fprintf(stderr, "Server Error: %s\n", bson_as_relaxed_extended_json(err_doc, NULL));
} else {
fprintf(stderr, "Client Error: %s\n", error.message);
}
return EXIT_FAILURE;
}
bson_destroy(to_insert);
mongoc_write_concern_destroy(wc);
bson_destroy(&opts);
mongoc_change_stream_destroy(stream);
mongoc_collection_destroy(coll);
mongoc_uri_destroy(uri);
mongoc_client_destroy(client);
mongoc_cleanup();
return EXIT_SUCCESS;
}
π¬ Starting and Resuming π¬
All watch functions accept several options to indicate where a change stream should start returning changes from: resumeAfter, startAfter, and startAtOperationTime. Pick your starting point, bestie π
All changes returned by mongoc_change_stream_next() include a resume token in the _id field. MongoDB 4.2 also includes an additional resume token in each "aggregate" and "getMore" command response, which points to the end of that response's batch. The current token is automatically cached by libmongoc. In the event of an error, libmongoc attempts to recreate the change stream starting where it left off by passing the cached resume token. libmongoc only attempts to resume once, but client applications can access the cached resume token with mongoc_change_stream_get_resume_token() and use it for their own resume logic by passing it as either the resumeAfter or startAfter option. DIY queen energy π
Additionally, change streams can start returning changes at an operation time by using the startAtOperationTime field. This can be the timestamp returned in the operationTime field of a command reply.
resumeAfter, startAfter, and startAtOperationTime are mutually exclusive options. Setting more than one will result in a server error. Pick one, bestie β the server will not be nice about it π
The following example implements custom resuming logic, persisting the resume token in a file.
example-resume.c
#include <mongoc/mongoc.h>
/* An example implementation of custom resume logic in a change stream.
* example-resume starts a client-wide change stream and persists the resume
* token in a file "resume-token.json". On restart, if "resume-token.json"
* exists, the change stream starts watching after the persisted resume token.
*
* This behavior allows a user to exit example-resume, and restart it later
* without missing any change events.
*/
#include <unistd.h>
static const char *RESUME_TOKEN_PATH = "resume-token.json";
static bool
_save_resume_token(const bson_t *doc)
{
FILE *file_stream;
bson_iter_t iter;
bson_t resume_token_doc;
char *as_json = NULL;
size_t as_json_len;
const bson_value_t *resume_token;
if (!bson_iter_init_find(&iter, doc, "_id")) {
fprintf(stderr, "reply does not contain operationTime.");
return false;
}
resume_token = bson_iter_value(&iter);
/* store the resume token in a document, { resumeAfter: <resume token> }
* which we can later append easily. */
file_stream = fopen(RESUME_TOKEN_PATH, "w+");
if (!file_stream) {
fprintf(stderr, "failed to open %s for writing\n", RESUME_TOKEN_PATH);
return false;
}
bson_init(&resume_token_doc);
BSON_APPEND_VALUE(&resume_token_doc, "resumeAfter", resume_token);
as_json = bson_as_canonical_extended_json(&resume_token_doc, &as_json_len);
bson_destroy(&resume_token_doc);
size_t n_written = 0;
while (n_written < as_json_len) {
ssize_t r = fwrite((void *)(as_json + n_written), sizeof(char), as_json_len - n_written, file_stream);
if (r == -1) {
fprintf(stderr, "failed to write to %s\n", RESUME_TOKEN_PATH);
bson_free(as_json);
fclose(file_stream);
return false;
}
n_written += r;
}
bson_free(as_json);
fclose(file_stream);
return true;
}
bool
_load_resume_token(bson_t *opts)
{
bson_error_t error;
bson_json_reader_t *reader;
bson_t doc;
/* if the file does not exist, skip. */
if (-1 == access(RESUME_TOKEN_PATH, R_OK)) {
return true;
}
reader = bson_json_reader_new_from_file(RESUME_TOKEN_PATH, &error);
if (!reader) {
fprintf(stderr, "failed to open %s for reading: %s\n", RESUME_TOKEN_PATH, error.message);
return false;
}
bson_init(&doc);
if (-1 == bson_json_reader_read(reader, &doc, &error)) {
fprintf(stderr, "failed to read doc from %s\n", RESUME_TOKEN_PATH);
bson_destroy(&doc);
bson_json_reader_destroy(reader);
return false;
}
printf("found cached resume token in %s, resuming change stream.\n", RESUME_TOKEN_PATH);
bson_concat(opts, &doc);
bson_destroy(&doc);
bson_json_reader_destroy(reader);
return true;
}
int
main(void)
{
int exit_code = EXIT_FAILURE;
const char *uri_string;
mongoc_uri_t *uri = NULL;
bson_error_t error;
mongoc_client_t *client = NULL;
bson_t pipeline = BSON_INITIALIZER;
bson_t opts = BSON_INITIALIZER;
mongoc_change_stream_t *stream = NULL;
const bson_t *doc;
const int max_time = 30; /* max amount of time, in seconds, that
mongoc_change_stream_next can block. */
mongoc_init();
uri_string = "mongodb://localhost:27017/db?replicaSet=rs0";
uri = mongoc_uri_new_with_error(uri_string, &error);
if (!uri) {
fprintf(stderr,
"failed to parse URI:\n"
"error message: %s\n",
error.message);
goto cleanup;
}
client = mongoc_client_new_from_uri(uri);
if (!client) {
goto cleanup;
}
if (!_load_resume_token(&opts)) {
goto cleanup;
}
BSON_APPEND_INT64(&opts, "maxAwaitTimeMS", max_time * 1000);
printf("listening for changes on the client (max %d seconds).\n", max_time);
stream = mongoc_client_watch(client, &pipeline, &opts);
while (mongoc_change_stream_next(stream, &doc)) {
char *as_json;
as_json = bson_as_canonical_extended_json(doc, NULL);
printf("change received: %s\n", as_json);
bson_free(as_json);
if (!_save_resume_token(doc)) {
goto cleanup;
}
}
exit_code = EXIT_SUCCESS;
cleanup:
mongoc_uri_destroy(uri);
bson_destroy(&pipeline);
bson_destroy(&opts);
mongoc_change_stream_destroy(stream);
mongoc_client_destroy(client);
mongoc_cleanup();
return exit_code;
}
okay and that's the first example fully wrapped up β cleanup label ate, resources freed, exit code returned, no leaks, we love to see it π
The next example is the one where we use startAtOperationTime to sync a change stream up with some other operation. honestly kind of iconic behavior.
example-start-at-optime.c
/* An example of starting a change stream with startAtOperationTime. */
#include <mongoc/mongoc.h>
int
main(void)
{
int exit_code = EXIT_FAILURE;
const char *uri_string;
mongoc_uri_t *uri = NULL;
bson_error_t error;
mongoc_client_t *client = NULL;
mongoc_collection_t *coll = NULL;
bson_t pipeline = BSON_INITIALIZER;
bson_t opts = BSON_INITIALIZER;
mongoc_change_stream_t *stream = NULL;
bson_iter_t iter;
const bson_t *doc;
bson_value_t cached_operation_time = {0};
int i;
bool r;
mongoc_init();
uri_string = "mongodb://localhost:27017/db?replicaSet=rs0";
uri = mongoc_uri_new_with_error(uri_string, &error);
if (!uri) {
fprintf(stderr,
"failed to parse URI:\n"
"error message: %s\n",
error.message);
goto cleanup;
}
client = mongoc_client_new_from_uri(uri);
if (!client) {
goto cleanup;
}
/* insert five documents. */
coll = mongoc_client_get_collection(client, "db", "coll");
for (i = 0; i < 5; i++) {
bson_t reply;
bson_t *insert_cmd = BCON_NEW("insert", "coll", "documents", "[", "{", "x", BCON_INT64(i), "}", "]");
r = mongoc_collection_write_command_with_opts(coll, insert_cmd, NULL, &reply, &error);
bson_destroy(insert_cmd);
if (!r) {
bson_destroy(&reply);
fprintf(stderr, "failed to insert: %s\n", error.message);
goto cleanup;
}
if (i == 0) {
/* cache the operation time in the first reply. */
if (bson_iter_init_find(&iter, &reply, "operationTime")) {
bson_value_copy(bson_iter_value(&iter), &cached_operation_time);
} else {
fprintf(stderr, "reply does not contain operationTime.");
bson_destroy(&reply);
goto cleanup;
}
}
bson_destroy(&reply);
}
/* start a change stream at the first returned operationTime. */
BSON_APPEND_VALUE(&opts, "startAtOperationTime", &cached_operation_time);
stream = mongoc_collection_watch(coll, &pipeline, &opts);
/* since the change stream started at the operation time of the first
* insert, the five inserts are returned. */
printf("listening for changes on db.coll:\n");
while (mongoc_change_stream_next(stream, &doc)) {
char *as_json;
as_json = bson_as_canonical_extended_json(doc, NULL);
printf("change received: %s\n", as_json);
bson_free(as_json);
}
exit_code = EXIT_SUCCESS;
cleanup:
mongoc_uri_destroy(uri);
bson_destroy(&pipeline);
bson_destroy(&opts);
if (cached_operation_time.value_type) {
bson_value_destroy(&cached_operation_time);
}
mongoc_change_stream_destroy(stream);
mongoc_collection_destroy(coll);
mongoc_client_destroy(client);
mongoc_cleanup();
return exit_code;
}
like, notice the little detail where cached_operation_time only gets destroyed if cached_operation_time.value_type is set? that's the "don't destroy something we never filled in" check and it's lowkey very good hygiene π§
mongoc_client_encryption_t</p>
Synopsis
typedef struct _mongoc_client_encryption_t mongoc_client_encryption_t;
so mongoc_client_encryption_t is the lil utility-functions holder for In-Use Encryption. that's its whole job bestie β¨
Thread Safety
ok so pay attention: mongoc_client_encryption_t is NOT thread-safe π¨ and you should only ever use it in the same thread as the mongoc_client_t that got configured via mongoc_client_encryption_opts_set_keyvault_client(). no sharing, no passing it around the group chat.
Lifecycle
the key vault client β the one you set up with mongoc_client_encryption_opts_set_keyvault_client() β must outlive the mongoc_client_encryption_t. it's the responsible older sibling in this relationship, fr.
SEE ALSO:
mongoc_client_enable_auto_encryption()mongoc_client_pool_enable_auto_encryption()- In-Use Encryption for libmongoc
- The MongoDB Manual for Client-Side Field Level Encryption
- The MongoDB Manual for Queryable Encryption
mongoc_client_encryption_datakey_opts_t
Synopsis
typedef struct _mongoc_client_encryption_datakey_opts_t mongoc_client_encryption_datakey_opts_t;
this one is literally just for setting options when you call mongoc_client_encryption_create_datakey(). that's it. that's the whole vibe π
SEE ALSO:
mongoc_client_encryption_create_datakey()
mongoc_client_encryption_rewrap_many_datakey_result_t
Synopsis
typedef struct _mongoc_client_encryption_rewrap_many_datakey_result_t
mongoc_client_encryption_rewrap_many_datakey_result_t;
you use this bad boy to get at the result of mongoc_client_encryption_rewrap_many_datakey(). results girlies stay winning π
SEE ALSO:
mongoc_client_encryption_rewrap_many_datakey()
mongoc_client_encryption_encrypt_opts_t
Synopsis
typedef struct _mongoc_client_encryption_encrypt_opts_t mongoc_client_encryption_encrypt_opts_t;
options container for mongoc_client_encryption_encrypt(). simple. clean. no notes β¨
SEE ALSO:
mongoc_client_encryption_encrypt()
mongoc_client_encryption_encrypt_string_opts_t
Synopsis
typedef struct _mongoc_client_encryption_encrypt_string_opts_t mongoc_client_encryption_encrypt_string_opts_t;
Changed in version 2.4.0: Renamed from the now-deprecated text API. π so if you see the old text API name floating around, that's the deprecated one, don't @ it.
StringOpts specifies index options for a Queryable Encryption field supporting "string" queries. and yeah, it's used to set options for mongoc_client_encryption_encrypt().
here's the rules bestie, memorize them:
- Case sensitive and diacritic sensitive must be set.
- If prefix or suffix are set, substring must not be set.
SEE ALSO:
mongoc_client_encryption_encrypt()mongoc_client_encryption_encrypt_opts_t
mongoc_client_encryption_encrypt_range_opts_t
Synopsis
typedef struct _mongoc_client_encryption_encrypt_range_opts_t mongoc_client_encryption_encrypt_range_opts_t;
New in version 1.24.0. π¦
RangeOpts specifies index options for a Queryable Encryption field supporting "range" queries, and it's used to set options for mongoc_client_encryption_encrypt().
the requirements, and please don't skip these:
- The options
min,max, trim factor,sparsity, andrangemust match the values set in theencryptedFieldsof the destination collection. mismatch = chaos, no cap. - For
doubleanddecimal128fields,min/max/precisionmust all be set, or all be unset. no halfsies π
SEE ALSO:
mongoc_client_encryption_encrypt()mongoc_client_encryption_encrypt_opts_t
mongoc_client_encryption_opts_t
Synopsis
typedef struct _mongoc_client_encryption_opts_t mongoc_client_encryption_opts_t;
options for mongoc_client_encryption_new(). setting-the-options-for-the-thing energy, you know how it is π
SEE ALSO:
mongoc_client_encryption_new()
mongoc_client_pool_t
a connection pool for multi-threaded programs. see Connection Pooling. this is the multithreading main character π
Synopsis
typedef struct _mongoc_client_pool_t mongoc_client_pool_t
mongoc_client_pool_t is the basis for multi-threading in the MongoDB C driver. since mongoc_client_t structures are not thread-safe, you use this structure to grab a fresh mongoc_client_t for a given thread. slay.
and here's the catch: this structure is thread-safe β except for its destructor method, mongoc_client_pool_destroy(), which is not thread-safe and must only be called from one thread. one. single. thread. don't be brave about it π
Example
example-pool.c
/* gcc example-pool.c -o example-pool $(pkg-config --cflags --libs
* libmongoc-1.0) */
/* ./example-pool [CONNECTION_STRING] */
#include <mongoc/mongoc.h>
#include <pthread.h>
#include <stdio.h>
static pthread_mutex_t mutex;
static bool in_shutdown = false;
static void *
worker(void *data)
{
mongoc_client_pool_t *pool = data;
mongoc_client_t *client;
bson_t ping = BSON_INITIALIZER;
bson_error_t error;
bool r;
BSON_APPEND_INT32(&ping, "ping", 1);
while (true) {
client = mongoc_client_pool_pop(pool);
/* Do something with client. If you are writing an HTTP server, you
* probably only want to hold onto the client for the portion of the
* request performing database queries.
*/
r = mongoc_client_command_simple(client, "admin", &ping, NULL, NULL, &error);
if (!r) {
fprintf(stderr, "%s\n", error.message);
}
mongoc_client_pool_push(pool, client);
pthread_mutex_lock(&mutex);
if (in_shutdown || !r) {
pthread_mutex_unlock(&mutex);
break;
}
pthread_mutex_unlock(&mutex);
}
bson_destroy(&ping);
return NULL;
}
int
main(int argc, char *argv[])
{
const char *uri_string = "mongodb://127.0.0.1/?appname=pool-example";
mongoc_uri_t *uri;
bson_error_t error;
mongoc_client_pool_t *pool;
pthread_t threads[10];
unsigned i;
void *ret;
pthread_mutex_init(&mutex, NULL);
mongoc_init();
if (argc > 1) {
uri_string = argv[1];
}
uri = mongoc_uri_new_with_error(uri_string, &error);
if (!uri) {
fprintf(stderr,
"failed to parse URI:\n"
"error message: %s\n",
error.message);
return EXIT_FAILURE;
}
pool = mongoc_client_pool_new(uri);
mongoc_client_pool_set_error_api(pool, 2);
for (i = 0; i < 10; i++) {
pthread_create(&threads[i], NULL, worker, pool);
}
sleep(10);
pthread_mutex_lock(&mutex);
in_shutdown = true;
pthread_mutex_unlock(&mutex);
for (i = 0; i < 10; i++) {
pthread_join(threads[i], &ret);
}
mongoc_client_pool_destroy(pool);
mongoc_uri_destroy(uri);
mongoc_cleanup();
return EXIT_SUCCESS;
}
ten threads, one pool, pop and push, pop and push β it's giving β¨efficientβ¨ fr fr
mongoc_client_session_t
use a session for a sequence of operations, optionally with causal consistency. see the MongoDB Manual Entry for Causal Consistency. like a little storyline that ties your operations together π«Ά
Synopsis
start a session with mongoc_client_start_session(), use that session for a sequence of operations and multi-document transactions, then free it with mongoc_client_session_destroy().
and here's the cleanup order rule that will bite you if you're not paying attention:
Anymongoc_cursor_tormongoc_change_stream_tusing a session must be destroyed before the session, and a session must be destroyed before themongoc_client_tit came from. order matters bestie, don't freestyle it π
Like, real talk: default sessions are causally consistent. If you're like "no thanks, I'd rather live unbothered," you gotta do it before you start the session β create a mongoc_session_opt_t with mongoc_session_opts_new(), then call mongoc_session_opts_set_causal_consistency(), then free the struct with mongoc_session_opts_destroy(). Order matters bestie, don't skip steps π
Also: unacknowledged writes are prohibited with sessions. Not "discouraged," not "frowned upon." Prohibited. π«
And listen β a mongoc_client_session_t must be used by only one thread at a time. Because of session pooling, mongoc_client_start_session() may hand you a session that has been idling for a while and is literally about to get closed once its idle timeout hits. So use your session within one minute of acquiring it β that refreshes it and keeps the timeout from snatching it away from you. β°
π΄ Fork Safety
A mongoc_client_session_t is only usable in the parent process after a fork. The child process has to call mongoc_client_reset() on the client field. That's the whole vibe check for forking, no cap.
β¨ Example β¨
example-session.c β presenting, the moment you've all been waiting for π
/* gcc example-session.c -o example-session \
* $(pkg-config --cflags --libs libmongoc-1.0) */
/* ./example-session [CONNECTION_STRING] */
#include <mongoc/mongoc.h>
#include <stdio.h>
int
main(int argc, char *argv[])
{
int exit_code = EXIT_FAILURE;
mongoc_client_t *client = NULL;
const char *uri_string = "mongodb://127.0.0.1/?appname=session-example";
mongoc_uri_t *uri = NULL;
mongoc_client_session_t *client_session = NULL;
mongoc_collection_t *collection = NULL;
bson_error_t error;
bson_t *selector = NULL;
bson_t *update = NULL;
bson_t *update_opts = NULL;
bson_t *find_opts = NULL;
mongoc_read_prefs_t *secondary = NULL;
mongoc_cursor_t *cursor = NULL;
const bson_t *doc;
char *str;
bool r;
mongoc_init();
if (argc > 1) {
uri_string = argv[1];
}
uri = mongoc_uri_new_with_error(uri_string, &error);
if (!uri) {
fprintf(stderr,
"failed to parse URI:\n"
"error message: %s\n",
error.message);
goto done;
}
client = mongoc_client_new_from_uri(uri);
if (!client) {
goto done;
}
mongoc_client_set_error_api(client, 2);
/* pass NULL for options - by default the session is causally consistent */
client_session = mongoc_client_start_session(client, NULL, &error);
if (!client_session) {
fprintf(stderr, "Failed to start session: %s\n", error.message);
goto done;
}
collection = mongoc_client_get_collection(client, "test", "collection");
selector = BCON_NEW("_id", BCON_INT32(1));
update = BCON_NEW("$inc", "{", "x", BCON_INT32(1), "}");
update_opts = bson_new();
if (!mongoc_client_session_append(client_session, update_opts, &error)) {
fprintf(stderr, "Could not add session to opts: %s\n", error.message);
goto done;
}
r = mongoc_collection_update_one(collection, selector, update, update_opts, NULL /* reply */, &error);
if (!r) {
fprintf(stderr, "Update failed: %s\n", error.message);
goto done;
}
bson_destroy(selector);
selector = BCON_NEW("_id", BCON_INT32(1));
secondary = mongoc_read_prefs_new(MONGOC_READ_SECONDARY);
find_opts = BCON_NEW("maxTimeMS", BCON_INT32(2000));
if (!mongoc_client_session_append(client_session, find_opts, &error)) {
fprintf(stderr, "Could not add session to opts: %s\n", error.message);
goto done;
}
/* read from secondary. since we're in a causally consistent session, the
* data is guaranteed to reflect the update we did on the primary. the query
* blocks waiting for the secondary to catch up, if necessary, or times out
* and fails after 2000 ms.
*/
cursor = mongoc_collection_find_with_opts(collection, selector, find_opts, secondary);
while (mongoc_cursor_next(cursor, &doc)) {
str = bson_as_relaxed_extended_json(doc, NULL);
fprintf(stdout, "%s\n", str);
bson_free(str);
}
if (mongoc_cursor_error(cursor, &error)) {
fprintf(stderr, "Cursor Failure: %s\n", error.message);
goto done;
}
exit_code = EXIT_SUCCESS;
done:
if (find_opts) {
bson_destroy(find_opts);
}
if (update) {
bson_destroy(update);
}
if (selector) {
bson_destroy(selector);
}
if (update_opts) {
bson_destroy(update_opts);
}
if (secondary) {
mongoc_read_prefs_destroy(secondary);
}
/* destroy cursor, collection, session before the client they came from */
if (cursor) {
mongoc_cursor_destroy(cursor);
}
if (collection) {
mongoc_collection_destroy(collection);
}
if (client_session) {
mongoc_client_session_destroy(client_session);
}
if (uri) {
mongoc_uri_destroy(uri);
}
if (client) {
mongoc_client_destroy(client);
}
mongoc_cleanup();
return exit_code;
}
π« mongoc_client_session_with_transaction_cb_t
Synopsis
typedef bool (*mongoc_client_session_with_transaction_cb_t) (
mongoc_client_session_t *session,
void *ctx,
bson_t **reply,
bson_error_t *error);
This is the callback you hand over to mongoc_client_session_with_transaction(). It should run a sequence of operations that are meant to live inside a transaction. And here's the thing β the callback should not attempt to start or commit transactions. That's not its job, bestie. Stay in your lane π
Parameters
session: Amongoc_client_session_t.ctx: Avoid*set to the the user-providedctxpassed tomongoc_client_session_with_transaction(). (Yes, the original says "the the." We're keeping it. Iconic typo energy π«Ά)reply: An optional location for abson_torNULL. The callback should set this if it runs any operations against the server and receives replies.error: Abson_error_t. The callback should set this if it receives any errors while running operations against the server.
Return
Returns true for success and false on failure. If cb returns false then it should also set error. No silently failing, that's not the move π
SEE ALSO: mongoc_client_session_with_transaction()
π₯οΈ mongoc_client_t
A single-threaded MongoDB connection. See Connection Pooling. One thread, one client, that's the rule β¨
Synopsis
typedef struct _mongoc_client_t mongoc_client_t;
typedef mongoc_stream_t *(*mongoc_stream_initiator_t) (
const mongoc_uri_t *uri,
const mongoc_host_list_t *host,
void *user_data,
bson_error_t *error);
mongoc_client_t is an opaque type that gives you access to a MongoDB server, replica set, or sharded cluster. It handles management of the underlying sockets and routes to individual nodes based on mongoc_read_prefs_t or mongoc_write_concern_t. It's giving backstage manager energy honestly π¬
Streams
The underlying transport for a given client can be customized, wrapped, or straight-up replaced by any implementation that fulfills mongoc_stream_t. You set a custom transport with mongoc_client_set_stream_initiator(). Mix and match, make it yours π¦
Thread Safety
mongoc_client_t is NOT thread-safe and should only be used from one thread at a time. Big bold NOT, we're not playing around here β οΈ When you're in multi-threaded scenarios, the recommendation is that you use the thread-safe mongoc_client_pool_t to retrieve a mongoc_client_t for your thread. That's the whole strategy, iykyk.
Fork Safety
A mongoc_client_t is only usable in the parent process after a fork. The child process must call mongoc_client_reset(). Same fork rules as before, no exceptions π΄
Example
example-client.c β the classic, the blueprint, the main character π
/* gcc example-client.c -o example-client $(pkg-config --cflags --libs
* libmongoc-1.0) */
/* ./example-client [CONNECTION_STRING [COLLECTION_NAME]] */
#include <mongoc/mongoc.h>
#include <stdio.h>
#include <stdlib.h>
int
main(int argc, char *argv[])
{
mongoc_client_t *client;
mongoc_collection_t *collection;
mongoc_cursor_t *cursor;
bson_error_t error;
const bson_t *doc;
const char *collection_name = "test";
bson_t query;
char *str;
const char *uri_string = "mongodb://127.0.0.1/?appname=client-example";
mongoc_uri_t *uri;
mongoc_init();
if (argc > 1) {
uri_string = argv[1];
}
if (argc > 2) {
collection_name = argv[2];
}
uri = mongoc_uri_new_with_error(uri_string, &error);
if (!uri) {
fprintf(stderr,
"failed to parse URI\n"
"error message: %s\n",
error.message);
return EXIT_FAILURE;
}
client = mongoc_client_new_from_uri(uri);
if (!client) {
return EXIT_FAILURE;
}
mongoc_client_set_error_api(client, 2);
bson_init(&query);
collection = mongoc_client_get_collection(client, "test", collection_name);
cursor = mongoc_collection_find_with_opts(collection,
&query,
NULL, /* additional options */
NULL); /* read prefs, NULL for default */
while (mongoc_cursor_next(cursor, &doc)) {
str = bson_as_canonical_extended_json(doc, NULL);
fprintf(stdout, "%s\n", str);
bson_free(str);
}
if (mongoc_cursor_error(cursor, &error)) {
fprintf(stderr, "Cursor Failure: %s\n", error.message);
return EXIT_FAILURE;
}
bson_destroy(&query);
mongoc_cursor_destroy(cursor);
mongoc_collection_destroy(collection);
mongoc_uri_destroy(uri);
mongoc_client_destroy(client);
mongoc_cleanup();
return EXIT_SUCCESS;
}
ποΈ mongoc_collection_t
Synopsis
typedef struct _mongoc_collection_t mongoc_collection_t;
mongoc_collection_t gives you access to a MongoDB collection. This handle is useful for actions for most CRUD operations, I.e. insert, update, delete, find, etc. Basically the whole daily routine, she does it all π
Read Preferences and Write Concerns
Read preferences and write concerns are inherited from the parent client. And if you want something different, they can be overridden by set_* commands if so desired. Your collection, your choices πΈ
π mongoc_cursor_t
Client-side cursor abstraction. Very demure, very mindful π§
Synopsis
typedef struct _mongoc_cursor_t mongoc_cursor_t;
mongoc_cursor_t provides access to a MongoDB query cursor. It wraps up the wire protocol negotiation required to initiate a query and retrieve an unknown number of documents. Unknown number, like, she doesn't know how many docs are coming and honestly neither do you π
Common cursor operations include:
- Peep which host you actually ended up connected to with
mongoc_cursor_get_host()π - Keep pulling more records by hitting
mongoc_cursor_next()over and over again π - Wanna run that same query again later? Clone it with
mongoc_cursor_clone()𧬠- Check for problems with
mongoc_cursor_error()π¨
Cursors are lazy β and I mean lazy lazy, bestie. Like, no connection gets established and literally zero network traffic happens until your very first call to mongoc_cursor_next(). It's giving "I'll get to it when I get to it" energy and honestly? Respect. β¨
π Thread Safety π
mongoc_cursor_t is NOT thread safe. Nope. Not even a little. It may only be used from within the thread in which it was created. Periodt. π
ββοΈ No passing it around to your other threads, that's how you get chaos.
π Example π
Query MongoDB and iterate results
/* gcc example-client.c -o example-client $(pkg-config --cflags --libs
* libmongoc-1.0) */
/* ./example-client [CONNECTION_STRING [COLLECTION_NAME]] */
#include <mongoc/mongoc.h>
#include <stdio.h>
#include <stdlib.h>
int
main(int argc, char *argv[])
{
mongoc_client_t *client;
mongoc_collection_t *collection;
mongoc_cursor_t *cursor;
bson_error_t error;
const bson_t *doc;
const char *collection_name = "test";
bson_t query;
char *str;
const char *uri_string = "mongodb://127.0.0.1/?appname=client-example";
mongoc_uri_t *uri;
mongoc_init();
if (argc > 1) {
uri_string = argv[1];
}
if (argc > 2) {
collection_name = argv[2];
}
uri = mongoc_uri_new_with_error(uri_string, &error);
if (!uri) {
fprintf(stderr,
"failed to parse URI\n"
"error message: %s\n",
error.message);
return EXIT_FAILURE;
}
client = mongoc_client_new_from_uri(uri);
if (!client) {
return EXIT_FAILURE;
}
mongoc_client_set_error_api(client, 2);
bson_init(&query);
collection = mongoc_client_get_collection(client, "test", collection_name);
cursor = mongoc_collection_find_with_opts(collection,
&query,
NULL, /* additional options */
NULL); /* read prefs, NULL for default */
while (mongoc_cursor_next(cursor, &doc)) {
str = bson_as_canonical_extended_json(doc, NULL);
fprintf(stdout, "%s\n", str);
bson_free(str);
}
if (mongoc_cursor_error(cursor, &error)) {
fprintf(stderr, "Cursor Failure: %s\n", error.message);
return EXIT_FAILURE;
}
bson_destroy(&query);
mongoc_cursor_destroy(cursor);
mongoc_collection_destroy(collection);
mongoc_uri_destroy(uri);
mongoc_client_destroy(client);
mongoc_cleanup();
return EXIT_SUCCESS;
}
ποΈ mongoc_database_t ποΈ
MongoDB Database Abstraction
β¨ Synopsis β¨
typedef struct _mongoc_database_t mongoc_database_t;
mongoc_database_t gives you access to a MongoDB database. This handle is the vibe for doing stuff with one particular database object β but heads up, it is not a container for mongoc_collection_t structures. Don't try to make it hold your collections, that's not its job π
Read preferences and write concerns get inherited from the parent client, and you can override them whenever you want with mongoc_database_set_read_prefs() and mongoc_database_set_write_concern(). Customize away π
πΈ Examples πΈ
#include <mongoc/mongoc.h>
int
main (int argc, char *argv[])
{
mongoc_database_t *database;
mongoc_client_t *client;
mongoc_init ();
client = mongoc_client_new ("mongodb://localhost/");
database = mongoc_client_get_database (client, "test");
mongoc_database_destroy (database);
mongoc_client_destroy (client);
mongoc_cleanup ();
return 0;
}
π§ mongoc_find_and_modify_opts_t π§
find_and_modify abstraction
β¨ Synopsis β¨
mongoc_find_and_modify_opts_t is a builder interface β it's how you construct the findAndModify command. Think of it as a little options bag you keep adding to before you send it off π
It exists specifically so new arguments to the findAndModify command could be added without breaking everything. Iconic forward-thinking behaviour, honestly π
As of MongoDB 3.2, the mongoc_write_concern_t specified on the mongoc_collection_t will be used, if any.
β Example β
flags.c
void
fam_flags(mongoc_collection_t *collection)
{
mongoc_find_and_modify_opts_t *opts;
bson_t reply;
bson_error_t error;
bson_t query = BSON_INITIALIZER;
bson_t *update;
bool success;
/* Find Zlatan Ibrahimovic, the striker */
BSON_APPEND_UTF8(&query, "firstname", "Zlatan");
BSON_APPEND_UTF8(&query, "lastname", "Ibrahimovic");
BSON_APPEND_UTF8(&query, "profession", "Football player");
BSON_APPEND_INT32(&query, "age", 34);
BSON_APPEND_INT32(&query, "goals", (16 + 35 + 23 + 57 + 16 + 14 + 28 + 84) + (1 + 6 + 62));
/* Add his football position */
update = BCON_NEW("$set", "{", "position", BCON_UTF8("striker"), "}");
opts = mongoc_find_and_modify_opts_new();
mongoc_find_and_modify_opts_set_update(opts, update);
/* Create the document if it didn't exist, and return the updated document */
mongoc_find_and_modify_opts_set_flags(opts, MONGOC_FIND_AND_MODIFY_UPSERT | MONGOC_FIND_AND_MODIFY_RETURN_NEW);
success = mongoc_collection_find_and_modify_with_opts(collection, &query, opts, &reply, &error);
if (success) {
char *str;
str = bson_as_canonical_extended_json(&reply, NULL);
printf("%s\n", str);
bson_free(str);
} else {
fprintf(stderr, "Got error: \"%s\" on line %d\n", error.message, (int)(__LINE__));
}
bson_destroy(&reply);
bson_destroy(update);
bson_destroy(&query);
mongoc_find_and_modify_opts_destroy(opts);
}
bypass.c
void
fam_bypass(mongoc_collection_t *collection)
{
mongoc_find_and_modify_opts_t *opts;
bson_t reply;
bson_t *update;
bson_error_t error;
bson_t query = BSON_INITIALIZER;
bool success;
/* Find Zlatan Ibrahimovic, the striker */
BSON_APPEND_UTF8(&query, "firstname", "Zlatan");
BSON_APPEND_UTF8(&query, "lastname", "Ibrahimovic");
BSON_APPEND_UTF8(&query, "profession", "Football player");
/* Bump his age */
update = BCON_NEW("$inc", "{", "age", BCON_INT32(1), "}");
opts = mongoc_find_and_modify_opts_new();
mongoc_find_and_modify_opts_set_update(opts, update);
/* He can still play, even though he is pretty old. */
mongoc_find_and_modify_opts_set_bypass_document_validation(opts, true);
success = mongoc_collection_find_and_modify_with_opts(collection, &query, opts, &reply, &error);
if (success) {
char *str;
str = bson_as_canonical_extended_json(&reply, NULL);
printf("%s\n", str);
bson_free(str);
} else {
fprintf(stderr, "Got error: \"%s\" on line %d\n", error.message, (int)(__LINE__));
}
bson_destroy(&reply);
bson_destroy(update);
bson_destroy(&query);
mongoc_find_and_modify_opts_destroy(opts);
}
update.c
void
fam_update(mongoc_collection_t *collection)
{
mongoc_find_and_modify_opts_t *opts;
bson_t *update;
bson_t reply;
bson_error_t error;
bson_t query = BSON_INITIALIZER;
bool success;
/* Find Zlatan Ibrahimovic */
BSON_APPEND_UTF8(&query, "firstname", "Zlatan");
BSON_APPEND_UTF8(&query, "lastname", "Ibrahimovic");
/* Make him a book author */
update = BCON_NEW("$set", "{", "author", BCON_BOOL(true), "}");
opts = mongoc_find_and_modify_opts_new();
/* Note that the document returned is the _previous_ version of the document
* To fetch the modified new version, use
* mongoc_find_and_modify_opts_set_flags (opts,
* MONGOC_FIND_AND_MODIFY_RETURN_NEW);
*/
mongoc_find_and_modify_opts_set_update(opts, update);
success = mongoc_collection_find_and_modify_with_opts(collection, &query, opts, &reply, &error);
if (success) {
char *str;
str = bson_as_canonical_extended_json(&reply, NULL);
printf("%s\n", str);
bson_free(str);
} else {
fprintf(stderr, "Got error: \"%s\" on line %d\n", error.message, (int)(__LINE__));
}
bson_destroy(&reply);
bson_destroy(update);
bson_destroy(&query);
mongoc_find_and_modify_opts_destroy(opts);
}
fields.c
void
fam_fields(mongoc_collection_t *collection)
{
mongoc_find_and_modify_opts_t *opts;
bson_t fields = BSON_INITIALIZER;
bson_t *update;
bson_t reply;
bson_error_t error;
bson_t query = BSON_INITIALIZER;
bool success;
/* Find Zlatan Ibrahimovic */
BSON_APPEND_UTF8(&query, "lastname", "Ibrahimovic");
BSON_APPEND_UTF8(&query, "firstname", "Zlatan");
/* Return his goal tally */
BSON_APPEND_INT32(&fields, "goals", 1);
/* Bump his goal tally */
update = BCON_NEW("$inc", "{", "goals", BCON_INT32(1), "}");
opts = mongoc_find_and_modify_opts_new();
mongoc_find_and_modify_opts_set_update(opts, update);
mongoc_find_and_modify_opts_set_fields(opts, &fields);
/* Return the new tally */
mongoc_find_and_modify_opts_set_flags(opts, MONGOC_FIND_AND_MODIFY_RETURN_NEW);
success = mongoc_collection_find_and_modify_with_opts(collection, &query, opts, &reply, &error);
if (success) {
char *str;
str = bson_as_canonical_extended_json(&reply, NULL);
printf("%s\n", str);
bson_free(str);
} else {
fprintf(stderr, "Got error: \"%s\" on line %d\n", error.message, (int)(__LINE__));
}
bson_destroy(&reply);
bson_destroy(update);
bson_destroy(&fields);
bson_destroy(&query);
mongoc_find_and_modify_opts_destroy(opts);
}
sort.c
void
fam_sort(mongoc_collection_t *collection)
{
mongoc_find_and_modify_opts_t *opts;
bson_t *update;
bson_t sort = BSON_INITIALIZER;
bson_t reply;
bson_error_t error;
bson_t query = BSON_INITIALIZER;
bool success;
/* Find all users with the lastname Ibrahimovic */
BSON_APPEND_UTF8(&query, "lastname", "Ibrahimovic");
/* Sort by age (descending) */
BSON_APPEND_INT32(&sort, "age", -1);
/* Bump his goal tally */
update = BCON_NEW("$set", "{", "oldest", BCON_BOOL(true), "}");
opts = mongoc_find_and_modify_opts_new();
mongoc_find_and_modify_opts_set_update(opts, update);
mongoc_find_and_modify_opts_set_sort(opts, &sort);
success = mongoc_collection_find_and_modify_with_opts(collection, &query, opts, &reply, &error);
if (success) {
char *str;
str = bson_as_canonical_extended_json(&reply, NULL);
printf("%s\n", str);
bson_free(str);
} else {
fprintf(stderr, "Got error: \"%s\" on line %d\n", error.message, (int)(__LINE__));
}
bson_destroy(&reply);
bson_destroy(update);
bson_destroy(&sort);
bson_destroy(&query);
mongoc_find_and_modify_opts_destroy(opts);
}
And bestie, here's the next file in the example set, opts.c β this one is literally the options flex π
:
void
fam_opts(mongoc_collection_t *collection)
{
mongoc_find_and_modify_opts_t *opts;
bson_t reply;
bson_t *update;
bson_error_t error;
bson_t query = BSON_INITIALIZER;
mongoc_write_concern_t *wc;
bson_t extra = BSON_INITIALIZER;
bool success;
/* Find Zlatan Ibrahimovic, the striker */
BSON_APPEND_UTF8(&query, "firstname", "Zlatan");
BSON_APPEND_UTF8(&query, "lastname", "Ibrahimovic");
BSON_APPEND_UTF8(&query, "profession", "Football player");
/* Bump his age */
update = BCON_NEW("$inc", "{", "age", BCON_INT32(1), "}");
opts = mongoc_find_and_modify_opts_new();
mongoc_find_and_modify_opts_set_update(opts, update);
/* Abort if the operation takes too long. */
mongoc_find_and_modify_opts_set_max_time_ms(opts, 100);
/* Set write concern w: 2 */
wc = mongoc_write_concern_new();
mongoc_write_concern_set_w(wc, 2);
mongoc_write_concern_append(wc, &extra);
/* Some future findAndModify option the driver doesn't support conveniently
*/
BSON_APPEND_INT32(&extra, "futureOption", 42);
mongoc_find_and_modify_opts_append(opts, &extra);
success = mongoc_collection_find_and_modify_with_opts(collection, &query, opts, &reply, &error);
if (success) {
char *str;
str = bson_as_canonical_extended_json(&reply, NULL);
printf("%s\n", str);
bson_free(str);
} else {
fprintf(stderr, "Got error: \"%s\" on line %d\n", error.message, (int)(__LINE__));
}
bson_destroy(&reply);
bson_destroy(&update);
bson_destroy(update);
bson_destroy(&query);
mongoc_write_concern_destroy(wc);
mongoc_find_and_modify_opts_destroy(opts);
}
Oops β correction era π: the real file is exactly this, no dupe, take it from the top:
void
fam_opts(mongoc_collection_t *collection)
{
mongoc_find_and_modify_opts_t *opts;
bson_t reply;
bson_t *update;
bson_error_t error;
bson_t query = BSON_INITIALIZER;
mongoc_write_concern_t *wc;
bson_t extra = BSON_INITIALIZER;
bool success;
/* Find Zlatan Ibrahimovic, the striker */
BSON_APPEND_UTF8(&query, "firstname", "Zlatan");
BSON_APPEND_UTF8(&query, "lastname", "Ibrahimovic");
BSON_APPEND_UTF8(&query, "profession", "Football player");
/* Bump his age */
update = BCON_NEW("$inc", "{", "age", BCON_INT32(1), "}");
opts = mongoc_find_and_modify_opts_new();
mongoc_find_and_modify_opts_set_update(opts, update);
/* Abort if the operation takes too long. */
mongoc_find_and_modify_opts_set_max_time_ms(opts, 100);
/* Set write concern w: 2 */
wc = mongoc_write_concern_new();
mongoc_write_concern_set_w(wc, 2);
mongoc_write_concern_append(wc, &extra);
/* Some future findAndModify option the driver doesn't support conveniently
*/
BSON_APPEND_INT32(&extra, "futureOption", 42);
mongoc_find_and_modify_opts_append(opts, &extra);
success = mongoc_collection_find_and_modify_with_opts(collection, &query, opts, &reply, &error);
if (success) {
char *str;
str = bson_as_canonical_extended_json(&reply, NULL);
printf("%s\n", str);
bson_free(str);
} else {
fprintf(stderr, "Got error: \"%s\" on line %d\n", error.message, (int)(__LINE__));
}
bson_destroy(&reply);
bson_destroy(&extra);
bson_destroy(update);
bson_destroy(&query);
mongoc_write_concern_destroy(wc);
mongoc_find_and_modify_opts_destroy(opts);
}
(ngl, peek at the top: update is a bson_t * from BCON_NEW, and the cleanup below it is bson_destroy(update); β the & only shows up on the stack-allocated query and extra. Also the &update in the destroy call is the only line that differs between the two passes, so always copy the last one, bestie π)
Okay and now the main character herself: fam.c π«Ά
int
main(void)
{
mongoc_collection_t *collection;
mongoc_database_t *database;
mongoc_client_t *client;
const char *uri_string = "mongodb://localhost:27017/admin?appname=find-and-modify-opts-example";
mongoc_uri_t *uri;
bson_error_t error;
bson_t *options;
mongoc_init();
uri = mongoc_uri_new_with_error(uri_string, &error);
if (!uri) {
fprintf(stderr,
"failed to parse URI:\n"
"error message: %s\n",
error.message);
return EXIT_FAILURE;
}
client = mongoc_client_new_from_uri(uri);
if (!client) {
return EXIT_FAILURE;
}
mongoc_client_set_error_api(client, 2);
database = mongoc_client_get_database(client, "databaseName");
options = BCON_NEW("validator",
"{",
"age",
"{",
"$lte",
BCON_INT32(34),
"}",
"}",
"validationAction",
BCON_UTF8("error"),
"validationLevel",
BCON_UTF8("moderate"));
collection = mongoc_database_create_collection(database, "collectionName", options, &error);
if (!collection) {
fprintf(stderr, "Got error: \"%s\" on line %d\n", error.message, (int)(__LINE__));
return EXIT_FAILURE;
}
fam_flags(collection);
fam_bypass(collection);
fam_update(collection);
fam_fields(collection);
fam_opts(collection);
fam_sort(collection);
mongoc_collection_drop(collection, NULL);
bson_destroy(options);
mongoc_uri_destroy(uri);
mongoc_database_destroy(database);
mongoc_collection_destroy(collection);
mongoc_client_destroy(client);
mongoc_cleanup();
return EXIT_SUCCESS;
}
<h4>β¨ Outputs:</h4>
{
"lastErrorObject": {
"updatedExisting": false,
"n": 1,
"upserted": {
"$oid": "56562a99d13e6d86239c7b00"
}
},
"value": {
"_id": {
"$oid": "56562a99d13e6d86239c7b00"
},
"age": 34,
"firstname": "Zlatan",
"goals": 342,
"lastname": "Ibrahimovic",
"profession": "Football player",
"position": "striker"
},
"ok": 1
}
{
"lastErrorObject": {
"updatedExisting": true,
"n": 1
},
"value": {
"_id": {
"$oid": "56562a99d13e6d86239c7b00"
},
"age": 34,
"firstname": "Zlatan",
"goals": 342,
"lastname": "Ibrahimovic",
"profession": "Football player",
"position": "striker"
},
"ok": 1
}
{
"lastErrorObject": {
"updatedExisting": true,
"n": 1
},
"value": {
"_id": {
"$oid": "56562a99d13e6d86239c7b00"
},
"age": 35,
"firstname": "Zlatan",
"goals": 342,
"lastname": "Ibrahimovic",
"profession": "Football player",
"position": "striker"
},
"ok": 1
}
{
"lastErrorObject": {
"updatedExisting": true,
"n": 1
},
"value": {
"_id": {
"$oid": "56562a99d13e6d86239c7b00"
},
"goals": 343
},
"ok": 1
}
{
"lastErrorObject": {
"updatedExisting": true,
"n": 1
},
"value": {
"_id": {
"$oid": "56562a99d13e6d86239c7b00"
},
"age": 35,
"firstname": "Zlatan",
"goals": 343,
"lastname": "Ibrahimovic",
"profession": "Football player",
"position": "striker",
"author": true
},
"ok": 1
}
π mongoc_gridfs_file_list_t π
Synopsis
#include <mongoc/mongoc.h>
typedef struct _mongoc_gridfs_file_list_t mongoc_gridfs_file_list_t;
Description
So mongoc_gridfs_file_list_t is your <b>gridfs file list abstraction</b>, bestie. It hands you iteration plus basic marshalling sitting right on top of a regular mongoc_collection_find_with_opts()-style query. And the interface? Lowkey styled after mongoc_cursor_t β so if you already know the cursor, you're literally at home already π β¨
Example
mongoc_gridfs_file_list_t *list;
mongoc_gridfs_file_t *file;
list = mongoc_gridfs_find_with_opts (gridfs, query, NULL);
while ((file = mongoc_gridfs_file_list_next (list))) {
do_something (file);
mongoc_gridfs_file_destroy (file);
}
mongoc_gridfs_file_list_destroy (list);
πΈ mongoc_gridfs_file_opt_t πΈ
Synopsis
typedef struct {
const char *md5;
const char *filename;
const char *content_type;
const bson_t *aliases;
const bson_t *metadata;
uint32_t chunk_size;
} mongoc_gridfs_file_opt_t;
Description
This lil struct is a bag of options you can set on a mongoc_gridfs_file_t. Various functions reach for it when they're creating a new gridfs file β so it's the "here's how I want my file born" form, no cap π
π¦ mongoc_gridfs_file_t π¦
Synopsis
typedef struct _mongoc_gridfs_file_t mongoc_gridfs_file_t;
Description
Okay so this is the MongoDB GridFS file abstraction β the actual file object, and she provides several APIs:
readv,writev,seek, andtell.- General file metadata such as filename and length.
- GridFS metadata such as
md5,filename,content_type,aliases,metadata,chunk_size, andupload_date.
Thread Safety
β οΈ This structure is <b>NOT</b> thread-safe, bestie π Only use it from <b>one thread at a time</b>, periodt. Don't be out here sharing her with your whole friend group.
Related
mongoc_client_tmongoc_gridfs_tmongoc_gridfs_file_list_tmongoc_gridfs_file_opt_t
π mongoc_gridfs_bucket_t π
Synopsis
#include <mongoc/mongoc.h>
typedef struct _mongoc_gridfs_bucket_t mongoc_gridfs_bucket_t;
Description
mongoc_gridfs_bucket_t gives you a <b>spec-compliant MongoDB GridFS implementation</b>, and she's superseding mongoc_gridfs_t β like, the older one is cute but this is the upgrade β¨ Peep the GridFS MongoDB documentation for the full tea.
Thread Safety
β οΈmongoc_gridfs_bucket_tis <b>NOT</b> thread-safe, and she should only ever be used in the <b>same thread as the owningmongoc_client_t</b>. Same-thread only, iykyk π
Lifecycle
It is an <b>error</b> to free a mongoc_gridfs_bucket_t <b>before freeing all derived instances of mongoc_stream_t</b> π¨ And the owning mongoc_client_t <b>must outlive</b> the mongoc_gridfs_bucket_t. Cleanup order is real, don't skip it π
Example
Behold: example-gridfs-bucket.c π
#include <mongoc/mongoc.h>
#include <stdio.h>
#include <stdlib.h>
int
main(int argc, char *argv[])
{
const char *uri_string = "mongodb://localhost:27017/?appname=new-gridfs-example";
mongoc_client_t *client;
mongoc_database_t *db;
mongoc_stream_t *file_stream;
mongoc_gridfs_bucket_t *bucket;
mongoc_cursor_t *cursor;
bson_t filter;
bool res;
bson_value_t file_id;
bson_error_t error;
const bson_t *doc;
char *str;
mongoc_init();
if (argc != 3) {
fprintf(stderr, "usage: %s SOURCE_FILE_PATH FILE_COPY_PATH\n", argv[0]);
return EXIT_FAILURE;
}
β¦and that's where this chunk runs out, bestie β the code keeps going in the next part π«Άβ¨
/* 1. Make a bucket. */
client = mongoc_client_new(uri_string);
db = mongoc_client_get_database(client, "test");
bucket = mongoc_gridfs_bucket_new(db, NULL, NULL, &error);
if (!bucket) {
printf("Error creating gridfs bucket: %s\n", error.message);
return EXIT_FAILURE;
}
/* 2. Insert a file. */
file_stream = mongoc_stream_file_new_for_path(argv[1], O_RDONLY, 0);
res = mongoc_gridfs_bucket_upload_from_stream(bucket, "my-file", file_stream, NULL, &file_id, &error);
if (!res) {
printf("Error uploading file: %s\n", error.message);
return EXIT_FAILURE;
}
mongoc_stream_close(file_stream);
mongoc_stream_destroy(file_stream);
/* 3. Download the file in GridFS to a local file. */
file_stream = mongoc_stream_file_new_for_path(argv[2], O_CREAT | O_RDWR, 0);
if (!file_stream) {
perror("Error opening file stream");
return EXIT_FAILURE;
}
res = mongoc_gridfs_bucket_download_to_stream(bucket, &file_id, file_stream, &error);
if (!res) {
printf("Error downloading file to stream: %s\n", error.message);
return EXIT_FAILURE;
}
mongoc_stream_close(file_stream);
mongoc_stream_destroy(file_stream);
/* 4. List what files are available in GridFS. */
bson_init(&filter);
cursor = mongoc_gridfs_bucket_find(bucket, &filter, NULL);
while (mongoc_cursor_next(cursor, &doc)) {
str = bson_as_canonical_extended_json(doc, NULL);
printf("%s\n", str);
bson_free(str);
}
/* 5. Delete the file that we added. */
res = mongoc_gridfs_bucket_delete_by_id(bucket, &file_id, &error);
if (!res) {
printf("Error deleting the file: %s\n", error.message);
return EXIT_FAILURE;
}
/* 6. Cleanup. */
mongoc_stream_close(file_stream);
mongoc_stream_destroy(file_stream);
mongoc_cursor_destroy(cursor);
bson_destroy(&filter);
mongoc_gridfs_bucket_destroy(bucket);
mongoc_database_destroy(db);
mongoc_client_destroy(client);
mongoc_cleanup();
return EXIT_SUCCESS;
}
Yup, that's the whole lil' six-step era β make a bucket, shove a file in, pull it back out, list the vibes, yeet the file, clean up your mess like a responsible icon π β¨
SEE ALSO π
- The MongoDB GridFS specification.
- The non spec-compliant
mongoc_gridfs_t.
π¦ mongoc_gridfs_t π¦
WARNING: This GridFS implementation does not conform to the MongoDB GridFS specification. For a spec compliant implementation, use mongoc_gridfs_bucket_t. Like, genuinely, bestie β read this warning twice. β
Synopsis β¨
#include <mongoc/mongoc.h>
typedef struct _mongoc_gridfs_t mongoc_gridfs_t;
Description π
mongoc_gridfs_t gives you a MongoDB gridfs implementation. The whole system is made up of gridfs objects, which contain gridfs_files and gridfs_file_lists β so basically it's a whole little file system API, cute right? π
There are, and I cannot stress this enough, extensive caveats about what kind of use cases gridfs is actually practical for. Especially: any writing after the initial file creation is likely to both break any concurrent readers and be like, super expensive. That said, this implementation does let you do arbitrary writes to an existing gridfs object β just use them with caution, okay? π
mongoc_gridfs also integrates super tightly with the mongoc_stream_t abstraction, which gives you some convenient wrapping for file creation and reading/writing. You can totally use it without that, but ngl it's worth peeking at to see if your problem can fit that model. π
WARNING: mongoc_gridfs_t does not support read preferences. In a replica set, GridFS queries are always routed to the primary. No choosing, no vibes check, just primary. periodt.
Thread Safety π§΅
mongoc_gridfs_t is NOT thread-safe and should only ever be used in the same thread as the owning mongoc_client_t. Like, same thread, same energy, no splitting up the squad. π
Lifecycle π±
It is an error to free a mongoc_gridfs_t before freeing all related instances of mongoc_gridfs_file_t and mongoc_gridfs_file_list_t. Clean up your children first, bestie, that's just manners. π«Ά
Example πΈ
example-gridfs.c
#include <mongoc/mongoc.h>
#include <fcntl.h>
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
int
main(int argc, char *argv[])
{
mongoc_gridfs_t *gridfs;
mongoc_gridfs_file_t *file;
mongoc_gridfs_file_list_t *list;
mongoc_gridfs_file_opt_t opt = {0};
mongoc_client_t *client;
const char *uri_string = "mongodb://127.0.0.1:27017/?appname=gridfs-example";
mongoc_uri_t *uri;
mongoc_stream_t *stream;
bson_t filter;
bson_t opts;
bson_t child;
bson_error_t error;
ssize_t r;
char buf[4096];
mongoc_iovec_t iov;
const char *filename;
const char *command;
bson_value_t id;
if (argc < 2) {
fprintf(stderr, "usage - %s command ...\n", argv[0]);
return EXIT_FAILURE;
}
mongoc_init();
iov.iov_base = (void *)buf;
iov.iov_len = sizeof buf;
/* connect to localhost client */
uri = mongoc_uri_new_with_error(uri_string, &error);
if (!uri) {
fprintf(stderr,
"failed to parse URI:\n"
"error message: %s\n",
error.message);
return EXIT_FAILURE;
}
client = mongoc_client_new_from_uri(uri);
assert(client);
mongoc_client_set_error_api(client, 2);
/* grab a gridfs handle in test prefixed by fs */
gridfs = mongoc_client_get_gridfs(client, "test", "fs", &error);
assert(gridfs);
command = argv[1];
filename = argv[2];
if (strcmp(command, "read") == 0) {
if (argc != 3) {
fprintf(stderr, "usage - %s read filename\n", argv[0]);
return EXIT_FAILURE;
}
file = mongoc_gridfs_find_one_by_filename(gridfs, filename, &error);
assert(file);
stream = mongoc_stream_gridfs_new(file);
assert(stream);
for (;;) {
r = mongoc_stream_readv(stream, &iov, 1, -1, 0);
assert(r >= 0);
if (r == 0) {
break;
}
if (fwrite(iov.iov_base, 1, r, stdout) != (size_t)r) {
fprintf(stderr, "Failed to write to stdout. Exiting.\n");
exit(1);
}
}
mongoc_stream_destroy(stream);
mongoc_gridfs_file_destroy(file);
} else if (strcmp(command, "list") == 0) {
bson_init(&filter);
bson_init(&opts);
bson_append_document_begin(&opts, "sort", -1, &child);
BSON_APPEND_INT32(&child, "filename", 1);
bson_append_document_end(&opts, &child);
list = mongoc_gridfs_find_with_opts(gridfs, &filter, &opts);
bson_destroy(&filter);
bson_destroy(&opts);
while ((file = mongoc_gridfs_file_list_next(list))) {
const char *name = mongoc_gridfs_file_get_filename(file);
printf("%s\n", name ? name : "?");
mongoc_gridfs_file_destroy(file);
}
mongoc_gridfs_file_list_destroy(list);
} else if (strcmp(command, "write") == 0) {
if (argc != 4) {
fprintf(stderr, "usage - %s write filename input_file\n", argv[0]);
return EXIT_FAILURE;
}
stream = mongoc_stream_file_new_for_path(argv[3], O_RDONLY, 0);
assert(stream);
opt.filename = filename;
/* the driver generates a file_id for you */
file = mongoc_gridfs_create_file_from_stream(gridfs, stream, &opt);
assert(file);
id.value_type = BSON_TYPE_INT32;
id.value.v_int32 = 1;
/* optional: the following method specifies a file_id of any
BSON type */
if (!mongoc_gridfs_file_set_id(file, &id, &error)) {
fprintf(stderr, "%s\n", error.message);
return EXIT_FAILURE;
}
if (!mongoc_gridfs_file_save(file)) {
mongoc_gridfs_file_error(file, &error);
fprintf(stderr, "Could not save: %s\n", error.message);
return EXIT_FAILURE;
}
mongoc_gridfs_file_destroy(file);
} else {
fprintf(stderr, "Unknown command");
return EXIT_FAILURE;
}
mongoc_gridfs_destroy(gridfs);
mongoc_uri_destroy(uri);
mongoc_client_destroy(client);
mongoc_cleanup();
return EXIT_SUCCESS;
}
SEE ALSO π
- The MongoDB GridFS specification.
- The spec-compliant
mongoc_gridfs_bucket_t. (yes, the good one π)
π mongoc_host_list_t π
Synopsis β¨
typedef struct {
mongoc_host_list_t *next;
char host[BSON_HOST_NAME_MAX + 1];
char host_and_port[BSON_HOST_NAME_MAX + 7];
uint16_t port;
int family;
void *padding[4];
} mongoc_host_list_t;
Description π
It's the host and port of a MongoDB server, bestie. And it can be part of a linked list β like, for example the return value of mongoc_uri_get_hosts() when multiple hosts are provided in the MongoDB URI. Look at it, being all social. π
SEE ALSO π
mongoc_uri_get_hosts()mongoc_cursor_get_host()
π₯ mongoc_insert_flags_t π₯
Flags for insert operations π«
Synopsis β¨
typedef enum {
MONGOC_INSERT_NONE = 0,
MONGOC_INSERT_CONTINUE_ON_ERROR = 1 << 0,
} mongoc_insert_flags_t;
#define MONGOC_INSERT_NO_VALIDATE (1U << 31)
Description π
These flags correspond to the MongoDB wire protocol. They may be bitwise or'd together β go ahead, mix 'em, that's literally the whole point. They may modify how an insert happens on the MongoDB server. β¨
π© Flag Values
MONGOC_INSERT_NONEβ literally just "no insert flags, thanks." The beige flag of insertion options πMONGOC_INSERT_CONTINUE_ON_ERRORβ keeps inserting the rest of the documents in your insertion set even if one of them flops. One bad doc doesn't take down the whole squad πMONGOC_INSERT_NO_VALIDATEβ skips validating your documents before doing the insert. Validation can be expensive, so this saves you some time if you already know your documents are valid. iykyk β don't come crying if they weren't π
π§΅ mongoc_iovec_t
Synopsis
Synopsis
bestie it genuinely says Synopsis twice in a row, I'm not making this up π anyway β
#include <mongoc/mongoc.h>
#ifdef _WIN32
typedef struct {
u_long iov_len;
char *iov_base;
} mongoc_iovec_t;
#else
typedef struct iovec mongoc_iovec_t;
#endif
The mongoc_iovec_t structure is a portability abstraction for consumers of the mongoc_stream_t interfaces. It's what lets you do scatter/gather I/O through the socket subsystem β iconic behavior, honestly β¨
WARNING: β οΈ if you're writing portable code, beware of the ordering ofiov_lenandiov_base, because those two get flipped around on different platforms. Which means: do not use C initializers to initialize this. Seriously. Don't. π
πͺͺ mongoc_oidc_callback_fn_t
Synopsis
typedef mongoc_oidc_credential_t *(*mongoc_oidc_callback_fn_t) (mongoc_oidc_callback_params_t *params);
This is the function pointer type stored by mongoc_oidc_callback_t. Yeah. That's the whole vibe.
Parameters
- params: a
mongoc_oidc_callback_params_tobject representing the in/out parameters of amongoc_oidc_callback_t.
Returns
A mongoc_oidc_credential_t object created with mongoc_oidc_credential_new(), or NULL to indicate an error or timeout. Here's the rules, no cap:
- The function MUST return a
mongoc_oidc_credential_tobject to indicate success. π - The function MUST return
NULLto indicate an error. π - The function MUST call
mongoc_oidc_callback_params_cancel_with_timeout()before returningNULLto indicate a timeout instead of an error.
The cancel_with_timeout out parameter gets ignored if the return value is not NULL. Periodt.
SEE ALSO:
mongoc_oidc_callback_tmongoc_oidc_callback_params_tmongoc_oidc_credential_t
π¦ mongoc_oidc_callback_params_t
Synopsis
typedef struct _mongoc_oidc_callback_params_t mongoc_oidc_callback_params_t;
This bad boy represents the in/out parameters of a mongoc_oidc_callback_t π«Ά
The parameters get passed to the mongoc_oidc_callback_fn_t stored inside an mongoc_oidc_callback_t object when it's invoked by a mongoc_client_t or mongoc_client_pool_t object.
Parameters
Here's the current roster of supported parameters:
versionβ Versions:1β the current OIDC callback API version number.user_dataβ Versions:1β a pointer to data you, the user, provided.timeoutβ Versions:1β the timestamp after which the callback function must report a timeout.usernameβ Versions:1β the username specified by the URI of the associated client object.cancel_with_timeoutβ Versions:1β an out parameter indicating cancellation of the callback function due to a timeout instead of an error.
The "Version" column indicates the OIDC callback API versions for which the parameter is applicable. (Yes, the column header says "Versions" and the sentence says "Version" β that's the manpage's business, not mine πΈ)
Version
The version parameter is how the OIDC callback API communicates backward compatible changes β like when a new parameter gets added. It's the API's little "hey, heads up" text. π±
You can use this parameter to detect when existing usage of mongoc_oidc_callback_t or a relevant callback function might need to be reviewed.
Fr fr, users can drop a check like this into their callback function:
mongoc_oidc_credential_t *
example_callback_fn (mongoc_oidc_callback_params_t *params)
{
// A runtime message that new features are available in the OIDC Callback API.
if (mongoc_oidc_callback_params_get_version (params) > 1) {
printf ("OIDC Callback API has been updated to a new version!");
}
// ...
}
User Data
The user_data parameter is your little side-channel: pass extra arguments into the callback function, or return extra values out of it. Very main character of it π
You must set this parameter in advance via mongoc_oidc_callback_set_user_data() before the mongoc_oidc_callback_t object gets associated with a mongoc_client_t or mongoc_client_pool_t object.
WARNING: β οΈ the lifetime of the object pointed to byuser_datais managed the user, not bymongoc_oidc_callback_t! So you own that memory, bestie. You. π«΅
typedef struct {
int counter;
const char *error_message;
} user_data_t;
mongoc_oidc_credential_t *
example_callback_fn (mongoc_oidc_callback_params_t *params)
{
user_data_t *user_data = (user_data_t *) mongoc_oidc_callback_params_get_user_data (params);
user_data->counter += 1;
// ...
if (/* ... */) {
user_data->error_message = "OIDC callback failed due to ...";
return NULL;
}
// ...
}
void
example (void)
{
mongoc_client_t *client = /* ... */;
bson_error_t error;
{
user_data_t *user_data = malloc (sizeof (*user_data));
*user_data = (user_data_t){.counter = 0, .error_message = NULL};
mongoc_oidc_callback_t *callback = mongoc_oidc_callback_new_with_user_data (&example_callback_fn, (void *) user_data);
BSON_ASSERT (mongoc_client_set_oidc_callback (client, callback));
mongoc_oidc_callback_destroy (callback);
}
// ... client operations ...
{
const mongoc_oidc_callback_t *callback = mongoc_client_get_oidc_callback (client);
user_data_t *user_data = (user_data_t *) mongoc_oidc_callback_get_user_data (callback);
if (error.code != 0) {
printf ("client error message: %s\n", error.message);
}
if (user_data->error_message) {
printf ("custom error message: %s\n", user_data->error_message);
}
printf ("The callback function was invoked %d times!", user_data->counter);
free (user_data);
}
mongoc_client_destroy (client);
}
Timeout
The timeout parameter is how the callback function knows when it should report cancellation due to a timeout β°
When System Clock is greater than timeout, the callback function must invoke mongoc_oidc_callback_params_cancel_with_timeout() and return NULL.
Username
The username parameter is the value of the username component of the URI of the associated mongoc_client_t or mongoc_client_pool_t object β the one the callback function gets invoked from. So it's like, whose URI is this anyway π
Cancel With Timeout
The cancel_with_timeout out parameter indicates cancellation of the callback function due to a timeout instead of an error. Big difference, don't mix 'em up π
IMPORTANT: π¨ the callback function MUST returnNULL, otherwise the invocation gets interpreted as a success even whencancel_with_timeoutis set. That's the trap. Do not fall in it.
mongoc_oidc_credential_t *
example_callback_fn (mongoc_oidc_callback_params_t *params) {
const int64_t *timeout = mongoc_oidc_callback_params_get_timeout (params);
// NULL means "infinite" timeout.
if (timeout && bson_get_monotonic_time () > *timeout) {
return mongoc_oidc_callback_params_cancel_with_timeout (params);
}
// ...
}
π¨ Error Handling π¨
ok so listen β a NULL return value (and without setting cancel_with_timeout, bestie, that detail matters) means "we couldn't get you an access token, there was an error." that's the signal. π
IMPORTANT: the callback function MUST return NULL, ok?? otherwise the invocation gets interpreted as a success and everything goes sideways. like, no cap, this is the whole vibe check right here. π
mongoc_oidc_credential_t *
example_callback_fn (mongoc_oidc_callback_params_t *params) {
// ...
if (/* ... */) {
// The OIDC callback function could not provide an access token due to an error.
return NULL;
}
// ...
}
SEE ALSO:
mongoc_oidc_callback_tmongoc_oidc_callback_fn_t
β¨ mongoc_oidc_callback_t β¨
Synopsis
typedef struct _mongoc_oidc_callback_t mongoc_oidc_callback_t;
mongoc_oidc_callback_t is literally just a wrapper for your own user-defined callback function, mongoc_oidc_callback_fn_t, that hands back an OIDC access token. iconic behavior honestly. π
the whole point? it lets you plug into OIDC providers that the built-in provider integrations don't support (peep Authentication Mechanism Properties for those, iykyk). π¦
Lifecycle π±
the function β plus any optional user data β that's stored by a mongoc_oidc_callback_t must outlive any associated client or client pool object that might invoke the stored callback function. like, don't let it die first, bestie, that's a whole mess. π
Thread Safety π
the callback function sitting inside a mongoc_oidc_callback_t object gets invoked by at most one thread at a time for an associated mongoc_client_t or mongoc_client_pool_t object. slay, no locking drama:
static mongoc_oidc_credential_t *
single_thread_only (mongoc_oidc_callback_params_t *params)
{
// This function does not need to support invocation by more than thread at a time.
// ...
}
void
with_single_client (void)
{
mongoc_client_t *client = /* ... */;
{
mongoc_oidc_callback_t *callback = mongoc_oidc_callback_new (&single_thread_only);
BSON_ASSERT (mongoc_client_set_oidc_callback (client, callback));
mongoc_oidc_callback_destroy (callback);
}
// ... client operations ...
mongoc_client_destroy (client);
}
void
with_single_pool (void)
{
mongoc_client_pool_t *pool = /* ... */;
{
mongoc_oidc_callback_t *callback = mongoc_oidc_callback_new (&single_thread_only);
BSON_ASSERT (mongoc_client_pool_set_oidc_callback (pool, callback));
mongoc_oidc_callback_destroy (callback);
}
// ... client pool operations ...
mongoc_client_pool_destroy (pool);
}
BUT. π«Ά if your callback is associated with more than one mongoc_client_t object (in multiple threads), or with more than one mongoc_client_pool_t object (even in a single thread), then the callback function MUST support invocation by more than one thread at a time. no exceptions, fr fr:
static mongoc_oidc_credential_t *
many_threads_possible (mongoc_oidc_callback_params_t *params)
{
// This function MUST support invocation by more than one thread at a time.
// ...
}
void
with_many_clients (void)
{
mongoc_client_t *client_a = /* ... */;
mongoc_client_t *client_b = /* ... */;
{
mongoc_oidc_callback_t *callback = mongoc_oidc_callback_new (&many_threads_possible);
BSON_ASSERT (mongoc_client_set_oidc_callback (client_a, callback));
BSON_ASSERT (mongoc_client_set_oidc_callback (client_b, callback));
mongoc_oidc_callback_destroy (callback);
}
pthread_t thread_a;
pthread_t thread_b;
if (pthread_create (&thread_a, NULL, /* thread_a_fn */, client_a) != 0) { /* ... */ }
if (pthread_create (&thread_b, NULL, /* thread_b_fn */, client_b) != 0) { /* ... */ }
// ... client operations using multiple threads ...
if (pthread_join (&thread_a, NULL) != 0) { /* ... */ }
if (pthread_join (&thread_b, NULL) != 0) { /* ... */ }
mongoc_client_destroy (client_a);
mongoc_client_destroy (client_b);
}
void
with_many_pools (void)
{
mongoc_client_pool_t *pool_a = /* ... */;
mongoc_client_pool_t *pool_b = /* ... */;
{
mongoc_oidc_callback_t *callback = mongoc_oidc_callback_new (&many_threads_possible);
BSON_ASSERT (mongoc_client_pool_set_oidc_callback (pool_a, callback));
BSON_ASSERT (mongoc_client_pool_set_oidc_callback (pool_b, callback));
mongoc_oidc_callback_destroy (callback);
}
// ... client operations using multiple client pools ...
mongoc_client_pool_destroy (pool_a);
mongoc_client_pool_destroy (pool_b);
}
SEE ALSO:
mongoc_client_tmongoc_client_pool_tmongoc_oidc_callback_fn_tmongoc_oidc_callback_params_tmongoc_oidc_credential_t
πΈ mongoc_oidc_credential_t πΈ
Synopsis
typedef struct _mongoc_oidc_credential_t mongoc_oidc_credential_t;
this bad boy represents the return value of a mongoc_oidc_callback_fn_t. π
so here's the tea: the value gets returned by the mongoc_oidc_callback_fn_t that's stored in an mongoc_oidc_callback_t object, whenever an associated mongoc_client_t or mongoc_client_pool_t object invokes it. that's the whole flow β¨
Return Values π
the list of currently supported return values, bestie β drink it in:
access_tokenβ Versions: 1 β The OIDC access token.expires_inβ Versions: 1 β An optional expiration duration (in milliseconds).
the "Version" column indicates the OIDC callback API versions for which the parameter is applicable. π
Access Token π
an OIDC access token (a signed JWT token).
WARNING: access_token is NOT directly validated by the driver. π like, the driver is trusting you here. that's it. that's the warning.
Expiry Duration β³
an optional expiry duration (in milliseconds) for the access token.
IMPORTANT: an unset value is interpreted as an infinite expiry duration. forever token, basically. π«Ά
SEE ALSO:
mongoc_oidc_callback_tmongoc_oidc_callback_fn_t
π mongoc_optional_t π
a struct to store optional boolean values. yes, optional booleans. we love a tri-state moment.
Synopsis
used to specify optional boolean flags, which may remain unset. π»
this is used within mongoc_server_api_t to track whether a flag was explicitly set. like, "did you actually say false or did you just not say anything?" iconic distinction, tbh. π
π₯ mongoc_query_flags_t π₯
flags for query operations. let's GO.
Synopsis
typedef enum {
MONGOC_QUERY_NONE = 0,
MONGOC_QUERY_TAILABLE_CURSOR = 1 << 1,
MONGOC_QUERY_SECONDARY_OK = 1 << 2,
MONGOC_QUERY_OPLOG_REPLAY = 1 << 3,
MONGOC_QUERY_NO_CURSOR_TIMEOUT = 1 << 4,
MONGOC_QUERY_AWAIT_DATA = 1 << 5,
MONGOC_QUERY_EXHAUST = 1 << 6,
MONGOC_QUERY_PARTIAL = 1 << 7,
} mongoc_query_flags_t;
Description π
these flags correspond to the MongoDB wire protocol, periodt. you can bitwise-or them together (go off, bitwise operators π ) and they may modify how a query is performed in the MongoDB server. cute, powerful, exactly what you'd expect. β
Flag Values
Okay so these are the query flag values, one per bullet, no skipping π€
MONGOC_QUERY_NONEβ literally just "specify no query flags." that's it, that's the whole vibe.MONGOC_QUERY_TAILABLE_CURSORβ the cursor will not be closed when the last data is retrieved, so you can resume it later. it's giving "to be continuedβ¦" energy.MONGOC_QUERY_SECONDARY_OKβ lets you query replica set secondaries. yes queen, spread the reads around.MONGOC_QUERY_OPLOG_REPLAYβ used internally by MongoDB. not your business, bestie πMONGOC_QUERY_NO_CURSOR_TIMEOUTβ the server normally times out an idle cursor after an inactivity period (10 minutes). this flag prevents that. lowkey a lifesaver.MONGOC_QUERY_AWAIT_DATAβ use withMONGOC_QUERY_TAILBLE_CURSOR... wait no,MONGOC_QUERY_TAILABLE_CURSOR. it blocks rather than returning no data, and after a period it times out.MONGOC_QUERY_EXHAUSTβ unused. that's it. unused. πMONGOC_QUERY_PARTIALβ get partial results frommongosif some shards are down, instead of throwing an error. imperfect but slaying anyway β¨
mongoc_rand
MongoDB Random Number Generator π²
Synopsis
void
mongoc_rand_add (const void *buf, int num, double entropy);
void
mongoc_rand_seed (const void *buf, int num);
int
mongoc_rand_status (void);
Description
The mongoc_rand family of functions gives you access to the low level randomness primitives that the MongoDB C Driver uses. specifically? they control the creation of cryptographically strong pseudo-random bytes, which some security mechanisms literally require. so yeah, kind of a big deal π
We can usually pull enough entropy from the environment, but depending on your OS, your hardware, and whatever other entropy consumers are running on the same system, you may be required to seed the PRNG manually. so like, sometimes you gotta help a girl out.
Entropy
mongoc_rand_add and mongoc_rand_seed let the user directly provide entropy. the difference is that mongoc_rand_seed requires that each bit provided is fully random, whereas mongoc_rand_add also lets you specify the degree of randomness in the bytes you're handing over. so add is the chill one and seed is the strict one, no cap.
Status
mongoc_rand_status lets you check the status of the mongoc PRNG. use it to guarantee sufficient entropy at program startup instead of waiting around for runtime errors to show up and ruin your whole day π proactive queen behaviour fr fr.
mongoc_read_concern_t
Read Concern abstraction π
Synopsis
New in MongoDB 3.2. β¨
The mongoc_read_concern_t lets clients choose a level of isolation for their reads. the default, MONGOC_READ_CONCERN_LEVEL_LOCAL, is right for the great majority of applications. like, it's the main character for a reason.
You can specify a read concern on connection objects, database objects, or collection objects. flexible? yes. iconic? also yes.
See readConcern on the MongoDB website for more information.
Important vibe check: Read Concern is only sent to MongoDB when it has explicitly been set bymongoc_read_concern_set_level()to anything other thanNULL. otherwise it's just sitting there at home not being sent anywhere π«Ά
Read Concern Levels
MONGOC_READ_CONCERN_LEVEL_LOCALβ level"local", the default. first available in MongoDB 3.2.MONGOC_READ_CONCERN_LEVEL_MAJORITYβ level"majority". first available in MongoDB 3.2.MONGOC_READ_CONCERN_LEVEL_LINEARIZABLEβ level"linearizable". first available in MongoDB 3.4.MONGOC_READ_CONCERN_LEVEL_AVAILABLEβ level"available". first available in MongoDB 3.6.MONGOC_READ_CONCERN_LEVEL_SNAPSHOTβ level"snapshot". first available in MongoDB 4.0.
For the sake of compatibility with future versions of MongoDB, mongoc_read_concern_set_level() allows any string, not just this list of known read concern levels. so like, it's not gonna gatekeep you π¦
See Read Concern Levels in the MongoDB manual for more information about the individual read concern levels.
mongoc_read_mode_t
Read Preference Modes π
Synopsis
typedef enum {
MONGOC_READ_PRIMARY = (1 << 0),
MONGOC_READ_SECONDARY = (1 << 1),
MONGOC_READ_PRIMARY_PREFERRED = (1 << 2) | MONGOC_READ_PRIMARY,
MONGOC_READ_SECONDARY_PREFERRED = (1 << 2) | MONGOC_READ_SECONDARY,
MONGOC_READ_NEAREST = (1 << 3) | MONGOC_READ_SECONDARY,
} mongoc_read_mode_t;
Description
This enum describes how reads should be dispatched. the default is MONGOC_READ_PRIMARY. β¨
Please see the MongoDB website for a description of Read Preferences.
mongoc_read_prefs_t
A read preference abstraction π
Synopsis
mongoc_read_prefs_t provides an abstraction on top of the MongoDB connection read preferences. it lets you hint to the driver which nodes in a replica set should be accessed first, and how. it's basically the driver's little gossip network π
You can specify a read preference mode on connection objects, database objects, collection objects, or per-operation. generally, it makes the most sense to stick with the global default mode, MONGOC_READ_PRIMARY β like, all of the other modes come with caveats that won't be covered in great detail here, so proceed with caution bestie π
MONGOC_READ_PRIMARYβ the default mode, bestie π all operations read from the current replica set primary, periodt.MONGOC_READ_SECONDARYβ all operations read from among the nearest secondary members of the replica set π¦MONGOC_READ_PRIMARY_PREFERREDβ in most situations operations read from the primary, but if it's unavailable, operations read from secondary members. it's giving "primary first, but we have a backup plan" β¨MONGOC_READ_SECONDARY_PREFERREDβ in most situations operations read from among the nearest secondary members, but if no secondaries are available, operations read from the primary π₯MONGOC_READ_NEARESTβ operations read from among the nearest members of the replica set, irrespective of the member's type π
π·οΈ Tag Sets
tag sets let you specify custom read preferences and write concerns so your application can target operations to specific members. iykyk π
custom read preferences and write concerns evaluate tag sets in different ways, and this is the part everyone gets confused about:
- read preferences consider the value of a tag when selecting a member to read from πΈ
- write concerns ignore the value of a tag when selecting a member, except to consider whether or not the value is unique π
you can specify tag sets with the following read preference modes:
primaryPreferredsecondarysecondaryPreferrednearest
tags are not compatible with MONGOC_READ_PRIMARY and, in general, only apply when selecting a secondary member of a set for a read operation. but here's the cute exception: the nearest read mode, when combined with a tag set, will select the nearest member that matches the specified tag set, which may be a primary or secondary π«Ά
tag sets are represented as a comma-separated list of colon-separated key-value pairs when provided as a connection string, e.g. dc:ny,rack:1 π
to specify a list of tag sets, use multiple readPreferenceTags, e.g.
readPreferenceTags=dc:ny,rack:1;readPreferenceTags=dc:ny;readPreferenceTags=
note the empty value for the last one β that means "match any secondary as a last resort" π§ and yes, order matters when using multiple readPreferenceTags. don't play with it π
tag sets can also be configured using mongoc_read_prefs_set_tags() β¨
all interfaces use the same member selection logic to choose the member to which to direct read operations, basing the choice on read preference mode and tag sets. so like, no matter which door you come in through, same brain, same decision π
β³ Max Staleness
when connected to a replica set running MongoDB 3.4 or later, the driver estimates the staleness of each secondary based on lastWriteDate values provided in server hello responses π
Max Staleness is the maximum replication lag in seconds (wall clock time) that a secondary can suffer and still be eligible for reads. the default is MONGOC_NO_MAX_STALENESS, which disables staleness checks entirely π otherwise, it must be a positive integer at least MONGOC_SMALLEST_MAX_STALENESS_SECONDS (90 seconds). no cap, you can't go lower than that π
max staleness is also supported by sharded clusters of replica sets if all servers run MongoDB 3.4 or later π
π¦ Hedged Reads
deprecated since version MongoDB: Server 8.0 π hedged reads are deprecated in MongoDB version 8.0 and will be removed in a future release. so like, enjoy them while they last, bestie π
when connecting to a sharded cluster running MongoDB 4.4 or later, reads can be sent in parallel to the two "best" hosts. once one result returns, any other outstanding operations that were part of the hedged read are cancelled π¦
when the read preference mode is MONGOC_READ_NEAREST and the sharded cluster is running MongoDB 4.4 or later, hedged reads are enabled by default β additionally, hedged reads may be explicitly enabled or disabled by calling mongoc_read_prefs_set_hedge() with a BSON document, e.g.
{
enabled: true
}
appropriate values for the enabled key are true or false π
ποΈ mongoc_remove_flags_t
flags for deletion operations. yes, we're talking about removing things. icon behavior π
Synopsis
typedef enum {
MONGOC_REMOVE_NONE = 0,
MONGOC_REMOVE_SINGLE_REMOVE = 1 << 0,
} mongoc_remove_flags_t;
Description
these flags correspond to the MongoDB wire protocol. they may be bitwise or'd together π₯ and lowkey they may change the number of documents that are removed during a remove command, so likeβ¦ pay attention π
Flag Values
MONGOC_REMOVE_NONEβ specify no removal flags. all matching documents will be removed πMONGOC_REMOVE_SINGLE_REMOVEβ only remove the first matching document from the selector π―
π« mongoc_server_api_t
a versioned API to use for connections. fancy β¨
Synopsis
used to specify which version of the MongoDB server's API to use for driver connections.
the server API type takes a mongoc_server_api_version_t. it can optionally be strict about the list of allowed commands in that API version, and can also optionally provide errors for deprecated commands in that API version π
a mongoc_server_api_t can be set on a client, and will then be sent to MongoDB for most commands run using that client π«Ά
π’ mongoc_server_api_version_t
a representation of server API version numbers π
Synopsis
used to specify which version of the MongoDB server's API to use for driver connections.
Supported API Versions
the driver currently supports the following MongoDB API versions:
MONGOC_SERVER_API_V1β MongoDB version string"1"β
π₯οΈ mongoc_server_description_t
server description. it's giving "who even is this server" energy π
Synopsis
#include <mongoc/mongoc.h>
typedef struct _mongoc_server_description_t mongoc_server_description_t
mongoc_server_description_t holds information about a mongod or mongos the driver is connected to π¦
Lifecycle
clean up a mongoc_server_description_t with mongoc_server_description_destroy() when necessary π§Ή literally don't leave it lying around, bestie π
Applications receive a temporary reference to a mongoc_server_description_t as a parameter to an SDAM Monitoring callback β and that reference must not be destroyed. Like, do not touch it, bestie. See Introduction to Application Performance Monitoring. β¨
SEE ALSO π«Ά
mongoc_client_get_server_descriptions()
π mongoc_session_opt_t
#include <mongoc/mongoc.h>
typedef struct _mongoc_session_opt_t mongoc_session_opt_t;
Synopsis
okay so you start a session with mongoc_client_start_session(), use that session for a sequence of operations and multi-document transactions, then free it with mongoc_client_session_destroy(). Any mongoc_cursor_t or mongoc_change_stream_t using a session must be destroyed before the session, and a session must be destroyed before the mongoc_client_t it came from. That order is not optional. π«Ά
By default, sessions are causally consistent. To disable causal consistency, before starting a session create a mongoc_session_opt_t with mongoc_session_opts_new() and call mongoc_session_opts_set_causal_consistency(), then free the struct with mongoc_session_opts_destroy().
Unacknowledged writes are prohibited with sessions.
A mongoc_client_session_t must be used by only one thread at a time. Due to session pooling, mongoc_client_start_session() may return a session that has been idle for some time and is about to be closed after its idle timeout. Use the session within one minute of acquiring it to refresh the session and avoid a timeout.
See the example code for mongoc_session_opts_set_causal_consistency().
𧦠mongoc_socket_t
Portable socket abstraction
Synopsis π«
#include <mongoc/mongoc.h>
typedef struct _mongoc_socket_t mongoc_socket_t
Synopsis π«§
This structure provides a socket abstraction that is friendlier for portability than BSD sockets directly. Inconsistencies between Linux, various BSDs, Solaris, and Windows are handled here.
π mongoc_ssl_opt_t
Synopsis
typedef struct {
const char *pem_file;
const char *pem_pwd;
const char *ca_file;
const char *ca_dir;
const char *crl_file;
bool weak_cert_validation;
bool allow_invalid_hostname;
void *internal;
void *padding[6];
} mongoc_ssl_opt_t;
NOTE: Though some API names include the term "ssl", the C driver only support TLS protocols, which supersede SSL. That's the tea. π«
Description
This structure is used to set the TLS options for a mongoc_client_t or mongoc_client_pool_t.
Beginning in version 1.2.0, once a pool or client has any TLS options set, all connections use TLS, even if ssl=true is omitted from the MongoDB URI. Before, TLS options were ignored unless tls=true was included in the URI.
As of 1.4.0, the mongoc_client_pool_set_ssl_opts() and mongoc_client_set_ssl_opts() will not only shallow copy the struct, but will also copy the const char*. It is therefore no longer needed to make sure the values remain valid after setting them.
SEE ALSO π
- Configuring TLS
mongoc_client_set_ssl_opts()mongoc_client_pool_set_ssl_opts()
π¦ mongoc_stream_buffered_t
Synopsis
typedef struct _mongoc_stream_buffered_t mongoc_stream_buffered_t;
Description
mongoc_stream_buffered_t should be considered a subclass of mongoc_stream_t. It performs buffering on an underlying stream.
SEE ALSO β¨
mongoc_stream_buffered_new()mongoc_stream_destroy()
π mongoc_stream_file_t
Synopsis
typedef struct _mongoc_stream_file_t mongoc_stream_file_t
mongoc_stream_file_t is a mongoc_stream_t subclass for working with standard UNIX style file-descriptors.
π§ mongoc_stream_socket_t
Synopsis
typedef struct _mongoc_stream_socket_t mongoc_stream_socket_t
mongoc_stream_socket_t should be considered a subclass of mongoc_stream_t that works upon socket streams.
πΈ mongoc_stream_t
Synopsis
typedef struct _mongoc_stream_t mongoc_stream_t
mongoc_stream_t provides a generic streaming IO abstraction based on a struct of pointers interface. The idea is to allow wrappers, perhaps other language drivers, to easily shim their IO system on top of mongoc_stream_t.
The API for the stream abstraction is currently private and non-extensible.
Stream Types π
There are a number of built in stream types that come with mongoc. The default configuration is a buffered unix stream. If TLS is in use, that in turn is wrapped in a tls stream.
SEE ALSO π
mongoc_stream_buffered_tmongoc_stream_file_tmongoc_stream_socket_tmongoc_stream_tls_t
π mongoc_stream_tls_t
Synopsis
typedef struct _mongoc_stream_tls_t mongoc_stream_tls_t
mongoc_stream_tls_t is a mongoc_stream_t subclass for working with TLS streams.
π mongoc_topology_description_t
Status of MongoDB Servers
Synopsis
typedef struct _mongoc_topology_description_t mongoc_topology_description_t;
mongoc_topology_description_t is an opaque type representing the driver's knowledge of the MongoDB server or servers it is connected to. Its API conforms to the SDAM Monitoring Specification.
Applications receive a temporary reference to a mongoc_topology_description_t as a parameter to an SDAM Monitoring callback that must not be destroyed. See Introduction to Application Performance Monitoring.
π
mongoc_transaction_opt_t
#include <mongoc/mongoc.h>
typedef struct _mongoc_transaction_opt_t mongoc_transaction_opt_t;
Synopsis
Options for starting a multi-document transaction.
When a session is first created with mongoc_client_start_session(), it inherits from the client the read concern, write concern, and read preference with which to start transactions. Each of these fields can be overridden independently. Create a mongoc_transaction_opt_t with mongoc_transaction_opts_new(), and pass a non-NULL option to any of the mongoc_transaction_opt_t setter functions:
mongoc_transaction_opts_set_read_concern()mongoc_transaction_opts_set_write_concern()mongoc_transaction_opts_set_read_prefs()
Pass the resulting transaction options to mongoc_client_session_start_transaction(). Each field set in the transaction options overrides the inherited client configuration.
Example π¬
example-transaction.c
/* gcc example-transaction.c -o example-transaction \
* $(pkg-config --cflags --libs libmongoc-1.0) */
/* ./example-transaction [CONNECTION_STRING] */
#include <mongoc/mongoc.h>
#include <stdio.h>
int
main(int argc, char *argv[])
{
int exit_code = EXIT_FAILURE;
mongoc_client_t *client = NULL;
mongoc_database_t *database = NULL;
mongoc_collection_t *collection = NULL;
mongoc_client_session_t *session = NULL;
mongoc_session_opt_t *session_opts = NULL;
mongoc_transaction_opt_t *default_txn_opts = NULL;
mongoc_transaction_opt_t *txn_opts = NULL;
mongoc_read_concern_t *read_concern = NULL;
mongoc_write_concern_t *write_concern = NULL;
const char *uri_string = "mongodb://127.0.0.1/?appname=transaction-example";
mongoc_uri_t *uri;
bson_error_t error;
bson_t *doc = NULL;
bson_t *insert_opts = NULL;
int32_t i;
int64_t start;
bson_t reply = BSON_INITIALIZER;
char *reply_json;
bool r;
mongoc_init();
if (argc > 1) {
uri_string = argv[1];
}
uri = mongoc_uri_new_with_error(uri_string, &error);
if (!uri) {
fprintf(stderr,
"failed to parse URI:\n"
"error message: %s\n",
error.message);
goto done;
}
client = mongoc_client_new_from_uri(uri);
if (!client) {
goto done;
}
mongoc_client_set_error_api(client, 2);
database = mongoc_client_get_database(client, "example-transaction");
/* inserting into a nonexistent collection normally creates it, but a
* collection can't be created in a transaction; create it now */
collection = mongoc_database_create_collection(database, "collection", NULL, &error);
if (!collection) {
/* code 48 is NamespaceExists, see error_codes.err in mongodb source */
if (error.code == 48) {
collection = mongoc_database_get_collection(database, "collection");
} else {
fprintf(stderr, "Failed to create collection: %s\n", error.message);
goto done;
}
}
/* a transaction's read preferences, read concern, and write concern can be
* set on the client, on the default transaction options, or when starting
* the transaction. for the sake of this example, set read concern on the
* default transaction options. */
default_txn_opts = mongoc_transaction_opts_new();
read_concern = mongoc_read_concern_new();
mongoc_read_concern_set_level(read_concern, "snapshot");
mongoc_transaction_opts_set_read_concern(default_txn_opts, read_concern);
session_opts = mongoc_session_opts_new();
mongoc_session_opts_set_default_transaction_opts(session_opts, default_txn_opts);
session = mongoc_client_start_session(client, session_opts, &error);
if (!session) {
fprintf(stderr, "Failed to start session: %s\n", error.message);
goto done;
}
/* in this example, set write concern when starting the transaction */
txn_opts = mongoc_transaction_opts_new();
write_concern = mongoc_write_concern_new();
mongoc_write_concern_set_wmajority(write_concern, 1000 /* wtimeout */);
mongoc_transaction_opts_set_write_concern(txn_opts, write_concern);
insert_opts = bson_new();
if (!mongoc_client_session_append(session, insert_opts, &error)) {
fprintf(stderr, "Could not add session to opts: %s\n", error.message);
goto done;
}
retry_transaction:
r = mongoc_client_session_start_transaction(session, txn_opts, &error);
if (!r) {
fprintf(stderr, "Failed to start transaction: %s\n", error.message);
goto done;
}
/* insert two documents - on error, retry the whole transaction */
for (i = 0; i < 2; i++) {
doc = BCON_NEW("_id", BCON_INT32(i));
bson_destroy(&reply);
r = mongoc_collection_insert_one(collection, doc, insert_opts, &reply, &error);
bson_destroy(doc);
if (!r) {
fprintf(stderr, "Insert failed: %s\n", error.message);
mongoc_client_session_abort_transaction(session, NULL);
/* a network error, primary failover, or other temporary error in a
* transaction includes {"errorLabels": ["TransientTransactionError"]},
* meaning that trying the entire transaction again may succeed
*/
if (mongoc_error_has_label(&reply, "TransientTransactionError")) {
goto retry_transaction;
}
goto done;
}
reply_json = bson_as_relaxed_extended_json(&reply, NULL);
printf("%s\n", reply_json);
bson_free(reply_json);
}
/* in case of transient errors, retry for 5 seconds to commit transaction */
start = bson_get_monotonic_time();
while (bson_get_monotonic_time() - start < 5 * 1000 * 1000) {
bson_destroy(&reply);
r = mongoc_client_session_commit_transaction(session, &reply, &error);
if (r) {
/* success */
break;
} else {
fprintf(stderr, "Warning: commit failed: %s\n", error.message);
if (mongoc_error_has_label(&reply, "TransientTransactionError")) {
goto retry_transaction;
} else if (mongoc_error_has_label(&reply, "UnknownTransactionCommitResult")) {
/* try again to commit */
continue;
}
/* unrecoverable error trying to commit */
break;
}
}
exit_code = EXIT_SUCCESS;
done:
bson_destroy(&reply);
bson_destroy(insert_opts);
mongoc_write_concern_destroy(write_concern);
mongoc_read_concern_destroy(read_concern);
mongoc_transaction_opts_destroy(txn_opts);
mongoc_transaction_opts_destroy(default_txn_opts);
mongoc_client_session_destroy(session);
mongoc_collection_destroy(collection);
mongoc_database_destroy(database);
mongoc_uri_destroy(uri);
mongoc_client_destroy(client);
mongoc_cleanup();
return exit_code;
}
πΈ mongoc_transaction_state_t πΈ
Literally the "where are we rn" enum for transactions β constants for transaction states, bestie π
β¨ Synopsis β¨
typedef enum {
MONGOC_TRANSACTION_NONE = 0,
MONGOC_TRANSACTION_STARTING = 1,
MONGOC_TRANSACTION_IN_PROGRESS = 2,
MONGOC_TRANSACTION_COMMITTED = 3,
MONGOC_TRANSACTION_ABORTED = 4,
} mongoc_transaction_state_t;
π«Ά Description π«Ά
Okay so these constants describe the current transaction state of a session. That's the whole vibe check β where's your transaction at, babes?
π Flag Values π
MONGOC_TRANSACTION_NONEβ There is no transaction in progress. Nothing's happening, we're chillin' πMONGOC_TRANSACTION_STARTINGβ A transaction has been started, but no operation has been sent to the server. Like, the group chat is made but nobody's typed yet πMONGOC_TRANSACTION_IN_PROGRESSβ A transaction is in progress. Main character energy, it's happening π₯MONGOC_TRANSACTION_COMMITTEDβ The transaction was committed. Slay, she's official πMONGOC_TRANSACTION_ABORTEDβ The transaction was aborted. Not me crying, it's fine, we move π
π¦ mongoc_update_flags_t π¦
Flags for update operations β the little bit-flags that tell your update how to behave β¨
β¨ Synopsis β¨
typedef enum {
MONGOC_UPDATE_NONE = 0,
MONGOC_UPDATE_UPSERT = 1 << 0,
MONGOC_UPDATE_MULTI_UPDATE = 1 << 1,
} mongoc_update_flags_t;
#define MONGOC_UPDATE_NO_VALIDATE (1U << 31)
π«Ά Description π«Ά
These flags correspond to the MongoDB wire protocol. They may be bitwise or'd together β yes bestie, | is your bestie here fr fr. They let you modify the way an update is performed in the MongoDB server.
π Flag Values π
MONGOC_UPDATE_NONEβ No update flags set. Default vibes, zero notes πMONGOC_UPDATE_UPSERTβ If an upsert should be performed. aka "if she's not there, make her there" πMONGOC_UPDATE_MULTI_UPDATEβ If more than a single matching document should be updated. By default only the first document is updated β so yeah, you gotta opt in to the "update everyone" life π€MONGOC_UPDATE_NO_VALIDATEβ Do not perform client side BSON validations when performing an update. This is useful if you already know your BSON documents are valid. Highkey a flex for the confident girlies β
π mongoc_uri_t π
β¨ Synopsis β¨
typedef struct _mongoc_uri_t mongoc_uri_t;
π«Ά Description π«Ά
mongoc_uri_t provides an abstraction on top of the MongoDB connection URI format. It gives you standardized parsing plus convenience methods for extracting useful information such as replica hosts or authorization information. It's giving "glow-up for a string" π
See Connection String URI Reference on the MongoDB website for more information.
π§ Format π§
mongodb[+srv]:// <1>
[username:password@] <2>
host1 <3>
[:port1] <4>
[,host2[:port2],...[,hostN[:portN]]] <5>
[/[database] <6>
[?options]] <7>
"mongodb"is the specifier of the MongoDB protocol. Use"mongodb+srv"with a single service name in place of"host1"to specify the initial list of servers with an SRV record. β¨- An optional username and password. Optional! Like your crush's texts π
- The only required part of the uri. This specifies either a hostname, IPv4 address, IPv6 address enclosed in
"["and"]", or UNIX domain socket. The non-negotiable one π - An optional port number. Defaults to
:27017. - Extra optional hosts and ports. You would specify multiple hosts, for example, for connections to replica sets. The more the merrier π«Ά
- The name of the database to authenticate if the connection string includes authentication credentials. If
/databaseis not specified and the connection string includes credentials, defaults to the'admin'database. - Connection specific options. The sprinkle-on-top part π
NOTE: Option names are case-insensitive. Do not repeat the same option (e.g. "mongodb://localhost/db?opt=value1&OPT=value2") since this may have unexpected results. Fr fr, don't do it, it's not the move π
The MongoDB C Driver exposes constants for each supported connection option. These constants make it easier to discover connection options, but their string values can be used as well.
For example, the following calls are equal. Like, literally identical energy π
uri = mongoc_uri_new ("mongodb://localhost/?" MONGOC_URI_APPNAME "=applicationName");
uri = mongoc_uri_new ("mongodb://localhost/?appname=applicationName");
uri = mongoc_uri_new ("mongodb://localhost/?appName=applicationName");
πΈ Replica Set Example πΈ
To describe a connection to a replica set named 'test' with the following mongod hosts:
- db1.example.com on port
27017 - db2.example.com on port
2500
You would use a connection string that resembles the following. Iconic, no notes π
mongodb://db1.example.com,db2.example.com:2500/?replicaSet=test
π SRV Example π
If you have configured an SRV record with a name like "_mongodb._tcp.server.example.com" whose records are a list of one or more MongoDB server hostnames, use a connection string like this:
uri = mongoc_uri_new ("mongodb+srv://server.example.com/?replicaSet=rs&appName=applicationName");
The driver prefixes the service name with "_mongodb._tcp.", then performs a DNS SRV query to resolve the service name to one or more hostnames. If this query succeeds, the driver performs a DNS TXT query on the service name (without the "_mongodb._tcp" prefix) for additional URI options configured as TXT records. She's doing research, okay? π
On Unix, the MongoDB C Driver relies on libresolv to look up SRV and TXT records. If libresolv is unavailable, then using a "mongodb+srv" URI will cause an error. If your libresolv lacks res_nsearch then the driver will fall back to res_search, which is not thread-safe. Yeah. Not the vibe, but forewarned is forearmed π
Set the environment variable MONGOC_EXPERIMENTAL_SRV_PREFER_TCP to prefer TCP for the initial queries. The environment variable is ignored for res_search. Large DNS responses over UDP may be truncated due to UDP size limitations. DNS resolvers are expected to retry over TCP if the UDP response indicates truncation. Some observed DNS environments do not set the truncation flag (TC), preventing the TCP retry. This environment variable is currently experimental and subject to change β so like, don't get too attached π«Ά
π IPv4 and IPv6
Okay so this is the part where DNS gets messy, bestie, listen up. If you're connecting to a hostname that has both IPv4 and IPv6 DNS records, the behavior follows RFC-6555. A connection to the IPv6 address is attempted first. If IPv6 fails, then a connection is attempted to the IPv4 address. And here's the spicy bit: if the connection attempt to IPv6 does not complete within 250ms, then IPv4 is tried in parallel. Whichever succeeds connection first cancels the other. The successful DNS result is cached for 10 minutes. β¨
As a consequence, attempts to connect to a mongod only listening on IPv4 may be delayed if there are both A (IPv4) and AAAA (IPv6) DNS records associated with the host. π
To avoid a delay, configure hostnames to match the MongoDB configuration. That is, only create an A record if the mongod is only listening on IPv4. Do that and you're slaying, fr fr π«Ά
π Connection Options
This is the big glow-up table of connection options β every constant, its key, its default, and what it actually does. Buckle up, there's a bunch. π
MONGOC_URI_RETRYREADS(retryreads) β default:true. If"true"and the server is a MongoDB 3.6+ standalone, replica set, or sharded cluster, the driver safely retries a read that failed due to a network error or replica set failover. π©ΉMONGOC_URI_RETRYWRITES(retrywrites) β default:trueif driver built w/ TLS. If"true"and the server is a MongoDB 3.6+ replica set or sharded cluster, the driver safely retries a write that failed due to a network error or replica set failover. Only inserts, updates of single documents, or deletes of single documents are retried. πMONGOC_URI_MAXADAPTIVERETRIES(maxadaptiveretries) β default:2. The maximum number of times the driver retries an operation that fails with aSystemOverloadedError. Must be a non-negative integer. This option works with MongoDB Atlas Server Version 9.0 and above. πMONGOC_URI_ENABLEOVERLOADRETARGETING(enableoverloadretargeting) β default:false. If"true", the driver deprioritizes servers which returned aSystemOverloadedErrorwhen selecting a server to retry an operation. This option works with MongoDB Atlas Server Version 9.0 and above. π―MONGOC_URI_APPNAME(appname) β default: Empty (no appname). The client application name. This value is used by MongoDB when it logs connection information and profile information, such as slow queries. πΈMONGOC_URI_TLS(tls) β default: Empty (not set, same as false).{true|false}, indicating if TLS must be used. (See alsomongoc_client_set_ssl_opts()andmongoc_client_pool_set_ssl_opts().) πMONGOC_URI_COMPRESSORS(compressors) β default: Empty (no compressors). Comma separated list of compressors, if any, to use to compress the wire protocol messages. Snappy, zlib, and zstd are optional build time dependencies, and enable the"snappy","zlib", and"zstd"values respectively. ποΈMONGOC_URI_CONNECTTIMEOUTMS/connecttimeoutmsβ default is10,000 ms(that's 10 seconds bestie β±οΈ). This one applies to new server connections, and it also doubles as the socket timeout for server discovery and monitoring operations. multitasking queen πMONGOC_URI_SOCKETTIMEOUTMS/sockettimeoutmsβ default300,000 ms(5 whole minutes, yes really π). It's the time in milliseconds the driver will attempt to send or receive on a socket before that attempt times out.MONGOC_URI_REPLICASET/replicasetβ default is empty (aka no replicaset, we're free agents π¦). This is the name of the Replica Set the driver should connect to.MONGOC_URI_ZLIBCOMPRESSIONLEVEL/zlibcompressionlevelβ default-1. WhenMONGOC_URI_COMPRESSORSincludes"zlib", this option configures the zlib compression level, used when the zlib compressor is compressing client data. tiny number, big job β¨MONGOC_URI_LOADBALANCED/loadbalancedβ defaultfalse. If it'strue, that's the driver's way of saying "hey, I'm connecting to a MongoDB cluster behind a load balancer." πMONGOC_URI_SRVMAXHOSTS/srvmaxhostsβ default0. If it's zero, the number of hosts in DNS results is unlimited (go off, infinite hosts π). If it's greater than zero, the number of hosts in DNS results is limited to being less than or equal to the value you gave.
β οΈ WARNING: Setting any of the*timeoutMSoptions above to either0or a negative value is discouraged β the behavior is unspecified and inconsistent, and we do not stan chaos π. The "default value" that historically got specified as a fallback for0or a negative value is NOT related to the default values for the*timeoutMSoptions documented above, okay? The meaning of a timeout of0or a negative value may vary depending on the operation being executed β even when it's specified by the same URI option. literally wild π. If you actually want the documented default value for a*timeoutMSoption, use theMONGOC_DEFAULT_*constants defined inmongoc-client.hinstead. periodt. π
π Authentication Options π
MONGOC_URI_AUTHMECHANISM/authmechanismβ specifies the mechanism to use when authenticating as the provided user. See Authentication for the supported values. πMONGOC_URI_AUTHMECHANISMPROPERTIES/authmechanismpropertiesβ extra properties for the mechanism you specified, in key-value pair format, likekey1:value1,key2:value2. πMONGOC_URI_AUTHSOURCE/authsourceβ the name of the database that authentication commands get sent to, or$externaldepending on the specified mechanism. It overrides the auth database in the URI when applicable. main character behavior honestly π
π Mechanism Properties
The following properties may be specified as key-value pairs for the MONGOC_URI_AUTHMECHANISMPROPERTIES option.
Invalid or unsupported properties may be reported as a client error when a corresponding authentication mechanism is also specified.
π¦ MONGODB-OIDC
ENVIRONMENTβ The name of a built-in OIDC provider integration. Must be one of["azure", "gcp", "k8s"].TOKEN_RESOURCEβ The URI of the target resource.ENVIRONMENTmust be one of["azure", "gcp"].
β οΈ WARNING: ATOKEN_RESOURCEproperty value MUST NOT contain the comma character","when specified as a connection string query option, even when percent-encoded. A value containing a comma character may be set usingmongoc_uri_set_mechanism_properties()instead. However, the value MAY contain the colon character":", as only the first colon is interpreted as a key-value delimiter.
β¨ GSSAPI
SERVICE_NAMEβ Optional. Defaults to"mongodb".CANONICALIZE_HOST_NAMEβ Optional. Must be one of["false", "true"]."false"performs no canonicalization (aka"none")."true"performs a forward DNS lookup and then a reverse lookup on that value to canonicalize the hostname (aka"forwardAndReverse").SERVICE_REALMβ Optional. May be needed for cross-realm authentication where the user and service exist in different realms.SERVICE_HOSTβ Optional. May be needed to use a service host that differs from the initial role.
π₯ MONGODB-AWS
AWS_SESSION_TOKENβ Optional. An AWS session token to use for authentication with temporary credentials.
π« Deprecated Mechanism Property Options
The following options have been deprecated and may be removed from future releases of libmongoc.
MONGOC_URI_CANONICALIZEHOSTNAMEβ constant whose key iscanonicalizehostname. Deprecated for:MONGOC_URI_AUTHMECHANISMPROPERTIES, keyauthmechanismproperties(CANONICALIZE_HOST_NAME).MONGOC_URI_GSSAPISERVICENAMEβ constant whose key isgssapiservicename. Deprecated for:MONGOC_URI_AUTHMECHANISMPROPERTIES, keyauthmechanismproperties(SERVICE_NAME).
π TLS Options
MONGOC_URI_TLSβ constant whose key istls. Description:{true|false}, indicating if TLS must be used.MONGOC_URI_TLSCERTIFICATEKEYFILEβ constant whose key istlscertificatekeyfile. Description: Path to PEM formatted Private Key, with its Public Certificate concatenated at the end.MONGOC_URI_TLSCERTIFICATEKEYFILEPASSWORDβ constant whose key istlscertificatekeypassword. Description: The password, if any, to use to unlock encrypted Private Key.MONGOC_URI_TLSCAFILE(tlscafile) β points at one Certificate Authority, or a whole bundle of them, that should be considered trusted. π«ΆMONGOC_URI_TLSALLOWINVALIDCERTIFICATES(tlsallowinvalidcertificates) β accept and ignore certificate verification errors (untrusted issuer, expired cert, etc.). Itβs giving βbestie, are we sure?β energy. πMONGOC_URI_TLSALLOWINVALIDHOSTNAMES(tlsallowinvalidhostnames) β ignore hostname verification of the certificate (like a Man In The Middle situation where the cert is valid but was issued for another hostname). πMONGOC_URI_TLSINSECURE(tlsinsecure) β takes{true|false}, indicating if insecure TLS options should be used. Currently this impliesMONGOC_URI_TLSALLOWINVALIDCERTIFICATESandMONGOC_URI_TLSALLOWINVALIDHOSTNAMES. So yeah, itβs the βturn off the safety railsβ option. π¨MONGOC_URI_TLSDISABLECERTIFICATEREVOCATIONCHECK(tlsdisablecertificaterevocationcheck) β takes{true|false}, indicating if revocation checking (CRL / OCSP) should be disabled. π₯MONGOC_URI_TLSDISABLEOCSPENDPOINTCHECK(tlsdisableocspendpointcheck) β takes{true|false}, indicating if OCSP responder endpoints should not be requested when an OCSP response is not stapled. β¨
See Configuring TLS for details about these options and about building libmongoc with TLS support. π
π Deprecated SSL Options π
The following options have been deprecated and may be removed from future releases of libmongoc. So likeβ¦ use the new ones, bestie. π
MONGOC_URI_SSL(ssl) β deprecated forMONGOC_URI_TLS(tls).MONGOC_URI_SSLCLIENTCERTIFICATEKEYFILE(sslclientcertificatekeyfile) β deprecated forMONGOC_URI_TLSCERTIFICATEKEYFILE(tlscertificatekeyfile).MONGOC_URI_SSLCLIENTCERTIFICATEKEYPASSWORD(sslclientcertificatekeypassword) β deprecated forMONGOC_URI_TLSCERTIFICATEKEYFILEPASSWORD(tlscertificatekeypassword).MONGOC_URI_SSLCERTIFICATEAUTHORITYFILE(sslcertificateauthorityfile) β deprecated forMONGOC_URI_TLSCAFILE(tlscafile).MONGOC_URI_SSLALLOWINVALIDCERTIFICATES(sslallowinvalidcertificates) β deprecated forMONGOC_URI_TLSALLOWINVALIDCERTIFICATES(tlsallowinvalidcertificates).MONGOC_URI_SSLALLOWINVALIDHOSTNAMESβ keysslallowinvalidhostnamesβ and this is the older name's modern glow-up:MONGOC_URI_TLSALLOWINVALIDHOSTNAMESwith keytlsallowinvalidhostnamesπ«
π Server Discovery, Monitoring, and Selection Options
Okay so this is the whole saga of how the driver goes out and finds your servers and then picks one, and honestly there's a lot happening behind the scenes, so let's get into it π«Ά
- Clients inside a
mongoc_client_pool_tshare a topology scanner, and that scanner lives on a background thread. The thread wakes up everyheartbeatFrequencyMS(default 10 seconds) and scans all the MongoDB servers it knows about, in parallel. Multi-tasking icon behaviour, honestly β¨ - Now, any time an application operation needs a server that isn't known yet β like if there's no known primary and your app tries an insert β that thread rescans all the servers every half-second. While that's going on, the pooled client waits up to
serverSelectionTimeoutMS(default 30 seconds) for the thread to find a server that actually suits the operation, and then it returns an error with domainMONGOC_ERROR_SERVER_SELECTIONπ it tried its best. - Technically β nerd glasses on π β the total amount of time an operation may wait while a pooled client scans the topology is controlled by both
serverSelectionTimeoutMSandconnectTimeoutMS. The absolute longest wait happens if the last scan begins right at the end of the selection timeout and a slow or down server needs the full connection timeout before the client finally gives up. That's the worst-case-scenario combo meal right there π - A non-pooled client is single-threaded, bestie. Every
heartbeatFrequencyMSit just blocks the next application operation while it does a parallel scan. And that scan takes as long as it needs to check the slowest server β roughlyconnectTimeoutMS. Which is precisely why the defaultheartbeatFrequencyMSfor single-threaded clients is bigger than for pooled clients: 60 seconds instead of 10. respectful of your time, we love to see it π - By default, single-threaded (non-pooled) clients scan only once when an operation needs a server it doesn't know about. So if you attempt an insert and there's no known primary, the client checks all the servers one time trying to find it, then either succeeds or returns an error with domain
MONGOC_ERROR_SERVER_SELECTION. But! If you setserverSelectionTryOnceto"false", the single-threaded client loops, checking all servers every half-second, untilserverSelectionTimeoutMSexpires. committed to the bit π«‘ - The total time an operation may wait for a single-threaded client to scan the topology is determined by
connectTimeoutMSin the try-once case, or byserverSelectionTimeoutMSandconnectTimeoutMSifserverSelectionTryOnceis set to"false".
π The Constants (and their keys)
MONGOC_URI_HEARTBEATFREQUENCYMSβ keyheartbeatfrequencymsβ the interval between server monitoring checks. Defaults to10,000ms(10 seconds) in pooled (multi-threaded) mode, and60,000ms(60 seconds) in non-pooled mode (single-threaded). β° it's giving different defaults for different lifestyles.MONGOC_URI_SERVERSELECTIONTIMEOUTMSβ keyserverselectiontimeoutmsβ a timeout in milliseconds to block for server selection before throwing an exception. The default is30,0000ms(30 seconds). And yes, that comma is literally written like that in the source β we are not fixing it, we are respecting it π it means 30 seconds, bestie, don't panic.MONGOC_URI_SERVERSELECTIONTRYONCEβ keyserverselectiontryonceβ if"true", the driver scans the topology exactly once after server selection fails, then either selects a server or returns an error. If it's"false", the driver searches again and again for a suitable server for up toserverSelectionTimeoutMSmilliseconds, pausing half a second between attempts. The default forserverSelectionTryOnceis"false"for pooled clients, otherwise"true". And plot twist: pooled clients ignoreserverSelectionTryOnceentirely β instead they signal the thread to rescan the topology every half-second untilserverSelectionTimeoutMSexpires. main character energy, refusing to follow the rules π«MONGOC_URI_SOCKETCHECKINTERVALMSβ keysocketcheckintervalmsβ only applies to single threaded clients. If a socket hasn't been used within this amount of time, its connection gets checked with a quick"hello"call before it's used again. Defaults to5,000ms(5 seconds). just a little wellness check before we get back to business πMONGOC_URI_DIRECTCONNECTIONβ keydirectconnectionβ if"true", the driver connects to a single server directly and will not monitor any additional servers. If"false", the driver connects based on the presence and value of thereplicaSetoption. pick a lane, but make it a documented one π―
π PSA bestie: setting any of the*TimeoutMSoptions above to0is gonna be interpreted as "use the default value". Not zero. Default. Don't @ me, that's just how she works. β¨
πββοΈ Connection Pool Options πββοΈ
Okay so these options are the ones that govern the behavior of a mongoc_client_pool_t. Heads up tho: a non-pooled mongoc_client_t ignores them completely β like, doesn't even look. π
MONGOC_URI_MAXPOOLSIZE, keymaxpoolsizeβ the maximum number of clients created by amongoc_client_pool_ttotal (both the ones sitting in the pool and the ones checked out). The default value is100. And once you hit that ceiling?mongoc_client_pool_pop()just blocks until another thread pushes a client back. No cap, you're waiting. πMONGOC_URI_WAITQUEUETIMEOUTMS, keywaitqueuetimeoutmsβ the maximum time to wait for a client to become available from the pool. β³
βοΈ Write Concern Options βοΈ
MONGOC_URI_W, keywβ determines the write concern (aka the guarantee, the "did it actually happen tho" factor). Valid values:0β the driver will not acknowledge write operations, but it will pass or handle any network and socket errors that it receives to the client. π«Ά And if you disable write concern but enable thegetLastErrorcommand'swoption,woverrides thewoption. (Yes, that's real, that's what it says.)1β provides basic acknowledgement of write operations. By specifying1, you require that a standalonemongodinstance, or the primary for replica sets, acknowledge all write operations. For drivers released after the default write concern change, this is the default write concern setting. πmajorityβ for replica sets, if you specify the specialmajorityvalue to thewoption, write operations will only return successfully after a majority of the configured replica set members have acknowledged the write operation. Like the group chat all reading your message before it counts as delivered. π
nβ for replica sets, if you specify a numberngreater than 1, operations with this write concern return only afternmembers of the set have acknowledged the write. Heads up tho π«Ά if you setnto a number that is greater than the number of available set members or members that hold data, MongoDB will wait β and we mean wait β potentially indefinitely, for these members to become available. Like, it will just sit there. Patiently. Forever. πtagsβ for replica sets, you can specify a tag set to require that all members of the set that have these tags configured return confirmation of the write operation. cute, tagged, accountable β¨
MONGOC_URI_WTIMEOUTMS,wtimeoutmsβ the time in milliseconds to wait for replication to succeed, as specified in thewoption, before timing out. And whenwtimeoutMSis0, write operations will never time out. never ever. no cap. πMONGOC_URI_JOURNAL,journalβ controls whether write operations will wait until themongodacknowledges the write operations and commits the data to the on disk journal. ποΈtrueβ enables journal commit acknowledgement write concern. Equivalent to specifying thegetLastErrorcommand with thejoption enabled.falseβ does not require thatmongodcommit write operations to the journal before acknowledging the write operation. This is the default option for thejournalparameter. so yeah, that's the vibe out of the box π
πΈ Read Concern Options πΈ
MONGOC_URI_READCONCERNLEVEL,readconcernlevelβ the level of isolation for read operations. If the level is left unspecified, the server default will be used. SeereadConcernin the MongoDB Manual for details. iykyk, the manual's got the lore π
π¦ Read Preference Options π¦
When connected to a replica set, the driver chooses which member to query using the read preference:
- Choose members whose type matches
readPreference. π― - From these, if there are any tags sets configured, choose members matching the first tag set. If there are none, fall back to the next tag set and so on, until some members are chosen or the tag sets are exhausted. it's giving "keep swiping till you find the one" π
- From the chosen servers, distribute queries randomly among the server with the fastest round-trip times. These include the server with the fastest time and any whose round-trip time is no more than
localThresholdMSslower. β‘ fastest besties only, but the near-fastest get to hang too
okay so next up it's the constants table, and it kicks off with Constant, Key, Description β stay tuned bestie, the good stuff's coming π
MONGOC_URI_READPREFERENCE, keyreadpreferenceβ tells the driver which replica set read preference this connection rolls with. Fr fr this one overrides anysecondaryOkvalue you set, so it's the main character here. The read preference values are literally these:primary(the default, bestie β¨)primaryPreferredsecondarysecondaryPreferrednearest
MONGOC_URI_READPREFERENCETAGS, keyreadpreferencetagsβ a representation of a tag set. Also see Tag Sets, iykyk πMONGOC_URI_LOCALTHRESHOLDMS, keylocalthresholdmsβ how far to distribute queries, beyond the server with the fastest round-trip time. By default, only servers within 15ms of the fastest round-trip time receive queries πMONGOC_URI_MAXSTALENESSSECONDS, keymaxstalenesssecondsβ the maximum replication lag, in wall clock time, that a secondary can suffer and still be eligible. Heads up: the smallest allowed value for maxStalenessSeconds is 90 seconds β±οΈ
NOTE: when connecting to more than one mongos, libmongoc'slocalThresholdMSapplies only to the selection of mongos servers. The threshold for selecting among replica set members in shards is controlled by the mongos'slocalThresholdcommand line option. Not me being specific about which threshold, but yes, they're different ones π
π°οΈ Legacy Options
For historical reasons, the following options are available. They should however not be used β like, they're giving deprecated, leave them in the past bestie π
MONGOC_URI_SAFE, keysafeβ{true|false}Same asw={1|0}
π Version Checks
Conditional compilation based on mongoc version β so you can vibe-check the library before you compile against it π¦
Description
The following preprocessor macros can be used to perform various checks based on the version of the library you are compiling against. This may be useful if you only want to enable a feature on a certain version of the library.
#include <mongoc/mongoc.h>
#define MONGOC_MAJOR_VERSION (x)
#define MONGOC_MINOR_VERSION (y)
#define MONGOC_MICRO_VERSION (z)
#define MONGOC_VERSION_S "x.y.z"
#define MONGOC_VERSION_HEX ((1 << 24) | (0 << 16) | (0 << 8) | 0)
#define MONGOC_CHECK_VERSION(major, minor, micro)
Only compile a block on MongoDB C Driver 1.1.0 and newer β iconic gatekeeping tbh:
#if MONGOC_CHECK_VERSION(1, 1, 0)
static void
do_something (void)
{
}
#endif
π mongoc_write_concern_t
The Write Concern abstraction. Absolutely essential, no cap π
Synopsis
mongoc_write_concern_t tells the driver what level of acknowledgement to await from the server. The default, MONGOC_WRITE_CONCERN_W_DEFAULT, is right for the great majority of applications β so if that's you, congrats, you can literally just leave it alone β¨
You can specify a write concern on connection objects, database objects, collection objects, or per-operation. Data-modifying operations typically use the write concern of the object they operate on, and check the server response for a write concern error or write concern timeout. For example, mongoc_collection_drop_index() uses the collection's write concern, and a write concern error or timeout in the response is considered a failure π
Exceptions to this principle are the generic command functions β these ones do not play by that rule:
mongoc_client_command_simple()mongoc_database_command_simple()mongoc_collection_command_simple()
These generic command functions do not automatically apply a write concern, and they do not check the server response for a write concern error or write concern timeout. So like, you gotta handle that yourself, bestie π«Ά
See Write Concern on the MongoDB website for more information π
π Write Concern Levels π
okay bestie, this is the dial that controls how much reassurance you want from the server that your write actually landed. You set it with mongoc_write_concern_set_w() and here's the whole menu:
MONGOC_WRITE_CONCERN_W_DEFAULT(1) β the vibe you get if you don't touch anything. By default, writes block awaiting acknowledgement from MongoDB. Acknowledged write concern is literally the reason clients can catch network errors, duplicate key errors, and other errors β it's giving safety net, and we respect it πMONGOC_WRITE_CONCERN_W_UNACKNOWLEDGED(0) β with this write concern, MongoDB does not acknowledge the receipt of write operation. Unacknowledged is similar to errors ignored; however, mongoc attempts to receive and handle network errors when possible. so likeβ¦ it's not fully ghosting you, but it's also not texting back fr π«MONGOC_WRITE_CONCERN_W_MAJORITY(majority) β block until a write has been propagated to a majority of the nodes in the replica set. crowd-pleaser behaviour, honestly πnβ block until a write has been propagated to at leastnnodes in the replica set. you pick the number, you set the stakes π―
π¦ Application Performance Monitoring (APM) π¦
The MongoDB C Driver allows you to monitor all the MongoDB operations the driver executes. This event-notification system conforms to two MongoDB driver specs β and yes, both of them are iconic in their own way:
- Command Logging and Monitoring β events related to all application operations.
- SDAM Monitoring β events related to the driver's Server Discovery And Monitoring logic.
To start receiving notifications, it's a three-step little ritual, no cap:
- Create a
mongoc_apm_callbacks_twithmongoc_apm_callbacks_new() - Set callbacks on it
- Pass it to
mongoc_client_set_apm_callbacks()ormongoc_client_pool_set_apm_callbacks()
that's it. that's the whole thing. slay β¨
π Command-Monitoring Example π
file goes by example-command-monitoring.c and honestly she's a whole mood:
/* gcc example-command-monitoring.c -o example-command-monitoring \
* $(pkg-config --cflags --libs libmongoc-1.0) */
/* ./example-command-monitoring [CONNECTION_STRING] */
#include <mongoc/mongoc.h>
#include <stdio.h>
typedef struct {
int started;
int succeeded;
int failed;
} stats_t;
void
command_started(const mongoc_apm_command_started_t *event)
{
char *s;
s = bson_as_relaxed_extended_json(mongoc_apm_command_started_get_command(event), NULL);
printf("Command %s started on %s:\n%s\n\n",
mongoc_apm_command_started_get_command_name(event),
mongoc_apm_command_started_get_host(event)->host,
s);
((stats_t *)mongoc_apm_command_started_get_context(event))->started++;
bson_free(s);
}
void
command_succeeded(const mongoc_apm_command_succeeded_t *event)
{
char *s;
s = bson_as_relaxed_extended_json(mongoc_apm_command_succeeded_get_reply(event), NULL);
printf("Command %s succeeded:\n%s\n\n", mongoc_apm_command_succeeded_get_command_name(event), s);
((stats_t *)mongoc_apm_command_succeeded_get_context(event))->succeeded++;
bson_free(s);
}
void
command_failed(const mongoc_apm_command_failed_t *event)
{
bson_error_t error;
mongoc_apm_command_failed_get_error(event, &error);
printf("Command %s failed:\n\"%s\"\n\n", mongoc_apm_command_failed_get_command_name(event), error.message);
((stats_t *)mongoc_apm_command_failed_get_context(event))->failed++;
}
int
main(int argc, char *argv[])
{
mongoc_client_t *client;
mongoc_apm_callbacks_t *callbacks;
stats_t stats = {0};
mongoc_collection_t *collection;
bson_error_t error;
const char *uri_string = "mongodb://127.0.0.1/?appname=cmd-monitoring-example";
mongoc_uri_t *uri;
const char *collection_name = "test";
bson_t *docs[2];
mongoc_init();
if (argc > 1) {
uri_string = argv[1];
}
uri = mongoc_uri_new_with_error(uri_string, &error);
if (!uri) {
fprintf(stderr,
"failed to parse URI:\n"
"error message: %s\n",
error.message);
return EXIT_FAILURE;
}
client = mongoc_client_new_from_uri(uri);
if (!client) {
return EXIT_FAILURE;
}
mongoc_client_set_error_api(client, 2);
callbacks = mongoc_apm_callbacks_new();
mongoc_apm_set_command_started_cb(callbacks, command_started);
mongoc_apm_set_command_succeeded_cb(callbacks, command_succeeded);
mongoc_apm_set_command_failed_cb(callbacks, command_failed);
mongoc_client_set_apm_callbacks(client, callbacks, (void *)&stats /* context pointer */);
collection = mongoc_client_get_collection(client, "test", collection_name);
mongoc_collection_drop(collection, NULL);
docs[0] = BCON_NEW("_id", BCON_INT32(0));
docs[1] = BCON_NEW("_id", BCON_INT32(1));
mongoc_collection_insert_many(collection, (const bson_t **)docs, 2, NULL, NULL, NULL);
/* duplicate key error on the second insert */
mongoc_collection_insert_one(collection, docs[0], NULL, NULL, NULL);
mongoc_collection_destroy(collection);
mongoc_apm_callbacks_destroy(callbacks);
mongoc_uri_destroy(uri);
mongoc_client_destroy(client);
printf("started: %d\nsucceeded: %d\nfailed: %d\n", stats.started, stats.succeeded, stats.failed);
bson_destroy(docs[0]);
bson_destroy(docs[1]);
mongoc_cleanup();
return EXIT_SUCCESS;
}
and this example program prints β brace yourself, it's a lot of lore:
Command drop started on 127.0.0.1:
{ "drop" : "test" }
Command drop succeeded:
{ "ns" : "test.test", "nIndexesWas" : 1, "ok" : 1.0 }
Command insert started on 127.0.0.1:
{
"insert" : "test",
"ordered" : true,
"documents" : [
{ "_id" : 0 }, { "_id" : 1 }
]
}
Command insert succeeded:
{ "n" : 2, "ok" : 1.0 }
Command insert started on 127.0.0.1:
{
"insert" : "test",
"ordered" : true,
"documents" : [
{ "_id" : 0 }
]
}
Command insert succeeded:
{
"n" : 0,
"writeErrors" : [
{ "index" : 0, "code" : 11000, "errmsg" : "duplicate key" }
],
"ok" : 1.0
}
started: 3
succeeded: 3
failed: 0
heads up bestie: the output has been edited and formatted for clarity. Depending on your server configuration, messages may include metadata like database name, logical session ids, or cluster times that are not shown here. so don't come for me if yours looks extra π
and the plot twist you need to understand: the final "insert" command is considered successful, despite the writeError, because the server replied to the overall command with "ok": 1. read that twice. it's giving "technically a win" energy π
π SDAM Monitoring Example π
next up, example-sdam-monitoring.c β the server-discovery bestie:
/* gcc example-sdam-monitoring.c -o example-sdam-monitoring \
* $(pkg-config --cflags --libs libmongoc-1.0) */
/* ./example-sdam-monitoring [CONNECTION_STRING] */
#include <mongoc/mongoc.h>
#include <stdio.h>
typedef struct {
int server_changed_events;
int server_opening_events;
int server_closed_events;
int topology_changed_events;
int topology_opening_events;
int topology_closed_events;
int heartbeat_started_events;
int heartbeat_succeeded_events;
int heartbeat_failed_events;
} stats_t;
static void
server_changed(const mongoc_apm_server_changed_t *event)
{
stats_t *context;
const mongoc_server_description_t *prev_sd, *new_sd;
context = (stats_t *)mongoc_apm_server_changed_get_context(event);
context->server_changed_events++;
prev_sd = mongoc_apm_server_changed_get_previous_description(event);
new_sd = mongoc_apm_server_changed_get_new_description(event);
printf("server changed: %s %s -> %s\n",
mongoc_apm_server_changed_get_host(event)->host_and_port,
mongoc_server_description_type(prev_sd),
mongoc_server_description_type(new_sd));
}
static void
server_opening(const mongoc_apm_server_opening_t *event)
{
stats_t *context;
context = (stats_t *)mongoc_apm_server_opening_get_context(event);
context->server_opening_events++;
printf("server opening: %s\n", mongoc_apm_server_opening_get_host(event)->host_and_port);
}
static void
server_closed(const mongoc_apm_server_closed_t *event)
{
stats_t *context;
context = (stats_t *)mongoc_apm_server_closed_get_context(event);
context->server_closed_events++;
printf("server closed: %s\n", mongoc_apm_server_closed_get_host(event)->host_and_port);
}
okay bestie, buckle up, we're deep in the code mines now π this is the rest of it and honestly it's giving main character energy β every callback, every print, verbatim, no cap:
static void
topology_changed(const mongoc_apm_topology_changed_t *event)
{
stats_t *context;
const mongoc_topology_description_t *prev_td;
const mongoc_topology_description_t *new_td;
mongoc_server_description_t **prev_sds;
size_t n_prev_sds;
mongoc_server_description_t **new_sds;
size_t n_new_sds;
size_t i;
mongoc_read_prefs_t *prefs;
context = (stats_t *)mongoc_apm_topology_changed_get_context(event);
context->topology_changed_events++;
prev_td = mongoc_apm_topology_changed_get_previous_description(event);
prev_sds = mongoc_topology_description_get_servers(prev_td, &n_prev_sds);
new_td = mongoc_apm_topology_changed_get_new_description(event);
new_sds = mongoc_topology_description_get_servers(new_td, &n_new_sds);
printf("topology changed: %s -> %s\n",
mongoc_topology_description_type(prev_td),
mongoc_topology_description_type(new_td));
if (n_prev_sds) {
printf(" previous servers:\n");
for (i = 0; i < n_prev_sds; i++) {
printf(" %s %s\n",
mongoc_server_description_type(prev_sds[i]),
mongoc_server_description_host(prev_sds[i])->host_and_port);
}
}
if (n_new_sds) {
printf(" new servers:\n");
for (i = 0; i < n_new_sds; i++) {
printf(" %s %s\n",
mongoc_server_description_type(new_sds[i]),
mongoc_server_description_host(new_sds[i])->host_and_port);
}
}
prefs = mongoc_read_prefs_new(MONGOC_READ_SECONDARY);
if (mongoc_topology_description_has_readable_server(new_td, prefs)) {
printf(" secondary AVAILABLE\n");
} else {
printf(" secondary UNAVAILABLE\n");
}
if (mongoc_topology_description_has_writable_server(new_td)) {
printf(" primary AVAILABLE\n");
} else {
printf(" primary UNAVAILABLE\n");
}
mongoc_read_prefs_destroy(prefs);
mongoc_server_descriptions_destroy_all(prev_sds, n_prev_sds);
mongoc_server_descriptions_destroy_all(new_sds, n_new_sds);
}
the way this one prints the old topology β the new topology AND dumps both server listsβ¦ obsessed β¨ and notice it checks for a readable secondary with MONGOC_READ_SECONDARY prefs and a writable server separately, then cleans up prefs and both server-description arrays. no leaks, we stan a hygienic icon π§Ό
static void
topology_opening(const mongoc_apm_topology_opening_t *event)
{
stats_t *context;
context = (stats_t *)mongoc_apm_topology_opening_get_context(event);
context->topology_opening_events++;
printf("topology opening\n");
}
static void
topology_closed(const mongoc_apm_topology_closed_t *event)
{
stats_t *context;
context = (stats_t *)mongoc_apm_topology_closed_get_context(event);
context->topology_closed_events++;
printf("topology closed\n");
}
tiny and adorable, just bump the counter and say hi π
static void
server_heartbeat_started(const mongoc_apm_server_heartbeat_started_t *event)
{
stats_t *context;
context = (stats_t *)mongoc_apm_server_heartbeat_started_get_context(event);
context->heartbeat_started_events++;
printf("%s heartbeat started\n", mongoc_apm_server_heartbeat_started_get_host(event)->host_and_port);
}
static void
server_heartbeat_succeeded(const mongoc_apm_server_heartbeat_succeeded_t *event)
{
stats_t *context;
char *reply;
context = (stats_t *)mongoc_apm_server_heartbeat_succeeded_get_context(event);
context->heartbeat_succeeded_events++;
reply = bson_as_canonical_extended_json(mongoc_apm_server_heartbeat_succeeded_get_reply(event), NULL);
printf("%s heartbeat succeeded: %s\n", mongoc_apm_server_heartbeat_succeeded_get_host(event)->host_and_port, reply);
bson_free(reply);
}
static void
server_heartbeat_failed(const mongoc_apm_server_heartbeat_failed_t *event)
{
stats_t *context;
bson_error_t error;
context = (stats_t *)mongoc_apm_server_heartbeat_failed_get_context(event);
context->heartbeat_failed_events++;
mongoc_apm_server_heartbeat_failed_get_error(event, &error);
printf(
"%s heartbeat failed: %s\n", mongoc_apm_server_heartbeat_failed_get_host(event)->host_and_port, error.message);
}
the succeeded one literally stringifies the reply with bson_as_canonical_extended_json and then bson_frees it β memory etiquette, periodt π
and the failed one pulls the bson_error_t out and prints error.message. iconic. (also: server_heartbeat_failed is π but in a good way)
and now the big one, main β the whole vibe check:
int
main(int argc, char *argv[])
{
mongoc_client_t *client;
mongoc_apm_callbacks_t *cbs;
stats_t stats = {0};
const char *uri_string = "mongodb://127.0.0.1/?appname=sdam-monitoring-example";
mongoc_uri_t *uri;
bson_t cmd = BSON_INITIALIZER;
bson_t reply;
bson_error_t error;
mongoc_init();
if (argc > 1) {
uri_string = argv[1];
}
uri = mongoc_uri_new_with_error(uri_string, &error);
if (!uri) {
fprintf(stderr,
"failed to parse URI:\n"
"error message: %s\n",
error.message);
return EXIT_FAILURE;
}
client = mongoc_client_new_from_uri(uri);
if (!client) {
return EXIT_FAILURE;
}
mongoc_client_set_error_api(client, 2);
cbs = mongoc_apm_callbacks_new();
mongoc_apm_set_server_changed_cb(cbs, server_changed);
mongoc_apm_set_server_opening_cb(cbs, server_opening);
mongoc_apm_set_server_closed_cb(cbs, server_closed);
mongoc_apm_set_topology_changed_cb(cbs, topology_changed);
mongoc_apm_set_topology_opening_cb(cbs, topology_opening);
mongoc_apm_set_topology_closed_cb(cbs, topology_closed);
mongoc_apm_set_server_heartbeat_started_cb(cbs, server_heartbeat_started);
mongoc_apm_set_server_heartbeat_succeeded_cb(cbs, server_heartbeat_succeeded);
mongoc_apm_set_server_heartbeat_failed_cb(cbs, server_heartbeat_failed);
mongoc_client_set_apm_callbacks(client, cbs, (void *)&stats /* context pointer */);
/* the driver connects on demand to perform first operation */
BSON_APPEND_INT32(&cmd, "buildinfo", 1);
mongoc_client_command_simple(client, "admin", &cmd, NULL, &reply, &error);
mongoc_uri_destroy(uri);
mongoc_client_destroy(client);
printf("Events:\n"
" server changed: %d\n"
" server opening: %d\n"
" server closed: %d\n"
" topology changed: %d\n"
" topology opening: %d\n"
" topology closed: %d\n"
" heartbeat started: %d\n"
" heartbeat succeeded: %d\n"
" heartbeat failed: %d\n",
stats.server_changed_events,
stats.server_opening_events,
stats.server_closed_events,
stats.topology_changed_events,
stats.topology_opening_events,
stats.topology_closed_events,
stats.heartbeat_started_events,
stats.heartbeat_succeeded_events,
stats.heartbeat_failed_events);
bson_destroy(&cmd);
bson_destroy(&reply);
mongoc_apm_callbacks_destroy(cbs);
mongoc_cleanup();
return EXIT_SUCCESS;
}
so many little details here bestie, don't skip 'em:
- default
uri_stringis"mongodb://127.0.0.1/?appname=sdam-monitoring-example"and you can override it withargv[1]ifargc > 1 - URI parse fail β
fprintftostderrwith the"failed to parse URI:\n"/"error message: %s\n"lines andreturn EXIT_FAILURE mongoc_client_new_from_urifail β plainreturn EXIT_FAILUREmongoc_client_set_error_api(client, 2)β yes, error API version2- all nine
mongoc_apm_set_*_cbcallbacks get registered, and the context pointer passed tomongoc_client_set_apm_callbacksis(void *)&stats - then the
/* the driver connects on demand to perform first operation */comment,BSON_APPEND_INT32(&cmd, "buildinfo", 1)andmongoc_client_command_simple(client, "admin", &cmd, NULL, &reply, &error) - cleanup is
mongoc_uri_destroy,mongoc_client_destroy,bson_destroy(&cmd),bson_destroy(&reply),mongoc_apm_callbacks_destroy(cbs),mongoc_cleanup(), and it exitsEXIT_SUCCESSπ«Ά
β THE LIL RUNBOOK SECTION β
Start a 3-node replica set on localhost with set name "rs" and start the program:
./example-sdam-monitoring "mongodb://localhost:27017,localhost:27018/?replicaSet=rs"
This example program prints something like:
topology opening
topology changed: Unknown -> ReplicaSetNoPrimary
secondary UNAVAILABLE
primary UNAVAILABLE
server opening: localhost:27017
server opening: localhost:27018
localhost:27017 heartbeat started
localhost:27018 heartbeat started
localhost:27017 heartbeat succeeded: { ... reply ... }
server changed: localhost:27017 Unknown -> RSPrimary
server opening: localhost:27019
topology changed: ReplicaSetNoPrimary -> ReplicaSetWithPrimary
new servers:
RSPrimary localhost:27017
secondary UNAVAILABLE
primary AVAILABLE
localhost:27019 heartbeat started
localhost:27018 heartbeat succeeded: { ... reply ... }
server changed: localhost:27018 Unknown -> RSSecondary
topology changed: ReplicaSetWithPrimary -> ReplicaSetWithPrimary
previous servers:
RSPrimary localhost:27017
new servers:
RSPrimary localhost:27017
RSSecondary localhost:27018
secondary AVAILABLE
primary AVAILABLE
localhost:27019 heartbeat succeeded: { ... reply ... }
server changed: localhost:27019 Unknown -> RSSecondary
topology changed: ReplicaSetWithPrimary -> ReplicaSetWithPrimary
previous servers:
RSPrimary localhost:27017
RSSecondary localhost:27018
new servers:
RSPrimary localhost:27017
RSSecondary localhost:27018
RSSecondary localhost:27019
secondary AVAILABLE
primary AVAILABLE
topology closed
Events:
server changed: 3
server opening: 3
server closed: 0
topology changed: 4
topology opening: 1
topology closed: 1
heartbeat started: 3
heartbeat succeeded: 3
heartbeat failed: 0
The driver connects to the mongods on ports 27017 and 27018, which were specified in the URI, and determines which is primary. It also discovers the third member, "localhost:27019", and adds it to the topology.
likeβ¦ it just found the third one?? on its own?? the topology discovery ate and left no crumbs π₯
π AUTHOR
MongoDB, Inc
π COPYRIGHT
2009-present, MongoDB, Inc.
and that's the whole thing, bestie β you made it to the end of the page ππ this wasMONGOC_REFERENCE(3), version2.5.6, datedOct 09, 2026. go forth and monitor some topologies, you gorgeous little sysadmin π¦β¨
π¦ where this page comes from
- epel 10: mongo-c-driver-devel 2.5.6-1.el10_3 Β· Apache-2.0 AND ISC AND MIT AND Zlib
- π girlified by deepseek-ai/DeepSeek-V4.1-Flash-1017 on : a language model's rewrite of the original, under its terms