Skip to content

Commit 0045265

Browse files
committed
Revert "fix: Use single file handle in atomic_write for Windows compatibility"
This reverts commit 7b7880a.
1 parent 1b88023 commit 0045265

9 files changed

Lines changed: 46 additions & 50 deletions

File tree

dash-spv/src/chain/chainlock_manager.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -336,7 +336,7 @@ impl ChainLockManager {
336336
let key = format!("chainlock_{}", chain_lock.block_height);
337337
let data = bincode::serialize(&chain_lock)
338338
.map_err(|e| StorageError::Serialization(e.to_string()))?;
339-
storage.store_metadata(&key, data).await?;
339+
storage.store_metadata(&key, &data).await?;
340340

341341
Ok(())
342342
}

dash-spv/src/network/persist.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ impl PeerStore {
6262
let json = serde_json::to_string_pretty(&saved)
6363
.map_err(|e| Error::Storage(StorageError::Serialization(e.to_string())))?;
6464

65-
atomic_write(&self.path, json.as_bytes().to_vec()).await.map_err(Error::Storage)?;
65+
atomic_write(&self.path, json.as_bytes()).await.map_err(Error::Storage)?;
6666

6767
log::debug!("Saved {} peers to {:?}", saved.peers.len(), self.path);
6868
Ok(())

dash-spv/src/network/reputation.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -426,7 +426,7 @@ impl PeerReputationManager {
426426
.collect();
427427

428428
let json = serde_json::to_string_pretty(&data)?;
429-
atomic_write(path, json.as_bytes().to_vec()).await.map_err(std::io::Error::other)
429+
atomic_write(path, json.as_bytes()).await.map_err(std::io::Error::other)
430430
}
431431

432432
/// Load reputation data from persistent storage

dash-spv/src/storage/headers.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,5 +73,5 @@ pub(super) async fn save_index_to_disk(
7373
let data = bincode::serialize(index)
7474
.map_err(|e| StorageError::WriteFailed(format!("Failed to serialize index: {}", e)))?;
7575

76-
atomic_write(path, data).await
76+
atomic_write(path, &data).await
7777
}

dash-spv/src/storage/io.rs

Lines changed: 34 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
//! Low-level I/O utilities for reading and writing segment files.
22
3-
use std::io::Write;
43
use std::path::{Path, PathBuf};
54

65
use crate::error::{StorageError, StorageResult};
@@ -21,7 +20,7 @@ fn get_temp_path(path: &Path) -> PathBuf {
2120

2221
/// Atomically write data to a file.
2322
/// Uses temporary file + sync + rename pattern for crash resilience.
24-
pub(crate) async fn atomic_write(path: &Path, data: Vec<u8>) -> StorageResult<()> {
23+
pub(crate) async fn atomic_write(path: &Path, data: &[u8]) -> StorageResult<()> {
2524
// Ensure parent directory exists
2625
if let Some(parent) = path.parent() {
2726
tokio::fs::create_dir_all(parent)
@@ -30,35 +29,32 @@ pub(crate) async fn atomic_write(path: &Path, data: Vec<u8>) -> StorageResult<()
3029
}
3130

3231
let temp_path = get_temp_path(path);
33-
let target_path = path.to_path_buf();
34-
35-
// Use blocking I/O to ensure file handle is fully closed before rename.
36-
// On Windows, async file drops don't guarantee synchronous closure.
37-
tokio::task::spawn_blocking(move || {
38-
// Write to temp file
39-
let write_result = (|| {
40-
let mut file = std::fs::File::create(&temp_path)?;
41-
file.write_all(&data)?;
42-
file.sync_all()?;
43-
// File handle closed here when `file` goes out of scope
44-
Ok::<(), std::io::Error>(())
45-
})();
46-
47-
if let Err(e) = write_result {
48-
let _ = std::fs::remove_file(&temp_path);
49-
return Err(StorageError::WriteFailed(format!("Failed to write temp file: {}", e)));
50-
}
5132

52-
// Atomic rename
53-
if let Err(e) = std::fs::rename(&temp_path, &target_path) {
54-
let _ = std::fs::remove_file(&temp_path);
55-
return Err(StorageError::WriteFailed(format!("Failed to rename temp file: {}", e)));
56-
}
33+
// Write to temporary file
34+
let write_result = async {
35+
tokio::fs::write(&temp_path, data).await?;
36+
37+
// Sync to disk - open the file and call sync_all
38+
let file = tokio::fs::File::open(&temp_path).await?;
39+
file.sync_all().await?;
40+
41+
Ok::<(), std::io::Error>(())
42+
}
43+
.await;
44+
45+
// Clean up temp file on error
46+
if let Err(e) = write_result {
47+
let _ = tokio::fs::remove_file(&temp_path).await;
48+
return Err(StorageError::WriteFailed(format!("Failed to write temp file: {}", e)));
49+
}
50+
51+
// Atomic rename
52+
if let Err(e) = tokio::fs::rename(&temp_path, path).await {
53+
let _ = tokio::fs::remove_file(&temp_path).await;
54+
return Err(StorageError::WriteFailed(format!("Failed to rename temp file: {}", e)));
55+
}
5756

58-
Ok(())
59-
})
60-
.await
61-
.map_err(|e| StorageError::WriteFailed(format!("atomic_write join error: {}", e)))?
57+
Ok(())
6258
}
6359

6460
#[cfg(test)]
@@ -91,7 +87,7 @@ mod tests {
9187
let path = temp_dir.path().join("test.dat");
9288

9389
let content = b"hello world";
94-
atomic_write(&path, content.to_vec()).await.unwrap();
90+
atomic_write(&path, content).await.unwrap();
9591

9692
assert!(path.exists());
9793
let read_content = tokio::fs::read(&path).await.unwrap();
@@ -104,7 +100,7 @@ mod tests {
104100
let path = temp_dir.path().join("nested").join("dirs").join("test.dat");
105101

106102
let content = b"nested content";
107-
atomic_write(&path, content.to_vec()).await.unwrap();
103+
atomic_write(&path, content).await.unwrap();
108104

109105
assert!(path.exists());
110106
let read_content = tokio::fs::read(&path).await.unwrap();
@@ -121,7 +117,7 @@ mod tests {
121117

122118
// Overwrite with atomic write
123119
let new_content = b"new content";
124-
atomic_write(&path, new_content.to_vec()).await.unwrap();
120+
atomic_write(&path, new_content).await.unwrap();
125121

126122
let read_content = tokio::fs::read(&path).await.unwrap();
127123
assert_eq!(read_content, new_content);
@@ -132,7 +128,7 @@ mod tests {
132128
let temp_dir = TempDir::new().unwrap();
133129
let path = temp_dir.path().join("test.dat");
134130

135-
atomic_write(&path, b"data".to_vec()).await.unwrap();
131+
atomic_write(&path, b"data").await.unwrap();
136132

137133
// Check no .tmp files remain
138134
let mut entries = tokio::fs::read_dir(temp_dir.path()).await.unwrap();
@@ -158,7 +154,7 @@ mod tests {
158154
// Try to write to an invalid path (directory instead of file)
159155
let invalid_path = temp_dir.path().join("test.dat").join("invalid");
160156

161-
let result = atomic_write(&invalid_path, b"new content".to_vec()).await;
157+
let result = atomic_write(&invalid_path, b"new content").await;
162158
assert!(result.is_err());
163159

164160
// Original file should still have original content
@@ -177,7 +173,7 @@ mod tests {
177173
// Try to write to a path where parent "directory" is actually a file
178174
let invalid_path = blocker_path.join("subdir").join("file.dat");
179175

180-
let result = atomic_write(&invalid_path, b"data".to_vec()).await;
176+
let result = atomic_write(&invalid_path, b"data").await;
181177
assert!(result.is_err());
182178

183179
// No temp files should remain in the base temp dir
@@ -197,7 +193,7 @@ mod tests {
197193

198194
// Write binary data with null bytes and various byte values
199195
let binary_data: Vec<u8> = (0u8..=255).collect();
200-
atomic_write(&path, binary_data.clone()).await.unwrap();
196+
atomic_write(&path, &binary_data).await.unwrap();
201197

202198
let read_content = tokio::fs::read(&path).await.unwrap();
203199
assert_eq!(read_content, binary_data);
@@ -210,7 +206,7 @@ mod tests {
210206

211207
// Write 1MB of data
212208
let large_data: Vec<u8> = (0..1_000_000).map(|i| (i % 256) as u8).collect();
213-
atomic_write(&path, large_data.clone()).await.unwrap();
209+
atomic_write(&path, &large_data).await.unwrap();
214210

215211
let read_content = tokio::fs::read(&path).await.unwrap();
216212
assert_eq!(read_content.len(), large_data.len());
@@ -226,7 +222,7 @@ mod tests {
226222
// you cannot rename a file over an existing directory
227223
tokio::fs::create_dir(&path).await.unwrap();
228224

229-
let result = atomic_write(&path, b"data".to_vec()).await;
225+
let result = atomic_write(&path, b"data").await;
230226
assert!(result.is_err());
231227

232228
// The target directory should still exist

dash-spv/src/storage/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,7 @@ pub trait StorageManager: Send + Sync {
114114
async fn load_filters(&self, range: Range<u32>) -> StorageResult<Vec<Vec<u8>>>;
115115

116116
/// Store metadata.
117-
async fn store_metadata(&mut self, key: &str, value: Vec<u8>) -> StorageResult<()>;
117+
async fn store_metadata(&mut self, key: &str, value: &[u8]) -> StorageResult<()>;
118118

119119
/// Load metadata.
120120
async fn load_metadata(&self, key: &str) -> StorageResult<Option<Vec<u8>>>;

dash-spv/src/storage/segments.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -508,7 +508,7 @@ impl<I: Persistable> Segment<I> {
508508
})?;
509509
}
510510

511-
atomic_write(&path, buffer).await?;
511+
atomic_write(&path, &buffer).await?;
512512

513513
self.state = SegmentState::Clean;
514514
Ok(())

dash-spv/src/storage/state.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ impl DiskStorageManager {
3333

3434
let path = self.base_path.join("state/chain.json");
3535
let json = state_data.to_string();
36-
atomic_write(&path, json.as_bytes().to_vec()).await?;
36+
atomic_write(&path, json.as_bytes()).await?;
3737

3838
Ok(())
3939
}
@@ -94,7 +94,7 @@ impl DiskStorageManager {
9494
))
9595
})?;
9696

97-
atomic_write(&path, json.as_bytes().to_vec()).await?;
97+
atomic_write(&path, json.as_bytes()).await?;
9898
Ok(())
9999
}
100100

@@ -130,7 +130,7 @@ impl DiskStorageManager {
130130
))
131131
})?;
132132

133-
atomic_write(&path, data).await?;
133+
atomic_write(&path, &data).await?;
134134
tracing::debug!("Stored chain lock at height {}", height);
135135
Ok(())
136136
}
@@ -195,7 +195,7 @@ impl DiskStorageManager {
195195
}
196196

197197
/// Store metadata.
198-
pub async fn store_metadata(&mut self, key: &str, value: Vec<u8>) -> StorageResult<()> {
198+
pub async fn store_metadata(&mut self, key: &str, value: &[u8]) -> StorageResult<()> {
199199
let path = self.base_path.join(format!("state/{}.dat", key));
200200
atomic_write(&path, value).await?;
201201
Ok(())
@@ -432,7 +432,7 @@ impl StorageManager for DiskStorageManager {
432432
self.filters.write().await.get_items(range).await
433433
}
434434

435-
async fn store_metadata(&mut self, key: &str, value: Vec<u8>) -> StorageResult<()> {
435+
async fn store_metadata(&mut self, key: &str, value: &[u8]) -> StorageResult<()> {
436436
Self::store_metadata(self, key, value).await
437437
}
438438

dash-spv/tests/segmented_storage_test.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -308,7 +308,7 @@ async fn test_mixed_operations() {
308308
}
309309

310310
// Store metadata
311-
storage.store_metadata("test_key", b"test_value".to_vec()).await.unwrap();
311+
storage.store_metadata("test_key", b"test_value").await.unwrap();
312312

313313
// Verify everything
314314
assert_eq!(storage.get_tip_height().await.unwrap(), Some(74_999));

0 commit comments

Comments
 (0)