El problema: PHP consumiendo 4 GB de RAM por cada visita al calendario
El sitio en cuestión es una asociación con una agenda de eventos activa, gestionada con The Events Calendar 6.16.5 y Events Calendar PRO sobre WordPress 7.0 y PHP 8.2. Con más de 400 eventos publicados acumulados a lo largo de los años, el servidor empezó a registrar OOM de forma recurrente. El momento más crítico: cuando los bots de Google rastreaban la URL /calendario/ generaban varias peticiones simultáneas que agotaban la memoria en minutos.
Los logs de PHP señalaban siempre el mismo punto: wp-includes/class-wpdb.php. El proceso llegaba a consumir hasta 4 GB de RAM antes de ser terminado por el sistema operativo.
La causa raíz: The Events Calendar, cuando opera sobre wp_posts, ejecuta JOINs con wp_postmeta para recuperar fechas y metadatos de cada evento. Sin límite de registros y con cientos de eventos, cada consulta se convierte en una operación masiva. Con peticiones concurrentes de rastreadores, la memoria se agota en segundos.
Por qué los bots lo disparan especialmente: un rastreador no espera entre páginas. Lanza varias peticiones en paralelo. Si cada visita al calendario consume 1-2 GB de RAM, tres peticiones simultáneas de Googlebot bastan para tumbar el servidor. El tráfico humano rara vez genera esa concurrencia; el tráfico de bots, sí.
Por qué estaba desactivado TEC Custom Tables
The Events Calendar lleva años desarrollando un sistema de tablas propias (Custom Tables V1, CT1) diseñado exactamente para este escenario: en lugar de consultar wp_posts, TEC usa tablas propias con índices optimizados por fecha. Las consultas son órdenes de magnitud más eficientes.
Las cuatro tablas que crea CT1:
| Tabla | Registros | Función |
|---|---|---|
wp_tec_events |
722 | Datos principales: fechas, timezone, hash único |
wp_tec_occurrences |
963 | Ocurrencias de eventos recurrentes |
wp_tec_series_relationships |
485 | Relaciones entre series de eventos |
wp_tec_kv_cache |
334 | Caché interna del plugin |
En producción, el wp-config.php tenía esta constante que desactivaba todo el sistema CT1:
define('TEC_CUSTOM_TABLES_V1_DISABLED', true);
Esta constante es habitual en sitios que tuvieron problemas durante la migración inicial a CT1, o donde alguien la añadió como "solución temporal" a algún bug y nunca la retiró. Las tablas CT1 existían en la base de datos con datos correctos — TEC las había creado y mantenido — pero el plugin las ignoraba completamente y seguía consultando wp_posts.
El primer intento fallido: simplemente quitar la constante
La lógica parecía directa: si la constante desactiva CT1, quitarla debería reactivarlo. Se eliminó la línea del wp-config.php y se esperó.
El resultado fue OOM inmediato. Todos los procesos PHP al 100% de CPU y RAM en cuestión de segundos. La situación fue peor que antes de intervenir.
El motivo: el estado de migración guardado en la base de datos era migration-not-required. Al arrancar con CT1 activo, TEC lee ese estado y lo interpreta como "todavía no se ha migrado nada — hay que lanzar el wizard de migración". El problema crítico: lo lanza en cada petición HTTP, no en un proceso en segundo plano. Con el wizard intentando migrar 400+ eventos en cada pageview, el servidor colapsa en segundos.
No hagas esto: quitar TEC_CUSTOM_TABLES_V1_DISABLED de wp-config.php sin preparar antes la base de datos provoca OOM inmediato si el estado de migración no es migration-complete. Restaurar el backup y volver a empezar.
Entorno de test: clonar producción antes de tocar nada
Tras el fallo del primer intento, se creó un entorno de test completo antes de volver a intervenir en producción:
- Subdominio de test apuntando al mismo servidor
- Clon del directorio de producción
- Copia de la base de datos de producción en una BD separada
wp-config.phpdel test apuntando a la BD de test
Todo el proceso se validó en el entorno de test antes de ejecutarlo en producción. Es el paso que más tiempo "parece" que cuesta pero que en realidad evita que un error afecte al sitio real con usuarios activos.
El orden correcto de activación: 4 pasos
La clave es preparar el estado de la base de datos antes de modificar wp-config.php. Si TEC arranca y ve CT1 activo con el estado migration-complete en la BD, no lanza el wizard y empieza a consultar directamente las tablas CT1.
Marcar migration-complete en la base de datos
Ejecutar en la BD antes de tocar wp-config.php. El valor 1751500000 es un Unix timestamp pasado — puede ser cualquier valor numérico en el pasado:
INSERT INTO wp_options (option_name, option_value, autoload)
VALUES ('tec_ct1_migration_state',
'a:3:{s:18:"complete_timestamp";i:1751500000;s:5:"phase";s:20:"migration-complete";s:19:"preview_unsupported";b:0;}',
'yes')
ON DUPLICATE KEY UPDATE option_value=VALUES(option_value);
Marcar la inicialización de CT1 como completa
Dos registros adicionales que TEC comprueba para verificar que las tablas CT1 están inicializadas. El valor 1783036000 es un timestamp futuro (aproximadamente un año adelante):
INSERT INTO wp_options (option_name, option_value, autoload)
VALUES ('tec_timed_tec_custom_tables_v1_initialized',
'a:3:{s:3:"key";s:32:"tec_custom_tables_v1_initialized";s:5:"value";i:1;s:10:"expiration";i:1783036000;}',
'yes')
ON DUPLICATE KEY UPDATE option_value=VALUES(option_value);
INSERT INTO wp_options (option_name, option_value, autoload)
VALUES ('tec_timed_tec_custom_tables_v1_ecp_initialized',
'a:3:{s:3:"key";s:36:"tec_custom_tables_v1_ecp_initialized";s:5:"value";i:1;s:10:"expiration";i:1783036000;}',
'yes')
ON DUPLICATE KEY UPDATE option_value=VALUES(option_value);
Eliminar la constante de wp-config.php
Solo después de ejecutar los SQL anteriores, eliminar la constante. Desde SSH:
sed -i '/TEC_CUSTOM_TABLES_V1_DISABLED/d' /var/www/vhosts/tudominio.es/httpdocs/wp-config.php
Flush de Redis (si el sitio usa object cache)
Si el sitio tiene Redis como object cache — habitual en entornos con Plesk o cPanel con Redis Object Cache — limpiar la caché para que TEC no lea estado obsoleto:
redis-cli FLUSHALL
Resultado
| Momento | Estado del servidor |
|---|---|
| Antes de intervenir | OOM cada 5-10 minutos, PHP a 4 GB RAM por proceso |
| Primer intento (sin SQL previo) | OOM inmediato, 100% CPU, caída total del sitio |
| Activación correcta (SQL previo + constante) | Sin OOM desde el primer momento, procesos PHP a 250-300 MB |
La diferencia de consumo de RAM es de un factor 10-16x. Con CT1 activo, TEC consulta sus propias tablas indexadas por fecha en lugar de hacer JOINs sobre wp_posts y wp_postmeta. Las consultas al calendario pasan de ser operaciones que escanean miles de registros genéricos a consultas específicas sobre índices de fecha dedicados.
Notas para el mantenimiento futuro
El mu-plugin de límite sigue siendo útil
El sitio tenía activo un mu-plugin que limita las consultas de TEC a 50 eventos por página y los feeds iCal a 200 registros. Con CT1 activo este límite es menos crítico porque las consultas son mucho más eficientes, pero se mantiene como salvaguarda. Ante un pico de tráfico inesperado o un rastreador agresivo, el límite de paginación protege igualmente.
Si se reactiva la constante
Advertencia: si en algún momento se vuelve a añadir define('TEC_CUSTOM_TABLES_V1_DISABLED', true) al wp-config.php — por cualquier motivo, incluida una actualización que restaure el archivo — TEC vuelve a consultar wp_posts y el OOM puede reaparecer de inmediato. Si se detectan problemas con CT1 en el futuro, la solución es investigar la causa concreta, no desactivar el sistema de tablas.
¿Tu asociación o entidad tiene una web con agenda de eventos lenta?
Asociaciones culturales, clubs deportivos, federaciones, entidades sociales — cualquier organización que publique eventos de forma recurrente en WordPress puede encontrarse con este problema al superar los 200-300 eventos acumulados. El síntoma más frecuente no es una caída total, sino una web que va progresivamente más lenta hasta que el hosting avisa de consumo excesivo de recursos.
El problema es técnico pero la solución no requiere cambiar de plataforma ni de hosting. En la mayoría de casos se resuelve con una intervención puntual: diagnóstico, aplicación del fix y configuración de salvaguardas para que no vuelva a ocurrir.
¿Tu web con The Events Calendar va lenta o cae bajo carga?
Puedo revisar el estado de las tablas CT1, el consumo de memoria real y la configuración del plugin en tu sitio. Si el problema es este, se resuelve en una sesión. Si es otro, también lo identificamos.
Ver servicio de mantenimiento Otro caso de optimización WordPress