mirror of
https://github.com/seaweedfs/seaweedfs.git
synced 2026-09-08 15:41:15 +02:00
[volume] preserve volume data mtime across tier moves (#9947)
* fix(tier): preserve volume data modification time * fix(tier): best-effort restore of data mtime on download A failed Chtimes should not abort an otherwise complete tier-down; warn and continue, matching the EC copy path. * fix(tier): preserve volume data mtime in rust volume server Mirror the Go fix: store the source .dat mtime on upload instead of the upload time, and restore it on the downloaded .dat. Without this a tiered-then-restored volume loads last_modified_ts_seconds from the upload/download time, extending its TTL across a restart or remount. * fix(tier): read source mtime via DiskFile.GetStat() GetStat() is nil-safe when the backend is closed concurrently and skips a redundant stat syscall; its cached modTime is the on-disk mtime a reload reads, since every .dat write or Chtimes is followed by a DiskFile (re)open. * fix(tier): surface mtime-restore failures on rust tier-down set_file_mtime now returns io::Result; the tier-down path warns on a failed restore instead of dropping it silently, so a wrong local .dat mtime (and the TTL drift it causes) is observable. Matches the Go download. The EC copy path keeps its best-effort silence.
This commit is contained in:
@@ -1247,7 +1247,7 @@ impl VolumeServer for VolumeGrpcService {
|
||||
.await
|
||||
.map_err(|e| Status::internal(e))?;
|
||||
if dat_modified_ts_ns > 0 {
|
||||
set_file_mtime(&dat_path, dat_modified_ts_ns);
|
||||
let _ = set_file_mtime(&dat_path, dat_modified_ts_ns);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1272,7 +1272,7 @@ impl VolumeServer for VolumeGrpcService {
|
||||
.await
|
||||
.map_err(|e| Status::internal(e))?;
|
||||
if idx_modified_ts_ns > 0 {
|
||||
set_file_mtime(&idx_path, idx_modified_ts_ns);
|
||||
let _ = set_file_mtime(&idx_path, idx_modified_ts_ns);
|
||||
}
|
||||
|
||||
// Copy .vif file (ignore if not found on source)
|
||||
@@ -1296,7 +1296,7 @@ impl VolumeServer for VolumeGrpcService {
|
||||
.await
|
||||
.map_err(|e| Status::internal(e))?;
|
||||
if vif_modified_ts_ns > 0 {
|
||||
set_file_mtime(&vif_path, vif_modified_ts_ns);
|
||||
let _ = set_file_mtime(&vif_path, vif_modified_ts_ns);
|
||||
}
|
||||
|
||||
// Remove the .note file
|
||||
@@ -3087,6 +3087,15 @@ impl VolumeServer for VolumeGrpcService {
|
||||
dat_path
|
||||
};
|
||||
|
||||
// Store the source .dat mtime, not the upload time, so a reload computes
|
||||
// TTL from real data age (matches Go VolumeTierMoveDatToRemote).
|
||||
let dat_modified_secs = std::fs::metadata(&dat_path)
|
||||
.and_then(|m| m.modified())
|
||||
.map_err(|e| Status::internal(format!("stat data file {}: {}", dat_path, e)))?
|
||||
.duration_since(std::time::UNIX_EPOCH)
|
||||
.map(|d| d.as_secs())
|
||||
.unwrap_or(0);
|
||||
|
||||
// Look up the S3 tier backend
|
||||
let backend = {
|
||||
let registry = self.state.s3_tier_registry.read().unwrap();
|
||||
@@ -3139,18 +3148,13 @@ impl VolumeServer for VolumeGrpcService {
|
||||
{
|
||||
let mut store = state.store.write().unwrap();
|
||||
if let Some((_, vol)) = store.find_volume_mut(vid) {
|
||||
let now_unix = std::time::SystemTime::now()
|
||||
.duration_since(std::time::UNIX_EPOCH)
|
||||
.unwrap_or_default()
|
||||
.as_secs();
|
||||
|
||||
vol.volume_info.files.push(volume_server_pb::RemoteFile {
|
||||
backend_type: backend_type.clone(),
|
||||
backend_id: backend_id.clone(),
|
||||
key,
|
||||
offset: 0,
|
||||
file_size: size,
|
||||
modified_time: now_unix,
|
||||
modified_time: dat_modified_secs,
|
||||
extension: ".dat".to_string(),
|
||||
});
|
||||
vol.refresh_remote_write_mode();
|
||||
@@ -3201,7 +3205,7 @@ impl VolumeServer for VolumeGrpcService {
|
||||
let vid = VolumeId(req.volume_id);
|
||||
|
||||
// Validate volume and get remote storage info
|
||||
let (dat_path, storage_name, storage_key) = {
|
||||
let (dat_path, storage_name, storage_key, remote_modified_secs) = {
|
||||
let store = self.state.store.read().unwrap();
|
||||
let (_, vol) = store
|
||||
.find_volume(vid)
|
||||
@@ -3231,7 +3235,14 @@ impl VolumeServer for VolumeGrpcService {
|
||||
)));
|
||||
}
|
||||
|
||||
(dat_path, storage_name, storage_key)
|
||||
let remote_modified_secs = vol
|
||||
.volume_info
|
||||
.files
|
||||
.first()
|
||||
.map(|f| f.modified_time)
|
||||
.unwrap_or(0);
|
||||
|
||||
(dat_path, storage_name, storage_key, remote_modified_secs)
|
||||
};
|
||||
|
||||
// Look up the S3 tier backend
|
||||
@@ -3279,6 +3290,15 @@ impl VolumeServer for VolumeGrpcService {
|
||||
))
|
||||
})?;
|
||||
|
||||
// Restore the .dat mtime so a reload computes TTL from real data age,
|
||||
// not download time (matches Go VolumeTierMoveDatFromRemote).
|
||||
if remote_modified_secs > 0 {
|
||||
let modified_ts_ns = (remote_modified_secs as i64).saturating_mul(1_000_000_000);
|
||||
if let Err(e) = set_file_mtime(&dat_path, modified_ts_ns) {
|
||||
tracing::warn!("volume {} restore data file {} modified time: {}", vid, dat_path, e);
|
||||
}
|
||||
}
|
||||
|
||||
if !keep_remote {
|
||||
// Delete remote file
|
||||
backend.delete_file(&storage_key).await.map_err(|e| {
|
||||
@@ -4150,17 +4170,16 @@ async fn ping_filer_target(
|
||||
use super::grpc_client::parse_grpc_address;
|
||||
|
||||
/// Set the modification time of a file from nanoseconds since Unix epoch.
|
||||
fn set_file_mtime(path: &str, modified_ts_ns: i64) {
|
||||
fn set_file_mtime(path: &str, modified_ts_ns: i64) -> std::io::Result<()> {
|
||||
use std::time::{Duration, SystemTime};
|
||||
let ts = if modified_ts_ns >= 0 {
|
||||
SystemTime::UNIX_EPOCH + Duration::from_nanos(modified_ts_ns as u64)
|
||||
} else {
|
||||
SystemTime::UNIX_EPOCH
|
||||
};
|
||||
if let Ok(file) = std::fs::File::open(path) {
|
||||
let ft = std::fs::FileTimes::new().set_accessed(ts).set_modified(ts);
|
||||
let _ = file.set_times(ft);
|
||||
}
|
||||
let file = std::fs::File::open(path)?;
|
||||
let ft = std::fs::FileTimes::new().set_accessed(ts).set_modified(ts);
|
||||
file.set_times(ft)
|
||||
}
|
||||
|
||||
/// Copy a file from a remote volume server via CopyFile streaming RPC.
|
||||
|
||||
Reference in New Issue
Block a user