I was asked to submit my proposal here.
I have 3 ideas for lower-level APIs for persistence.
Level DB Style
This is highly influenced by https://github.com/google/leveldb#features
- Keys and values are arbitrary byte arrays.
- Data is stored sorted by key.
- Callers can provide a custom comparison function to override the sort order.
- The basic operations are Put(key,value), Get(key), Delete(key).
- Multiple changes can be made in one atomic batch.
- Users can create a transient snapshot to get a consistent view of data.
- Forward and backward iteration is supported over the data.
// Basic operations
db.put(key, value) -> Promise
db.get(key) -> Promise<value>
db.delete(key) -> Promise
// Atomic Batch Writes
db.batch() -> Batch
batch.put(key, value) -> Batch
batch.delete(key) -> Batch
batch.write() -> Promise
// Snapshot reads
db.snapshot() -> Promise<Snapshot>
snapshot.get(key) -> Promise<value>
snapshot.iterate() -> Iterator
db.iterate() -> Iterator
it.seekToFirst() -> Promise
it.seekToEnd() -> Promise
it.valid() -> Promise<isValid>
it.prev() -> Promise
it.next() -> Promise
it.key() -> key
it.value() -> Promise<value>
This is less well designed, but we'll need some globals for bootstrapping this process:
window.openStorage(name, options) -> Promise<DB> // create if not found
// options.compare(a, b) -> Number - custom comparator
window.delStorage(name) -> Promise
window.listStorage() -> Promise<names>
Some applications may prefer a different approach to storage. Consider if we wanted to compile sqlite3 using emscripten and wanted to persist the data somehow.
window.mmap(name) -> Promise<MMap>
mm.lock(start, end) -> Promise<ByteArray> // Ensure a range of bytes is in ram and get access as byte array
mm.unlock(start, end) // Free a range to be cleared from ram cache.
mm.msync() -> Promise // Flush all changes in memory to disk.
// If you try to access a bytearray that's not in ram cache, an exception will be thrown.
Many databases can be implemented with simply an append-only stream.
window.logStore(name) -> Promise<LogStore>
log.write(value) -> Promise // Write to file, promise resolves when data in kernel space
log.fsync() -> Promise // Do a full sync (flush kernel buffers)
log.close() -> Promise // sync and close.
This would need more APIs for reading the logs for later playback obviously.