Crate phazer

Source
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.
PhazerBuilder
PhazerBuilder and PhazerBuilderWithTarget are used to create a customized Phazer.
PhazerBuilderWithTarget
PhazerBuilder and PhazerBuilderWithTarget are used to create a customized Phazer.
RenameWithRetryStrategy
RenameWithRetryStrategy uses the Standard Library rename function to transition the working file to the target file and retries if that fails with a PermissionDenied error.
SimpleRenameStrategy
SimpleRenameStrategy uses the Standard Library rename 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.