LevelDB for UWP Applications

Datetime:2016-08-23 00:45:51          Topic: Leveldb  DataBase           Share

Introduction

LevelDBWinRT is a Windows Runtime component which enables you to use LevelDB in your Windows 10 Universal Platform (UWP). It's designed to replicate the LevelDB API as close as possible giving the developer full control over core LevelDB . For those who don't know what LevelDB is, it's a fast key-value storage library written at Google that provides an ordered mapping from string keys to string values.

This document gives you a brief overview for getting started with LevelDB in UWP application using the LevelDBWinRT .

Why LevelDB?

LevelDB is a no-brainer and blazing fast key-value store! Here are some of the basic features this runtime component provides you:

  • Keys and values can be arbitrary byte arrays (Slices)
  • Data is stored and sorted by key
  • Callers can provide custom comparison function to override the sort order (experimental support)
  • Basic Get(key)/Put(key, value)/Delete(key) support
  • Atomic batch operations can make multiple changes
  • Users can create transient snapshot to get a consistent view of data
  • Iterator support, with forward and backward iteration
  • Basic wrappers for WriteOptions , ReadOptions , Options
  • Builtin compression support with Snappy
  • Custom comparator support (experimental)
  • ARM, x86, and x64 architecture support
  • Supports Windows 8 and WP 8.1

Installing

You can install LevelDBWinRT by simply installing nuget from here . Or by simply running  Install-Package LevelDB.UWP  on you Package Management Console .

Creating Database

Creating and opening database is really simple. Everything lives under LevelDBWinRT namespace, import the namespace first by using LevelDBWinRT . Each database corresponds to a file, creating a database means creating a LevelDB file. The primary class for manipulating a database is LevelDBWinRT.DB . The constructor takes two parameters - the first one is options to create database, the second one is the file path. Example:

var db = new DB(new Options { CreateIfMissing = true }, "foo.db");

This creates database file foo.db under ApplicationData.Current.LocalFolder.Path , if you don't specify full path to the file (i.e., it doesn't start with X:\ where X is drive letter) the default folder is assumed to be ApplicationData.Current.LocalFolder.Path . The operation will fail if you don't have access on the folder or your path does not exist. In case of failure, a COMException is thrown with captured error code and error message. If you want to only open database if it exists and fail otherwise, remove CreateIfMissing option. Under the hood, CreateIfMissing is hooked up to leveldb::Options object (Almost all options are available for your disposal and they are pretty powerful). We will keep it simple for now, most of the default options are already tuned for a good balance.

Disposing a Database

The DB class is disposable. So calling Dispose() actually deletes the underlaying leveldb object, which in turn properly releases memory and closes any opened handles to the files. So once you are done using a database, be sure to dispose it off.

db.Dispose();

Not disposing the database object and losing reference to the object leaves it upto GC for cleanup, which may have undesired results, so be sure to dispose your objects properly.

Adding, and Removing Entries

Once database has been opened, one can easily add and remove entries to it. To add or remove anything from LevelDB , you need to provide slices, a key slice and a value slice. Slices are nothing but a wrapper around byte array in LevelDB terms. LevelDBWinRT.Slice is the class you can use to create slices from byte[] or string . Although byte[] is enough for generating any type of slice, string overloads have been provided due to its frequent usage. Inserting a key-value pair "foo" => "bar" is as simple as:

db.Put(new WriteOptions(), Slice.FromString("foo"), Slice.FromString("bar"));

You can use WriteOptions to control if you want to Sync changes to disc right away or not. Delete has similar API, deleting " foo " is as simple as:

db.Delete(new WriteOptions(), Slice.FromString("foo"));

Flushing changes to disk immediately is pretty simple, just set Sync to true in any of the write operations, e.g.

db.Put(new WriteOptions{ Sync = true },
Slice.FromString("foo"), Slice.FromString("bar"));

Bytes and Slices

By now, we can see Slices are an integral part of LevelDB and bytes are a fundamental part of slice. One of the design choices made here is to only provide two methods to create a Slice. There are two static methods, Slice.FromString and Slice.FromByteArray . The first method Slice.FromString for being frequent, and Slice.FromByteArray to support vitally everything else. Reverse methods are also available, ToByteArray and AsString to convert values back. You can use BitConverter and any sort of object serializers to convert your complex objects to byte[] , LevelDB just stays out of your way and lets you take care of serailization business.

Reading the Values

Reading individual values is also pretty simple. You can simply do:

Slice slice = db.Get(new ReadOptions(), Slice.FromString("foo"));
string sliceValue = slice.AsString(); // "bar"

ReadOptions again exposes few options to give you more control over your read. Details will be discussed in a different section.

Batch Writes

Just like any good database, LevelDB provides options to do batch inserts and deletes in a single write operation (atomic operations). A write batch can be created by WriteBatch class provided by LevelDB . You can create and commit a batch like the following:

using (var batch = new WriteBatch())
{
    batch.Put(Slice.FromString("foo"), Slice.FromString("bar"));
    batch.Delete(Slice.FromString("del"));
    db.Write(new WriteOptions(), batch); 
}

A more complex example can be:

using(var writeBatch = new WriteBatch())
{
    for (var i = 0; i < 10; i++)
    {
       writeBatch.Put(Slice.FromString("Key"+i), Slice.FromByteArray(BitConverter.GetBytes(i)));
    }
    
    db.Write(new WriteOptions(), writeBatch);
}

It Has More Advanced Stuff (Iterators and Snapshots)

There is more than meets the eyes. LevelDB has snapshots and iterators that provide read-only views to the entire database. Checkout the source code and detailed documentation on the official GitHub repository .

Download the attached sample project to see the code in action.





About List