Esempio n. 1
0
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);
}
Esempio n. 4
0
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;
}