36 URLs en 500 desde el cutover: las mismas 9 cartas de otras semanas en fr/it/en/pt. En espanol iban bien, y contra CDMON las mismas URLs daban 200, o sea que era regresion nuestra. La reescritura de enlaces internos al idioma activo llamaba a url_to_postid() una vez por enlace, y estas cartas traen unos 40. Cuando la URL no encaja en ninguna regla de reescritura, la WP_Query que monta esa funcion se queda sin clausula que la acote y se trae las 32.311 entradas CON su contenido, dos veces por peticion. 256 MB agotados en class-wpdb.php y 500. Se sustituye por fea_href_a_post_id(): ultimo segmento del path y una consulta con LIMIT 1, que no puede degenerar, cacheada por peticion. El ORDER BY reproduce a quien sirve WordPress esa misma URL, y no es cosmetico: hay 4 slugs compartidos por una pagina de primer nivel y una entrada, donde gana la pagina, y 58 compartidos por dos entradas -duplicados del import de Joomla- donde gana la de post_date mas reciente. Mi primer intento ordenaba por ID ASC y en esos 58 habria traducido el enlace equivocado. Verificado: 116/116 cartas traducidas en 200, 200 entradas al azar en 200, E2E 13/13, y el resolutor nuevo coincide con url_to_postid() en 461 de 461 slugs de los casos donde url_to_postid() no revienta. La consulta gorda ha desaparecido del performance_schema. Van dos centinelas a la suite E2E, una carta italiana y una francesa de las que fallaban. Nota de metodo para el futuro: esto no se reproduce con wp-cli, porque Polylang no instancia su frontend en CLI y el filtro sale antes de tiempo. Se cazo mirando events_statements_history_long en MySQL mientras se pedia la pagina por HTTP. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
3.6 KiB
Los 500 de las cartas traducidas (url_to_postid())
✅ RESUELTO — 4-ago-2026, 02:00 UTC
36 URLs devolvían 500: las mismas 9 «cartas de otras semanas» en los cuatro idiomas traducidos. En español las 9 iban bien.
/fr/lumiere-et-phare/ /it/luce-e-faro/ /en/light-and-beacon/ /pt/luz-e-farol/ … ×9
Era una regresión del cutover, no algo heredado: las mismas URLs contra CDMON respondían 200.
La causa
fea-homepage.php reescribe los enlaces internos al idioma activo, y para eso llamaba a
url_to_postid() una vez por enlace. Estas cartas traen unos 40.
url_to_postid() acaba construyendo una WP_Query a partir de las reglas de reescritura. Cuando
la URL no encaja en ninguna, la query se queda sin cláusula que la acote y sale esto:
SELECT wp_posts.* FROM wp_posts WHERE 1=1 AND wp_posts.post_type = 'post' ORDER BY post_date DESC
Las 32.311 entradas con su contenido entero. Dos veces por petición. Los 256 MB de PHP se
agotaban en class-wpdb.php:2322 y Apache devolvía el 500.
Encaja con el patrón observado: el filtro se salta el español (if (!$lang || $lang === 'es') return), que es justo el idioma que no fallaba, y solo actúa en is_singular().
El arreglo
Se sustituye url_to_postid() por fea_href_a_post_id(): la estructura de enlaces es
/%postname%/, así que basta el último segmento del path y una consulta con LIMIT 1, que no
puede degenerar por rara que sea la URL. El resultado se cachea por petición.
El orden del ORDER BY no es cosmético, reproduce a quién sirve WordPress esa misma URL:
| caso | cuántos | criterio |
|---|---|---|
| página de primer nivel con el mismo slug que una entrada | 4 | gana la página (reglas verbosas de reescritura) |
| dos entradas con el mismo slug (duplicados del import de Joomla) | 58 | gana la de post_date más reciente |
Ordenar por ID ASC, que fue el primer intento, devolvía la entrada vieja y habría traducido el
enlace equivocado en esos 58 casos.
Verificación
| Las 116 cartas traducidas (29 × 4 idiomas) | 116/116 en 200 |
| Muestra de 200 entradas al azar (40 por idioma) | todas en 200 |
fea_href_a_post_id() vs url_to_postid(), 61 slugs repetidos + 400 al azar |
461/461 idénticos |
| La consulta gorda | desaparecida: la que más filas devuelve ahora son 640 de wp_options |
| Suite E2E | 13/13 en 200 |
| Errores PHP y 5xx tras el despliegue | 0 y 0 |
Los enlaces se siguen reescribiendo, y algunos más que antes: url_to_postid() fallaba en
URLs que sí resuelven bien por slug. Comprobado contra CDMON que los nuevos pares son de verdad la
misma entrada (mismo grupo de traducción de Polylang): como-bendecir-la-mesa →
comment-benir-la-table, felices-6 → heureux, 3-temario → programme…
Centinelas
tools/e2e/sites/www.json incorpora carta-trad-it y carta-trad-fr, dos de las que reventaban.
Si esto vuelve, lo dice la suite.
⚠️ Para la próxima
url_to_postid() no es seguro con URLs arbitrarias en un sitio grande. El comentario que ya
había en el fichero decía que en los listados agotaba memoria, y por eso se había acotado a
is_singular(). La cura se quedó corta: el problema no era el listado, era la función.
Y una trampa de método: esto no se reproduce con wp-cli. Polylang no instancia su clase de
frontend en CLI, así que el filtro sale antes de tiempo y no pasa nada. Se cazó mirando
performance_schema.events_statements_history_long en MySQL mientras se pedía la página por HTTP,
que muestra la secuencia real de sentencias de la petición.