這篇文章是寫給自己的。
周三的時候我在維護公司的一個wordpress項目頁面時發現了一個非常奇怪的情況:當我嘗試更新網站上的一個頁面後,在wordpress後台的編輯器中發現其內容並沒有按我預期的將圖片的網址替換下來(網站開啟了百度雲插件,插件會抓取文章中的圖片,然後將圖片上傳至百度雲,並將文章中的地址替換),但是,我查看前台的頁面卻發現頁面顯示時正常的、檢查頁面中的圖片網址也是做過替換的,總之一句話,就是前台文章展示頁和後台編輯器中的內容不一致。這個bug真詭異,下面就把排查這個bug的過程記錄下來,以備忘。
1,懷疑網站上有其他插件與百度雲插件沖突
這個其實有可能,因為網站上也安裝了不少wordpress插件。但想了下,好像也沒有其它插件會與百度雲插件起沖突。
2,與技術老大一起結合數據庫中的wp_posts表來排查
wp_posts表結構如下:
這個表下面幾個字段很重要:
post_status(文章狀態)的值可以是:trash(回收站)、publish(發布)、inherit(繼承)、draft(草稿)、auto-draft(自動保存的草稿)
post_type(文章類型)的值可以是:post(文章)、page(頁面)、revision(修訂版)、attachement(附件)、nav_menu_item(菜單)
post_parent(父文章的ID)。
post_status決定了此條是否已經發布,post_type則決定了某條記錄是草稿、文章、頁面、還是文章的修訂版本。文章可以被修改很多次,所以,它會有很多個修訂版本,post_parent存儲的是當前此條記錄的父文章的ID(這個只有post_status為inherit或draft時才會用到,其他情況下默認為0)。
debug過程基本就是這樣:每點擊一次“更新按鈕”,就看wp_posts表中的記錄變化,結果發現:
(1)網站中發布的頁面(假定ID為1234),在數據庫中對應的記錄(post_status為publish,post_type為page)已經被正常修改了。這可以說明,編輯器中的內容不是取的1234這條記錄,它取的是ID=1234這篇文章的某個修訂版。檢查了幾次,果然是這樣。
(2)那麼它獲取的是哪個修訂版呢?經過幾次排查發現,編輯器總是獲取最新revision之前的一個revision。准確的描述應該是這樣,每點擊一次更新,數據表中會多處一個revision和一個auto-draft,最新的revision中的文章內容是被替換過img src的正確內容,而auto_draft則與編輯器中的一致。在編輯器狀態下,wordpress頻繁的自動保存,每產生一個新的auto draft就會把之前的auto draft刪除。編輯器獲取內容時也不是從auto draft中獲取的。總之,最後也沒弄清楚,它到底取的是哪個revision。但它應該取最新的revision,這樣就可以與ID=1234的post內容保持一致了。
3,緩存搞的鬼?
開始懷疑是浏覽器緩存,嘗試清除浏覽器緩存後,發現問題還存在。那就是服務端的緩存了,重啟下服務器上的memcached: service memcached restart,然後發現編輯器中的內容正常了。果然是緩存搞的鬼。
解決bug:
在百度雲插件更新數據庫的地方增加更新最新的revision和刷新緩存的代碼,這個bug才算解決。
1 // 更新最新的revision的posts表中的post_content字段 2 $revisions = wp_get_post_revisions( $post_id ); 3 krsort( $revisions ); 4 $newest_id = reset( array_keys( $revisions ) ); 5 $flag = $wpdb->update( $wpdb->posts, array('post_content' => $post->post_content), array('ID' => $newest_id) ); 6 // 更新posts數據表的post_content字段 7 $wpdb->update( $wpdb->posts, array('post_content' => $post->post_content), array('ID' => $post->ID)); 8 // 刷新緩存 9 wp_cache_flush();
下面是惡補的wordpress的緩存和全局變量的相關知識:
(1)wp_cache_flush()函數是如何定義的?
1 /** 2 * Removes all cache items. 3 * 4 * @since 2.0.0 5 * 6 * @see WP_Object_Cache::flush() 7 * @global WP_Object_Cache $wp_object_cache Object cache global instance. 8 * 9 * @return bool False on failure, true on success 10 */ 11 function wp_cache_flush() { 12 global $wp_object_cache; 13 14 return $wp_object_cache->flush(); 15 }
接著問,$wp_objecg_cache->flush()函數是如何定義的?
1 /** 2 * Clears the object cache of all data. 3 * 4 * @since 2.0.0 5 * @access public 6 * 7 * @return true Always returns true. 8 */ 9 public function flush() { 10 $this->cache = array(); 11 12 return true; 13 }
這個$this->cache初始化時是如何賦值的?或者在類中是如何定義的?
1 /** 2 * Holds the cached objects. 3 * 4 * @since 2.0.0 5 * @access private 6 * @var array 7 */ 8 private $cache = array();
$this->cache是個數組,而wp_cache_flush()只是把這個數組賦值為空數組。
(2)接著問,WP_Object_Cache都緩存了哪些數據?是如何緩存的?
先根據cache.php文件頭注釋的指引(有注釋就是好)來看下官方文檔的說明:
WP_Object_Cache is WordPress' class for caching data which may be computationally expensive to regenerate, such as the result of complex database queries. The object cache is defined in wp-includes/cache.php
.
Do not use the class directly in your code when writing plugins, but use the wp_cache functions listed below.
By default, the object cache is non-persistent. This means that data stored in the cache resides in memory only and only for the duration of the request. Cached data will not be stored persistently across page loads unless you install a persistent caching plugin.
注意上面標紅的文字,大意是說:WP_Object_Cache緩存需要重復生成消耗計算資源的數據,比如數據庫查詢結果。默認情況下,WP_Object_Cache不是可持續的,它只是在某次請求的時間內存在(一旦請求結束,緩存也會被刪除),如果要想跨頁面(跨請求)使用這些緩存,那就需要安裝一個可持續的緩存插件。
(3)網站上安裝了什麼緩存插件呢?memecached,但在插件列表中沒有找到。
然後百度了下,發現了這篇文章wordpress如何啟用Memcached內存緩存,結果發現它被安裝到wp-content目錄下面。
看來,wordpress還有很多要學的東西呢。
參考:
wordpress官方參考手冊
wordpress如何啟用Memcached內存緩存