php - Transacciones CodeIgniter-trans_status y trans_complete devuelven true pero no se está confirmando nada
mysql (6)
¿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 (adjuntaorder_id
) e inserta el producto entbl_order_products
, - Guarda
order_product_id
y lo adjunta a una variedad de opciones de asistencia de usuarios y lo inserta entbl_order_attendance
- Toma la matriz de transacciones de pago (adjunta el
order_id
) y lo inserta entbl_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
yvar_dump
todos los datos deinsert_ids
retorno y todos funcionan, también heinsert_ids
lasinsert_ids
affected_rows()
de la consultaUPDATE
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 hemoslog_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 unsimple_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'']);
Basado en EDIT 5 :
Esta
$this->db->set(''discount_redeem_count'', ''discount_redeem_count-1'', false);
debería funcionar (el marcado en retroceso no lo haría ... todo el punto de pasar el tercer parámetro false
es para que CI no escape sus parámetros con comillas invertidas, lo que evitaría que la declaración de conjunto se pase como una cadena).
Hice algunas pruebas rápidas en mi propio código de desarrollo con una actualización similar a esa y la única forma de que fallara fue alterar la tabla que se estaba actualizando para que el campo ( discount_redeem_count
en su caso) no fuera numérico. Si mi campo fuera, por ejemplo, un VARCHAR, no funcionaría, pero cuando lo probé en un campo INT, funcionó sin problemas.
¿Estás seguro de que el campo discount_redeem_count
es numérico?
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.
Estoy pensando en el campo de la base de datos nombre de discount_redeem_count
.
¿Estás seguro de que discount_redeem_count
no es número? porque aquí estás tratando de empujar un valor de cadena. Así que el campo de la base de datos debe ser var o text.
Tal vez útil.
Gracias.