We have moved our forum to GitHub Discussions. For questions about Phalcon v3/v4/v5 you can visit here and for Phalcon v6 here.

What connection would Model::find() use in a transaction?

As Title said, and see codes below:

<?php

try {
        $transaction = $manager->get();
        $modelA = new FooModel();
        $modelA->setTransaction($transaction);
        $modelA->a = "xxx";
        $modelA->b = "yyy";
        if($modelA->save() == false) {   // use $transaction->getConnection() to do the save operation
            $transaction->rollbak();
        }

        $modelB = new BarModel();
        $modelB->setTransaction($transaction);
        $modelB->a = "xxx";
        $modelB->b = "yyy";
        if($modelB->save() == false) { // use $transaction->getConnection() to do the save operation
            $transaction->rollback();
        }
        $count = FooModel::count();  // What conneciton would this line use???
        $transaction->commit();
} catch(TxFailed $e) {
    // ...
}

return $count;

Someone would say, you can do the calculating after the transaction. Yes, I can. But I splite read and write connection in my app. There is lantency between master and slaves.

So, is there a good way to do this work?

edited Jan '16

It would use the model connection which is either specified explicitly, or falls back to the service db.

Without checking the Phalcon internals, it should in theory be the same connection as what's returned when you execute

//Returns Database service from the DI container
(new FooModel())->getReadConnectionService(); //service name
(new FooModel())->getReadConnection(); //service resolve (object)

See https://docs.phalcon.io/en/latest/api/Phalcon_Mvc_Model.html :)

So, you mean it use a different db connection with the one in transaction?

It would use the model connection which is either specified explicitly, or falls back to the service db.

Without checking the Phalcon internals, it should in theory be the same connection as what's returned when you execute

//Returns Database service from the DI container
(new FooModel())->getReadConnectionService(); //service name
(new FooModel())->getReadConnection(); //service resolve (object)

See https://docs.phalcon.io/en/latest/api/Phalcon_Mvc_Model.html :)

It depends how your 'db' service is setup. If the service is shared, then same connection is used - if it's not shared then it is a different connection.

I believe \Phalcon\Di\FactoryDefault has 'db' not set as a shared service if that's what you're using.

tl;dr: It depends if your 'db' service is set as shared or not really.

So, you mean it use a different db connection with the one in transaction?

It would use the model connection which is either specified explicitly, or falls back to the service db.

Without checking the Phalcon internals, it should in theory be the same connection as what's returned when you execute

//Returns Database service from the DI container
(new FooModel())->getReadConnectionService(); //service name
(new FooModel())->getReadConnection(); //service resolve (object)

See https://docs.phalcon.io/en/latest/api/Phalcon_Mvc_Model.html :)

edited Jan '16

I basicly agree with you. But if you go deep into the Phalcon Internals, you can see:


        /**
         * Gets the connection used to read data for the model
         */
        public function getReadConnection() -> <AdapterInterface>
        {
                var transaction;

                let transaction = <TransactionInterface> this->_transaction;
                if typeof transaction == "object" {
                        return transaction->getConnection();
                }

                return (<ManagerInterface> this->_modelsManager)->getReadConnection(this);
        }

So, do you think it wii use the conneciton of transaction or not?

It depends how your 'db' service is setup. If the service is shared, then same connection is used - if it's not shared then it is a different connection.

I believe \Phalcon\Di\FactoryDefault has 'db' not set as a shared service if that's what you're using.

tl;dr: It depends if your 'db' service is set as shared or not really.

So, you mean it use a different db connection with the one in transaction?

It would use the model connection which is either specified explicitly, or falls back to the service db.

Without checking the Phalcon internals, it should in theory be the same connection as what's returned when you execute

//Returns Database service from the DI container
(new FooModel())->getReadConnectionService(); //service name
(new FooModel())->getReadConnection(); //service resolve (object)

See https://docs.phalcon.io/en/latest/api/Phalcon_Mvc_Model.html :)



6.9k
Accepted
answer

Based the order that your code is executing your code, I would say yes.

One way to check would be:

<?php

try {
        $transaction = $manager->get();
        $modelA = new FooModel();
        $modelA->setTransaction($transaction);
        $modelA->a = "xxx";
        $modelA->b = "yyy";
        if($modelA->save() == false) {   // use $transaction->getConnection() to do the save operation
            $transaction->rollbak();
        }

        $modelB = new BarModel();
        $modelB->setTransaction($transaction);
        $modelB->a = "xxx";
        $modelB->b = "yyy";
        if($modelB->save() == false) { // use $transaction->getConnection() to do the save operation
            $transaction->rollback();
        }

        if((new FooModel())->getReadConnectionService()->isUnderTransaction()){
            $count = FooModel::count();  // What conneciton would this line use???
        } else {
            $transaction->rollback();
            throw new \Exception('Unable to count database rows with same connection');
        }
        $transaction->commit();
} catch(TxFailed $e) {
    // ...
}

return $count;

I basicly agree with you. But if you go deep into the Phalcon Internals, you can see:


       /**
        * Gets the connection used to read data for the model
        */
       public function getReadConnection() -> <AdapterInterface>
       {
               var transaction;

               let transaction = <TransactionInterface> this->_transaction;
               if typeof transaction == "object" {
                       return transaction->getConnection();
               }

               return (<ManagerInterface> this->_modelsManager)->getReadConnection(this);
       }

So, do you think it wii use the conneciton of transaction or not?

It depends how your 'db' service is setup. If the service is shared, then same connection is used - if it's not shared then it is a different connection.

I believe \Phalcon\Di\FactoryDefault has 'db' not set as a shared service if that's what you're using.

tl;dr: It depends if your 'db' service is set as shared or not really.

So, you mean it use a different db connection with the one in transaction?

It would use the model connection which is either specified explicitly, or falls back to the service db.

Without checking the Phalcon internals, it should in theory be the same connection as what's returned when you execute

//Returns Database service from the DI container
(new FooModel())->getReadConnectionService(); //service name
(new FooModel())->getReadConnection(); //service resolve (object)

See https://docs.phalcon.io/en/latest/api/Phalcon_Mvc_Model.html :)

edited Jan '16

Yes! I have tested it as you said. The result is:

// codes: 
var_dump((new FooModel())->getReadConnectionService());
var_dump((new FooModel())->getReadConnection()->isUnderTransaction());

// output:
string(13) "msg_box_slave"
bool(false)

So here we are: It will use another db connection even in a transaction context.

Do you know if there is a way to use a specific db-connection when do Model::find() ?

If yes, maybe I can do something like this:

FooModel::find("age>5", $transaction->getConnection());

Based the order that your code is executing your code, I would say yes.

One way to check would be:

<?php

try {
       $transaction = $manager->get();
       $modelA = new FooModel();
       $modelA->setTransaction($transaction);
       $modelA->a = "xxx";
       $modelA->b = "yyy";
       if($modelA->save() == false) {   // use $transaction->getConnection() to do the save operation
           $transaction->rollbak();
       }

       $modelB = new BarModel();
       $modelB->setTransaction($transaction);
       $modelB->a = "xxx";
       $modelB->b = "yyy";
       if($modelB->save() == false) { // use $transaction->getConnection() to do the save operation
           $transaction->rollback();
       }

      if((new FooModel())->getReadConnectionService()->isUnderTransaction()){
           $count = FooModel::count();  // What conneciton would this line use???
      } else {
          $transaction->rollback();
          throw new \Exception('Unable to count database rows with same connection');
      }
       $transaction->commit();
} catch(TxFailed $e) {
   // ...
}

return $count;

I basicly agree with you. But if you go deep into the Phalcon Internals, you can see:


       /**
        * Gets the connection used to read data for the model
        */
       public function getReadConnection() -> <AdapterInterface>
       {
               var transaction;

               let transaction = <TransactionInterface> this->_transaction;
               if typeof transaction == "object" {
                       return transaction->getConnection();
               }

               return (<ManagerInterface> this->_modelsManager)->getReadConnection(this);
       }

So, do you think it wii use the conneciton of transaction or not?

It depends how your 'db' service is setup. If the service is shared, then same connection is used - if it's not shared then it is a different connection.

I believe \Phalcon\Di\FactoryDefault has 'db' not set as a shared service if that's what you're using.

tl;dr: It depends if your 'db' service is set as shared or not really.

So, you mean it use a different db connection with the one in transaction?

It would use the model connection which is either specified explicitly, or falls back to the service db.

Without checking the Phalcon internals, it should in theory be the same connection as what's returned when you execute

//Returns Database service from the DI container
(new FooModel())->getReadConnectionService(); //service name
(new FooModel())->getReadConnection(); //service resolve (object)

See https://docs.phalcon.io/en/latest/api/Phalcon_Mvc_Model.html :)

Yes! I have tested it as you said. The result is:

// codes: 
var_dump((new FooModel())->getReadConnectionService());
var_dump((new FooModel())->getReadConnection()->isUnderTransaction());

// output:
string(13) "msg_box_slave"
bool(false)

So here we are: It will use another db connection even in a transaction context.

Do you know if there is a way to use a specific db-connection when do Model::find() ?

If yes, maybe I can do something like this:

FooModel::find("age>5", $transaction->getConnection());

In theory you could do so with the modelsManager service: https://docs.phalcon.io/en/latest/api/Phalcon_Mvc_Model_Manager.html

(It will be a service 'modelsManager' resolved service in your DI.)

\Phalcon\Di::getDefault()->get('modelsManager')->setReadConnection('Model', $transaction->getDbService());

setReadConnectionService
getReadConnection

Will be the functions of interest to you.

However the accuracy of ($model->create() === true) should be enough to verify that it was written to the database, rather than executing a count query?

Based the order that your code is executing your code, I would say yes.

One way to check would be:

<?php

try {
       $transaction = $manager->get();
       $modelA = new FooModel();
       $modelA->setTransaction($transaction);
       $modelA->a = "xxx";
       $modelA->b = "yyy";
       if($modelA->save() == false) {   // use $transaction->getConnection() to do the save operation
           $transaction->rollbak();
       }

       $modelB = new BarModel();
       $modelB->setTransaction($transaction);
       $modelB->a = "xxx";
       $modelB->b = "yyy";
       if($modelB->save() == false) { // use $transaction->getConnection() to do the save operation
           $transaction->rollback();
       }

     if((new FooModel())->getReadConnectionService()->isUnderTransaction()){
           $count = FooModel::count();  // What conneciton would this line use???
     } else {
         $transaction->rollback();
         throw new \Exception('Unable to count database rows with same connection');
     }
       $transaction->commit();
} catch(TxFailed $e) {
   // ...
}

return $count;

I basicly agree with you. But if you go deep into the Phalcon Internals, you can see:


       /**
        * Gets the connection used to read data for the model
        */
       public function getReadConnection() -> <AdapterInterface>
       {
               var transaction;

               let transaction = <TransactionInterface> this->_transaction;
               if typeof transaction == "object" {
                       return transaction->getConnection();
               }

               return (<ManagerInterface> this->_modelsManager)->getReadConnection(this);
       }

So, do you think it wii use the conneciton of transaction or not?

It depends how your 'db' service is setup. If the service is shared, then same connection is used - if it's not shared then it is a different connection.

I believe \Phalcon\Di\FactoryDefault has 'db' not set as a shared service if that's what you're using.

tl;dr: It depends if your 'db' service is set as shared or not really.

So, you mean it use a different db connection with the one in transaction?

It would use the model connection which is either specified explicitly, or falls back to the service db.

Without checking the Phalcon internals, it should in theory be the same connection as what's returned when you execute

//Returns Database service from the DI container
(new FooModel())->getReadConnectionService(); //service name
(new FooModel())->getReadConnection(); //service resolve (object)

See https://docs.phalcon.io/en/latest/api/Phalcon_Mvc_Model.html :)

In theory you could do so with the modelsManager service: https://docs.phalcon.io/en/latest/api/Phalcon_Mvc_Model_Manager.html

(It will be a service 'modelsManager' resolved service in your DI.)

\Phalcon\Di::getDefault()->get('modelsManager')->setReadConnection('Model', $transaction->getDbService());

setReadConnectionService
getReadConnection

Will be the functions of interest to you.

But if there are other actions after that transaction, I have to change that back to slave service.

However the accuracy of ($model->create() === true) should be enough to verify that it was written to the database, rather than executing a count query?

The code is just a sample to describe this problem. We do not use this code in production.

Thank you very much.

edited Apr '16

maybe i should implement Model::selectReadConnection() , and that works for me.

class FooModel extends \Phalcon\Mvc\Model
{
    public function selectReadConnection()
    {
        $txm = $this->di->get('txm');
        $txm->setDbService($this->getWriteConnectionService());
        $transaction = $txm->get(false); // set autoBegin =  false
        if($transaction->isValid()) {
            return $this->getWriteConnection();
        } else {
            return $this->getReadConnection();
        }
    }
}

Yes! I have tested it as you said. The result is:

// codes: 
var_dump((new FooModel())->getReadConnectionService());
var_dump((new FooModel())->getReadConnection()->isUnderTransaction());

// output:
string(13) "msg_box_slave"
bool(false)

So here we are: It will use another db connection even in a transaction context.

Do you know if there is a way to use a specific db-connection when do Model::find() ?

If yes, maybe I can do something like this:

FooModel::find("age>5", $transaction->getConnection());

In theory you could do so with the modelsManager service: https://docs.phalcon.io/en/latest/api/Phalcon_Mvc_Model_Manager.html

(It will be a service 'modelsManager' resolved service in your DI.)

\Phalcon\Di::getDefault()->get('modelsManager')->setReadConnection('Model', $transaction->getDbService());

setReadConnectionService
getReadConnection

Will be the functions of interest to you.

However the accuracy of ($model->create() === true) should be enough to verify that it was written to the database, rather than executing a count query?

Based the order that your code is executing your code, I would say yes.

One way to check would be:

<?php

try {
       $transaction = $manager->get();
       $modelA = new FooModel();
       $modelA->setTransaction($transaction);
       $modelA->a = "xxx";
       $modelA->b = "yyy";
       if($modelA->save() == false) {   // use $transaction->getConnection() to do the save operation
           $transaction->rollbak();
       }

       $modelB = new BarModel();
       $modelB->setTransaction($transaction);
       $modelB->a = "xxx";
       $modelB->b = "yyy";
       if($modelB->save() == false) { // use $transaction->getConnection() to do the save operation
           $transaction->rollback();
       }

    if((new FooModel())->getReadConnectionService()->isUnderTransaction()){
           $count = FooModel::count();  // What conneciton would this line use???
    } else {
        $transaction->rollback();
        throw new \Exception('Unable to count database rows with same connection');
    }
       $transaction->commit();
} catch(TxFailed $e) {
   // ...
}

return $count;

I basicly agree with you. But if you go deep into the Phalcon Internals, you can see:


       /**
        * Gets the connection used to read data for the model
        */
       public function getReadConnection() -> <AdapterInterface>
       {
               var transaction;

               let transaction = <TransactionInterface> this->_transaction;
               if typeof transaction == "object" {
                       return transaction->getConnection();
               }

               return (<ManagerInterface> this->_modelsManager)->getReadConnection(this);
       }

So, do you think it wii use the conneciton of transaction or not?

It depends how your 'db' service is setup. If the service is shared, then same connection is used - if it's not shared then it is a different connection.

I believe \Phalcon\Di\FactoryDefault has 'db' not set as a shared service if that's what you're using.

tl;dr: It depends if your 'db' service is set as shared or not really.

So, you mean it use a different db connection with the one in transaction?

It would use the model connection which is either specified explicitly, or falls back to the service db.

Without checking the Phalcon internals, it should in theory be the same connection as what's returned when you execute

//Returns Database service from the DI container
(new FooModel())->getReadConnectionService(); //service name
(new FooModel())->getReadConnection(); //service resolve (object)

See https://docs.phalcon.io/en/latest/api/Phalcon_Mvc_Model.html :)