README.md

# PersistentStorage

Stores and retrieves terms from small flat files on embedded systems.

`PersistentStorage` is intended for trivial persistent storage of basic system and application configuration information on embedded systems.   It is not intended to be a replacement for dets, sqlite, or many other far more capable databases.  It favors simplicity and robustness over performance and capability.

**IMPORTANT -- INCOMPATIBLE API CHANGE**

The API has has changed significantly for PersistentStorage 0.10.x -- you can find a summary of the changes in the CHANGELOG.md.

If your project requires compatibility with the old (now deprecated) api, please make sure you specify "~> 0.9.0" in your project dependencies.

## Philosophy

PersistentStorage manages one more more persistent _tables_, each identified by an atom.  Each table maps to a user-configured directory in the filesystem.   The table keys and associated directories are configured in the application environment using `config.exs`.

Each table persists a set of _keys_ and associated values, where each key maps to a flat file in the table's directory that holds the serialized term.

The simple one-file-per-key/value model favors robustness over speed, reducing risk of write corruption on embedded devices that may power down unexpectedly.

Writes always write and close a file, so are very slow (but plenty fast enough for the kind of device configuration data that doesn't churn frequently).

Reads of a key from disk are slow the first time (they open and read a file), but the term is subsequently cached in ets, so further reads of the same key in a storage area are very fast, and no application-level cacheing is needed.

## Install and Configure

1. Add `persistent_storage` to your list of dependencies in `mix.exs`:

  ```elixir
  def deps do
    [{:persistent_storage, "~> 0.10.0"}]
  end
  ```

2. Define one or more storage areas in your `config/config.exs` or env specific config file

  ```elixir
  config :persistent_storage, tables: [
    settings: [path: "/root/storage/settings"],
    provisioning: [path: "/boot/provisioning"]
  ]
  ```

3. Ensure `persistent_storage` is started before your application:

  ```elixir
  def application do
    [applications: [:persistent_storage]]
  end
  ```

## Usage

Let's say your device wants a couple kinds of non-volatile storage.  

The first kind, which we'll call `settings`, will be used for storing user
settings. We're going to put it on the standard Nerves application data volume, `/root`.

We're going to assume that the /root partition might be rewritten anytime the user does something "resets to factory settings".

The second kind, which we'll call `provisioning`, is more persistent, and is perhaps on a volume usually mounted read-only (like /boot), that persists even when the application volume gets reformatted.

#### Writing

Writing to setttings, assuming the configuration in the previous section.  This will create the file at /root/storage/settings/network.

```elixir
iex> PersistentStorage.put :settings, :network, %{
  ip_address: {192,168,1,100}, mode: :static
}
```

#### Reading

Assuming we wrote as above, we can read it (this reads from ets cache if possible)...

```elixir
iex> PersistentStorage.get :settings, :network
%{ip_address: {192,168,1,100}, mode: :static}
```
Read some provisioning data -- for purposes of this example, we
assume it was written when device  was first flashed)

```elixir
iex> PersistentStorage.get :provisioning, :device_data
[serial_number: "302F1010", mfg_timestamp: "2016-05-04T03:28:35.279977Z"]
```