static bool txn_abort (mongoc_client_session_t *session, bson_t *reply, bson_error_t *error) { bson_t cmd = BSON_INITIALIZER; bson_t opts = BSON_INITIALIZER; bson_error_t err_local; bson_error_t *err_ptr = error ? error : &err_local; bson_t reply_local = BSON_INITIALIZER; mongoc_write_err_type_t error_type; bool r = false; _mongoc_bson_init_if_set (reply); if (!mongoc_client_session_append (session, &opts, err_ptr)) { GOTO (done); } if (session->txn.opts.write_concern) { if (!mongoc_write_concern_append (session->txn.opts.write_concern, &opts)) { bson_set_error (err_ptr, MONGOC_ERROR_TRANSACTION, MONGOC_ERROR_TRANSACTION_INVALID_STATE, "Invalid transaction write concern"); GOTO (done); } } BSON_APPEND_INT32 (&cmd, "abortTransaction", 1); /* will be reinitialized by mongoc_client_write_command_with_opts */ bson_destroy (&reply_local); r = mongoc_client_write_command_with_opts ( session->client, "admin", &cmd, &opts, &reply_local, err_ptr); /* Transactions Spec: "Drivers MUST retry the commitTransaction command once * after it fails with a retryable error", same for abort */ error_type = _mongoc_write_error_get_type (r, err_ptr, &reply_local); if (error_type == MONGOC_WRITE_ERR_RETRY) { bson_destroy (&reply_local); r = mongoc_client_write_command_with_opts ( session->client, "admin", &cmd, &opts, &reply_local, err_ptr); } if (!r) { /* we won't return an error from abortTransaction, so warn */ MONGOC_WARNING ("Error in abortTransaction: %s", err_ptr->message); } done: bson_destroy (&reply_local); bson_destroy (&cmd); bson_destroy (&opts); return r; }
static bool txn_finish (mongoc_client_session_t *session, mongoc_txn_intent_t intent, bson_t *reply, bson_error_t *error) { const char *cmd_name; bson_t cmd = BSON_INITIALIZER; bson_t opts = BSON_INITIALIZER; bson_error_t err_local; bson_error_t *err_ptr = error ? error : &err_local; bson_t reply_local = BSON_INITIALIZER; mongoc_write_err_type_t error_type; bool r = false; _mongoc_bson_init_if_set (reply); cmd_name = (intent == TXN_COMMIT ? "commitTransaction" : "abortTransaction"); if (!mongoc_client_session_append (session, &opts, err_ptr)) { GOTO (done); } if (session->txn.opts.write_concern) { if (!mongoc_write_concern_append (session->txn.opts.write_concern, &opts)) { bson_set_error (err_ptr, MONGOC_ERROR_TRANSACTION, MONGOC_ERROR_TRANSACTION_INVALID_STATE, "Invalid transaction write concern"); GOTO (done); } } BSON_APPEND_INT32 (&cmd, cmd_name, 1); /* will be reinitialized by mongoc_client_write_command_with_opts */ bson_destroy (&reply_local); r = mongoc_client_write_command_with_opts ( session->client, "admin", &cmd, &opts, &reply_local, err_ptr); /* Transactions Spec: "Drivers MUST retry the commitTransaction command once * after it fails with a retryable error", same for abort */ error_type = _mongoc_write_error_get_type (r, err_ptr, &reply_local); if (error_type == MONGOC_WRITE_ERR_RETRY) { bson_destroy (&reply_local); r = mongoc_client_write_command_with_opts ( session->client, "admin", &cmd, &opts, &reply_local, err_ptr); error_type = _mongoc_write_error_get_type (r, err_ptr, &reply_local); } /* Transactions Spec: "add the UnknownTransactionCommitResult error label * when commitTransaction fails with a network error, server selection * error, or write concern failed / timeout." */ if (intent == TXN_COMMIT && reply) { if ((!r && err_ptr->domain == MONGOC_ERROR_SERVER_SELECTION) || error_type == MONGOC_WRITE_ERR_RETRY || error_type == MONGOC_WRITE_ERR_WRITE_CONCERN) { bson_copy_to_excluding_noinit ( &reply_local, reply, "errorLabels", NULL); copy_labels_plus_unknown_commit_result (&reply_local, reply); } else { /* maintain invariants: reply & reply_local are valid until the end */ bson_destroy (reply); bson_steal (reply, &reply_local); bson_init (&reply_local); } } else if (intent == TXN_ABORT && !r) { /* we won't return an error from abortTransaction, so warn */ MONGOC_WARNING ("Error in %s: %s", cmd_name, err_ptr->message); } done: bson_destroy (&reply_local); bson_destroy (&cmd); bson_destroy (&opts); return r; }
static bool txn_commit (mongoc_client_session_t *session, bool explicitly_retrying, bson_t *reply, bson_error_t *error) { bson_t cmd = BSON_INITIALIZER; bson_t opts = BSON_INITIALIZER; bson_error_t err_local; bson_error_t *err_ptr = error ? error : &err_local; bson_t reply_local = BSON_INITIALIZER; mongoc_write_err_type_t error_type; bool r = false; bool retrying_after_error = false; mongoc_write_concern_t *retry_wc = NULL; _mongoc_bson_init_if_set (reply); BSON_APPEND_INT32 (&cmd, "commitTransaction", 1); retry: if (!mongoc_client_session_append (session, &opts, err_ptr)) { GOTO (done); } /* Transactions Spec: "When commitTransaction is retried, either by the * driver's internal retry-once logic or explicitly by the user calling * commitTransaction again, drivers MUST apply w:majority to the write * concern of the commitTransaction command." */ if (!retry_wc && (retrying_after_error || explicitly_retrying)) { retry_wc = create_commit_retry_wc (session->txn.opts.write_concern ? session->txn.opts.write_concern : session->client->write_concern); } if (retry_wc || session->txn.opts.write_concern) { if (!mongoc_write_concern_append ( retry_wc ? retry_wc : session->txn.opts.write_concern, &opts)) { bson_set_error (err_ptr, MONGOC_ERROR_TRANSACTION, MONGOC_ERROR_TRANSACTION_INVALID_STATE, "Invalid transaction write concern"); GOTO (done); } } /* will be reinitialized by mongoc_client_write_command_with_opts */ bson_destroy (&reply_local); r = mongoc_client_write_command_with_opts ( session->client, "admin", &cmd, &opts, &reply_local, err_ptr); /* Transactions Spec: "Drivers MUST retry the commitTransaction command once * after it fails with a retryable error", same for abort */ error_type = _mongoc_write_error_get_type (r, err_ptr, &reply_local); if (!retrying_after_error && error_type == MONGOC_WRITE_ERR_RETRY) { retrying_after_error = true; /* retry after error only once */ bson_reinit (&opts); GOTO (retry); } /* Transactions Spec: "add the UnknownTransactionCommitResult error label * when commitTransaction fails with a network error, server selection * error, or write concern failed / timeout." */ if (reply) { if ((!r && err_ptr->domain == MONGOC_ERROR_SERVER_SELECTION) || error_type == MONGOC_WRITE_ERR_RETRY || error_type == MONGOC_WRITE_ERR_WRITE_CONCERN) { bson_copy_to_excluding_noinit ( &reply_local, reply, "errorLabels", NULL); copy_labels_plus_unknown_commit_result (&reply_local, reply); } else { /* maintain invariants: reply & reply_local are valid until the end */ bson_destroy (reply); bson_steal (reply, &reply_local); bson_init (&reply_local); } } done: bson_destroy (&reply_local); bson_destroy (&cmd); bson_destroy (&opts); if (retry_wc) { mongoc_write_concern_destroy (retry_wc); } return r; }