Expand description
Imagine, if you will, building an application that downloads a file from a website. Let’s say the application is downloading baby name data from the U.S. Social Security Administration (https://www.ssa.gov/oact/babynames/names.zip).
A common failure when getting data from the internet is an interrupted download. Unless precautions are taken, the file ends up truncated (essentially corrupt). That could easily result in a bad experience. The application might stop running after outputting a cryptic error error regarding an unreadable ZIP file.
A similar problem occurs with configuration files. We want our service to only see a complete configuration file. A partial configuration file might even introduce a security vulnerablility.
The purpose of this crate is to present a file to a system in a finished state or not at all. Either the entire names.zip file is downloaded or the file is missing. Either the old complete configuration file is used or the new complete configuration file is used.
The following example shows how an interrupted application using this crate avoids putting a partial file in use.
fn main() -> Result<(), Box<dyn std::error::Error>> {
let p = Phazer::new("test.cfg");
let mut w = p.simple_writer()?;
writeln!(w, "[Settings]")?;
writeln!(w, "Port=1")?;
// A crash here does not corrupt the "test.cfg" file because this code was not writing
// directly to "test.cfg". If "test.cfg" did not exist before and this program crashes then
// "test.cfg" still does not exist. If "test.cfg" did exist and this program crashes then
// the existing "test.cfg" is left untouched.
//panic!("test.cfg never exists because of this panic. Removing this line results in test.cfg being \"created\" atomically.");
writeln!(w, "Timeout=10")?;
drop(w);
p.commit()?;
Ok(())
}
The same code writing directly to the configuration file would leave a file behind that’s missing the timeout setting. The configuration file would essentially be corrupt.
§Glossary
Two important terms are used throughout this documentation…
- target - This is the “final” file. Continuing from the earlier examples, this would be the downloaded file when it has been successfully downloaded or the new configuration file when it’s ready to be used.
- working - This is the “temporary” file. Writing is to the working file. This crate manages the working file including generating a unique filename and discarding the file if it is not committed.
§Getting Started
Phazer
is the core component of this crate. One Phazer
is constructed for each target
file. Phazer
is essentially a wrapper over the target path. For example, if the
application downloads three files from the internet then one Phazer
is created for each
file.
Phazer
provides a convenient way to manage the working file.
When working with the Rust Standard Library, the simple_writer
method returns a
SimplePhazerWriter
which provides read / write access to the working file.
When working with the tokio crate, the tokio_writer
method
returns a TokioPhazerWriter
which provides async read / write access to the working file.
By default, Phazer
uses a simple rename commit strategy (SIMPLE_RENAME_STRATEGY
). When
Phazer::commit
is called, rename
is used to replace the target file with the working
file. PhazerBuilder
can be used to construct a Phazer
with a different commit strategy.
The one other commit strategy available with this crate is RENAME_WITH_RETRY_STRATEGY
.
Modules§
- simple_
writer - A file-like thing used to build a working file using the Standard Library.
- tokio_
writer - A file-like thing used to build a working file using Tokio.
Structs§
- Phazer
Phazer
manages the transition of the working file to the target file.- Phazer
Builder PhazerBuilder
andPhazerBuilderWithTarget
are used to create a customizedPhazer
.- Phazer
Builder With Target PhazerBuilder
andPhazerBuilderWithTarget
are used to create a customizedPhazer
.- Rename
With Retry Strategy RenameWithRetryStrategy
uses the Standard Libraryrename
function to transition the working file to the target file and retries if that fails with aPermissionDenied
error.- Simple
Rename Strategy SimpleRenameStrategy
uses the Standard Libraryrename
function to transition the working file to the target file.
Constants§
- RENAME_
WITH_ RETRY_ STRATEGY - A ready-to-use instance of
RenameWithRetryStrategy
. - SIMPLE_
RENAME_ STRATEGY - A ready-to-use instance of
SimpleRenameStrategy
.