laravel-mongodb icon indicating copy to clipboard operation
laravel-mongodb copied to clipboard

Database Transactions not working

Open klferreira opened this issue 8 years ago • 40 comments

Hello everyone, i stumbled on this issue today:

When i try to use laravel's transactions like this

DB::transaction(function() use($data) {
        $this->repository->create($data);
});

I get the following:

Call to a member function beginTransaction() on null
/vendor/laravel/framework/src/Illuminate/Database/Concerns/ManagesTransactions.php:108
/vendor/laravel/framework/src/Illuminate/Database/Concerns/ManagesTransactions.php:92
/vendor/laravel/framework/src/Illuminate/Database/Concerns/ManagesTransactions.php:23
/vendor/laravel/framework/src/Illuminate/Database/DatabaseManager.php:327
/vendor/laravel/framework/src/Illuminate/Support/Facades/Facade.php:221

I'm using laravel 5.4 and php version 7.1.

Is there any workaround for this issue?

This package is helping me out a lot with my personal project and i guess there is no other option for laravel and mongodb, so anything that solves it will do for me i guess.

klferreira avatar Oct 14 '17 16:10 klferreira

I'm having the same issue and can't seem to find a solution.

jim5359 avatar Oct 23 '17 02:10 jim5359

Mongodb not support transactions. It has another logic for this. Check https://docs.mongodb.com/manual/tutorial/perform-two-phase-commits/

upsilon-den avatar Oct 24 '17 12:10 upsilon-den

MongoDB 4.0 now supports multi-document transactions.

@jenssegers Any chance to have transactions working here? Let me know if you need help.

fntneves avatar Jul 04 '18 22:07 fntneves

Any update on this @jenssegers ?

coolsam726 avatar Aug 24 '18 08:08 coolsam726

This would be another excellent feature of the package!

eleazarbr avatar Sep 03 '18 18:09 eleazarbr

Any update on this @jenssegers ?

deyikong avatar Dec 27 '18 18:12 deyikong

use DB::connection('mongodb')->getMongoClient() to access original MongoDB\Driver and use DB::connection('mongodb')->getMongoClient()->startSession() to create a session and then start a transaction.

ycgambo avatar Jan 07 '19 09:01 ycgambo

DB::connection('mongodb')->getMongoClient()->startSession() does not work, even i use getDatabase method. but dont have this function.

got any idea two collection if got error, rollback the data.

leong918 avatar Jan 18 '19 11:01 leong918

mongo transaction is a little different from mysql. you need to create a session first, and pass this session as a option when you do update.

ycgambo avatar Jan 18 '19 23:01 ycgambo

@ycgambo How to create a session and pass the session as option by using laravel-mongodb ?

leong918 avatar Jan 22 '19 15:01 leong918

I got it working like this

$session = DB::getMongoClient()->startSession();
$session->startTransaction();
try {
    // Perform actions.
    $session->commitTransaction();
} catch(Exception $e) {
    $session->abortTransaction();
}

As answered here

salalaslam avatar Mar 21 '19 08:03 salalaslam

I got it working like this

$session = DB::getMongoClient()->startSession();
$session->startTransaction();
try {
    // Perform actions.
    $session->commitTransaction();
} catch(Exception $e) {
    $session->abortTransaction();
}

As answered here

it's not working for me is any ideal ,i was wrong?

here is my code

$book = $this->model->find(1);
$session = DB::getMongoClient()->startSession();
$session->startTransaction();
try {
    $this->repository->updateBook($book, $attributes);
    //testing when error
    throw \Exception('db error testing');
    // Perform actions.
    $session->commitTransaction();
} catch(Exception $e) {
    $session->abortTransaction();
    dd($e->getMessage());
}

Repository
public function updateBook($book, array $attributes)
{
    return $book->update($attributes);
}

it response 'db error testing' string, but my mongoDB didn't rollback.

thanks a lot

leoku7020 avatar Apr 10 '19 04:04 leoku7020

pass this session as a option when you do update.

$session = MongoDB::startSession();
$session->startTransaction();
try {
    Player::document()->update($updates, ['session' => $session]);
    $session->commitTransaction();
    return true;
} catch (\Exception $e) {
    $session->abortTransaction();
    return false;
}

ycgambo avatar Apr 10 '19 05:04 ycgambo

From the MongoDB documentation

To associate read and write operations with a transaction, you must pass the session to each operation in the transaction. For examples, see Transactions in Applications.

This is needed to be done internally by the author.

salalaslam avatar Apr 10 '19 05:04 salalaslam

pass this session as a option when you do update.

$session = MongoDB::startSession();
$session->startTransaction();
try {
    Player::document()->update($updates, ['session' => $session]);
    $session->commitTransaction();
    return true;
} catch (\Exception $e) {
    $session->abortTransaction();
    return false;
}

@salalaslam @ycgambo Thanks for replying,But It still not work My MongoDB did not rollback how can i fix it ?

here is my code

        $book = $this->model->find(1);
        $session = DB::getMongoClient()->startSession();
        $session->startTransaction();
        try {
            //update bookmark
            $result = $this->repository->updateBook($book, $attributes, $session);
            throw new \Exception('db error');
            $session->commitTransaction();
        } catch (\Exception $e) {
            $session->abortTransaction();
            dd($e->getMessage());
        }

        Repository
        public function updateBook($book, array $attributes, $session)
        {
            return $book->update($attributes, ['session' => $session]);
        }

leoku7020 avatar Apr 10 '19 07:04 leoku7020

The same problem with my transaction is not supported @leoku7020 Did you find any solution for that?

rahamanh939 avatar May 31 '19 06:05 rahamanh939

@rahamanh939 Sry , I don't have any idea

leoku7020 avatar May 31 '19 07:05 leoku7020

@leoku7020 you should do update on the mongo document object (not the model)

ycgambo avatar Jun 01 '19 00:06 ycgambo

@ycgambo Can you give us an example.

rahamanh939 avatar Jun 03 '19 05:06 rahamanh939

$mongoClient = DB::connection('mongodb')->getMongoClient(); $session = $mongoClient->startSession(); try { $mongoClient->{data_base_name}->{collection_name}->deleteMany(['type' => $this->service::ROUND_NUMBER_SIMILAR]); $mongoClient->{data_base_name}->{collection_name}->insertMany($roundNumbers); $session->commitTransaction(); } catch (\Exception $e) { $session->abortTransaction(); $success = false; }

somayeh-bajelan avatar Jun 09 '19 07:06 somayeh-bajelan

Hallo, im have a problem with abortTransaction if insert 2 table

$session = DB::connection('mongodb')->getMongoClient()->startSession();
$session->startTransaction();
try {
    DB::connection('mongodb')->collection('table1')->insert($data1);
    DB::connection('mongodb')->collection('table2')->insert($data2); // failed insert
    $session->commitTransaction();
    return true;
} catch (\Exception $e) {
    $session->abortTransaction();
    return false;
}

in my case the first data still entered into table1 not aborted, but table2 data not entered can help me ?

kangyasin avatar Aug 07 '19 04:08 kangyasin

$mongoClient = DB::connection('mongodb')->getMongoClient(); $session = $mongoClient->startSession(); try { $mongoClient->{data_base_name}->{collection_name}->deleteMany(['type' => $this->service::ROUND_NUMBER_SIMILAR]); $mongoClient->{data_base_name}->{collection_name}->insertMany($roundNumbers); $session->commitTransaction(); } catch (\Exception $e) { $session->abortTransaction(); $success = false; }

Does it work?

masssoud avatar Nov 26 '19 11:11 masssoud

pass this session as a option when you do update.

$session = MongoDB::startSession();
$session->startTransaction();
try {
    Player::document()->update($updates, ['session' => $session]);
    $session->commitTransaction();
    return true;
} catch (\Exception $e) {
    $session->abortTransaction();
    return false;
}

How About create method?

masssoud avatar Dec 01 '19 08:12 masssoud

@masssoud Just use the empty array [] for option $session->startTransaction([]);

Desgan avatar Jan 22 '20 12:01 Desgan

PR with implementation https://github.com/jenssegers/laravel-mongodb/pull/1904

Smolevich avatar Jan 22 '20 13:01 Smolevich

It would be great to have this functions working on this package. So, I've been doing some tests about transactions using Laravel 8 and this package and I noted some things. First of all: Anyone, wherever you are, whatever you do, (if you're reading this too) transactions never gonna work if you have not previously configured your mongod to run as a standalone replica or with a sets of replicas. This is super important, and it's a requirement that not should be overlooked (self experience). Then: 1- I've been noticed that transactions work, but only if they run over MongoDB native functions and not over Laravel models. Example: User::create($data); will always run, even if you are using the session on this way User::create($data, ['session' => $session]); After execute: $session->abortTransaction(); the User will be populated to database anyway. 2- When I used it as this way, the transactions control worked as expected:

$collection = $client->{database_name}->{collection_name};
$collection->insertOne($data, ['session' => $session]);

After $session->abortTransaction(); the changes was not made. After $session->commitTransaction(); changes was made. 3- The horrible thing about this, on this moment, is that you can't create (or at least IDK how to do it) a model with all they fields as timestamps and so on, the others useful things offers by a Laravel Model. In this way (hypothetical example): $collection->insertOne($data, ['session' => $session]); the array will be saved on they exact form. 4- The other thing (and the only one I think) that I asking for is to get available (and working of course) in this package the Laravel functions as DB::beginTransaction(), DB::commit(), DB::rollBack(), etc... And also, of course, the native Model functions as create, update, remove, etc... As this functions won't work right now in none of its variants. Or at least I've not been able to make them work (using transactions). 5- If someone has solved this issue (but really solved, with an strongly approach), it would be helpful if could share the solution. 6- If anyone has doubts about my (super long) speech and need more info about it, don't hesitate to contact me or even comment right here, as I think this is not solved yet. Finally, this is an example of my coding testing ( having a previous $input = $request->all(); by example )

$client = DB::connection('mongodb')->getMongoClient();
$session = $client->startSession();
$session->startTransaction([]);
$collection = $client->administration_schema->users;
$collection->insertOne($input, ['session' => $session]);
// $session->commitTransaction(); // Use it to save your data
$session->abortTransaction();     // You know what it means

ZivotSen avatar Oct 21 '20 04:10 ZivotSen

$session = DB::connection('mongodb')->getMongoClient()->startSession();
$session->startTransaction();
try {
    DB::connection('mongodb')->collection('table1')->updateMany($data1);
    $session->commitTransaction();
    return true;
} catch (\Exception $e) {
    $session->abortTransaction();
    return false;
}

 MongoDB\Driver\Exception\RuntimeException

  Multi-document transactions are not supported by this server version

I get the error like this, please help me

Laravel 8 and "jenssegers/mongodb": "^3.8.0"

henrypham299 avatar Oct 26 '20 09:10 henrypham299

$session = DB::connection('mongodb')->getMongoClient()->startSession();
$session->startTransaction();
try {
    DB::connection('mongodb')->collection('table1')->updateMany($data1);
    $session->commitTransaction();
    return true;
} catch (\Exception $e) {
    $session->abortTransaction();
    return false;
}
 MongoDB\Driver\Exception\RuntimeException

  Multi-document transactions are not supported by this server version

I get the error like this, please help me

Laravel 8 and "jenssegers/mongodb": "^3.8.0"

Hello,

Let me guess, you're using one server? MongoDB transactions does work only with replica sets and sharded clusters.

Thanks!

divine avatar Oct 26 '20 09:10 divine

@divine you are right, thank you.

henrypham299 avatar Oct 27 '20 01:10 henrypham299

    $input = ['name' => 'aaa', 'status' => 2];
    $client = DB::connection('rep')->getMongoClient();
    /**@var $session Session */
    $session = $client->startSession();
    $session->startTransaction([]);
    /**@var $collection \MongoDB\Collection */
    $collection = $client->test->avatar;
    $collection->insertOne($input, ['session' => $session]);
    // $session->commitTransaction(); // Use it to save your data
    /**@var $user \MongoDB\Collection */
    $user = $client->test->user;
    $user->updateOne(['age' => 30], ['$set' => ['name' => 'cccc']], ['session' => $session]);

// $session->abortTransaction(); // You know what it means $session->commitTransaction();

database.php

connections => [ 'rep' => [ 'driver' => 'mongodb', 'dsn' => 'mongodb://1.cn:27017,1.cn:27018,1.cn:27019/?replicaSet=mongo_clus' , 'database' => env('MONGO_DB_DATABASE', 'test'), ],

]

it works

Justniceone avatar Nov 27 '20 06:11 Justniceone

@klferreira

I think you would need to change it to:

\DB::beginTransaction(); try { $this->repository->create($data); \DB::commit(); } catch (\Exception $e) { \DB::rollback(); }

julio-mesa avatar Dec 28 '20 22:12 julio-mesa

Unfortunately you must use the mongo driver syntax, you can't use model eloquent methods, and you must also always pass ['session' => $session] as an option

fidan-mkdir avatar Dec 29 '20 14:12 fidan-mkdir

@fidan-mkdir It seems to be included in the eloquent with jessengers and it seems to work properly with beginTransaction/commit/rollback I tested it with it and transaction seems to be working good.

image

julio-mesa avatar Jan 11 '21 17:01 julio-mesa

Just a friendly reminder. If you have multiple database connection, make sure you define it first before you initialize transaction. File : config/database.php

...
'connections' => [
    ...
    'mongodb' => [
        // Your mongo db config
    ]
]
...

Then, you can use @salalaslam's answer

File : app/Controllers/YourController.php

$session = DB::connection('mongodb')->getMongoClient()->startSession();
$session->startTransaction();
try {
     // Perform actions.
     $session->commitTransaction();
} catch(Exception $e) {
    $session->abortTransaction();
}

fendis0709 avatar Jan 26 '21 04:01 fendis0709

hello i have issue its not working for me controller: ` $session = DB::connection('mongodb')->getMongoClient()->startSession(); $session->startTransaction(); try {

        $db = UserWebsite::create([
            'userID' => $dbUser->id,
            'categoryID' => $dbCategory->id,
            'name' => $request->name,
            'website' => $request->website,
            'favIcon' => $request->favIcon,
            'resourcePath' => null,
            'isDelete' => false,
        ]);
        $result = $this->createDirectoryResource2($db->id);
        $db->resourcePath = $result;
        $db->save();
        $session->commitTransaction();
        return generateJsonResponse::simpleJson($db , 200 , Environment::storeSuccess);
    }catch (\Exception $exception){
        $session->abortTransaction();
        return generateJsonResponse::simpleJson(null , 500 , $exception->getMessage());
        return generateJsonResponse::simpleJson(null , 500 , Environment::storeResponse500);
    }

config: 'connections' => [ 'mongodb' => [ 'driver' => 'mongodb', 'host' => env('DB_HOST', '127.0.0.1'), 'port' => env('DB_PORT', 27017), 'database' => env('DB_DATABASE'), 'username' => env('DB_USERNAME'), 'password' => env('DB_PASSWORD'), 'options' => [ 'database' => env('DB_AUTHENTICATION_DATABASE', 'admin'), // required with Mongo 3+ ], ], ` can anyone help me

mbehzad-bhz avatar Jul 27 '21 00:07 mbehzad-bhz

@julio-mesa Hi, how are you doing? I couldn't get this method to work. I'm getting Call to a member function beginTransaction() on null. Is there something I'm doing wrong?

salvationarinze avatar May 24 '22 21:05 salvationarinze

@julio-mesa Hi, how are you doing? I couldn't get this method to work. I'm getting Call to a member function beginTransaction() on null. Is there something I'm doing wrong?

MongoDB transactions will only take effect under the replica set architecture

Justniceone avatar May 25 '22 02:05 Justniceone

@klferreira

I think you would need to change it to:

\DB::beginTransaction(); try { $this->repository->create($data); \DB::commit(); } catch (\Exception $e) { \DB::rollback(); }

@Justniceone Thanks for your response. I was asking in response to this format. It's really concise and smooth but it gives it this error unless the error is due to the fact its not a replica set. I just think the format is not working for me

salvationarinze avatar May 25 '22 07:05 salvationarinze

@salvationarinze I could be wrong, but the change @julio-mesa is mentioning doesn't seem to be in the main repo, but in a fork. It has a pending pull request...

https://github.com/jenssegers/laravel-mongodb/pull/1904 https://github.com/jenssegers/laravel-mongodb/blob/041d02bbafe71778fb1db8c44cedad32c9a759e6/src/Jenssegers/Mongodb/Connection.php

The fork: https://github.com/klinson/laravel-mongodb

alanglaisA4 avatar May 27 '22 19:05 alanglaisA4

@alanglaisA4 I really appreciate the information. Thank you so much. In order to use this feature, is it recommended to use the fork directly or do I just stick with traditional method? What do you think?

salvationarinze avatar May 28 '22 05:05 salvationarinze