Infocurci - programmatore Php Roma
Infocurci - programmatore Php Roma
"faster than 98% of all tested websites" (tools.pingdom.com) - 100/100 Google PageSpeed Insights - Benvenuti :)

Cms / framework : WooCommerce aggiunge record alla tabella wp_options, la soluzione

Una delle pecche maggiori di WooCommerce è l'assenza di una valida garbage collection, che ripulisca la tabella wp_options dalle sessioni degli utenti. Elaboriamo uno script per la risoluzione del problema.

Cms / framework

Quando un utente entra in un sito Wordpress basato sul plugin WooCommerce, la tabella wp_options viene popolata di diversi record per tracciare la sessione di navigazione. Sono almeno una decina le variabili aggiunte per ciascun utente, che WooCommerce lascia nel database anche quando la sessione viene terminata, a prescindere dall'inoltro o meno di ordini. Va da se che tutto questo porta ad un rapido aumento delle dimensioni della tabella wp_options, peraltro condivisa con altri plugin e con il core di Worpress stesso. Le conseguenze pratiche sono alquanto fastidiose, sia in termini di prestazioni che di limiti tecnici consentiti dai fornitori di hosting.

La soluzione è semplice, basta entrare nel pannello di amministrazione di Wordpress, cliccare su "WooCoomerce", scegliere il link "System Status". Nella schermata che viene aperta, cliccare sul tab "Tools" e quindi su "Clear all sessions" a fianco a "Customer Sessions".

Tutto bene, non fosse per due problemi:

  • questa soluzione cancella tutte le sessioni (comprese quelle in corso, per cui un cliente potrebbe trovarsi azzerato il proprio carrello... e probabilmente non tornare mai più sul vostro sito!)
  • questa soluzione potrebbe non funzionare se siete arrivati "troppo tardi", cioè se la tabella è diventata ormai enorme e le prestazioni del vostro spazio di hosting non consentono allo script di funzionare a dovere, per via di un timeout nella query
  •  

Per ovviare a queste problematiche, ho elaborato una patch, da caricare nella root del vostro server e richiamare via browser.


        <?php
            /* procedura che cancella le sessioni obsolete da wp_options */
            $connessione = mysql_connect('HOST','USER','PASSWORD') or die('errore nella connessione al server');
            $baseDati = mysql_select_db('DB') or die('errore nella connessione al database');
           
            //estrae il primo #id record con scadenza superiore a oggi
            $data_string = mktime(0,0,0,date('n'),date('j')+1,date('Y'));

            $sql = 'select unix_timestamp("'.date("Y-m-d H:i:s",$data_string).'")';
            $result = mysql_query($sql) or die(mysql_error());
            $ts_limite = mysql_result($result,0,0);
           
            $sql = 'select count(*) from wp_options';
            $result = mysql_query($sql) or die(mysql_error());
            $record_precedenti = mysql_result($result,0,0);
           
            $record_da_eliminare = array();
           
            $dati_da_elaborare = array('_wc_session_','_transient_timeout_wc_related_','_transient_wc_related_','_transient_wc_average_rating_','_transient_timeout_wc_average_rating_','_transient_timeout_wc_rating_count_','_transient_wc_rating_count_','_transient_wc_max_related_','_transient_timeout_wc_max_related_','_transient_wc_review_count_','_transient_timeout_wc_review_count_');
           
            echo '<table border="1">';
            foreach($dati_da_elaborare as $dato){
                $conta_elimina = 0;
                $sql = 'select count(*) from wp_options  where SUBSTRING(option_name,1,'.strlen($dato).') ="'.$dato.'"';
                $result = mysql_query($sql) or die(mysql_error());
                $totale = mysql_result($result,0,0);
                if($totale > 300){
                    $sql = 'select option_id from wp_options  where SUBSTRING(option_name,1,'.strlen($dato).') ="'.$dato.'"  order by option_id asc limit 0,50000';
                    $result = mysql_query($sql) or die(mysql_error());
                    $verifica = mysql_num_rows($result);
                   
                    while($row = mysql_fetch_array($result)){
                        $conta_elimina++;
                        if($totale < 50000 && $conta_elimina == ($totale-200)){ //patch per evitare che svuoti completamente la tabella quando ci sono pochi record
                            break;
                        }
                        $record_da_eliminare[] = $row['option_id'];
                    }
                }

                echo '<tr> <td>'.$dato.' </td> <td> '.$totale.' record trovati </td> <td> '.$conta_elimina.' record da cancellare </td> </tr>';
            }
           
            if(!empty($record_da_eliminare)){
           
                $sql = 'delete from wp_options where option_id in ('.implode(',',$record_da_eliminare).')';
                $result = mysql_query($sql) or die(mysql_error());
            }
            echo '</table> <br /><br />';
           
            $sql = 'select count(*) from wp_options';
            $result = mysql_query($sql) or die(mysql_error());
            $record_attuali = mysql_result($result,0,0);
           
            echo 'Operazione conclusa. Attualmente la tabella contiene '.number_format($record_attuali,0,',','.').' righe, mentre prima ne conteneva '.number_format($record_precedenti,0,',','.');
        ?>
   
   

Questa patch cancella "quasi" tutti i record (ne lascia almeno 200 per evitare di scollegare carrelli attivi) e ne elimina al massimo 50mila per ogni tipo di variabile di sessione. Questo per evitare timeout sui server meno performanti. Nel caso in cui abbiate bisogno di cancellare più di 50mila record, potete premere F5 sul browser e lanciare la patch più volte: un riepilogo vi indicherà lo stato dei record per ciascuna variabile.
Nulla vi vieta di installare questa patch in un processo automatico (crontab) da eseguire ogni giorno, anche più volte.


Ovviamente, vi consiglio di eseguire un backup del database oppure di lanciare la patch in una copia locale del sito; non posso infatti conoscere a priori quali altri plugin avete installato. Ho testato questo script su siti di produzione e non ho riscontrato problemi, tuttavia con l'utilizzo mi sollevate da qualsiasi responsabilità relativa ad eventuali danni/problemi sorti sul vostro db. Dovrebbe esser scontato, ma non si sa mai