Depuración de consultas a la base de datos

Encontrar los cuellos de botella de la base de datos en WordPress puede llegar a complicarse sin querer. Y aunque tenemos el Query Monitor, sin duda no siempre es fácil acceder al problema.

Para usar Query Debug necesitaremos instalar una extensión.

wp package install runcommand/query-debug

A partir de aquí, sólo hemos de indicar la URL a analizar y listar sus consultas:

wp query-debug --url=https://example.com/2021/08/01/hello-world/ --format=table --path=/webs/example.com/

Esto nos devolverá algo tal que así:

+----------+----------------------------------------------------------------------------------------+----------------------------------------------------------------------------------------+
| seconds  | backtrace                                                                              | query                                                                                  |
+----------+----------------------------------------------------------------------------------------+----------------------------------------------------------------------------------------+
| 0.000137 | WP_CLI\Runner->load_wordpress, require('wp-settings.php'), include_once('/plugins/heal | SELECT VERSION()                                                                       |
|          | th-check/health-check.php'), require_once('/plugins/health-check/includes/class-health |                                                                                        |
|          | -check-site-status.php'), Health_Check_Site_Status->__construct, Health_Check_Site_Sta |                                                                                        |
|          | tus->init, Health_Check_Site_Status->prepare_sql_data                                  |                                                                                        |
| 0.000433 | wp, WP->main, WP->query_posts, WP_Query->query, WP_Query->get_posts                    | SELECT SQL_CALC_FOUND_ROWS  wpe21jz2_posts.ID FROM wpe21jz2_posts  WHERE 1=1  AND wpe2 |
|          |                                                                                        | 1jz2_posts.post_type = 'post' AND (wpe21jz2_posts.post_status = 'publish')  ORDER BY w |
|          |                                                                                        | pe21jz2_posts.post_date DESC LIMIT 0, 100                                              |
| 4.8E-5   | wp, WP->main, WP->query_posts, WP_Query->query, WP_Query->get_posts, WP_Query->set_fou | SELECT FOUND_ROWS()                                                                    |
|          | nd_posts                                                                               |                                                                                        |
| 0.000214 | require_once('wp-includes/template-loader.php'), get_front_page_template, get_query_te | SELECT   wpe21jz2_posts.* FROM wpe21jz2_posts  WHERE 1=1  AND wpe21jz2_posts.post_name |
|          | mplate, locate_block_template, resolve_block_template, get_block_templates, WP_Query-> |  IN ('front-page') AND (    0 = 1 ) AND wpe21jz2_posts.post_type = 'wp_template' AND ( |
|          | __construct, WP_Query->query, WP_Query->get_posts                                      | (wpe21jz2_posts.post_status = 'publish')) GROUP BY wpe21jz2_posts.ID ORDER BY wpe21jz2 |
|          |                                                                                        | _posts.post_date DESC                                                                  |
| 0.000108 | require_once('wp-includes/template-loader.php'), get_home_template, get_query_template | SELECT   wpe21jz2_posts.* FROM wpe21jz2_posts  WHERE 1=1  AND wpe21jz2_posts.post_name |
|          | , locate_block_template, resolve_block_template, get_block_templates, WP_Query->__cons |  IN ('home','index') AND (    0 = 1 ) AND wpe21jz2_posts.post_type = 'wp_template' AND |
|          | truct, WP_Query->query, WP_Query->get_posts                                            |  ((wpe21jz2_posts.post_status = 'publish')) GROUP BY wpe21jz2_posts.ID ORDER BY wpe21j |
|          |                                                                                        | z2_posts.post_date DESC                                                                |
| 9.2E-5   | require_once('wp-includes/template-loader.php'), include('/themes/twentytwentyone/inde | SELECT   wpe21jz2_posts.ID FROM wpe21jz2_posts  WHERE 1=1  AND (    0 = 1 ) AND wpe21j |
|          | x.php'), get_header, locate_template, load_template, require_once('/themes/twentytwent | z2_posts.post_type = 'wp_global_styles' AND ((wpe21jz2_posts.post_status = 'publish')) |
|          | yone/header.php'), wp_head, do_action('wp_head'), WP_Hook->do_action, WP_Hook->apply_f |  GROUP BY wpe21jz2_posts.ID ORDER BY wpe21jz2_posts.post_date DESC LIMIT 0, 1          |
|          | ilters, wp_enqueue_scripts, do_action('wp_enqueue_scripts'), WP_Hook->do_action, WP_Ho |                                                                                        |
|          | ok->apply_filters, gutenberg_experimental_global_styles_enqueue_assets, WP_Theme_JSON_ |                                                                                        |
|          | Resolver_Gutenberg::get_merged_data, WP_Theme_JSON_Resolver_Gutenberg::get_user_data,  |                                                                                        |
|          | WP_Theme_JSON_Resolver_Gutenberg::get_user_data_from_custom_post_type, wp_get_recent_p |                                                                                        |
|          | osts, get_posts, WP_Query->query, WP_Query->get_posts                                  |                                                                                        |
| 0.000332 | require_once('wp-includes/template-loader.php'), include('/themes/twentytwentyone/inde | SELECT   wpe21jz2_posts.ID FROM wpe21jz2_posts  WHERE 1=1  AND wpe21jz2_posts.post_typ |
|          | x.php'), get_footer, locate_template, load_template, require_once('/themes/twentytwent | e = 'post' AND ((wpe21jz2_posts.post_status = 'publish'))  ORDER BY wpe21jz2_posts.pos |
|          | yone/footer.php'), get_template_part, locate_template, load_template, require('/themes | t_date DESC LIMIT 0, 5                                                                 |
|          | /twentytwentyone/template-parts/footer/footer-widgets.php'), dynamic_sidebar, WP_Widge |                                                                                        |
|          | t->display_callback, WP_Widget_Recent_Posts->widget, WP_Query->__construct, WP_Query-> |                                                                                        |
|          | query, WP_Query->get_posts                                                             |                                                                                        |
+----------+----------------------------------------------------------------------------------------+----------------------------------------------------------------------------------------+

Actualizar todos los Salt-Keys

WordPress incluye, por seguridad, una serie de claves únicas y aleatorias para mejorar la seguridad de acceso al sistema, las llamadas «Salt Keys».

Se encuentran en el fichero WP-Config y se parecen a algo tal que esto:

define( 'AUTH_KEY', 'Em7t5e2Zq4GgaCff9d3AqtbDHJb4swf6JxAk' );
define( 'SECURE_AUTH_KEY', 'Ct4BtVLmvm2g8cQaBXSTZMz2CX65Uz5SuQNp' );
define( 'LOGGED_IN_KEY', '8qqZZDuBmAGUtVhRM4RkrdhhjGU9jhPFegRg' );
define( 'NONCE_KEY', 'x7B45gtBKjy2GN78PVnxKNQ6ntb579rBQDbX' );
define( 'AUTH_SALT', 'nUu8hgUtAGe8UQxXQzDmfhA42mZzNE6m6NFa' );
define( 'SECURE_AUTH_SALT', '2T4XD65j3Kx2JJcV3NCS3u5RjYNGmv9F5fPt' );
define( 'LOGGED_IN_SALT', 'xvXU9PMZEvMnTc3fe2XjY7wx7XBhAwE9AyZj' );
define( 'NONCE_SALT', 'zy2jyzwQUVq8fXeX5qsFZupNgfep9ptr8j6z' );

En cualquier caso, siempre tenemos la herramienta / API para generarlo.

Pero ¿qué ocurre si has de cambiar las claves de todos tus sitios WordPress por algún tipo de cambio de seguridad? Puedes hacerlo de la siguiente manera.

Lo primero que definiremos es a partir de qué carpeta ha de buscar en todo el sistema. En este caso va a ser en /webs/:

#####
HOST_PATH="/webs/"
#####

Actualizaremos WP-CLI:

# PONIENDO AL DIA WP-CLI

echo ""
echo "Actualizando WP-CLI a la última versión:"
wp cli check-update --quiet
wp cli update --quiet
wp cli version

Y, si no está disponible, instalará la extensión de FIND.

# INSTALANDO WP-CLI FIND

echo ""
echo "Revisando el buscador de WP-CLI:"
if [ `wp package list --fields=name --format=csv | grep 'wp-cli/find-command'` ]
then
  echo "Buscador de WP-CLI instalado."
else
  echo "Instalando buscador de WP-CLI:"
  wp package install wp-cli/find-command --quiet
  if [ -z wp package list --fields=name --format=csv | grep 'wp-cli/find-command' ]
  then
  echo "Buscador de WP-CLI instalado."
  else
    echo "Se ha producido un error. No se ha podido instalar el buscador de WP-CLI."
    echo "Prueba a instalarlo manualmente."
    echo ""
    echo "wp package install wp-cli/find-command"
    echo ""
    exit 1
  fi
fi

Una vez esté, buscaremos todos los sitios del sistema:

# BUSCANDO SITIOS

echo ""
echo "Buscando sitios WordPress:"

WP_DATA=`wp find $HOST_PATH --format=csv --fields=wp_path,version | awk FNR-1`
WP_TOTAL=`echo "$WP_DATA" | wc -l`

echo "Se ha encontrado un total de ${WP_TOTAL} WordPress".

Y actualizaremos sus Salt-Keys:

  echo ""
  echo "Actualizando Salt-Keys"
  echo ""

  WP_SALTS=`wp config shuffle-salts --force --path="${WP_PATH}"`

  echo "- Estado: ${WP_SALTS}"

Y, el código completo:

#!/bin/bash

#####
HOST_PATH="/webs/"
#####

# PONIENDO AL DIA WP-CLI

echo ""
echo "Actualizando WP-CLI a la última versión:"
wp cli check-update --quiet
wp cli update --quiet
wp cli version

# INSTALANDO WP-CLI FIND

echo ""
echo "Revisando el buscador de WP-CLI:"
if [ `wp package list --fields=name --format=csv | grep 'wp-cli/find-command'` ]
then
  echo "Buscador de WP-CLI instalado."
else
  echo "Instalando buscador de WP-CLI:"
  wp package install wp-cli/find-command --quiet
  if [ -z wp package list --fields=name --format=csv | grep 'wp-cli/find-command' ]
  then
  echo "Buscador de WP-CLI instalado."
  else
    echo "Se ha producido un error. No se ha podido instalar el buscador de WP-CLI."
    echo "Prueba a instalarlo manualmente."
    echo ""
    echo "wp package install wp-cli/find-command"
    echo ""
    exit 1
  fi
fi

# BUSCANDO SITIOS

echo ""
echo "Buscando sitios WordPress:"

WP_DATA=`wp find $HOST_PATH --format=csv --fields=wp_path,version | awk FNR-1`
WP_TOTAL=`echo "$WP_DATA" | wc -l`

echo "Se ha encontrado un total de ${WP_TOTAL} WordPress".

for WP_D in $WP_DATA
do
  echo ""
  echo "********************************************************************************"
  echo ""
  echo "WordPress"
  echo ""

  WP_PATH=`echo "$WP_D" | awk -F, 'NR { print $1 }'`
  echo "- Ruta: ${WP_PATH}"

  echo ""
  echo "Actualizando Salt-Keys"
  echo ""

  WP_SALTS=`wp config shuffle-salts --force --path="${WP_PATH}"`

  echo "- Estado: ${WP_SALTS}"

  echo ""
  echo "********************************************************************************"

done

echo ""
echo " -- FIN --"
echo ""
echo ""

Este código ha sido creado por:

Javier Casares

Sitio personal: Javier Casares
Sitio profesional: WPSysAdmin

Encontrar WordPress y actualizaciones pendientes

¿Sabes donde están los WordPress en tu servidor? ¿Y si alguno tiene actualizaciones pendientes?

Con este bash podrás indicar la ruta a partir de la cual buscar los WordPress y listar las actualizaciones que tenga pendientes.

Primero indicaremos la ruta a partir de la cual buscar:

HOST_PATH="/webs/"

Posteriormente actualiza WP-CLI:

# PONIENDO AL DIA WP-CLI

echo ""
echo "Actualizando WP-CLI a la última versión:"
wp cli check-update --quiet
wp cli update --quiet
wp cli version

Para buscar WordPress es necesaria la extensión del FIND. En caso de no estar, la instalará.

# INSTALANDO WP-CLI FIND

echo ""
echo "Revisando el buscador de WP-CLI:"
if [ `wp package list --fields=name --format=csv | grep 'wp-cli/find-command'` ]
then
  echo "Buscador de WP-CLI instalado."
else
  echo "Instalando buscador de WP-CLI:"
  wp package install wp-cli/find-command --quiet
  if [ -z wp package list --fields=name --format=csv | grep 'wp-cli/find-command' ]
  then
  echo "Buscador de WP-CLI instalado."
  else
    echo "Se ha producido un error. No se ha podido instalar el buscador de WP-CLI."
    echo "Prueba a instalarlo manualmente."
    echo ""
    echo "wp package install wp-cli/find-command"
    echo ""
    exit 1
  fi
fi

Buscaremos todos los sitios:

# BUSCANDO SITIOS

echo ""
echo "Buscando sitios WordPress:"

WP_DATA=`wp find $HOST_PATH --format=csv --fields=wp_path,version | awk FNR-1`
WP_TOTAL=`echo "$WP_DATA" | wc -l`

echo "Se ha encontrado un total de ${WP_TOTAL} WordPress".

A partir de aquí, mostramos la información de los WordPress que se encuentren:

  echo ""
  echo "WordPress"
  echo ""

  WP_PATH=`echo "$WP_D" | awk -F, 'NR { print $1 }'`
  echo "- Ruta:                ${WP_PATH}"

  WP_SITE_NAME=`wp option get blogname --path="${WP_PATH}"`
  echo "- Nombre:              ${WP_SITE_NAME}"

  WP_SITE_URL=`wp option get siteurl --path="${WP_PATH}"`
  echo "- URL:                 ${WP_SITE_URL}"

  WP_VERSION=`echo "$WP_D" | awk -F, 'NR { print $2 }'`
  echo "- Versión WP:          ${WP_VERSION}"

  WP_VERSION_UPD=`wp core check-update --path="${WP_PATH}" --format=csv | awk FNR-1 | awk -F, 'NR { print $1 }'`
  if [ $WP_VERSION_UPD ]
  then
    echo "- Actualización:       ${WP_VERSION_UPD}"
  fi

De los temas:

  echo ""
  echo "Themes"
  echo ""

  WP_THEMES=`wp theme list --fields=name,update,version,update_version --format=csv --path="${WP_PATH}" | awk FNR-1`
  WP_TOTAL_THEMES=`echo "$WP_THEMES" | wc -l`
  echo "- Temas:               ${WP_TOTAL_THEMES} temas".

  for WP_T in $WP_THEMES
  do
    WP_THEME_UPD=`echo "${WP_T}" | awk -F, 'NR { print $2 }'`
    if [ $WP_THEME_UPD == "available" ]
    then
      WP_THEME_NAME=`echo "${WP_T}" | awk -F, 'NR { print $1 }'`
      WP_THEME_ACT=`echo "${WP_T}" | awk -F, 'NR { print $3 }'`
      WP_THEME_NEW=`echo "${WP_T}" | awk -F, 'NR { print $4 }'`
      echo "- Tema actualizable:   ${WP_THEME_NAME} ${WP_THEME_ACT} -> ${WP_THEME_NEW}"
    fi
  done

Y de los plugins:

  echo ""
  echo "Plugins"
  echo ""
  WP_PLUGINS=`wp plugin list --fields=name,update,version,update_version --format=csv --path="${WP_PATH}" | awk FNR-1`
  WP_TOTAL_PLUGINS=`echo "$WP_PLUGINS" | wc -l`
  echo "- Plugins:             ${WP_TOTAL_PLUGINS} plugins".

  for WP_P in $WP_PLUGINS
  do
    WP_PLUGIN_UPD=`echo "${WP_P}" | awk -F, 'NR { print $2 }'`
    if [ $WP_PLUGIN_UPD == "available" ]
    then
      WP_PLUGIN_NAME=`echo "${WP_P}" | awk -F, 'NR { print $1 }'`
      WP_PLUGIN_ACT=`echo "${WP_P}" | awk -F, 'NR { print $3 }'`
      WP_PLUGIN_NEW=`echo "${WP_P}" | awk -F, 'NR { print $4 }'`
      echo "- Plugin actualizable: ${WP_PLUGIN_NAME} ${WP_PLUGIN_ACT} -> ${WP_PLUGIN_NEW}"
    fi
  done

Y el bash script completo aquí:

#!/bin/bash

#####
HOST_PATH="/webs/"
#####

# PONIENDO AL DIA WP-CLI

echo ""
echo "Actualizando WP-CLI a la última versión:"
wp cli check-update --quiet
wp cli update --quiet
wp cli version

# INSTALANDO WP-CLI FIND

echo ""
echo "Revisando el buscador de WP-CLI:"
if [ `wp package list --fields=name --format=csv | grep 'wp-cli/find-command'` ]
then
  echo "Buscador de WP-CLI instalado."
else
  echo "Instalando buscador de WP-CLI:"
  wp package install wp-cli/find-command --quiet
  if [ -z wp package list --fields=name --format=csv | grep 'wp-cli/find-command' ]
  then
  echo "Buscador de WP-CLI instalado."
  else
    echo "Se ha producido un error. No se ha podido instalar el buscador de WP-CLI."
    echo "Prueba a instalarlo manualmente."
    echo ""
    echo "wp package install wp-cli/find-command"
    echo ""
    exit 1
  fi
fi

# BUSCANDO SITIOS

echo ""
echo "Buscando sitios WordPress:"

WP_DATA=`wp find $HOST_PATH --format=csv --fields=wp_path,version | awk FNR-1`
WP_TOTAL=`echo "$WP_DATA" | wc -l`

echo "Se ha encontrado un total de ${WP_TOTAL} WordPress".

for WP_D in $WP_DATA
do
  echo ""
  echo "********************************************************************************"
  echo ""
  echo "WordPress"
  echo ""

  WP_PATH=`echo "$WP_D" | awk -F, 'NR { print $1 }'`
  echo "- Ruta:                ${WP_PATH}"

  WP_SITE_NAME=`wp option get blogname --path="${WP_PATH}"`
  echo "- Nombre:              ${WP_SITE_NAME}"

  WP_SITE_URL=`wp option get siteurl --path="${WP_PATH}"`
  echo "- URL:                 ${WP_SITE_URL}"

  WP_VERSION=`echo "$WP_D" | awk -F, 'NR { print $2 }'`
  echo "- Versión WP:          ${WP_VERSION}"

  WP_VERSION_UPD=`wp core check-update --path="${WP_PATH}" --format=csv | awk FNR-1 | awk -F, 'NR { print $1 }'`
  if [ $WP_VERSION_UPD ]
  then
    echo "- Actualización:       ${WP_VERSION_UPD}"
  fi

  echo ""
  echo "Themes"
  echo ""

  WP_THEMES=`wp theme list --fields=name,update,version,update_version --format=csv --path="${WP_PATH}" | awk FNR-1`
  WP_TOTAL_THEMES=`echo "$WP_THEMES" | wc -l`
  echo "- Temas:               ${WP_TOTAL_THEMES} temas".

  for WP_T in $WP_THEMES
  do
    WP_THEME_UPD=`echo "${WP_T}" | awk -F, 'NR { print $2 }'`
    if [ $WP_THEME_UPD == "available" ]
    then
      WP_THEME_NAME=`echo "${WP_T}" | awk -F, 'NR { print $1 }'`
      WP_THEME_ACT=`echo "${WP_T}" | awk -F, 'NR { print $3 }'`
      WP_THEME_NEW=`echo "${WP_T}" | awk -F, 'NR { print $4 }'`
      echo "- Tema actualizable:   ${WP_THEME_NAME} ${WP_THEME_ACT} -> ${WP_THEME_NEW}"
    fi
  done

  echo ""
  echo "Plugins"
  echo ""
  WP_PLUGINS=`wp plugin list --fields=name,update,version,update_version --format=csv --path="${WP_PATH}" | awk FNR-1`
  WP_TOTAL_PLUGINS=`echo "$WP_PLUGINS" | wc -l`
  echo "- Plugins:             ${WP_TOTAL_PLUGINS} plugins".

  for WP_P in $WP_PLUGINS
  do
    WP_PLUGIN_UPD=`echo "${WP_P}" | awk -F, 'NR { print $2 }'`
    if [ $WP_PLUGIN_UPD == "available" ]
    then
      WP_PLUGIN_NAME=`echo "${WP_P}" | awk -F, 'NR { print $1 }'`
      WP_PLUGIN_ACT=`echo "${WP_P}" | awk -F, 'NR { print $3 }'`
      WP_PLUGIN_NEW=`echo "${WP_P}" | awk -F, 'NR { print $4 }'`
      echo "- Plugin actualizable: ${WP_PLUGIN_NAME} ${WP_PLUGIN_ACT} -> ${WP_PLUGIN_NEW}"
    fi
  done

  echo "********************************************************************************"

done

echo ""
echo " -- FIN --"
echo ""
echo ""

Este código ha sido creado por:

Javier Casares

Sitio personal: Javier Casares
Sitio profesional: WPSysAdmin

Ejecutar WP-CLI siempre como root

Aunque no está recomendado, en algunos entornos es posible que se trabaje como root y que sea necesario lanzar WP-CLI.

Cuando ejecutamos WP-CLI como root, nos aparece un mensaje diciendo que para lanzar ese comando hemos de añadir el parámetro --allow-root al comando. Y como se puede hacer pesado, aquí un pequeño cambio para que siempre que lo ejecutemos incluya ese parámetro:

vim ~/.bashrc

En la parte final del fichero, podemos agregar esto:

wp() {
  /usr/local/bin/wp "$@" --allow-root
}

Lo que hace es modificar el alias de wp por el nuevo, añadiendo a cualquier petición el parámetro.

Una vez lo guardemos, ejecutaremos la actualización:

source ~/.bashrc

Autocompletar para WP-CLI

Cuando se trabaja en bash, una de las herramientas más habituales es la de usar el tabulador para autocompletar comandos. Y WP-CLI incorpora uno.

Si queremos que nuestra consola disponga de la funcionalidad, debemos descargarnos la herramienta e implementarla en nuestra cuenta.

cd ~
mkdir wp-completion
cd wp-completion
wget https://github.com/wp-cli/wp-cli/raw/master/utils/wp-completion.bash

Una vez descargado, guardaremos el comando para que se pueda ejecutar en el bash.

echo "source /home/wordpress/wp-completion/wp-completion.bash" >> ~/.bashrc
source ~/.bashrc

Y con esto tendremos nuestro autocompletado lanzado con el tabulador.

Cron para WordPress MultiSite

Cuando decidimos ejecutar los crones en WordPress, hemos de añadir habitualmente una línea por cada uno de los sitios. En el caso de WordPress MultiSite, una línea por cada subsitio.

Con este bash podemos indicar el path del WordPress Multiste, que liste todos los sitios incluidos, y que lance los eventos pendientes a ejecutar.

#!/bin/bash
WP_PATH="/webs/example.com/"

SITE_URLS=`wp site list --fields=url --archived=0 --deleted=0 --format=csv --path="$WP_PATH" | sed 1d`

for SITE_URL in $SITE_URLS
do
  wp cron event run  --due-now --url="$SITE_URL" --path="$WP_PATH" --quiet
done