Skip to content

CaptChrisD/persistent_storage

 
 

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

25 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

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:
def deps do
  [{:persistent_storage, "~> 0.10.0"}]
end
  1. Define one or more storage areas in your config/config.exs or env specific config file
config :persistent_storage, tables: [
  settings: [path: "/root/storage/settings"],
  provisioning: [path: "/boot/provisioning"]
]
  1. Ensure persistent_storage is started before your application:
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.

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)...

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)

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

About

Perisistently store erlang terms to the filesystem

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages

  • Elixir 100.0%