# mongoc_client_session_with_transaction()

## Synopsis

    bool
    mongoc_client_session_with_transaction (mongoc_client_session_t *session,
                                            mongoc_client_session_with_transaction_cb_t cb,
                                            const mongoc_transaction_opt_t *opts,
                                            void *ctx,
                                            bson_t *reply,
                                            bson_error_t *error);

This method will start a new transaction on `session`, run `cb`, and then commit the transaction. If it cannot commit the transaction, the entire sequence may be retried, and `cb` may be run multiple times. `ctx` will be passed to `cb` each time it is called.

This method has an internal time limit of 120 seconds, and will retry until that time limit is reached. This timeout is not configurable.

`cb` should not attempt to start new transactions, but should simply run operations meant to be contained within a transaction. The `cb` does not need to commit transactions; this is handled by the mongoc_client_session_with_transaction(). If `cb` does commit or abort a transaction, however, this method will return without taking further action.

The parameter `reply` is initialized even upon failure to simplify memory management.

## Parameters

- `session`: A mongoc_client_session_t.
- `cb`: A mongoc_client_session_with_transaction_cb_t callback, which will run inside of a new transaction on the session. See example below.
- `opts`: An optional mongoc_transaction_opt_t.
- `ctx`: A `void*`. This user-provided data will be passed to `cb`.
- `reply`: An optional location to initialize a `bson_t` or `NULL`. This should be on the stack.
- `error`: An optional location for a `bson_error_t` or `NULL`.

## Return

Returns `true` if the transaction was completed successfully. Otherwise, returns `false` in case of failure. In cases of failure `error` will also be set, except if the passed-in `cb` fails without setting `error`. If a non-NULL `reply` is passed in, `reply` will be set to the value of the last server response, except if the passed-in `cb` fails without setting a `reply`.

## Example

Use with_transaction() to run a callback within a transaction

    /* gcc example-with-transaction-cb.c -o example-with-transaction-cb $(pkg-config
     * --cflags --libs libmongoc-1.0) */

    /* ./example-with-transaction-cb [CONNECTION_STRING] */

    #include <mongoc/mongoc.h>
    #include <stdio.h>
    #include <stdlib.h>

    /*
     * We pass this context object to mongoc_client_session_with_transaction() along
     * with our callback function. The context object will be passed to our callback
     * function when it runs, so we can access it.
     */
    typedef struct {
       mongoc_collection_t *collection;
       bson_t *insert_opts;
    } ctx_t;

    /*
     * We pass this method as the callback to
     * mongoc_client_session_with_transaction(). The insert that this method
     * performs will happen inside of a new transaction.
     */
    bool
    create_and_insert_doc (mongoc_client_session_t *session,
                           void *ctx,
                           bson_t **reply, /* out param for our server reply */
                           bson_error_t *error)
    {
       /*
        * mongoc_collection_insert_one requires an uninitialized, stack-allocated
        * bson_t to receive the update result
        */
       bson_t local_reply;
       bson_t *doc = NULL;
       ctx_t *data = NULL;
       bool retval;

       /*
        * Create a new bson document - { id: 1 }
        */
       doc = BCON_NEW ("_id", BCON_INT32 (1));

       printf (
          "Running the user-defined callback in a newly created transaction...\n");
       data = (ctx_t *) ctx;
       retval = mongoc_collection_insert_one (
          data->collection, doc, data->insert_opts, &local_reply, error);

       /*
        * To return to the mongoc_client_session_with_transaction() method, set
        * *reply to a new copy of our local_reply before destroying it.
        */
       *reply = bson_copy (&local_reply);
       bson_destroy (&local_reply);

       bson_destroy (doc);
       return retval;
    }

    int
    main (int argc, char *argv[])
    {
       int exit_code = EXIT_FAILURE;

       mongoc_uri_t *uri = NULL;
       const char *uri_string = "mongodb://127.0.0.1/?appname=with-txn-cb-example";
       mongoc_client_t *client = NULL;
       mongoc_database_t *database = NULL;
       mongoc_collection_t *collection = NULL;
       mongoc_client_session_t *session = NULL;
       bson_t *insert_opts = NULL;
       bson_t reply;
       ctx_t ctx;
       char *str;
       bson_error_t error;

       /*
        * Required to initialize libmongoc's internals
        */
       mongoc_init ();

       /*
        * Optionally get MongoDB URI from command line
        */
       if (argc > 1) {
          uri_string = argv[1];
       }

       /*
        * Safely create a MongoDB URI object from the given string
        */
       uri = mongoc_uri_new_with_error (uri_string, &error);
       if (!uri) {
          MONGOC_ERROR ("failed to parse URI: %s\n"
                        "error message:       %s\n",
                        uri_string,
                        error.message);
          goto done;
       }

       /*
        * Create a new client instance
        */
       client = mongoc_client_new_from_uri (uri);
       if (!client) {
          goto done;
       }

       mongoc_client_set_error_api (client, 2);

       /*
        * Get a handle on the database "example-with-txn-cb"
        */
       database = mongoc_client_get_database (client, "example-with-txn-cb");

       /*
        * 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 {
             MONGOC_ERROR ("Failed to create collection: %s", error.message);
             goto done;
          }
       }

       /*
        * Pass NULL for options - by default the session is causally consistent
        */
       session = mongoc_client_start_session (client, NULL, &error);
       if (!session) {
          MONGOC_ERROR ("Failed to start session: %s", error.message);
          goto done;
       }

       /*
        * Append a logical session id to command options
        */
       insert_opts = bson_new ();
       if (!mongoc_client_session_append (session, insert_opts, &error)) {
          MONGOC_ERROR ("Could not add session to opts: %s", error.message);
          goto done;
       }

       ctx.collection = collection;
       ctx.insert_opts = insert_opts;

       /*
        * This method will start a new transaction on session, run our callback
        * function, i.e., &create_and_insert_doc, passing &ctx as an argument and
        * commit the transaction.
        */
       if (!mongoc_client_session_with_transaction (
              session, &create_and_insert_doc, NULL, &ctx, &reply, &error)) {
          MONGOC_ERROR ("Insert failed: %s", error.message);
          goto done;
       }

       str = bson_as_json (&reply, NULL);
       printf ("%s\n", str);

       exit_code = EXIT_SUCCESS;

    done:
       bson_free (str);
       bson_destroy (&reply);
       bson_destroy (insert_opts);
       mongoc_client_session_destroy (session);
       mongoc_collection_destroy (collection);
       mongoc_database_destroy (database);
       mongoc_client_destroy (client);
       mongoc_uri_destroy (uri);

       mongoc_cleanup ();

       return exit_code;
    }