From c6ab2f8d06342a758d6fdb2c19f08aad1a57d50e Mon Sep 17 00:00:00 2001 From: Bugen Zhao Date: Mon, 13 Jan 2025 16:38:47 +0800 Subject: [PATCH] fix(license): ignore access event when watching license key file changes (#20122) Signed-off-by: Bugen Zhao --- src/meta/src/manager/license.rs | 37 ++++++++++++++++++++++++++------- 1 file changed, 29 insertions(+), 8 deletions(-) diff --git a/src/meta/src/manager/license.rs b/src/meta/src/manager/license.rs index e3abb1770fca3..5caf26a2e0fb3 100644 --- a/src/meta/src/manager/license.rs +++ b/src/meta/src/manager/license.rs @@ -12,6 +12,9 @@ // See the License for the specific language governing permissions and // limitations under the License. +use std::sync::atomic::AtomicUsize; +use std::sync::atomic::Ordering::Relaxed; + use anyhow::Context; use notify::Watcher; use risingwave_common::system_param::LICENSE_KEY_KEY; @@ -22,6 +25,9 @@ use tokio::task::JoinHandle; use super::MetaSrvEnv; use crate::MetaResult; +/// For test purposes, we count the number of times the license key file is reloaded. +static RELOAD_TIMES: AtomicUsize = AtomicUsize::new(0); + impl MetaSrvEnv { /// Spawn background tasks to watch the license key file and update the system parameter, /// if configured. @@ -36,15 +42,22 @@ impl MetaSrvEnv { let mut watcher = notify::recommended_watcher(move |event: Result| { - if let Err(e) = event { - tracing::warn!( - error = %e.as_report(), - "error occurred while watching license key file" - ); - return; + match event { + Ok(event) => { + if event.kind.is_access() { + // Ignore access events as they do not indicate changes and will be + // triggered on our own read operations. + } else { + let _ = changed_tx.send(()); + } + } + Err(e) => { + tracing::warn!( + error = %e.as_report(), + "error occurred while watching license key file" + ); + } } - // We don't check the event type but always notify the updater for simplicity. - let _ = changed_tx.send(()); }) .context("failed to create license key file watcher")?; @@ -56,6 +69,7 @@ impl MetaSrvEnv { let updater = { let mgr = self.system_params_manager_impl_ref(); let path = path.to_path_buf(); + async move { // Let the watcher live until the end of the updater to prevent dropping (then stopping). let _watcher = watcher; @@ -65,6 +79,7 @@ impl MetaSrvEnv { // will do the initialization then. while changed_rx.changed().await.is_ok() { tracing::info!(path = %path.display(), "license key file changed, reloading..."); + RELOAD_TIMES.fetch_add(1, Relaxed); let content = match tokio::fs::read_to_string(&path).await { Ok(v) => v, @@ -144,6 +159,7 @@ mod tests { // Since we've filled the key file with the initial key, the license should be loaded. tokio::time::sleep(Duration::from_secs(1)).await; + assert_eq!(RELOAD_TIMES.load(Relaxed), 1); let license = LicenseManager::get().license().unwrap(); assert_eq!(license.sub, "rw-test"); assert_eq!(license.tier, Tier::Free); @@ -151,7 +167,12 @@ mod tests { // Update the key file with an empty content, which should reset the license to the default. std::fs::write(key_file.path(), "").unwrap(); tokio::time::sleep(Duration::from_secs(1)).await; + assert_eq!(RELOAD_TIMES.load(Relaxed), 2); let license = LicenseManager::get().license().unwrap(); assert_eq!(license, License::default()); + + // Show that our "access" on the key file does not trigger a reload recursively. + tokio::time::sleep(Duration::from_secs(3)).await; + assert_eq!(RELOAD_TIMES.load(Relaxed), 2); } }