Line data Source code
1 : use tracing::error;
2 : use utils::sync::gate::GateGuard;
3 :
4 : use crate::context::RequestContext;
5 :
6 : use super::{
7 : MaybeFatalIo, VirtualFile,
8 : owned_buffers_io::{
9 : io_buf_aligned::IoBufAligned, io_buf_ext::FullSlice, write::OwnedAsyncWriter,
10 : },
11 : };
12 :
13 : /// A wrapper around [`super::VirtualFile`] that deletes the file on drop.
14 : /// For use as a [`OwnedAsyncWriter`] in [`super::owned_buffers_io::write::BufferedWriter`].
15 : #[derive(Debug)]
16 : pub struct TempVirtualFile {
17 : inner: Option<Inner>,
18 : }
19 :
20 : #[derive(Debug)]
21 : struct Inner {
22 : file: VirtualFile,
23 : /// Gate guard is held on as long as we need to do operations in the path (delete on drop)
24 : _gate_guard: GateGuard,
25 : }
26 :
27 : impl OwnedAsyncWriter for TempVirtualFile {
28 141492 : fn write_all_at<Buf: IoBufAligned + Send>(
29 141492 : &self,
30 141492 : buf: FullSlice<Buf>,
31 141492 : offset: u64,
32 141492 : ctx: &RequestContext,
33 141492 : ) -> impl std::future::Future<Output = (FullSlice<Buf>, std::io::Result<()>)> + Send {
34 141492 : VirtualFile::write_all_at(self, buf, offset, ctx)
35 141492 : }
36 :
37 84 : async fn set_len(&self, len: u64, ctx: &RequestContext) -> std::io::Result<()> {
38 84 : VirtualFile::set_len(self, len, ctx).await
39 84 : }
40 : }
41 :
42 : impl Drop for TempVirtualFile {
43 20004 : fn drop(&mut self) {
44 20004 : let Some(Inner { file, _gate_guard }) = self.inner.take() else {
45 11208 : return;
46 : };
47 8796 : let path = file.path();
48 12 : if let Err(e) =
49 8796 : std::fs::remove_file(path).maybe_fatal_err("failed to remove the virtual file")
50 : {
51 12 : error!(err=%e, path=%path, "failed to remove");
52 8784 : }
53 8796 : drop(_gate_guard);
54 20004 : }
55 : }
56 :
57 : impl std::ops::Deref for TempVirtualFile {
58 : type Target = VirtualFile;
59 :
60 483298 : fn deref(&self) -> &Self::Target {
61 483298 : &self
62 483298 : .inner
63 483298 : .as_ref()
64 483298 : .expect("only None after into_inner or drop")
65 483298 : .file
66 483298 : }
67 : }
68 :
69 : impl std::ops::DerefMut for TempVirtualFile {
70 0 : fn deref_mut(&mut self) -> &mut Self::Target {
71 0 : &mut self
72 0 : .inner
73 0 : .as_mut()
74 0 : .expect("only None after into_inner or drop")
75 0 : .file
76 0 : }
77 : }
78 :
79 : impl TempVirtualFile {
80 : /// The caller is responsible for ensuring that the path of `virtual_file` is not reused
81 : /// until after this TempVirtualFile's `Drop` impl has completed.
82 : /// Failure to do so will result in unlinking of the reused path by the original instance's Drop impl.
83 : /// The best way to do so is by using a monotonic counter as a disambiguator.
84 : /// TODO: centralize this disambiguator pattern inside this struct.
85 : /// => <https://github.com/neondatabase/neon/pull/11549#issuecomment-2824592831>
86 20784 : pub fn new(virtual_file: VirtualFile, gate_guard: GateGuard) -> Self {
87 20784 : Self {
88 20784 : inner: Some(Inner {
89 20784 : file: virtual_file,
90 20784 : _gate_guard: gate_guard,
91 20784 : }),
92 20784 : }
93 20784 : }
94 :
95 : /// Dismantle this wrapper and return the underlying [`VirtualFile`].
96 : /// This disables auto-unlinking functionality that is the essence of this wrapper.
97 : ///
98 : /// The gate guard is dropped as well; it is the callers responsibility to ensure filesystem
99 : /// operations after calls to this functions are still gated by some other gate guard.
100 : ///
101 : /// TODO:
102 : /// - centralize the common usage pattern of callers (sync_all(self), rename(self, dst), sync_all(dst.parent))
103 : /// => <https://github.com/neondatabase/neon/pull/11549#issuecomment-2824592831>
104 11208 : pub fn disarm_into_inner(mut self) -> VirtualFile {
105 11208 : self.inner
106 11208 : .take()
107 11208 : .expect("only None after into_inner or drop, and we are into_inner, and we consume")
108 11208 : .file
109 11208 : }
110 : }
|