-
-
Notifications
You must be signed in to change notification settings - Fork 2.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Workaround for Large File Corruption During Save #4276
Workaround for Large File Corruption During Save #4276
Conversation
I could also imagine tokio::fs::rename(&tmp_path, path).await?; being used instead of a copy and remove |
Using |
I went the route of making the cached file match the filename instead of having a file called "cachefile" or something just in case multiple saves and crashes occur at the same time |
I don't get why the builds are failing. The file saves to exactly where it's supposed to. Could it potentially be an async thing, where it's checking too quickly and the file hasn't been copied to the new location yet? |
I'm not sure. The async operation should be completed successfully before it's checked. |
Since it's writing to the cache folder, maybe the CI doesn't allow that? |
Maybe? But I would assume it isn't looking there, it should be looking for the file that got created in the working directory? In which case, it saves to the temp file, then moves it to the correct location, then the assert should look there, yeah? |
oh you mean that it could be failing to write to the cache folder, therefore it never gets moved into the correct spot afterwards... That makes sense to me |
If it wasn't able to write to the cache directory, then the write operation would fail. I'm assuming that's why the file is empty. |
wait, no, because I ran the integration tests locally just now and they still fail, so it can't be that |
Could you test with reverting the |
yeah that made it pass super weird |
I'm going to upload this version for now. Maybe dead10ck has some idea of what's going on, I sure don't 😅 |
…20/helix into workaround-large-file-corruption
Interesting. That's probably what it is. The test is making a temporary file in Edit: nope, spoke too soon. Could you change the log level to debug here? |
hmmmm, the integration test is still failing. Works when I run them locally though, even with the copy/delete |
Yeah I came to the same conclusion after more testing. Bummer haha |
Makes it even more confusing though why it would fail to read the contents, as clearly its getting renamed properly and absolutely has the correct string in it. |
something fundamentally has to be different between copy and rename. |
2022-10-18T11:58:50.285 helix_view::document [INFO] actual file ino: 979359
2022-10-18T11:58:50.286 helix_view::document [INFO] before rename
2022-10-18T11:58:50.286 helix_view::document [INFO] tmpfile exist: true
2022-10-18T11:58:50.286 helix_view::document [INFO] actual exist: true
2022-10-18T11:58:50.286 helix_view::document [INFO] tmpfile ino: 980418
2022-10-18T11:58:50.286 helix_view::document [INFO] actual ino: 979359
2022-10-18T11:58:50.286 helix_view::document [INFO] after rename
2022-10-18T11:58:50.286 helix_view::document [INFO] actual ino: 980418
2022-10-18T11:58:50.286 helix_view::document [INFO] tmpfile exist: false
2022-10-18T11:58:50.286 helix_view::document [INFO] actual exist: true
2022-10-18T11:58:50.293 integration::test::write [INFO] TEST:: ino after keysequence: 980418
2022-10-18T11:58:50.295 integration::test::write [INFO] TEST:: ino of newfile: 979359 weird. If I clone the testfile after the rename, the cloned file has the original ino, not the new one... |
I wrote this unit test: #[tokio::test]
async fn save() {
use std::io::Read;
let mut file = tempfile::NamedTempFile::new().unwrap();
let mut doc = Document::open(file.path(), None, None).unwrap();
let view = ViewId::default();
doc.set_selection(view, Selection::single(0, 0));
let transaction = Transaction::change(doc.text(), [(0, 0, Some("ithe gostak distims the doshes".into()))].into_iter());
doc.apply(&transaction, view);
doc.save(false).await.unwrap();
let mut file_content = String::new();
file.as_file_mut().read_to_string(&mut file_content).unwrap();
assert_eq!(file_content, "ithe gostak distims the doshes");
} And it fails with |
That is very confusing. I don't understand how it could have access to write a new file, but not to rename/overwrite. |
Maybe one of the earlier processes is keeping the file open? Permission denied could occur if another of the async functions still has it open for r/w right? |
I found this issue: Stebalien/tempfile#131 and it might be Windows-specific. Could you see what the panic is on a unix machine? This passed: #[tokio::test]
async fn save() {
use std::io::Read;
let file = tempfile::NamedTempFile::new().unwrap();
// Need to close the temp file.
let path = file.into_temp_path();
let mut doc = Document::open(&path, None, None).unwrap();
let view = ViewId::default();
doc.set_selection(view, Selection::single(0, 0));
let transaction = Transaction::insert(
doc.text(),
doc.selection(view),
"ithe gostak distims the doshes".into(),
);
doc.apply(&transaction, view);
doc.save(false).await.unwrap();
let file_content = std::fs::read_to_string(path).unwrap();
assert_eq!(file_content, "ithe gostak distims the doshes");
} |
I get this on Linux ---- test::write::save stdout ----
thread 'test::write::save' panicked at 'called `Result::unwrap()` on an `Err` value: Bad file descriptor (os error 9)', helix-term/tests/test/write.rs:29:27
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace #[tokio::test]
async fn save() {
use helix_view::document::Document;
use helix_core::Transaction;
use helix_view::ViewId;
use std::io::Read;
let file = tempfile::NamedTempFile::new().unwrap();
// Need to close the temp file.
let path = file.into_temp_path();
let mut doc = Document::open(&path, None, None).unwrap();
let view = ViewId::default();
doc.set_selection(view, Selection::single(0, 0));
let transaction = Transaction::insert(
doc.text(),
doc.selection(view),
"ithe gostak distims the doshes".into(),
);
doc.apply(&transaction, view);
doc.save(false).await.unwrap();
let file_content = std::fs::read_to_string(path).unwrap();
assert_eq!(file_content, "ithe gostak distims the doshes");
} |
How about this? #[tokio::test]
async fn save() {
use std::io::Read;
let file = tempfile::NamedTempFile::new().unwrap();
let (_, path) = file.keep().unwrap();
let mut doc = Document::open(&path, None, None).unwrap();
let view = ViewId::default();
doc.set_selection(view, Selection::single(0, 0));
let transaction = Transaction::insert(
doc.text(),
doc.selection(view),
"ithe gostak distims the doshes".into(),
);
doc.apply(&transaction, view);
doc.save(false).await.unwrap();
let file_content = std::fs::read_to_string(path).unwrap();
assert_eq!(file_content, "ithe gostak distims the doshes");
} |
same error. Specifically, it's failing on the |
Could you see where it originates from in the backtrace? |
Here's the basic stacktrace ---- test::write::save stdout ----
thread 'test::write::save' panicked at 'called `Result::unwrap()` on an `Err` value: Bad file descriptor (os error 9)', helix-term/tests/test/write.rs:28:27
stack backtrace:
0: rust_begin_unwind
at /rustc/fe5b13d681f25ee6474be29d748c65adcd91f69e/library/std/src/panicking.rs:584:5
1: core::panicking::panic_fmt
at /rustc/fe5b13d681f25ee6474be29d748c65adcd91f69e/library/core/src/panicking.rs:143:14
2: core::result::unwrap_failed
at /rustc/fe5b13d681f25ee6474be29d748c65adcd91f69e/library/core/src/result.rs:1785:5
3: core::result::Result<T,E>::unwrap
at /rustc/fe5b13d681f25ee6474be29d748c65adcd91f69e/library/core/src/result.rs:1078:23
4: integration::test::write::save::{{closure}}
at ./tests/test/write.rs:28:5
5: <core::future::from_generator::GenFuture<T> as core::future::future::Future>::poll
at /rustc/fe5b13d681f25ee6474be29d748c65adcd91f69e/library/core/src/future/mod.rs:91:19
6: <core::pin::Pin<P> as core::future::future::Future>::poll
at /rustc/fe5b13d681f25ee6474be29d748c65adcd91f69e/library/core/src/future/future.rs:124:9
7: tokio::runtime::scheduler::current_thread::CoreGuard::block_on::{{closure}}::{{closure}}::{{closure}}
at /home/ngraham/.cargo/registry/src/github.aaakk.us.kg-1ecc6299db9ec823/tokio-1.21.2/src/runtime/scheduler/current_thread.rs:525:48
8: tokio::coop::with_budget::{{closure}}
at /home/ngraham/.cargo/registry/src/github.aaakk.us.kg-1ecc6299db9ec823/tokio-1.21.2/src/coop.rs:102:9
9: std::thread::local::LocalKey<T>::try_with
at /rustc/fe5b13d681f25ee6474be29d748c65adcd91f69e/library/std/src/thread/local.rs:442:16
10: std::thread::local::LocalKey<T>::with
at /rustc/fe5b13d681f25ee6474be29d748c65adcd91f69e/library/std/src/thread/local.rs:418:9
11: tokio::coop::with_budget
at /home/ngraham/.cargo/registry/src/github.aaakk.us.kg-1ecc6299db9ec823/tokio-1.21.2/src/coop.rs:95:5
12: tokio::coop::budget
at /home/ngraham/.cargo/registry/src/github.aaakk.us.kg-1ecc6299db9ec823/tokio-1.21.2/src/coop.rs:72:5
13: tokio::runtime::scheduler::current_thread::CoreGuard::block_on::{{closure}}::{{closure}}
at /home/ngraham/.cargo/registry/src/github.aaakk.us.kg-1ecc6299db9ec823/tokio-1.21.2/src/runtime/scheduler/current_thread.rs:525:25
14: tokio::runtime::scheduler::current_thread::Context::enter
at /home/ngraham/.cargo/registry/src/github.aaakk.us.kg-1ecc6299db9ec823/tokio-1.21.2/src/runtime/scheduler/current_thread.rs:349:19
15: tokio::runtime::scheduler::current_thread::CoreGuard::block_on::{{closure}}
at /home/ngraham/.cargo/registry/src/github.aaakk.us.kg-1ecc6299db9ec823/tokio-1.21.2/src/runtime/scheduler/current_thread.rs:524:36
16: tokio::runtime::scheduler::current_thread::CoreGuard::enter::{{closure}}
at /home/ngraham/.cargo/registry/src/github.aaakk.us.kg-1ecc6299db9ec823/tokio-1.21.2/src/runtime/scheduler/current_thread.rs:595:57
17: tokio::macros::scoped_tls::ScopedKey<T>::set
at /home/ngraham/.cargo/registry/src/github.aaakk.us.kg-1ecc6299db9ec823/tokio-1.21.2/src/macros/scoped_tls.rs:61:9
18: tokio::runtime::scheduler::current_thread::CoreGuard::enter
at /home/ngraham/.cargo/registry/src/github.aaakk.us.kg-1ecc6299db9ec823/tokio-1.21.2/src/runtime/scheduler/current_thread.rs:595:27
19: tokio::runtime::scheduler::current_thread::CoreGuard::block_on
at /home/ngraham/.cargo/registry/src/github.aaakk.us.kg-1ecc6299db9ec823/tokio-1.21.2/src/runtime/scheduler/current_thread.rs:515:19
20: tokio::runtime::scheduler::current_thread::CurrentThread::block_on
at /home/ngraham/.cargo/registry/src/github.aaakk.us.kg-1ecc6299db9ec823/tokio-1.21.2/src/runtime/scheduler/current_thread.rs:161:24
21: tokio::runtime::Runtime::block_on
at /home/ngraham/.cargo/registry/src/github.aaakk.us.kg-1ecc6299db9ec823/tokio-1.21.2/src/runtime/mod.rs:490:46
22: integration::test::write::save
at ./tests/test/write.rs:31:5
23: integration::test::write::save::{{closure}}
at ./tests/test/write.rs:12:7
24: core::ops::function::FnOnce::call_once
at /rustc/fe5b13d681f25ee6474be29d748c65adcd91f69e/library/core/src/ops/function.rs:227:5
25: core::ops::function::FnOnce::call_once
at /rustc/fe5b13d681f25ee6474be29d748c65adcd91f69e/library/core/src/ops/function.rs:227:5
note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace. |
Could you try putting the test in |
sorry, I'm super unfamiliar with the testing framework 😅. I tried |
wait hangon |
|
and I put the test in the test mod in document.rs? |
#[cfg(test)]
mod test {
use super::*;
#[tokio::test]
async fn save() {
use std::io::Read;
let file = tempfile::NamedTempFile::new().unwrap();
let (_, path) = file.keep().unwrap();
let mut doc = Document::open(&path, None, None).unwrap();
let view = ViewId::default();
doc.set_selection(view, Selection::single(0, 0));
let transaction = Transaction::insert(
doc.text(),
doc.selection(view),
"ithe gostak distims the doshes".into(),
);
doc.apply(&transaction, view);
doc.save(false).await.unwrap();
let file_content = std::fs::read_to_string(path).unwrap();
assert_eq!(file_content, "ithe gostak distims the doshes");
} error[E0433]: failed to resolve: use of undeclared crate or module `tempfile`
--> helix-view/src/document.rs:1208:20
|
1208 | let file = tempfile::NamedTempFile::new().unwrap();
| ^^^^^^^^ use of undeclared crate or module `tempfile`
warning: unused import: `std::io::Read`
--> helix-view/src/document.rs:1207:13
|
1207 | use std::io::Read;
| ^^^^^^^^^^^^^
|
= note: `#[warn(unused_imports)]` on by default
error[E0277]: the size for values of type `[u8]` cannot be known at compilation time
--> helix-view/src/document.rs:1209:17
|
1209 | let (_, path) = file.keep().unwrap();
| ^^^^ doesn't have a size known at compile-time
|
= help: within `std::path::Path`, the trait `Sized` is not implemented for `[u8]`
= note: required because it appears within the type `std::path::Path`
= note: all local variables must have a statically known size
= help: unsized locals are gated as an unstable feature
error[E0277]: the size for values of type `[u8]` cannot be known at compilation time
--> helix-view/src/document.rs:1221:52
|
1221 | let file_content = std::fs::read_to_string(path).unwrap();
| ----------------------- ^^^^ doesn't have a size known at compile-time
| |
| required by a bound introduced by this call
|
= help: within `std::path::Path`, the trait `Sized` is not implemented for `[u8]`
= note: required because it appears within the type `std::path::Path`
note: required by a bound in `std::fs::read_to_string`
--> /home/ngraham/.rustup/toolchains/1.61.0-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/fs.rs:267:23
|
267 | pub fn read_to_string<P: AsRef<Path>>(path: P) -> io::Result<String> {
| ^ required by this bound in `std::fs::read_to_string`
Some errors have detailed explanations: E0277, E0433.
For more information about an error, try `rustc --explain E0277`.
warning: `helix-view` (lib test) generated 1 warning
error: could not compile `helix-view` due to 3 previous errors; 1 warning emitted
warning: build failed, waiting for other jobs to finish... |
You also have to add |
Thanks for being patient with me 😅 The test fails for the same reason in here running 1 test
test document::test::save ... FAILED
failures:
---- document::test::save stdout ----
thread 'document::test::save' panicked at 'called `Result::unwrap()` on an `Err` value: Bad file descriptor (os error 9)', helix-view/src/document.rs:1219:31
stack backtrace:
0: rust_begin_unwind
at /rustc/fe5b13d681f25ee6474be29d748c65adcd91f69e/library/std/src/panicking.rs:584:5
1: core::panicking::panic_fmt
at /rustc/fe5b13d681f25ee6474be29d748c65adcd91f69e/library/core/src/panicking.rs:143:14
2: core::result::unwrap_failed
at /rustc/fe5b13d681f25ee6474be29d748c65adcd91f69e/library/core/src/result.rs:1785:5
3: core::result::Result<T,E>::unwrap
at /rustc/fe5b13d681f25ee6474be29d748c65adcd91f69e/library/core/src/result.rs:1078:23
4: helix_view::document::test::save::{{closure}}
at ./src/document.rs:1219:9
5: <core::future::from_generator::GenFuture<T> as core::future::future::Future>::poll
at /rustc/fe5b13d681f25ee6474be29d748c65adcd91f69e/library/core/src/future/mod.rs:91:19
6: <core::pin::Pin<P> as core::future::future::Future>::poll
at /rustc/fe5b13d681f25ee6474be29d748c65adcd91f69e/library/core/src/future/future.rs:124:9
7: tokio::runtime::scheduler::current_thread::CoreGuard::block_on::{{closure}}::{{closure}}::{{closure}}
at /home/ngraham/.cargo/registry/src/github.aaakk.us.kg-1ecc6299db9ec823/tokio-1.21.2/src/runtime/scheduler/current_thread.rs:525:48
8: tokio::coop::with_budget::{{closure}}
at /home/ngraham/.cargo/registry/src/github.aaakk.us.kg-1ecc6299db9ec823/tokio-1.21.2/src/coop.rs:102:9
9: std::thread::local::LocalKey<T>::try_with
at /rustc/fe5b13d681f25ee6474be29d748c65adcd91f69e/library/std/src/thread/local.rs:442:16
10: std::thread::local::LocalKey<T>::with
at /rustc/fe5b13d681f25ee6474be29d748c65adcd91f69e/library/std/src/thread/local.rs:418:9
11: tokio::coop::with_budget
at /home/ngraham/.cargo/registry/src/github.aaakk.us.kg-1ecc6299db9ec823/tokio-1.21.2/src/coop.rs:95:5
12: tokio::coop::budget
at /home/ngraham/.cargo/registry/src/github.aaakk.us.kg-1ecc6299db9ec823/tokio-1.21.2/src/coop.rs:72:5
13: tokio::runtime::scheduler::current_thread::CoreGuard::block_on::{{closure}}::{{closure}}
at /home/ngraham/.cargo/registry/src/github.aaakk.us.kg-1ecc6299db9ec823/tokio-1.21.2/src/runtime/scheduler/current_thread.rs:525:25
14: tokio::runtime::scheduler::current_thread::Context::enter
at /home/ngraham/.cargo/registry/src/github.aaakk.us.kg-1ecc6299db9ec823/tokio-1.21.2/src/runtime/scheduler/current_thread.rs:349:19
15: tokio::runtime::scheduler::current_thread::CoreGuard::block_on::{{closure}}
at /home/ngraham/.cargo/registry/src/github.aaakk.us.kg-1ecc6299db9ec823/tokio-1.21.2/src/runtime/scheduler/current_thread.rs:524:36
16: tokio::runtime::scheduler::current_thread::CoreGuard::enter::{{closure}}
at /home/ngraham/.cargo/registry/src/github.aaakk.us.kg-1ecc6299db9ec823/tokio-1.21.2/src/runtime/scheduler/current_thread.rs:595:57
17: tokio::macros::scoped_tls::ScopedKey<T>::set
at /home/ngraham/.cargo/registry/src/github.aaakk.us.kg-1ecc6299db9ec823/tokio-1.21.2/src/macros/scoped_tls.rs:61:9
18: tokio::runtime::scheduler::current_thread::CoreGuard::enter
at /home/ngraham/.cargo/registry/src/github.aaakk.us.kg-1ecc6299db9ec823/tokio-1.21.2/src/runtime/scheduler/current_thread.rs:595:27
19: tokio::runtime::scheduler::current_thread::CoreGuard::block_on
at /home/ngraham/.cargo/registry/src/github.aaakk.us.kg-1ecc6299db9ec823/tokio-1.21.2/src/runtime/scheduler/current_thread.rs:515:19
20: tokio::runtime::scheduler::current_thread::CurrentThread::block_on
at /home/ngraham/.cargo/registry/src/github.aaakk.us.kg-1ecc6299db9ec823/tokio-1.21.2/src/runtime/scheduler/current_thread.rs:161:24
21: tokio::runtime::Runtime::block_on
at /home/ngraham/.cargo/registry/src/github.aaakk.us.kg-1ecc6299db9ec823/tokio-1.21.2/src/runtime/mod.rs:490:46
22: helix_view::document::test::save
at ./src/document.rs:1222:9
23: helix_view::document::test::save::{{closure}}
at ./src/document.rs:1206:11
24: core::ops::function::FnOnce::call_once
at /rustc/fe5b13d681f25ee6474be29d748c65adcd91f69e/library/core/src/ops/function.rs:227:5
25: core::ops::function::FnOnce::call_once
at /rustc/fe5b13d681f25ee6474be29d748c65adcd91f69e/library/core/src/ops/function.rs:227:5
note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace.
failures:
document::test::save
test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured; 39 filtered out; finished in 0.03s |
This is on Windows, right? The unit test is keeping the original file open, and on Windows, IIRC deleting or overwriting an open file is not allowed. |
The debugging code attempts to read from a write-only file descriptor. |
I think I figured out the issue since I dealt with the same thing for persistent undo... turns out you need to seek to the beginning of the file after any read or write operation otherwise you will read a blank string. |
Superseded by #9236 |
PR as a workaround for #3967