void mongoc_change_stream_destroy (mongoc_change_stream_t *stream) { BSON_ASSERT (stream); bson_destroy (&stream->pipeline_to_append); bson_destroy (&stream->full_document); bson_destroy (&stream->opts); bson_destroy (&stream->resume_token); bson_destroy (&stream->err_doc); if (stream->cursor) { mongoc_cursor_destroy (stream->cursor); } if (stream->implicit_session) { mongoc_client_session_destroy (stream->implicit_session); } mongoc_collection_destroy (stream->coll); bson_free (stream); }
void mongoc_change_stream_destroy (mongoc_change_stream_t *stream) { if (!stream) { return; } bson_destroy (&stream->pipeline_to_append); bson_destroy (&stream->resume_token); bson_destroy (stream->full_document); bson_destroy (&stream->err_doc); _mongoc_change_stream_opts_cleanup (&stream->opts); mongoc_cursor_destroy (stream->cursor); mongoc_client_session_destroy (stream->implicit_session); mongoc_read_prefs_destroy (stream->read_prefs); mongoc_read_concern_destroy (stream->read_concern); bson_free (stream); }
static void test_with_transaction_timeout (void *ctx) { mongoc_client_t *client; mongoc_client_session_t *session; bson_error_t error; bool res; client = test_framework_client_new (); session = mongoc_client_start_session (client, NULL, &error); ASSERT_OR_PRINT (session, error); session->with_txn_timeout_ms = 10; /* Test Case 1: Test that if the callback returns an error with the TransientTransactionError label and we have exceeded the timeout, withTransaction fails. */ res = mongoc_client_session_with_transaction ( session, with_transaction_fail_transient_txn, NULL, NULL, &error); ASSERT (!res); /* Test Case 2: If committing returns an error with the UnknownTransactionCommitResult label and we have exceeded the timeout, withTransaction fails. */ session->fail_commit_label = UNKNOWN_COMMIT_RESULT; res = mongoc_client_session_with_transaction ( session, with_transaction_do_nothing, NULL, NULL, &error); ASSERT (!res); /* Test Case 3: If committing returns an error with the TransientTransactionError label and we have exceeded the timeout, withTransaction fails. */ session->fail_commit_label = TRANSIENT_TXN_ERR; res = mongoc_client_session_with_transaction ( session, with_transaction_do_nothing, NULL, NULL, &error); ASSERT (!res); mongoc_client_session_destroy (session); mongoc_client_destroy (client); }
int main (int argc, char *argv[]) { int exit_code = EXIT_FAILURE; mongoc_client_t *client; mongoc_client_session_t *client_session = NULL; mongoc_collection_t *collection = NULL; const char *uristr = "mongodb://127.0.0.1/?appname=session-example"; 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) { uristr = argv[1]; } client = mongoc_client_new (uristr); if (!client) { fprintf (stderr, "Failed to parse URI.\n"); 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); bson_destroy (selector); mongoc_read_prefs_destroy (secondary); bson_destroy (find_opts); while (mongoc_cursor_next (cursor, &doc)) { str = bson_as_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 (client) { mongoc_client_destroy (client); } mongoc_cleanup (); return exit_code; }
bool mongoc_change_stream_next (mongoc_change_stream_t *stream, const bson_t **bson) { bson_iter_t iter; bool ret = false; BSON_ASSERT (stream); BSON_ASSERT (bson); if (stream->err.code != 0) { goto end; } BSON_ASSERT (stream->cursor); if (!mongoc_cursor_next (stream->cursor, bson)) { const bson_t *err_doc; bson_error_t err; bool resumable = false; if (!mongoc_cursor_error_document (stream->cursor, &err, &err_doc)) { /* no error occurred, just no documents left. */ goto end; } resumable = _is_resumable_error (err_doc); if (resumable) { /* recreate the cursor. */ mongoc_cursor_destroy (stream->cursor); stream->cursor = NULL; if (!_make_cursor (stream)) { goto end; } if (!mongoc_cursor_next (stream->cursor, bson)) { resumable = !mongoc_cursor_error_document (stream->cursor, &err, &err_doc); if (resumable) { /* empty batch. */ goto end; } } } if (!resumable) { stream->err = err; bson_destroy (&stream->err_doc); bson_copy_to (err_doc, &stream->err_doc); goto end; } } /* we have received documents, either from the first call to next or after a * resume. */ if (!bson_iter_init_find (&iter, *bson, "_id")) { bson_set_error (&stream->err, MONGOC_ERROR_CURSOR, MONGOC_ERROR_CHANGE_STREAM_NO_RESUME_TOKEN, "Cannot provide resume functionality when the resume " "token is missing"); goto end; } /* copy the resume token. */ bson_reinit (&stream->resume_token); BSON_APPEND_VALUE ( &stream->resume_token, "resumeAfter", bson_iter_value (&iter)); /* clear out the operation time, since we no longer need it to resume. */ _mongoc_timestamp_clear (&stream->operation_time); ret = true; end: /* Driver Sessions Spec: "When an implicit session is associated with a * cursor for use with getMore operations, the session MUST be returned to * the pool immediately following a getMore operation that indicates that the * cursor has been exhausted." */ if (stream->implicit_session) { /* if creating the change stream cursor errored, it may be null. */ if (!stream->cursor || stream->cursor->cursor_id == 0) { mongoc_client_session_destroy (stream->implicit_session); stream->implicit_session = NULL; } } return ret; }