¿Se recolecta la basura de los transients en WordPress?

9 ene 2011, 02:20:17
Vistas: 35K
Votos: 69

Esta pregunta me hizo pensar ¿Los feeds RSS transients en wp_options no se eliminan automáticamente?

Se supone que los transients deben expirar y eliminarse. Sin embargo, la única forma en que veo que esto se maneja es cuando el transient está expirado y es solicitado, entonces se elimina durante esa petición.

¿Qué sucede si el transient está expirado pero nunca se vuelve a solicitar después de eso? Por la descripción en el Codex, pensé que algún tipo de recolección de basura estaba implícita. Ahora no estoy tan seguro y no puedo encontrar ningún código que realice tal función.

¿Entonces simplemente permanecerá en la base de datos para siempre?

25
Comentarios

teóricamente deberían eliminarse cuando se ejecute el cron (si han expirado)

onetrickpony onetrickpony
9 ene 2011 02:34:01

@Ambitious Amoeba no veo nada con esa funcionalidad enganchada al cron. Por eso pregunto - parece ser una suposición de la que no estoy seguro que sea válida.

Rarst Rarst
9 ene 2011 02:37:59

Según mi entendimiento, los transients no son realmente procesos cron reales, al menos requieren que alguien solicite una página para que se creen/eliminen (pero es lo más parecido a un proceso cron real). No he monitoreado mis transients, ¿estás viendo que los transients permanecen frecuentemente después de expirar?

t31os t31os
9 ene 2011 18:35:47

@t31os sí, los veo colgados, pero no tengo información sobre cuánto tiempo pueden estar colgando antes de que se pueda decir definitivamente que no son recolectados como basura

Rarst Rarst
9 ene 2011 18:38:59

@Rarst - Tampoco estoy seguro de cómo se determina la limpieza, ¿estás viendo este problema con algún transitorio en particular o con diferentes?

t31os t31os
9 ene 2011 18:46:09

@t31os No voy a perder tiempo codificando un artilugio de registro de transitorios antes de saber si se supone que deben ser recolectados como basura. :)

Rarst Rarst
9 ene 2011 18:58:46

@Rarst - No tengo idea compañero, solo quería compartir algunos pensamientos.. :)

t31os t31os
9 ene 2011 19:11:06

parece que los transientes expirados se eliminan cuando se ejecuta get_transient - http://core.trac.wordpress.org/browser/tags/3.0.4/wp-includes/functions.php#L721

onetrickpony onetrickpony
9 ene 2011 19:11:44

Entonces no deberías ver ningún transiente expirado en la base de datos, a menos que algo haya salido mal con delete_option()

onetrickpony onetrickpony
9 ene 2011 19:13:18

@Ambitious Amoeba sí, algo mencioné eso en la pregunta. Mi punto es - que se cree un transitorio no asume ni garantiza que alguna vez vaya a ser solicitado. Insistiendo en la pregunta original - ¿cuándo y cómo se elimina un transitorio expirado si nunca lo obtengo?

Rarst Rarst
9 ene 2011 19:17:47

pero ¿cuál es el punto de usar transitorios entonces?

onetrickpony onetrickpony
9 ene 2011 19:19:58

@Ambitious Amoeba el punto es que los transitorios son un mecanismo de caché. El concepto de caché asume datos que expiran y no asume aciertos garantizados. Si la caché no limpia los datos expirados entonces está filtrando recursos.

Rarst Rarst
9 ene 2011 19:29:23

asume que limpias los datos expirados, pero sí, tienes razón, hay situaciones en las que nunca se eliminarían. Como eliminar un widget que usa transients. Deberías enviar un ticket en el trac para esto :)

onetrickpony onetrickpony
9 ene 2011 19:36:11

@Rarst - ¿Suena como algo perfecto para escribir un parche y enviarlo al trac?

MikeSchinkel MikeSchinkel
9 ene 2011 20:46:39

@MikeSchinkel síiiii... después de que alguien responda definitivamente al fin si los malditos transients son (o se supone que deben ser) recolectados como basura :)

Rarst Rarst
9 ene 2011 21:01:55

@Rarst - La única forma de saberlo con certeza es rastrear el código...

MikeSchinkel MikeSchinkel
10 ene 2011 05:29:29

No necesitan ser "recolectados como basura". Si nunca los recuperas, entonces no importa si están ahí o no.

Otto Otto
12 sept 2011 05:51:55

@Otto si empiezas a tener decenas de miles de entradas basura en la tabla de opciones (lo cual puede y sucede en la práctica, ver pregunta vinculada) creo que sí importa bastante, ¿no?

Rarst Rarst
12 sept 2011 11:12:09

No, realmente no lo hace. Las bases de datos pueden tener millones y millones de filas sin una ralentización apreciable. Se llama "indexación", y se mantiene increíblemente rápido incluso con muchas, muchas filas: http://en.wikipedia.org/wiki/Index_(database).

Además, llamar a SQL DELETE en ellas no las elimina realmente de la base de datos. Solo las elimina del índice, hasta que también hagas un OPTIMIZE TABLE en la tabla, que es una operación de larga duración. Generalmente no hay necesidad de "limpiar" registros en una base de datos. Deja que la base de datos haga su trabajo. Es mejor en ello que tú.

Otto Otto
12 sept 2011 12:11:48

Para ser más específico, puedes notar que los transitorios en la base de datos tienen su bandera autoload establecida en "no", lo que significa que no se cargan al inicio. La principal ralentización de cualquier consulta a la base de datos es la transferencia real de datos desde la base de datos al programa. Las consultas en sí mismas, si están escritas correctamente y adecuadamente indexadas (lo que significa que la consulta no causa un escaneo de tabla), toman virtualmente nada de tiempo en comparación. No importa si tienes 100 registros o 100,000, ya que hacer un simple SELECT en un campo indexado es una operación O(log(n)). Esto realmente no cambia hasta que llegas a 1M+ registros o más.

Otto Otto
12 sept 2011 12:26:28

@Otto ¿Podrías mover esto a una respuesta para que la información sea más visible? No discuto que se necesitarán muchas entradas de basura para estropear las cosas... Pero si algo está filtrando recursos (lo que fácilmente sucede con los transitorios porque tienen un límite de longitud de clave que no se verifica, es fácil generar un montón de ellos y nunca tocarlos de nuevo porque la clave está rota) entonces tarde o temprano estropeará las cosas. No estoy gritando "arregla esto en el núcleo", pero tampoco veo la limpieza como algo inútil.

Rarst Rarst
12 sept 2011 12:36:39

"Screw things up" es una afirmación un poco vaga. Lo máximo que puedo ver que suceda es que la base de datos se vuelva demasiado grande para un espacio de cuenta limitado. No va a romper nada realmente hasta que se vuelva extremadamente grande. Trabajo con tablas que tienen 20 millones de registros. Buscar en ellas es un poco lento, pero no de manera irrazonable. Tienes razón sobre el límite de longitud de la clave, pero 45 caracteres son suficientes para todos los casos realistas que se me ocurren. Claro, es posible hacer algo loco, pero ¿sucede a menudo? Parece que el autor del plugin debería ser notificado en lugar de buscar una solución alternativa...

Otto Otto
12 sept 2011 20:54:56

@Otto "no a menudo" y "nunca" son cosas diferentes. Si llego a hacer un plugin con esto, planeo agregar una advertencia sobre la longitud de la clave también. Sin embargo, no veo el punto de mantener datos basura en la base de datos solo porque los transitorios funcionan así. Puede que no sea un error, pero difícilmente es una característica.

Rarst Rarst
12 sept 2011 21:12:59

Mi punto no es mantenerlos en la base de datos solo porque funcionan así. Mi punto es que intentar "recolectar basura" cuesta más de lo que ahorra, en prácticamente todos los casos. De hecho, es contraproducente.

Otto Otto
12 sept 2011 21:33:04

Ticket relacionado en Trac: http://core.trac.wordpress.org/ticket/20316

Stephen Harris Stephen Harris
30 sept 2013 00:19:32
Mostrar los 20 comentarios restantes
Todas las respuestas a la pregunta 3
11
53

Ahora lo son

A partir de WordPress 3.7 los transitorios expirados se eliminan durante las actualizaciones de la base de datos, ver #20316


Respuesta antigua

Si alguien no puede demostrarme lo contrario, parece que los transitorios no se eliminan automáticamente después de todo. Lo que lo hace peor es que, a diferencia de las opciones, no están garantizados de almacenarse en la base de datos. Por lo tanto, no hay una forma confiable de obtener una lista de todos los transitorios para verificar su expiración.

Un código provisional para realizar la limpieza si se usa la base de datos como almacenamiento:

add_action( 'wp_scheduled_delete', 'delete_expired_db_transients' );

function delete_expired_db_transients() {

    global $wpdb, $_wp_using_ext_object_cache;

    if( $_wp_using_ext_object_cache )
        return;

    $time = isset ( $_SERVER['REQUEST_TIME'] ) ? (int)$_SERVER['REQUEST_TIME'] : time() ;
    $expired = $wpdb->get_col( "SELECT option_name FROM {$wpdb->options} WHERE option_name LIKE '_transient_timeout%' AND option_value < {$time};" );

    foreach( $expired as $transient ) {

        $key = str_replace('_transient_timeout_', '', $transient);
        delete_transient($key);
    }
}
10 ene 2011 11:55:47
Comentarios

$time = $_SERVER['REQUEST_TIME']; y luego usar $time en la consulta SQL - no hagas eso. Maneja con más cuidado las variables/valores de $_SERVER para prevenir inyecciones SQL.

hakre hakre
10 ene 2011 14:01:55

@hakre hm... lo tomé de una presentación sobre rendimiento en PHP que lo recomendaba sobre usar time() que puede causar errores (la ejecución no es instantánea por naturaleza). El tiempo de solicitud lo establece PHP mismo, no proviene de ningún tipo de dato suministrado por el usuario. ¿Por qué es esto una vulnerabilidad?

Rarst Rarst
10 ene 2011 14:08:39

@Rarst: No dije que no deberías usarlo, solo deberías asegurarte de que esté codificado de manera segura para usarse dentro de la consulta SQL. Deberías hacer esto con cada variable de una fuente externa. Las variables $_SERVER podrían no estar configuradas como se espera, y en su lugar, ser establecidas por el usuario que realiza la solicitud. Solo quería promover buenas prácticas de codificación. Como siempre, para conocer el estado real de disponibilidad, consulta la documentación. Para PHP 4, por ejemplo, dicha variable no existe y podría ser sobrescrita por un encabezado personalizado o variable de entorno - http://php.net/manual/en/reserved.variables.server.php

hakre hakre
10 ene 2011 14:12:14

@hakre corregido (creo), gracias por el recordatorio de PHP4 por cierto (no puedo esperar a que WordPress deje de darle soporte)

Rarst Rarst
10 ene 2011 14:19:14

Eso se ve mucho mejor a mis ojos ;). Esperemos que no haya problemas con time() y los enteros negativos que podrían eliminar todos o ningún transient por accidente. Nunca confíes en un sistema en funcionamiento :P

hakre hakre
10 ene 2011 14:26:37

En caso de que no lo supieras, _ es un comodín de un solo carácter para las sentencias LIKE, y lo ideal sería escaparlo. :)

Denis de Bernardy Denis de Bernardy
10 ene 2011 15:42:05

@Denis sí, lo sé... Pero ¿no hay diferencia práctica en esta consulta?.. A menos que alguien logre nombrar una opción XtransientXtimeoutX o algo así.

Rarst Rarst
10 ene 2011 16:35:31

¿No deberías usar $wpdb->prepare() para protegerte adecuadamente de datos contaminados como lo que mencionaba @hakre? Esto también resolvería el escape del '_'. Lo recomendaría como una buena práctica.

Tom Auger Tom Auger
22 jun 2012 04:31:21

@Tom aparte de "para estar realmente seguro", esta consulta específica no necesita realmente prepare y no me molesté en agregarlo.

Rarst Rarst
22 jun 2012 11:35:16

Tienes razón ahora que lo miro. El (int) probablemente sea toda la protección que necesitas en esa variable del servidor.

Tom Auger Tom Auger
22 jun 2012 20:24:37

¿No debería ser _transient_timeout_%? Solo para asegurarnos de que realmente es el prefijo que usa WP ;)

kaiser kaiser
28 jul 2013 18:04:54
Mostrar los 6 comentarios restantes
9
21

Trasladando algunos de los comentarios de la discusión a una respuesta, con reescritura y reformateo...

Básicamente, todo se reduce a que, a menos que tengas un caso extremadamente excepcional, realmente no necesitan ser "recolectados como basura". Si nunca los recuperas, entonces no importa si están ahí o no.

Verás, los transitorios se almacenan en la tabla de opciones por defecto. En una instalación básica, la tabla de opciones tendrá quizás 100 entradas. Cada transitorio añade dos entradas más, pero incluso si tienes miles, no afectan la velocidad del sitio, ya que no se cargan automáticamente.

Al iniciar, WordPress carga las opciones en memoria, pero solo carga las opciones que tienen su bandera de autoload activada. Los transitorios no tienen esto, por lo que no se cargan en memoria. Solo los transitorios que realmente se usan más tarde incurrirán en un costo.

Desde la perspectiva de la base de datos, la tabla de opciones tiene índices tanto en el ID de la opción como en el nombre de la opción. Los transitorios siempre se cargan basados en el nombre (clave), por lo que las búsquedas son siempre selecciones simples en un único valor de clave. Por lo tanto, la búsqueda es O(log(n)) y es súper rápida. Con un Big-O de log(n), tendrías que llegar a millones y millones de filas antes de que se notara. Francamente, la sobrecarga en la configuración y finalización de la consulta, junto con la transferencia real de datos, es mucho más larga. La consulta en sí se ejecuta en esencialmente tiempo cero en comparación. Así que simplemente tener filas extra no utilizadas no afecta nada más que el uso de espacio en disco adicional.

La indexación en bases de datos es uno de esos conceptos profundos que no tienen sentido para las personas que no han entendido realmente lo que sucede detrás de escena. Las bases de datos están diseñadas para la recuperación rápida de datos, desde sus cimientos, y pueden manejar este tipo de cosas sin problemas. Esta es una lectura bastante buena: http://en.wikipedia.org/wiki/Index_(database)

Ahora, la limpieza de la manera más obvia (llamando a SQL DELETE sobre ellos) en realidad no los elimina de la base de datos. Solo los elimina del índice y marca la fila como "eliminada". Nuevamente, así es como funcionan las bases de datos. Para realmente liberar espacio en disco, tienes que continuar y hacer un OPTIMIZE TABLE después, y esta no es una operación rápida. Toma tiempo. Probablemente más tiempo del que vale la pena. Probablemente no es suficiente para ahorrar tiempo de CPU, en total.

Si tienes algún caso que está causando una inserción continua de nuevos transitorios que no se están utilizando, entonces necesitas encontrar el problema subyacente. ¿Qué está insertando estos transitorios? ¿Están usando una clave cambiante o mutante? Si es así, entonces el plugin o código que causa esto debería ser corregido para, básicamente, no hacer eso. Eso será más útil, porque es probable que el código que no los crea correctamente tampoco los recupere, y por lo tanto esté haciendo más trabajo del que debe.

Por otro lado, puede haber un caso donde los transitorios se crean para algo como cada publicación. Esto puede ser perfectamente aceptable. Yo mismo hago esto en SFC, para almacenar comentarios entrantes de Facebook. Cada publicación tiene un posible transitorio asociado, lo que significa dos filas extra por publicación. Si tienes 10k publicaciones, eventualmente tendrás 20k filas en la tabla de opciones. Esto no es malo ni lento, porque nuevamente, hay muy poca diferencia entre 100 filas y 20,000 filas en lo que a las bases de datos les importa. Todo está indexado. Es rapidísimo. Sub-sub-milisegundos.

Cuando empiezas a entrar en millones de filas, entonces me preocuparía. Cuando el tamaño de la tabla de opciones aumenta por encima de cientos de megabytes, entonces me preocuparía lo suficiente como para mirar más de cerca. Pero en términos generales, esto no es un problema excepto para casos extremos. Ciertamente no es un problema para nada más pequeño que algo como un gran sitio de noticias, con cientos de miles de publicaciones. Y para cualquier sitio lo suficientemente grande como para que sea un problema, deberías estar usando una caché de objetos externa de algún tipo, y en ese caso, los transitorios se almacenan automágicamente allí en lugar de en la base de datos.

12 sept 2011 21:09:10
Comentarios

NOTA: los transitorios sin expiración se cargan automáticamente, y no tener expiración es el valor predeterminado, por lo que cuando una aplicación/plugin crea muchos transitorios sin establecer una expiración, estarán consumiendo fragmentos de memoria en cada carga de página/publicación.

webaware webaware
29 jul 2013 03:48:50

No hay razón para usar un "transitorio sin expiración", porque eso es básicamente idéntico a una "opción" normal.

Otto Otto
30 jul 2013 02:43:05

Claro, pero es el valor predeterminado. Como tal, muchos autores de plugins están añadiendo transitorios sin expiración.

webaware webaware
30 jul 2013 03:19:35

Además, no es idéntico a una opción, ya que se purgará cuando se use una caché de objetos -- consulta el artículo reciente en WPEngine para más detalles.

webaware webaware
30 jul 2013 03:21:32

Bueno, la solución aquí es simple: No uses esos plugins. Lo están haciendo mal. Los transients no deben usarse como sesiones, no deberías usarlos sin un tiempo de expiración significativo, y no deberían tener claves mutables o cambiantes.

Otto Otto
30 jul 2013 19:12:57

:) (porque no es que el valor predeterminado de WordPress esté equivocado, ¿eh?)

webaware webaware
31 jul 2013 03:19:50

Eso depende. ¿Qué valor predeterminado razonable considerarías allí en su lugar?

Otto Otto
31 jul 2013 17:47:52

Digamos, 7 días. Si un autor de plugin/tema quiere algo más grande o pequeño, lo especificará. Si quieren autoload, no deberían tener que especificar 0 para la expiración (= infinito), pero eso es lo que tienen actualmente con el parámetro de expiración haciendo doble función como el parámetro sí/no de autoload. En cualquier caso, la expiración predeterminada tampoco debería llevar a autoload=sí por defecto; eso es simplemente pedir problemas.

webaware webaware
1 ago 2013 04:02:58

En mi opinión considerada, no especificar una expiración debería lanzar un error fatal y romper el sitio. Pero entonces, no estoy a cargo. Un transitorio sin expiración es estúpido y sin sentido. Si quieres usar la caché de objetos, entonces usa la caché de objetos directamente con las funciones wp_cache. Dicho esto, hay tickets para que futuras versiones de WordPress limpien los transitorios antiguos, principalmente porque es "antiestético" más que cualquier otra cosa.

Otto Otto
19 mar 2014 07:20:35
Mostrar los 4 comentarios restantes
2
20

Otto - No podría estar más en desacuerdo contigo. El problema es que, eventualmente, con tantos transitorios, el tamaño de la tabla se vuelve ridículo. No se necesitan millones de filas para ralentizar el sistema. Actualmente estoy lidiando con una tabla de opciones que tiene más de 130k filas y se bloquea regularmente. Debido a que el campo de valor es de tipo texto largo, incluso buscar solo las filas con "autoload" se convierte en una pesadilla de rendimiento. Esos campos de valor se almacenan por separado del resto de los datos de la fila. Aunque lógicamente es parte de la misma tabla, se deben realizar joins para recuperar las filas que deseas. Joins que ahora tardan una eternidad porque los datos que necesitas están esparcidos por todas partes en el disco. El perfilado (usando Jet Profiler para MySQL) ha demostrado esto.

Agregar auto-load a la clave agrupada podría ayudar a resolver este problema. Agrupar por Autoload DESC, ID ASC, por ejemplo, permitiría que todas las filas de autoload se agrupen primero en el disco. Aún así, creo que estarías enfrentando una enorme carga desde la perspectiva de la base de datos.

Personalmente, creo que el diseño de este sistema es una locura. La tabla de opciones parece haberse convertido en un cajón de sastre para muchas cosas. Eso está bien si el campo de valor es lo suficientemente pequeño como para incluirse en la misma página que el resto de los datos de la fila y puede indexarse efectivamente. Desafortunadamente, ese no es el caso. Quien diseñó esto necesita volver a la clase de Bases de Datos 101.

2 dic 2011 21:43:05
Comentarios

cierto, pero considera que cuando comenzó el desarrollo de WordPress, nadie pensó que llegaría a tener miles de plugins usando la tabla de opciones como su almacenamiento de datos :)

onetrickpony onetrickpony
2 dic 2011 22:39:26

@onetrickpony por eso es importante siempre tomarse el tiempo y hacer las cosas bien, ya sea que esperes que sea enorme algún día o no :)

Mahmoud Al-Qudsi Mahmoud Al-Qudsi
12 dic 2017 01:16:23