require('proof')(2, async okay => {
Memento is a pure JavaScript database. Is is an actual database. Writes data
to file, pages it in and out of memory as needed. Memento is concurrent,
indexed, transactional and persistent with a contemporary async
/await
interface.
This unit test represents a tour of Memento and is a stub for some actual documentation that I may write someday. It is part of Memento’s unit test suite and lives in the Memento repository. You can run this readme yourself.
git clone git@github.com:bigeasy/memento.git
cd memento
npm install --no-package-lock --no-save
node test/readme.t.js
Note that you should run node test/readme.t.js
and not npm test
to see
the output from this walk-through.
This walk-through uses the Proof unit
test framework. It will setup an async
function that will catch and report
any exceptions. We’ll use the okay
function to assert the points we make
about Memento.
require('proof')(2, async okay => {
To use Memento in your project it you’ll want to install it from NPM.
npm install memento
You can then include Memento in your program with require
.
const Memento = require('memento')
But, because we’re running in the Memento project we have to use a relative path to the root index. Use the above, not the below.
const Memento = require('..')
okay(Memento != null, 'require')
We’re going to do some file manipulation in this walk-though.
const path = require('path')
const fs = require('fs').promises
We’re going to reset our example directory.
const directory = path.resolve(__dirname, './tmp/readme')
await fs.rmdir(directory, { recursive: true })
await fs.mkdir(directory, { recursive: true })
Memento has a lot of options. TODO come back and write a function call of this.
We create a database object with the static async Memento.open
function. It returns an open database ready for use.
The first argument to async Memento.open()
is an options object.
The second argument is an async
database upgrade function. You are only
able to create new stores and indices in the update function. Once the
database is open you’re not allowed to make any schema changes.
let memento = await Memento.open({ directory }, async schema => {
switch (schema.version) {
case 1:
await schema.store('president', { lastName: String, firstName: String })
break
}
})
In order to add or remove data from the database you invoke
Memento.mutator()
with an async
mutation function.
The mutator function represents an atomic transaction against the database. Changes made within the function are only visible within the function. They only become visible outside of the function when the function returns successfully.
If the function raises and exception, the changes are rolled back.
await memento.mutator(async mutator => {
mutator.set('president', { firstName: 'George', lastName: 'Washington' })
const got = await mutator.get('president', [ 'Washington', 'George' ])
okay(got, {
firstName: 'George', lastName: 'Washington'
}, 'isolated view of inserted record')
})
You’ll notice that the mutator.set()
method is a synchronous function.
This is because we want inserts and deletes to be fast. Rather than
performing asynchronous file operations for each insert and delete, we
cache the changes in memory and write them out in batches.
The mutator.get()
method on the other hand is an async
function. We
have to go and check the database to see if the value is there and
compare it with our write cache. Checking the database may require a read
operation, or it may not, depending on the database cache.
So, mutator.set()
ought to be pretty quick, making batch inserts
relatively painless. async mutator.get()
not so quick because it has to
go out through the Promise
s event loop.
We’ll make up for this discrepancy when we look at ranged queries, iterators, and joins.
TODO A snapshot.
await memento.close()
})