From a299979870fbeba8ccc75dff774a8dbba18cf65f Mon Sep 17 00:00:00 2001 From: ashuralyk Date: Fri, 21 Nov 2025 08:16:08 +0800 Subject: [PATCH 1/2] feat: make decoders cache refreshable by adding a new setting option --- settings.mainnet.toml | 3 +++ settings.toml | 5 ++++- src/decoder/helpers.rs | 29 ++++++++++++++++++++++++++--- src/main.rs | 12 ++++++++++++ src/types.rs | 2 ++ 5 files changed, 47 insertions(+), 4 deletions(-) diff --git a/settings.mainnet.toml b/settings.mainnet.toml index fce017f..a848391 100644 --- a/settings.mainnet.toml +++ b/settings.mainnet.toml @@ -19,6 +19,9 @@ dobs_cache_directory = "cache/dobs" # expiration time indicator for cleaning whole dobs cache, zero means never clean dobs_cache_expiration_sec = 300 +# expiration time in minutes dedicated for type_id and type_script decoder cache, zero means always update +decoders_cache_expiration_minutes = 1440 + # all deployed on-chain Spore contracts binary hash (order from new to old) # refer to: https://github.com/sporeprotocol/spore-contract/blob/master/docs/VERSIONS.md [[available_spores]] diff --git a/settings.toml b/settings.toml index 028c208..a9d5207 100644 --- a/settings.toml +++ b/settings.toml @@ -11,7 +11,7 @@ ckb_rpc = "https://testnet.ckbapp.dev/" rpc_server_address = "0.0.0.0:8090" # directory that stores decoders on hard-disk, including on-chain and off-chain binary files -decoders_cache_directory = "cache/decoders" +decoders_cache_directory = "cache/decoders/testnet" # directory that stores DOBs rendering results on hard-disk dobs_cache_directory = "cache/dobs" @@ -19,6 +19,9 @@ dobs_cache_directory = "cache/dobs" # expiration time indicator for cleaning whole dobs cache, zero means never clean dobs_cache_expiration_sec = 300 +# expiration time in minutes dedicated for type_id and type_script decoder cache, zero means always update +decoders_cache_expiration_minutes = 1440 + # all deployed on-chain Spore contracts binary hash (order from new to old) # refer to: https://github.com/sporeprotocol/spore-contract/blob/master/docs/VERSIONS.md [[available_spores]] diff --git a/src/decoder/helpers.rs b/src/decoder/helpers.rs index 4755b1f..4e0ac27 100644 --- a/src/decoder/helpers.rs +++ b/src/decoder/helpers.rs @@ -1,4 +1,4 @@ -use std::path::PathBuf; +use std::{path::PathBuf, time::SystemTime}; use ckb_jsonrpc_types::Either; use ckb_sdk::{constants::TYPE_ID_CODE_HASH, rpc::ckb_indexer::Tx, traits::CellQueryOptions}; @@ -32,6 +32,29 @@ fn build_type_script_search_option(type_script: Script) -> CellQueryOptions { CellQueryOptions::new_type(type_script) } +fn file_older_than_minutes(file_path: &PathBuf, minutes: u64) -> bool { + match std::fs::metadata(file_path) { + Ok(metadata) => { + let Ok(mut duration) = SystemTime::now().duration_since(SystemTime::UNIX_EPOCH) else { + return true; + }; + if let Ok(Ok(checkpoint)) = metadata + .modified() + .map(|time| time.duration_since(SystemTime::UNIX_EPOCH)) + { + duration = duration.saturating_sub(checkpoint); + } else if let Ok(Ok(checkpoint)) = metadata + .created() + .map(|time| time.duration_since(SystemTime::UNIX_EPOCH)) + { + duration = duration.saturating_sub(checkpoint); + } + duration.as_secs() / 60 >= minutes + } + Err(_) => true, + } +} + fn build_batch_search_options( type_args: &[u8; 32], available_script_ids: &[ScriptId], @@ -267,7 +290,7 @@ pub async fn parse_decoder_path( DecoderLocationType::TypeId => { let hash = decoder.hash.as_ref().ok_or(Error::DecoderHashNotFound)?; decoder_path.push(format!("type_id_{}.bin", hex::encode(hash))); - if !decoder_path.exists() { + if file_older_than_minutes(&decoder_path, settings.decoders_cache_expiration_minutes) { let decoder_search_option = build_type_id_search_option(hash.clone().into()); let decoder_binary = fetch_decoder_binary(rpc, decoder_search_option).await?; std::fs::write(decoder_path.clone(), decoder_binary) @@ -284,7 +307,7 @@ pub async fn parse_decoder_path( "type_script_{}.bin", hex::encode(script.calc_script_hash().raw_data()) )); - if !decoder_path.exists() { + if file_older_than_minutes(&decoder_path, settings.decoders_cache_expiration_minutes) { let decoder_search_option = build_type_script_search_option(script); let decoder_binary = fetch_decoder_binary(rpc, decoder_search_option).await?; std::fs::write(decoder_path.clone(), decoder_binary) diff --git a/src/main.rs b/src/main.rs index 375225f..cd6a5ef 100644 --- a/src/main.rs +++ b/src/main.rs @@ -25,6 +25,18 @@ async fn main() { "server settings: {}", serde_json::to_string_pretty(&settings).unwrap() ); + + tracing::info!("ensuring cache directories exist"); + fs::create_dir_all(&settings.decoders_cache_directory) + .expect("failed to create decoders cache directory"); + fs::create_dir_all(&settings.dobs_cache_directory) + .expect("failed to create DOBs cache directory"); + tracing::info!( + "decoders cache directory: {:?}", + settings.decoders_cache_directory + ); + tracing::info!("DOBs cache directory: {:?}", settings.dobs_cache_directory); + let rpc_server_address = settings.rpc_server_address.clone(); let cache_expiration = settings.dobs_cache_expiration_sec; let decoder = decoder::DOBDecoder::new(settings); diff --git a/src/types.rs b/src/types.rs index fb72861..b389faf 100644 --- a/src/types.rs +++ b/src/types.rs @@ -94,6 +94,7 @@ impl From for ErrorObjectOwned { #[derive(Deserialize)] #[cfg_attr(test, derive(serde::Serialize, PartialEq, Debug))] pub struct ClusterDescriptionField { + #[allow(dead_code)] pub description: String, pub dob: DOBClusterFormat, } @@ -257,6 +258,7 @@ pub struct Settings { pub decoders_cache_directory: PathBuf, pub dobs_cache_directory: PathBuf, pub dobs_cache_expiration_sec: u64, + pub decoders_cache_expiration_minutes: u64, pub onchain_decoder_deployment: Vec, pub available_spores: Vec, pub available_clusters: Vec, From e23827401360f2a6145562d01469017034972d00 Mon Sep 17 00:00:00 2001 From: ashuralyk Date: Wed, 3 Dec 2025 10:12:26 +0800 Subject: [PATCH 2/2] chore: accurate file elapsed time calculation --- src/decoder/helpers.rs | 37 +++++++++++++++++-------------------- 1 file changed, 17 insertions(+), 20 deletions(-) diff --git a/src/decoder/helpers.rs b/src/decoder/helpers.rs index 4e0ac27..3db9581 100644 --- a/src/decoder/helpers.rs +++ b/src/decoder/helpers.rs @@ -1,4 +1,4 @@ -use std::{path::PathBuf, time::SystemTime}; +use std::path::PathBuf; use ckb_jsonrpc_types::Either; use ckb_sdk::{constants::TYPE_ID_CODE_HASH, rpc::ckb_indexer::Tx, traits::CellQueryOptions}; @@ -33,25 +33,22 @@ fn build_type_script_search_option(type_script: Script) -> CellQueryOptions { } fn file_older_than_minutes(file_path: &PathBuf, minutes: u64) -> bool { - match std::fs::metadata(file_path) { - Ok(metadata) => { - let Ok(mut duration) = SystemTime::now().duration_since(SystemTime::UNIX_EPOCH) else { - return true; - }; - if let Ok(Ok(checkpoint)) = metadata - .modified() - .map(|time| time.duration_since(SystemTime::UNIX_EPOCH)) - { - duration = duration.saturating_sub(checkpoint); - } else if let Ok(Ok(checkpoint)) = metadata - .created() - .map(|time| time.duration_since(SystemTime::UNIX_EPOCH)) - { - duration = duration.saturating_sub(checkpoint); - } - duration.as_secs() / 60 >= minutes - } - Err(_) => true, + if minutes == 0 { + return true; + } + let metadata = match std::fs::metadata(file_path) { + Ok(m) => m, + Err(_) => return true, // File doesn't exist or we can't access it, so it's "old". + }; + + let file_time = match metadata.modified().or_else(|_| metadata.created()) { + Ok(time) => time, + Err(_) => return true, // Can't get a timestamp, assume it's old. + }; + + match file_time.elapsed() { + Ok(elapsed) => elapsed.as_secs() >= minutes * 60, + Err(_) => true, // System clock is earlier than file time, assume it's old to be safe. } }