這篇文章是寫給自己的。
週三的時候我在維護公司的一個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才算解決。
<span style="color: #008080;">1</span> <span style="color: #008000;">//</span><span style="color: #008000;"> 更新最新的revision的posts表中的post_content字段</span> <span style="color: #008080;">2</span> <span style="color: #800080;">$revisions</span> = wp_get_post_revisions( <span style="color: #800080;">$post_id</span><span style="color: #000000;"> ); </span><span style="color: #008080;">3</span> <span style="color: #008080;">krsort</span>( <span style="color: #800080;">$revisions</span><span style="color: #000000;"> ); </span><span style="color: #008080;">4</span> <span style="color: #800080;">$newest_id</span> = <span style="color: #008080;">reset</span>( <span style="color: #008080;">array_keys</span>( <span style="color: #800080;">$revisions</span><span style="color: #000000;"> ) ); </span><span style="color: #008080;">5</span> <span style="color: #800080;">$flag</span> = <span style="color: #800080;">$wpdb</span>->update( <span style="color: #800080;">$wpdb</span>->posts, <span style="color: #0000ff;">array</span>('post_content' => <span style="color: #800080;">$post</span>->post_content), <span style="color: #0000ff;">array</span>('ID' => <span style="color: #800080;">$newest_id</span><span style="color: #000000;">) ); </span><span style="color: #008080;">6</span> <span style="color: #008000;">//</span><span style="color: #008000;"> 更新posts数据表的post_content字段</span> <span style="color: #008080;">7</span> <span style="color: #800080;">$wpdb</span>->update( <span style="color: #800080;">$wpdb</span>->posts, <span style="color: #0000ff;">array</span>('post_content' => <span style="color: #800080;">$post</span>->post_content), <span style="color: #0000ff;">array</span>('ID' => <span style="color: #800080;">$post</span>-><span style="color: #000000;">ID)); </span><span style="color: #008080;">8</span> <span style="color: #008000;">//</span><span style="color: #008000;"> 刷新缓存</span> <span style="color: #008080;">9</span> wp_cache_flush();
下面是惡補的wordpress的快取和全域變數的相關知識:
(1)wp_cache_flush()函數是如何定義的?
<span style="color: #008080;"> 1</span> <span style="color: #008000;">/*</span><span style="color: #008000;">* </span><span style="color: #008080;"> 2</span> <span style="color: #008000;"> * Removes all cache items. </span><span style="color: #008080;"> 3</span> <span style="color: #008000;"> * </span><span style="color: #008080;"> 4</span> <span style="color: #008000;"> * @since 2.0.0 </span><span style="color: #008080;"> 5</span> <span style="color: #008000;"> * </span><span style="color: #008080;"> 6</span> <span style="color: #008000;"> * @see WP_Object_Cache::flush() </span><span style="color: #008080;"> 7</span> <span style="color: #008000;"> * @global WP_Object_Cache $wp_object_cache Object cache global instance. </span><span style="color: #008080;"> 8</span> <span style="color: #008000;"> * </span><span style="color: #008080;"> 9</span> <span style="color: #008000;"> * @return bool False on failure, true on success </span><span style="color: #008080;">10</span> <span style="color: #008000;">*/</span> <span style="color: #008080;">11</span> <span style="color: #0000ff;">function</span><span style="color: #000000;"> wp_cache_flush() { </span><span style="color: #008080;">12</span> <span style="color: #0000ff;">global</span> <span style="color: #800080;">$wp_object_cache</span><span style="color: #000000;">; </span><span style="color: #008080;">13</span> <span style="color: #008080;">14</span> <span style="color: #0000ff;">return</span> <span style="color: #800080;">$wp_object_cache</span>-><span style="color: #008080;">flush</span><span style="color: #000000;">(); </span><span style="color: #008080;">15</span> }
接著問,$wp_objecg_cache->flush()函數是如何定義的?
<span style="color: #008080;"> 1</span> <span style="color: #008000;">/*</span><span style="color: #008000;">* </span><span style="color: #008080;"> 2</span> <span style="color: #008000;"> * Clears the object cache of all data. </span><span style="color: #008080;"> 3</span> <span style="color: #008000;"> * </span><span style="color: #008080;"> 4</span> <span style="color: #008000;"> * @since 2.0.0 </span><span style="color: #008080;"> 5</span> <span style="color: #008000;"> * @access public </span><span style="color: #008080;"> 6</span> <span style="color: #008000;"> * </span><span style="color: #008080;"> 7</span> <span style="color: #008000;"> * @return true Always returns true. </span><span style="color: #008080;"> 8</span> <span style="color: #008000;">*/</span> <span style="color: #008080;"> 9</span> <span style="color: #0000ff;">public</span> <span style="color: #0000ff;">function</span> <span style="color: #008080;">flush</span><span style="color: #000000;">() { </span><span style="color: #008080;">10</span> <span style="color: #800080;">$this</span>->cache = <span style="color: #0000ff;">array</span><span style="color: #000000;">(); </span><span style="color: #008080;">11</span> <span style="color: #008080;">12</span> <span style="color: #0000ff;">return</span> <span style="color: #0000ff;">true</span><span style="color: #000000;">; </span><span style="color: #008080;">13</span> }
這個$this->cache初始化時是如何賦值的?或是在類別中是如何定義的?
<span style="color: #008080;">1</span> <span style="color: #008000;">/*</span><span style="color: #008000;">* </span><span style="color: #008080;">2</span> <span style="color: #008000;"> * Holds the cached objects. </span><span style="color: #008080;">3</span> <span style="color: #008000;"> * </span><span style="color: #008080;">4</span> <span style="color: #008000;"> * @since 2.0.0 </span><span style="color: #008080;">5</span> <span style="color: #008000;"> * @access private </span><span style="color: #008080;">6</span> <span style="color: #008000;"> * @var array </span><span style="color: #008080;">7</span> <span style="color: #008000;">*/</span> <span style="color: #008080;">8</span> <span style="color: #0000ff;">private</span> <span style="color: #800080;">$cache</span> = <span style="color: #0000ff;">array</span>();
$this->cache是個數組,而wp_cache_flush()只是把這個數組賦值為空數組。
(2)接著問,WP_Object_Cache都快取了哪些資料?是如何快取的?
先根據cache.php文件頭註解的指引(有註解就是好)來看下官方文件的說明:
WP_Object_Cache is WordPress' class for WP_Object_Cache is WordPress' class for WP_Object_Cache
is WordPress' class for
is WordPress' class for WP_Object_Cache is WordPress' class for
WP_Object_Cacheis WordPress' class for WP_Object_Cache is WordPress' class for
WP_Object_Cacheis WordPress' class for
) data which may be computationally expensive to regenerate, such as the result of complex database queries. The object inache ache.
Do not use the class directly in your code when writing plugins, butuse 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 page will not be duration of the request。 caching plugin.
注意上面標紅的文字,大意是說:WP_Object_Cache快取需要重複產生消耗運算資源的數據,例如資料庫查詢結果。預設情況下,WP_Object_Cache不是可持續的,它只是在某次請求的時間內存在(一旦請求結束,快取也會被刪除),如果要想跨頁面(跨請求)使用這些緩存,那就需要安裝一個永續的快取插件。
(3)網站上安裝了什麼快取外掛呢? memecached,但在插件列表中沒有找到。
然後百度了下,發現了這篇文章wordpress如何啟用Memcached記憶體緩存,結果發現它被安裝到wp-content目錄下面。