For network services the goal of persistency is to be able to keep some state between separate client requests. Persistent storage can be used for many things, but the ultimate goal is the same - to keep some data available, so that it survives not only across requests, but between service or server restarts.
You could have added a connection to a full-blown database and store your data there. But that would require too much hassle of bringing up and configuring the database and shoehorning the data you want to keep into relational tables or hooking up to an ORM. Besides, you will have to decide on how different cages and different modules will share the same database.
Instead, for quick-and-easy persistency Pythomnic offers a facility called "module safe". This is a small database only visible inside an application module, each module thus has its own private safe. Technically, module safe is an instance of a shelf, standard Python mechanism for serializing objects backed up by embedded BerkeleyDB database.
Your application modules access their safes using the pmnc_safe construct
pmnc_safe[key] = value
value = pmnc_safe[key]
value = pmnc_safe.get(key, default)
del pmnc_safe[key]
which behaves like dict, providing keyed access to a set of records (inheriting this behaviour from a shelf). A shelf also allows storing all sorts of data, thus pmnc_safe can contain nearly everything, for example:
pmnc_safe["foo"] = { (None, ): [ 1, "bar", Decimal("1.0") ] }
Each module's safe files are stored in safe/node.cage.module/ directory - one file with data and several files with transaction logs. BerkeleyDB is a "real" database supporting transactions and everything, therefore pmnc_safe among other things enjoys atomicity and durability - a safe operation cannot fail halfway through, leaving the data corrupt, and having returned from
pmnc_safe["foo"] = "bar"
your code can be certain the data is stored for good.
The pmnc_safe facility does not support explicit transactions spanning multiple safe accesses. Instead, each access to the safe is performed in a separate implicit transaction with auto-commit. Therefore in
pmnc_safe["foo"] = "bar"
pmnc_safe["biz"] = "baz"
each assignment is a separate transaction, and if there is a failure between the two, the safe will contain only the first record. Moreover, a concurrent thread may only see only the first record, but not the second. This is by design and your application modules should account for that.