LevelDB for UWP applications

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

Introduction

LevelDBWinRT is a Windows Runtime component which enables you to use LevelDB in you Windows 8.1, Windows Phone 8.1, and Windows 10 Universal Platform (UWP). It's designed to replicate the LevelDB API as close as possible giving 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 feature 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 the extension by going visual studio extensions site or in visual studio go to  tool > extensions and updates .

Search for LevelDBWinRT under online tab.

Once installed you can easily add reference to your UWP or Windows 8.1 project by adding project reference. Simply right click on your project references and click add reference. Under reference manager dialog select Extensions and select the LevelDB extension.

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 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 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 loosing 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 it's 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 integral part of LevelDB and bytes are 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 . 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 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 entire database. Checkout source code and detail documentation on official GitHub repository .

Download the attached sample project to see code in action.





About List