php - Transacciones CodeIgniter-trans_status y trans_complete devuelven true pero no se está confirmando nada




mysql (4)

¿Tal vez intente reemplazar su código de actualización con una llamada a simple_query?

Cambio:

if(!empty($discount)){
    $this->db->set('discount_redeem_count', 'discount_redeem_count-1', false);
    $this->db->where('discount_id', $discount['discount_id']);
    $this->db->update('tbl_discounts');
}

A:

if(!empty($discount)){
    $this->db->simple_query('UPDATE `tbl_discounts` SET '.
    'discount_redeem_count = discount_redeem_count-1 WHERE '.
    '`discount_id` = \''.$discount['discount_id'].'\'');
}

Revisé un poco el código fuente de CodeIgniter, y parece que la función de consulta predeterminada hace un montón de tareas domésticas que pueden estar arruinando las cosas. Y la función simple_query tiene este conjunto de documentación:

/**
 * Simple Query
 * This is a simplified version of the query() function. Internally
 * we only use it when running transaction commands since they do
 * not require all the features of the main query() function.
 *
 * @param   string  the sql query
 * @return  mixed
 */

Problema:

He escrito una función en mi modelo para insertar un pedido en mi base de datos. Estoy usando transacciones para asegurarme de que todo se comprometa o de lo contrario se revertirá.

Mi problema es que CodeIgniter no muestra ningún error en la base de datos, sin embargo, está trans_status la transacción pero luego devuelve TRUE para trans_status . Sin embargo, esto solo sucede si hay un descuento en el pedido. Si no hay descuento en el pedido, todo se compromete y funciona correctamente.

Actualmente estoy usando CodeIgniter 3.19, PHP (7.2), mySQL (5.7) y Apache 2.4. (Trabajando en Ubuntu 18.04)

La función lógica funciona como tal:

  • Inserta la matriz de orden en tbl_orders
  • Guarda order_id y recorre cada uno de los productos de pedido (adjunta order_id ) e inserta el producto en tbl_order_products ,
  • Guarda order_product_id y lo adjunta a una variedad de opciones de asistencia de usuarios y lo inserta en tbl_order_attendance
  • Toma la matriz de transacciones de pago (adjunta el order_id ) y lo inserta en tbl_transactions
  • Si hay un descuento en el pedido , disminuye el número de discount_redeem_count (número de códigos de descuento) en 1.

Función actual

[Función]:

public function add_order(Order $order, array $order_products, Transaction $transaction = NULL){
  $this->db->trans_start();

  $order->create_order_code();
  $order_array = $order->create_order_array();

  $this->db->insert('tbl_orders', $order_array);
  $order_id = $this->db->insert_id();
  $new_order = new Order($order_id);

  foreach($order_products as $key=>$value){
    $order_products[$key]->set_order($new_order);
    $order_product_array = $order_products[$key]->create_order_product_array();

    $this->db->insert('tbl_order_products', $order_product_array);
    $order_product_id = $this->db->insert_id();

    $product = $order_products[$key]->get_product();

    switch ($product->get_product_class()){
        case 'Iteration':
            $this->db->select('module_id, webcast_capacity, in_person_capacity');
            $this->db->from('tbl_modules');
            $this->db->where('iteration_id', $product->get_product_class_id());
            $results = $this->db->get()->result_array();
            break;
        case 'Module':
            $this->db->select('module_id, webcast_capacity, in_person_capacity');
            $this->db->from('tbl_modules');
            $this->db->where('module_id', $product->get_product_class_id());
            $results = $this->db->get->result_array();
            break;
      }

      if(!empty($results)){
        foreach($results as $result){
        $module_id = $result['module_id'];

        if($result['webcast_capacity'] !== NULL && $result['in_person_capacity'] !== NULL){
          $attendance_method = $order_products[$key]->get_attendance_method();
        }elseif($result['webcast_capacity'] !== NULL && $result['in_person_capacity'] === NULL){
          $attendance_method = 'webcast';
        }elseif($result['webcast_capacity'] === NULL && $result['in_person_capacity'] !== NULL){
          $attendance_method = 'in-person';
        }

        $order_product_attendance_array = array(
          'order_product_id' => $order_product_id,
          'user_id' => $order_products[$key]->get_customer(true),
          'module_id' => $module_id,
          'attendance_method' => $attendance_method,
        );

        $order_product_attendance[] = $order_product_attendance_array;
      }
      $this->db->insert_batch('tbl_order_product_attendance', $order_product_attendance);
    }

    if(!empty($order_products[$key]->get_discount())){
      $discount = $order_products[$key]->get_discount();
    }
  }

  if(!empty($transaction)){
    $transaction->set_order($new_order);
    $transaction_array = $transaction->create_transaction_array();
    $this->db->insert('tbl_transactions', $transaction_array);
    $transaction_id = $this->db->insert_id();
  }

  if(!empty($discount)){
    $this->db->set('discount_redeem_count', 'discount_redeem_count-1', false);
    $this->db->where('discount_id', $discount->get_discount_id());
    $this->db->update('tbl_discounts');
  }

  if($this->db->trans_status() !== false){
    $result['outcome'] = true;
    $result['insert_id'] = $order_id;
    return $result;
  }else{
    $result['outcome'] = false;
    return $result;
  }
}

Cuando esta función se completa con un descuento , tanto trans_complete como trans_status devuelven TRUE . Sin embargo la transacción nunca se compromete.

Lo que he intentado:

  • He volcado el contenido de $this->db->error() después de cada consulta y no hay errores en ninguna de las consultas.

  • He usado this->db->last_query() para imprimir cada consulta y luego verifiqué la sintaxis en línea para ver si había algún problema, no había ninguno.

  • También intenté cambiar a utilizar las transacciones manuales de CodeIgniters como:

[Ejemplo]

$this->db->trans_begin();
 // all the queries
if($this->db->trans_status() !== false){
    $this->db->trans_commit();
    $result['outcome'] = true;
    $result['insert_id'] = $order_id;
    return $result;
}else{
    $this->db->trans_rollback();
    $result['outcome'] = false;
    return $result;
}
  • He intentado hacer echo y var_dump todos los datos de insert_ids retorno y todos funcionan, también he insert_ids las insert_ids affected_rows() de la consulta UPDATE y muestra que se actualizó 1 fila. Sin embargo, todavía no se está cometiendo nada:

[Valores descargados]

int(10) // order_id
int(10) // order_product_id
array(3) { 
    ["module_id"]=> string(1) "1" 
    ["webcast_capacity"]=> string(3) "250" 
    ["in_person_capacity"]=> string(3) "250" } // $results array (modules)

array(1) { 
    [0]=> array(4) { 
        ["order_product_id"]=> int(10 
        ["user_id"]=> string(1) "5" 
        ["module_id"]=> string(1) "1" 
        ["attendance_method"]=> string(7) "webcast" } } // order_product_attendance array

int(9) // transaction_id
int(1) // affected rows
string(99) "UPDATE `tbl_discounts` 
            SET discount_redeem_count = discount_redeem_count- 1 
            WHERE `discount_id` = 1" // UPDATE query

- También he intentado reemplazar la última consulta de UPDATE por una completamente diferente que intenta actualizar una tabla diferente con valores diferentes. Esa consulta TAMBIÉN no funcionó, lo que me hace pensar que estoy llegando a algún tipo de límite de memoria con la transacción. Sin embargo, al monitorear los procesos de mysqld , ninguno de ellos parece aumentar o tener dificultades.

  • ¡He intentado enviar un pedido que no tiene descuento y funciona todo el proceso! Lo que me lleva a creer que mi problema es con mi consulta de ACTUALIZACIÓN. [Después de la actualización:] Pero parece que la consulta de actualización también está funcionando.

Sugerencias intentadas:

  • Hemos intentado establecer log_threshold en 4 y hemos log_threshold los archivos de registro de CodeIgniter que no muestran el historial de una reversión.

  • Hemos revisado el registro de consultas mySQL:

[Registro de consultas]

2018-12-03T15:20:09.452725Z         3 Query     UPDATE `tbl_discounts` SET discount_redeem_count = discount_redeem_count-1 WHERE `discount_id` = '1'
2018-12-03T15:20:09.453673Z         3 Quit

Muestra que se está enviando un comando QUIT directamente después de la consulta UPDATE . Esto iniciaría una reversión, sin embargo, trans_status está devolviendo TRUE .

También cambié mi archivo my.cnf para que mySQL tenga innodb_buffer_pool_size=256M e innodb_log_file_size=64M . No hubo ningún cambio en el resultado.

  • Como recomendó @ebcode, cambié la consulta de UPDATE para usar un simple_query() lugar de usar los métodos predeterminados de la clase de generador de consultas de CodeIgniter:

[Consulta simple]

if(!empty($discount)){
    $this->db->simple_query('UPDATE `tbl_discounts` SET '.
    'discount_redeem_count = discount_redeem_count-1 WHERE '.
    '`discount_id` = \''.$discount['discount_id'].'\'');
}

Sin embargo, esto producido no afectó el resultado de manera diferente.

Si tiene una idea que aún no he probado o si necesita más información de mi parte, comente y le responderé de inmediato.

Pregunta:

¿Por qué trans_status devuelve TRUE si no se está confirmando ninguna de mis transacciones?

Para probar y aclarar a los usuarios que solo encuentran esta pregunta ahora, las últimas actualizaciones de la publicación aparecerán en cursiva *


(Ambas sugerencias fueron probadas, sin éxito.)

Sugerencia 1

Quizás esta es la respuesta real:

trans_status () debe ejecutarse cuando estás dentro de una transacción. En su ejemplo, trans_complete () restablece el indicador de estado.

(Sin embargo, esto es triste si está utilizando Galera o Replicación de grupo, ya que una transacción aún puede fallar cuando se ejecuta COMMIT ).

Sugerencia 2

These are  `== NULL`:  NULL, '', FALSE, 0
These are `!== NULL`:        '', FALSE, 0

Observe cómo usa el "triple" !== para algunas pruebas contra NULL, pero use el "doble" == para otras.

Haga esto para ver lo que realmente está recibiendo:

var_dump($result['webcast_capacity']);

En primer lugar, debe asegurarse de activar el modo de transacción estricta antes de iniciar las transacciones.

$this->db->trans_strict(TRUE);
$this->db->trans_start();

En segundo lugar, compruebe la variable $ order. ¿Esto es una matriz? o una clase? Si esto es una clase, entonces probablemente falló en esta línea

$this->db->insert('tbl_orders', $order);

En tercer lugar, si $ orden variable es una clase, entonces esta línea tendrá éxito. Si $ orden variable es una matriz, esta línea fallará.

$discount = $order->get_discount();


Encontré mi problema. Quiero dar las gracias a todos los que intentaron ayudar, sin embargo, este fue mi culpa.

Anteriormente, en el método del controlador que llama a esta función, llamé a otra función que inicia una transacción. Esa transacción nunca se cerró y, por lo tanto, continuó en esta nueva transacción.

Debido a que la transacción simplemente no se realizó y no hubo errores, no pude encontrar ningún error ni ningún historial de reversión. Sin embargo tan pronto como cerré la transacción anterior todo funcionó.

No hubo evidencia de ningún problema en el registro de consultas de mySQL, el registro de errores de mySQL o los registros de errores de CodeIgniter. Solo pude encontrar este problema leyendo lentamente todo el registro de consultas de mySQL.

Para cualquier persona que se encuentre con este problema: revise sus otras transacciones y asegúrese de que todas estén cerradas.





codeigniter