Files
feadulta/docs/cutover/500-cartas-traducidas-url-to-postid.md
T
rafa 78879aef68 fea-homepage: fuera url_to_postid(), reventaba 36 cartas traducidas
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>
2026-08-03 22:02:10 -04:00

79 lines
3.6 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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:
```sql
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.