Sunt tranzienții colectați automat ca gunoi?
Această întrebare mi-a venit în minte după ce am văzut Feed-urile RSS tranziente din wp_options nu sunt eliminate automat?
Tranzienții ar trebui să expire și să fie șterși. Cu toate acestea, singura modalitate în care văd că acest lucru este gestionat este atunci când un tranzient este expirat și este solicitat, apoi este șters în timpul cererii.
Ce se întâmplă dacă un tranzient este expirat dar nu mai este solicitat după aceea? Din descrierea din Codex am crezut că există un fel de colectare automată a gunoiului. Acum nu mai sunt așa sigur și nu pot găsi niciun cod care să efectueze acest lucru.
Deci va rămâne blocat în baza de date pentru totdeauna?
Acum sunt
Începând cu WordPress 3.7, transient-urile expirate sunt șterse la actualizarea bazei de date, vezi #20316
Răspuns vechi
Dacă cineva nu-mi poate demonstra contrariul, se pare că transient-urile nu sunt colectate ca gunoi după expirare. Ceea ce înrăutățește situația este faptul că, spre deosebire de opțiuni, ele nu sunt garantate a fi stocate în baza de date. Deci nu există o metodă sigură de a obține o listă cu toate transient-urile pentru a le verifica expirarea.
Un cod improvizat pentru a face colectarea gunoiului dacă baza de date este folosită pentru stocare:
add_action( 'wp_scheduled_delete', 'delete_expired_db_transients' );
function delete_expired_db_transients() {
global $wpdb, $_wp_using_ext_object_cache;
if( $_wp_using_ext_object_cache )
return;
$time = isset ( $_SERVER['REQUEST_TIME'] ) ? (int)$_SERVER['REQUEST_TIME'] : time() ;
$expired = $wpdb->get_col( "SELECT option_name FROM {$wpdb->options} WHERE option_name LIKE '_transient_timeout%' AND option_value < {$time};" );
foreach( $expired as $transient ) {
$key = str_replace('_transient_timeout_', '', $transient);
delete_transient($key);
}
}

$time = $_SERVER['REQUEST_TIME']; și apoi utilizarea lui $time în interogarea SQL - nu face asta. Manipulează cu mai multă grijă variabilele / valorile $_SERVER pentru a preveni injecțiile SQL.

@hakre hm... Am luat asta dintr-o prezentare despre performanța PHP care o recomanda în locul folosirii time()
care poate cauza bug-uri (execuția nu este instantanee prin natura sa). Timpul cererii este setat de PHP însuși, nu provine din nicio sursă de date furnizată de utilizator. De ce este aceasta o vulnerabilitate?

@Rarst: Nu am spus că nu ar trebui să-l folosești, ci doar să te asiguri că este codificat în siguranță pentru a fi utilizat în interogarea SQL. Ar trebui să faci asta cu fiecare variabilă dintr-o sursă externă. Variabilele $_SERVER ar putea să nu fie setate conform așteptărilor, și în schimb, să fie setate chiar de către utilizatorul care face cererea. Voiam doar să propag o bună practică de codare. Ca întotdeauna, pentru a afla starea reală a disponibilității, consultă documentația. Pentru PHP 4, de exemplu, o astfel de variabilă nu există și ar putea fi suprascrisă de un antet personalizat sau o variabilă de mediu - http://php.net/manual/en/reserved.variables.server.php

@hakre reparat (cred), mulțumesc pentru amintirea despre PHP4 (abia aștept ca WordPress să renunțe la suportul pentru el)

Arată mult mai bine în ochii mei ;). Să sperăm că nu vor fi probleme cu time() și numerele negative care ar putea șterge toate sau niciun transient din greșeală. Niciodată să nu ai încredere într-un sistem care funcționează :P

În caz că nu știai, _ este un wildcard de un singur caracter pentru instrucțiunile LIKE și ar trebui ideal să fie escap-at. :)

@Denis da, știu asta... Dar nicio diferență practică în această interogare?.. Doar dacă cineva reușește să denumească opțiunea XtransientXtimeoutX sau ceva de genul.

Nu ai folosi $wpdb->prepare() pentru a te proteja corespunzător de date suspecte, cum menționa @hakre? Acest lucru ar rezolva și escaparea caracterului '_'. Aș recomanda utilizarea lui, ca o practică bună.

@Tom în afară de "a fi extrem de sigur", această interogare specifică nu are nevoie cu adevărat de prepare și nu m-am obosit să o adaug.

Ai dreptate, acum când mă uit. Probabil că (int) este singura protecție de care ai nevoie pentru acea variabilă de server.

Mutând unele dintre comentariile din discuție într-un răspuns, cu reformulare și reformatare...
Practic, se rezumă la faptul că, dacă nu ai un caz extrem de special, acestea nu trebuie neapărat „curățate”. Dacă nu le accesezi niciodată, atunci nu contează dacă sunt acolo sau nu.
Vezi, implicit, transient-urile sunt stocate în tabelul de opțiuni. Într-o instalare standard, tabelul de opțiuni poate avea aproximativ 100 de intrări. Fiecare transient adaugă două intrări suplimentare, dar chiar dacă ai mii, acestea nu afectează viteza site-ului, deoarece nu sunt încărcate automat.
La pornire, WordPress încarcă opțiunile în memorie, dar doar pe cele care au flag-ul de autoload activat. Transient-urile nu au acest flag, așa că nu sunt încărcate în memorie. Doar transient-urile care sunt efectiv utilizate ulterior vor implica un cost.
Din perspectiva bazei de date, tabelul de opțiuni are indexuri atât pe ID-ul opțiunii, cât și pe numele opțiunii. Transient-urile sunt întotdeauna încărcate pe baza numelui (cheii), așa că căutările pentru ele sunt întotdeauna select-uri simple pe o singură valoare de cheie unică. Astfel, căutarea este O(log(n)) și este extrem de rapidă. Cu o complexitate de log(n), ar trebui să ajungi la milioane și milioane de rânduri înainte să devină observabil. Sincer, overhead-ul în configurarea și finalizarea interogării, împreună cu transferul efectiv de date, este mult mai lung. Interogarea în sine rulează în timp practic zero în comparație. Deci simplul fapt de a avea rânduri neutilizate nu afectează nimic, în afară de utilizarea unui spațiu suplimentar pe disc.
Indexarea în bazele de date este una dintre acele idei complexe care nu au sens pentru cei care nu au înțeles ce se întâmplă în spate. Bazele de date sunt concepute pentru recuperarea rapidă a datelor, de la bază, și pot gestiona acest tip de lucruri fără probleme. Acesta este un material destul de bun: http://en.wikipedia.org/wiki/Index_(database)
Acum, curățarea în modul cel mai evident (apelând SQL DELETE pe ele) nu le șterge efectiv din baza de date. Doar le elimină din index și marchează rândul ca „șters”. Din nou, așa funcționează bazele de date. Pentru a elibera efectiv spațiul pe disc, trebuie să continui cu un OPTIMIZE TABLE după aceea, iar aceasta nu este o operație rapidă. Durează timp. Probabil mai mult decât merită. Probabil nu este suficient pentru a economisi timp CPU, în total.
Dacă ai un caz care provoacă o inserare continuă de transient-uri noi care nu sunt utilizate, atunci trebuie să găsești problema de bază. Ce inserează aceste transient-uri? Folosesc o cheie care se schimbă sau se modifică? Dacă da, atunci plugin-ul sau codul care provoacă acest lucru ar trebui reparat pentru a, practic, nu mai face asta. Asta va fi mai util, pentru că e probabil ca codul care le creează necorespunzător să nu le și recupereze, făcând astfel mai multă muncă decât ar trebui.
Pe de altă parte, poate exista un caz în care transient-urile sunt create pentru ceva precum fiecare articol. Acest lucru poate fi perfect acceptabil. Eu fac asta în SFC, pentru a stoca comentariile primite de pe Facebook. Fiecare articol are un potențial transient asociat, ceea ce înseamnă două rânduri suplimentare per articol. Dacă ai 10k articole, vei avea 20k rânduri în tabelul de opțiuni (eventual). Nu este rău sau lent, pentru că, din nou, diferența dintre 100 de rânduri și 20.000 de rânduri este foarte mică din punctul de vedere al bazelor de date. Totul este indexat. Este extrem de rapid. Sub-sub-milisecunde.
Când începi să ajungi la milioane de rânduri, atunci aș fi îngrijorat. Când dimensiunea tabelului de opțiuni depășește sute de megaocteți, atunci aș fi îngrijorat suficient pentru a arunca o privire mai atentă. Dar, în general, aceasta nu este o problemă, cu excepția cazurilor extreme. Cu siguranță nu este o problemă pentru orice mai mic decât un site de știri mari, cu sute de mii de articole. Și pentru orice site suficient de mare pentru a fi o problemă, ar trebui să folosești un cache extern de obiecte, iar în acest caz, transient-urile sunt stocate automat acolo în loc de în baza de date.

Notă: transient-urile fără termen de expirare sunt încărcate automat, iar absența unui termen de expirare este implicită, așadar atunci când o aplicație/un plugin creează multe transient-uri fără a seta un termen de expirare, acestea vor ocupa porțiuni de memorie la fiecare încărcare de pagină/postare.

Nu există niciun motiv să folosești un "transient fără termen de expirare", deoarece acesta este practic identic cu o "opțiune" normală.

Desigur, dar aceasta este valoarea implicită. Ca urmare, mulți autori de plugin-uri adaugă transient-uri fără termen de expirare.

De asemenea, nu este identic cu o opțiune, deoarece va fi eliminat când se utilizează un cache de obiecte -- vezi articolul recent pe WPEngine pentru detalii.

Ei bine, soluția aici este simplă: Nu folosi acele plugin-uri. Ele fac lucrurile greșit. Tranzientele nu trebuie folosite ca sesiuni, nu ar trebui să le folosiți fără o expirare semnificativă și nu ar trebui să aibă chei care se modifică sau se schimbă.

:) (pentru că nu este pentru că setarea implicită din WordPress este greșită, nu-i așa?)

Să zicem, 7 zile. Dacă un autor de plugin/temă vrea ceva mai mare sau mai mic, îl va specifica. Dacă vor autoload, nu ar trebui să fie nevoie să specifice 0 pentru expirare (= infinit), dar asta e ce au acum cu parametrul de expirare care face dublu rol ca parametru da/nu pentru autoload. În orice caz, expirarea implicită nu ar trebui să ducă și la autoload=da ca valoare implicită; asta e doar o invitație la probleme.

În opinia mea bine chibzuită, ne-specificarea unei expirări ar trebui să arunce o eroare fatală și să rupă site-ul. Dar, ei bine, nu eu sunt la cârmă. Un transient fără expirare este stupid și fără sens. Dacă vrei să folosești cache-ul de obiecte, atunci folosește direct cache-ul de obiecte cu funcțiile wp_cache. Cu toate acestea, există tichete pentru a avea versiuni viitoare de WordPress care să curețe tranșientele vechi, mai mult pentru că sunt "inestetice" decât pentru orice alt motiv.

Otto - Nu pot să fiu mai în dezacord cu tine. Problema este că, în cele din urmă, cu toate acele transient-uri, dimensiunea tabelului devine ridicolă. Nu sunt necesare milioane de rânduri pentru a încetini lucrurile. În prezent mă confrunt cu un tabel de opțiuni care are peste 130k de rânduri și se blochează regulat. Deoarece câmpul `value` este de tip text lung, chiar și căutarea doar a rândurilor cu "autoload" devine un coșmar din punct de vedere al performanței. Acele câmpuri value sunt stocate separat de restul datelor din rând. Chiar dacă din punct de vedere logic fac parte din același tabel, trebuie să se facă join-uri pentru a obține rândurile dorite. Join-uri care acum durează o eternitate deoarece datele de care ai nevoie sunt împrăștiate peste tot pe disc. Profilarea (folosind Jet Profiler pentru MySQL) a demonstrat acest lucru.
Adăugarea auto-load la cheia clustered ar putea ajuta la rezolvarea acestei probleme. Clustered pe Autoload Desc, ID ASC de exemplu, ar permite tuturor rândurilor autoload să se grupeze împreună mai întâi pe disc. Totuși, cred că tot ai o încărcare uriașă din perspectiva bazei de date.
Personal, cred că designul acestui sistem este prost. Tabelul de opțiuni pare să fi devenit un loc de depozitare generală pentru multe lucruri. Asta e ok dacă câmpul value este suficient de mic pentru a fi inclus pe aceeași pagină cu restul datelor din rând și poate fi indexat eficient. Din păcate, nu este cazul. Cine a proiectat asta trebuie să revină la cursul de Baze de Date 101.

adevărat, dar ia în considerare faptul că atunci când a început dezvoltarea WordPress, nimeni nu s-a gândit că va ajunge să aibă mii de plugin-uri care folosesc tabelul de opțiuni ca stocare de date :)
