Bộ nhớ đệm đa ngôn ngữ của WP Rocket: nguyên nhân bộ nhớ đệm của bản dịch biến mất khi lưu bản nháp và cách khắc phục

Bộ nhớ đệm đa ngôn ngữ của WP Rocket là cache trang được lưu riêng dưới thư mục ngôn ngữ cho từng URL bản dịch của TranslatePress. Trên máy chủ WordPress cấu hình Nginx + Apache, mã của WP Rocket đã được lần theo để tìm nguyên nhân cache của bản dịch biến mất cùng cả thư mục ngôn ngữ mỗi khi lưu bản nháp, và vấn đề được giải quyết bằng đoạn mã loại bài chưa xuất bản khỏi việc xóa cache bản dịch, đồng thời báo lại cho preload về các URL tự xóa.
Byte đầu tiên đo bên trong cùng máy chủ là 0,015 giây khi có cache bản dịch và 2,492 giây khi không có. Khi thư mục ngôn ngữ trống, khách truy cập bản dịch sẽ nhận phản hồi chậm như vậy cho đến khi có người mở lại URL đó và cache được tạo lại.
1️⃣ Cấu trúc bộ nhớ đệm đa ngôn ngữ của WP Rocket: thư mục theo ngôn ngữ và việc xóa bản dịch
Bản dịch là trang hiển thị bài gốc bằng ngôn ngữ khác, nên dễ nghĩ cache của nó đi chung với bản gốc, nhưng WP Rocket lưu cache URL bản dịch như một trang riêng. Bên dưới thư mục cache, các thư mục ngôn ngữ như [en] và [ja] nằm cạnh thư mục bài gốc, và bài bản dịch được lưu bên trong theo tên slug đã dịch.
Khi sửa bài, tích hợp TranslatePress của WP Rocket cũng xóa luôn cache bản dịch của bài đó. Tài liệu chính thức cũng cho biết sửa thông thường sẽ xóa mọi bản dịch, còn sửa trong trình chỉnh sửa bản dịch của TranslatePress chỉ xóa ngôn ngữ đã thay đổi.
// wp-rocket/inc/ThirdParty/Plugins/I18n/TranslatePress.php (3.23.5.1)
'post_updated' => 'clear_post_languages', // chạy mỗi khi bài được lưu
public function clear_post_languages( $post_id ) {
// …
$default_permalink = get_permalink( $post_id ); // không kiểm tra trạng thái bài
foreach ( $trp_settings['translation-languages'] as $language ) {
if ( $language === $trp_settings['default-language'] ) {
continue;
}
$clear_urls[] = $converter->get_url_for_language( $language, $default_permalink, '' );
}
// …
rocket_clean_files( $clear_urls );
}Hàm tích hợp chuyển URL của bài thành URL bản dịch cho từng ngôn ngữ, rồi chuyển cho hàm dọn tệp cache [rocket_clean_files]. Trong quá trình này không có dòng nào kiểm tra trạng thái bài, nên dù bài đã xuất bản hay là bản nháp, URL bản dịch vẫn được tạo và xóa theo cùng một trình tự.
2️⃣ Lưu bản nháp: toàn bộ thư mục ngôn ngữ bị xóa
Lưu bản nháp không phải không liên quan đến cache bản dịch. Trước khi xuất bản, đường dẫn tĩnh của bài có dạng [?p=ID bài] chứ không phải slug, và TranslatePress chuyển URL này thành URL có thêm đường dẫn ngôn ngữ như [/en/?p=ID bài].
[rocket_clean_files] bỏ truy vấn khỏi URL và chỉ dựa vào đường dẫn để tìm vị trí cache. Nếu đường dẫn còn lại là [/en/] thì đó là cả thư mục cache của ngôn ngữ ấy, và nếu đích là thư mục, nó bị xóa đệ quy cả các thư mục con.
// wp-rocket/inc/functions/files.php (3.23.5.1) — bên trong rocket_clean_files()
$parsed_url = get_rocket_parse_url( $url ); // tách đường dẫn và truy vấn
// …
$entry = $dir . $parsed_url['path']; // …/example.com/en/
// …
if ( $filesystem->is_dir( $entry ) ) {
rocket_rrmdir( $entry, [], $filesystem ); // xóa đệ quy cả thư mục
} else {
$filesystem->delete( $entry );
}Trang chủ cũng đi qua cùng con đường đó. URL trang chủ [/] được chuyển thành [/en/], nên ngay cả khi lưu trang chủ đã xuất bản, toàn bộ thư mục ngôn ngữ cũng bị xóa. Chỉ sửa tiêu đề bản nháp rồi lưu lại cũng làm trống thư mục cache của cả bốn ngôn ngữ, và từ thời điểm lưu cùng nhật ký xóa cache đã xác nhận cùng một lần xóa lặp lại hơn 30 lần trong một đêm có nhiều lần lưu bản nháp.
Theo mã, tự động lưu trong trình soạn thảo cũng đi cùng con đường đó. Các bản sửa đổi được lưu trong lúc bạn sửa bài đã xuất bản cũng dùng URL dạng [?p=].
Trên trang thử nghiệm cấu hình Nginx + Apache, bài đã xuất bản được sửa trong trình soạn thảo trong khi đếm số tệp trong thư mục ngôn ngữ. Sau lần tự động lưu đầu tiên, thư mục tiếng Anh và tiếng Nhật vẫn còn mỗi thư mục 20 tệp, và sau lần tự động lưu thứ hai một phút sau, cả hai thư mục còn 0 tệp.
Lần tự động lưu đầu tiên tạo bản sửa đổi mới, từ lần thứ hai trở đi bản sửa đổi đó được sửa và lưu lại, nên [post_updated] được chạy. Nếu bạn tiếp tục sửa nội dung sau lần tự động lưu đầu tiên, cache bản dịch sẽ bị xóa cùng cả thư mục ngôn ngữ dù không bấm nút lưu.
Cùng triệu chứng này cũng đang mở trong kho chính thức của WP Rocket với issue #6130 từ ngày 31 tháng 8 năm 2023. Pull request #7616 để sửa lỗi được gửi vào tháng 9 năm 2025 nhưng chưa được hợp nhất, và nhật ký thay đổi của 3.23.5.1 phát hành ngày 2 tháng 10 năm 2026 cũng không có bản sửa.
3️⃣ Mã khắc phục: chỉ xóa cache bản dịch của bài đã xuất bản
Cho đến khi có bản sửa chính thức, hàm tích hợp được tháo ra và thay bằng hàm chỉ xóa bản dịch khi URL chắc chắn. Cách vượt qua được đăng trong bình luận của issue cũng theo cùng hướng, tức là thoát ngay khỏi hàm tích hợp nếu bài chưa xuất bản.
Tuy nhiên, điều kiện chỉ kiểm tra trạng thái bài vẫn còn hai trường hợp. Trang chủ dù ở trạng thái xuất bản vẫn xóa toàn bộ thư mục ngôn ngữ khi lưu, và khi hạ bài đã xuất bản xuống bản nháp, cache bản dịch không bị xóa nên bài đã hạ vẫn mở được ở URL bản dịch.
<?php
/**
* Plugin Name: Rocket TranslatePress Draft Fix
* Description: Giới hạn việc xóa cache bản dịch của tích hợp TranslatePress trong WP Rocket chỉ ở những trường hợp URL chắc chắn
*/
// 1. Tháo hàm tích hợp của WP Rocket khỏi post_updated và giữ lại
add_action( 'init', function () {
global $wp_filter;
if ( empty( $wp_filter['post_updated'] ) ) {
return;
}
foreach ( $wp_filter['post_updated']->callbacks as $priority => $callbacks ) {
foreach ( $callbacks as $cb ) {
$fn = $cb['function'];
if ( is_array( $fn ) && is_object( $fn[0] )
&& 0 === strpos( get_class( $fn[0] ), 'WP_Rocket\\' )
&& false !== stripos( get_class( $fn[0] ), 'TranslatePress' ) ) {
remove_action( 'post_updated', $fn, $priority );
$GLOBALS['rtdf_original'] = $fn;
}
}
}
}, 9999 );
// 2. Hàm thay thế
add_action( 'post_updated', function ( $post_id, $after, $before ) {
if ( empty( $GLOBALS['rtdf_original'] ) || ! class_exists( 'TRP_Translate_Press' ) ) {
return;
}
$trp = TRP_Translate_Press::get_trp_instance();
$converter = $trp->get_component( 'url_converter' );
$settings = $trp->get_component( 'settings' )->get_settings();
$languages = array_diff( $settings['translation-languages'], [ $settings['default-language'] ] );
// Trang chủ — chỉ tệp cache trang chủ ngôn ngữ, không phải thư mục ngôn ngữ
if ( 'page' === get_option( 'show_on_front' ) && (int) get_option( 'page_on_front' ) === (int) $post_id ) {
$urls = [];
add_filter( 'trp_add_language_to_home_url_check_for_admin', '__return_false' );
foreach ( $languages as $lang ) {
$home = $converter->get_url_for_language( $lang, home_url( '/' ), '' );
$dir = WP_ROCKET_CACHE_PATH . wp_parse_url( $home, PHP_URL_HOST ) . wp_parse_url( $home, PHP_URL_PATH );
array_map( 'unlink', glob( $dir . 'index*.html*' ) ?: [] );
$urls[] = $home;
}
remove_filter( 'trp_add_language_to_home_url_check_for_admin', '__return_false' );
do_action( 'rocket_after_clean_terms', $urls ); // báo cho preload (5️⃣)
return;
}
// Lưu bài đã xuất bản — giữ nguyên hàm gốc của WP Rocket
if ( 'publish' === $after->post_status ) {
call_user_func( $GLOBALS['rtdf_original'], $post_id );
return;
}
// Đã xuất bản → bản nháp·riêng tư·thùng rác — chỉ bản dịch của URL trước khi hạ
if ( 'publish' === $before->post_status ) {
$old = get_permalink( $before );
$urls = [];
add_filter( 'trp_add_language_to_home_url_check_for_admin', '__return_false' );
foreach ( $languages as $lang ) {
$urls[] = $converter->get_url_for_language( $lang, $old, '' );
}
remove_filter( 'trp_add_language_to_home_url_check_for_admin', '__return_false' );
rocket_clean_files( $urls );
}
// Các trường hợp khác (bản nháp·bản sửa đổi·tự động lưu) không có URL bản dịch nên không xóa
}, 10, 3 );Phần đầu tìm hàm tích hợp mà WP Rocket đã đăng ký, tháo khỏi [post_updated] và giữ lại hàm gốc. Hàm thay thế chỉ xóa tệp cache trang chủ ngôn ngữ khi là trang chủ, chạy nguyên hàm gốc đã giữ khi là bài đã xuất bản, và khi hạ bài đã xuất bản, sẽ tìm bản dịch theo URL trước khi hạ rồi xóa.
Nếu lưu thành tệp PHP trong thư mục [wp-content/mu-plugins/], mã có hiệu lực ngay mà không cần kích hoạt, và khi xóa tệp, WP Rocket trở lại cách hoạt động ban đầu. Kết quả thử với mã hoạt động tương tự: lưu lại bản nháp thì số tệp trong thư mục ngôn ngữ không đổi, còn lưu lại bài đã xuất bản thì chỉ thư mục gốc và bản dịch của bài đó bị xóa.
Trước và sau khi thêm đoạn mã trên, số tệp trong thư mục ngôn ngữ của bốn trường hợp đã được so sánh trong cùng môi trường. Thư mục tiếng Anh và tiếng Nhật mỗi thư mục có 20 tệp cache PC·di động của trang chủ ngôn ngữ và bốn bài.
Trước khi thêm mã, cả ba trường hợp lưu bản nháp, lưu trang chủ và hủy xuất bản đều làm hai thư mục còn 0 tệp, và chỉ lưu bài đã xuất bản là xóa riêng cache bản dịch của bài đó.
Sau khi thêm mã, 20 tệp vẫn còn nguyên khi lưu bản nháp và khi tự động lưu trong trình soạn thảo. Lưu trang chủ chỉ xóa 4 tệp cache trang chủ ngôn ngữ, còn lưu bài đã xuất bản và hủy xuất bản chỉ xóa 4 tệp bản dịch của bài đó nên còn 16 tệp, và URL bản dịch tiếng Anh của bài đã hạ trả về 404.
| Trường hợp | Trước khi sửa | Sau khi thêm mã | Cache bị xóa sau khi thêm mã |
|---|---|---|---|
| Lưu bản nháp | 20 → 0 | 20 → 20 | Không có |
| Lưu bài đã xuất bản | Chỉ xóa bản dịch của bài đó | 20 → 16 | 4 tệp bản dịch của bài đó |
| Lưu trang chủ | 20 → 0 | 20 → 16 | 4 tệp cache trang chủ ngôn ngữ |
| Hủy xuất bản | 20 → 0 | 20 → 16 | 4 tệp bản dịch của bài đó · URL bản dịch 404 |
4️⃣ Preload không tạo lại: trạng thái hoàn tất còn sót trong hàng đợi
Dù đã chặn việc lưu bản nháp, tệp cache bị xóa mà không có tín hiệu xóa từ hàm của WP Rocket sẽ không được preload điền lại. Preload của WP Rocket dùng một bảng hàng đợi ghi trạng thái cho từng URL: URL chờ gửi yêu cầu ở trạng thái chờ, URL đã gửi yêu cầu ở trạng thái đang xử lý, URL đã tạo cache chuyển sang hoàn tất, và nếu không xong trong 3 phút sẽ thành thất bại.
URL đã hoàn tất quay lại trạng thái chờ trong ba trường hợp: tệp bị xóa vì hết thời gian sống của cache, WP Rocket phát tín hiệu xóa khi xóa nó, hoặc đã quá 12 giờ kể từ khi thất bại. Nếu truyền false làm đối số thứ ba của [rocket_clean_files] hoặc xóa tệp trực tiếp, tín hiệu này không được phát, nên chỉ tệp cache biến mất trong khi hàng đợi vẫn ghi là hoàn tất.
Đối số thứ ba được truyền false bởi đoạn mã nhận tín hiệu xóa rồi xóa tiếp cache khác, để tránh gọi lặp. Khi đối chiếu cache bản dịch, 3 trong 65 bản dịch tiếng Anh và 7 trong 65 bản dịch tiếng Nhật vẫn ghi là hoàn tất trong hàng đợi trong khi không có tệp cache.
Một cộng tác viên của nhóm hỗ trợ WP Rocket cũng thừa nhận trong bình luận của issue #6130 rằng sau khi xóa cache, URL trong bảng vẫn ở trạng thái hoàn tất nên trạng thái bị lệch. Đề xuất định kỳ đối chiếu bảng với tệp cache (#2939) đã bị đóng vào năm 2020 mà không được thực hiện.
5️⃣ Đăng ký lại preload: báo cho hàng đợi các URL tự xóa
Với URL tự xóa, một dòng báo cho preload ngay sau khi xóa là đủ. Khi preload nhận tín hiệu [rocket_after_clean_terms], nó đưa các URL nhận được về trạng thái chờ và thêm mới các URL chưa có trong bảng.
// Sau khi xóa không có tín hiệu xóa — đăng ký lại mảng URL đã xóa vào hàng đợi preload
rocket_clean_files( $urls, null, false ); // đối số thứ ba false = không có tín hiệu xóa
do_action( 'rocket_after_clean_terms', $urls ); // preload đưa các URL này về chờ (pending)Bên trong WP Rocket, nơi nhận tín hiệu này chỉ có hàm hàng đợi preload và tích hợp hosting SpinUpWP, và hàm preload chỉ sửa các dòng hàng đợi, không xóa tệp cache của WP Rocket hay phát lại tín hiệu xóa, nên không xảy ra gọi lặp. Trang chỉ xóa cache bằng hàm của WP Rocket và để đối số thứ ba ở giá trị mặc định thì đã phát tín hiệu sẵn, nên không cần dòng này.
Sau khi thêm dòng này, khi xóa cache của một bài có bản dịch bốn ngôn ngữ, trong 2 giây bốn dòng hàng đợi đã chuyển sang chờ. Thử hai lần trên máy chủ chạy cron mỗi phút, cả 8 tệp cache PC·di động của bản dịch được tạo lại sau 101 giây và 153 giây. Có thể kiểm tra qua trạng thái trong bảng hàng đợi và tệp trong thư mục cache.
# Trạng thái của URL đó trong bảng hàng đợi (wp_ trước tên bảng là tiền tố của trang)
wp db query <<'SQL'
SELECT url, status, modified FROM wp_wpr_rocket_cache WHERE url LIKE '%/en/sample-post-en'
SQLurl status modified
https://example.com/en/sample-post-en pending 2026-10-17 09:10:48# Tệp cache đã xuất hiện lại sau khoảng 1 phút chưa
ls -l wp-content/cache/wp-rocket/example.com/en/sample-post-en/total 240
-rw-r--r-- 1 example nobody 95084 Oct 17 18:12 index-https.html
-rw-r--r-- 1 example nobody 22352 Oct 17 18:12 index-https.html_gzip
-rw-r--r-- 1 example nobody 95083 Oct 17 18:12 index-mobile-https.html
-rw-r--r-- 1 example nobody 22348 Oct 17 18:12 index-mobile-https.html_gzipNgay sau khi xóa, trạng thái chuyển thành [pending], và khi preload gửi yêu cầu rồi tệp cache được tạo, trạng thái thành [completed]. Nếu trạng thái là [completed] mà không có tệp cache thì URL đó đã bị xóa mà không có tín hiệu.
Khi lưu trang chủ và đoạn mã trên xóa tệp cache trang chủ ngôn ngữ rồi phát tín hiệu, URL trang chủ tiếng Anh và tiếng Nhật lại vào hàng đợi. Cache PC·di động của trang chủ ngôn ngữ được tạo lại muộn nhất ở lần chạy cron thứ hai.
Qua hai lần thử, từ lúc lưu đến tệp cache cuối cùng mất 47 giây và 91 giây.
🔢 FAQ & nội dung đề xuất














ℹ️ Thông báo tiếp thị liên kết
Nội dung trên trang này có chứa liên kết affiliate (tiếp thị liên kết). Khi khách truy cập mua sản phẩm hoặc dịch vụ qua các liên kết này, trang sẽ nhận hoa hồng từ bên bán. Trong quá trình đó, số tiền người mua phải trả (sẽ giảm khi có chương trình khuyến mãi ↓) không hề tăng lên. Giá, khuyến mãi và tình trạng hàng được đăng theo thời điểm viết bài và có thể khác với thực tế, vì vậy hãy kiểm tra lại lần cuối với bên bán trước khi mua. Việc chọn và đánh giá sản phẩm dựa trên tiêu chí riêng của trang; việc có nhận hoa hồng hay không không ảnh hưởng đến thứ tự giới thiệu hay nội dung đánh giá.

