• Stars
    star
    138
  • Rank 264,508 (Top 6 %)
  • Language
    Erlang
  • License
    Apache License 2.0
  • Created almost 7 years ago
  • Updated about 1 year ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

A RocksDB backend plugin for mnesia, based on mnesia_eleveldb

Mnesia Rocksdb - Rocksdb backend plugin for Mnesia

Copyright (c) 2013-21 Klarna AB

Authors: Ulf Wiger ([email protected]).

The Mnesia DBMS, part of Erlang/OTP, supports 'backend plugins', making it possible to utilize more capable key-value stores than the dets module (limited to 2 GB per table). Unfortunately, this support is undocumented. Below, some informal documentation for the plugin system is provided.

Table of Contents

  1. Usage

  2. Prerequisites

  3. Getting started

  4. Special features

  5. Customization

  6. Handling of errors in write operations

  7. Caveats

  8. Mnesia backend plugins

  9. Background

  10. Design

  11. Mnesia index plugins

  12. Rocksdb

Usage

Prerequisites

Getting started

Call mnesia_rocksdb:register() immediately after starting mnesia.

Put {rocksdb_copies, [node()]} into the table definitions of tables you want to be in RocksDB.

Special features

RocksDB tables support efficient selects on prefix keys.

The backend uses the sext module (see https://github.com/uwiger/sext) for mapping between Erlang terms and the binary data stored in the tables. This provides two useful properties:

  • The records are stored in the Erlang term order of their keys.

  • A prefix of a composite key is ordered just before any key for which it is a prefix. For example, {x, '_'} is a prefix for keys {x, a},{x, b} and so on.

This means that a prefix key identifies the start of the sequence of entries whose keys match the prefix. The backend uses this to optimize selects on prefix keys.

Customization

RocksDB supports a number of customization options. These can be specified by providing a {Key, Value} list named rocksdb_opts under user_properties, for example:

mnesia:create_table(foo, [{rocksdb_copies, [node()]},
                          ...
                          {user_properties,
                              [{rocksdb_opts, [{max_open_files, 1024}]}]
                          }])

Consult the RocksDB documentation for information on configuration parameters. Also see the section below on handling write errors.

The default configuration for tables in mnesia_rocksdb is:

default_open_opts() ->
    [ {create_if_missing, true}
      , {cache_size,
         list_to_integer(get_env_default("ROCKSDB_CACHE_SIZE", "32212254"))}
      , {block_size, 1024}
      , {max_open_files, 100}
      , {write_buffer_size,
         list_to_integer(get_env_default(
                           "ROCKSDB_WRITE_BUFFER_SIZE", "4194304"))}
      , {compression,
         list_to_atom(get_env_default("ROCKSDB_COMPRESSION", "true"))}
      , {use_bloomfilter, true}
    ].

It is also possible, for larger databases, to produce a tuning parameter file. This is experimental, and mostly copied from mnesia_leveldb. Consult the source code in mnesia_rocksdb_tuning.erl and mnesia_rocksdb_params.erl. Contributions are welcome.

Caveats

Avoid placing bag tables in RocksDB. Although they work, each write requires additional reads, causing substantial runtime overheads. There are better ways to represent and process bag data (see above about prefix keys).

The mnesia:table_info(T, size) call always returns zero for RocksDB tables. RocksDB itself does not track the number of elements in a table, and although it is possible to make the mnesia_rocksdb backend maintain a size counter, it incurs a high runtime overhead for writes and deletes since it forces them to first do a read to check the existence of the key. If you depend on having an up to date size count at all times, you need to maintain it yourself. If you only need the size occasionally, you may traverse the table to count the elements.

Mnesia backend plugins

Background

Mnesia was initially designed to be a RAM-only DBMS, and Erlang's ets tables were developed for this purpose. In order to support persistence, e.g. for configuration data, a disk-based version of ets (called dets) was created. The dets API mimicks the ets API, and dets is quite convenient and fast for (nowadays) small datasets. However, using a 32-bit bucket system, it is limited to 2GB of data. It also doesn't support ordered sets. When used in Mnesia, dets-based tables are called disc_only_copies.

To circumvent these limitations, another table type, called disc_copies was added. This is a combination of ets and disk_log, where Mnesia periodically snapshots the ets data to a log file on disk, and meanwhile maintains a log of updates, which can be applied at startup. These tables are quite performant (especially on read access), but all data is kept in RAM, which can become a serious limitation.

A backend plugin system was proposed by Ulf Wiger in 2016, and further developed with Klarna's support, to finally become included in OTP 19. Klarna uses a LevelDb backend, but Aeternity, in 2017, instead chose to implement a Rocksdb backend plugin.

Design

As backend plugins were added on a long-since legacy-stable Mnesia, they had to conform to the existing code structure. For this reason, the plugin callbacks hook into the already present low-level access API in the mnesia_lib module. As a consequence, backend plugins have the same access semantics and granularity as ets and dets. This isn't much of a disadvantage for key-value stores like LevelDb and RocksDB, but a more serious issue is that the update part of this API is called on after the point of no return. That is, Mnesia does not expect these updates to fail, and has no recourse if they do. As an aside, this could also happen if a disc_only_copies table exceeds the 2 GB limit (mnesia will not check it, and dets will not complain, but simply drop the update.)

Mnesia index plugins

When adding support for backend plugins, index plugins were also added. Unfortunately, they remain undocumented.

An index plugin can be added in one of two ways:

  1. When creating a schema, provide {index_plugins, [{Name, Module, Function}]} options.

  2. Call the function mnesia_schema:add_index_plugin(Name, Module, Function)

Name must be an atom wrapped as a 1-tuple, e.g. {words}.

The plugin callback is called as Module:Function(Table, Pos, Obj), where Pos=={words} in our example. It returns a list of index terms.

Example

Given the following index plugin implementation:

-module(words).
-export([words_f/3]).

words_f(_,_,Obj) when is_tuple(Obj) ->
    words_(tuple_to_list(Obj)).

words_(Str) when is_binary(Str) ->
    string:lexemes(Str, [$\s, $\n, [$\r,$\n]]);
words_(L) when is_list(L) ->
    lists:flatmap(fun words_/1, L);
words_(_) ->
    [].

We can register the plugin and use it in table definitions:

Eshell V12.1.3  (abort with ^G)
1> mnesia:start().
ok
2> mnesia_schema:add_index_plugin({words}, words, words_f).
{atomic,ok}
3> mnesia:create_table(i, [{index, [{words}]}]).
{atomic,ok}

Note that in this case, we had neither a backend plugin, nor even a persistent schema. Index plugins can be used with all table types. The registered indexing function (arity 3) must exist as an exported function along the node's code path.

To see what happens when we insert an object, we can turn on call trace.

4> dbg:tracer().
{ok,<0.108.0>}
5> dbg:tp(words, x).
{ok,[{matched,nonode@nohost,3},{saved,x}]}
6> dbg:p(all,[c]).
{ok,[{matched,nonode@nohost,60}]}
7> mnesia:dirty_write({i,<<"one two">>, [<<"three">>, <<"four">>]}).
(<0.84.0>) call words:words_f(i,{words},{i,<<"one two">>,[<<"three">>,<<"four">>]})
(<0.84.0>) returned from words:words_f/3 -> [<<"one">>,<<"two">>,<<"three">>,
                                             <<"four">>]
(<0.84.0>) call words:words_f(i,{words},{i,<<"one two">>,[<<"three">>,<<"four">>]})
(<0.84.0>) returned from words:words_f/3 -> [<<"one">>,<<"two">>,<<"three">>,
                                             <<"four">>]
ok
8> dbg:ctp('_'), dbg:stop().
ok
9> mnesia:dirty_index_read(i, <<"one">>, {words}).
[{i,<<"one two">>,[<<"three">>,<<"four">>]}]

(The fact that the indexing function is called twice, seems like a performance bug.)

We can observe that the indexing callback is able to operate on the whole object. It needs to be side-effect free and efficient, since it will be called at least once for each update (if an old object exists in the table, the indexing function will be called on it too, before it is replaced by the new object.)

Rocksdb

Usage

Modules

mnesia_rocksdb
mnesia_rocksdb_admin
mnesia_rocksdb_app
mnesia_rocksdb_lib
mnesia_rocksdb_params
mnesia_rocksdb_sup
mnesia_rocksdb_tuning
mrdb
mrdb_index
mrdb_mutex
mrdb_select

More Repositories

1

aeternity

æternity blockchain - scalable blockchain for the people - smart contracts, state channels, names, tokens
Erlang
1,057
star
2

elixir-node

Elixir full node implementation of the aeternity specification
Elixir
212
star
3

protocol

Specification of the æternity blockchain protocol
Python
204
star
4

aepp-sdk-js

JavaScript SDK for the æternity blockchain
TypeScript
120
star
5

aesophia

Stand alone compiler for the Sophia smart contract language
Erlang
49
star
6

enoise

Erlang implementation of the Noise protocol
Erlang
45
star
7

aepp-base

Base æpp
Vue
42
star
8

aepp-components

deprecated: aepp-components to be used in all aepps.
Vue
42
star
9

superhero-wallet

Superhero wallet
Vue
40
star
10

app_ctrl

Alternative application controller for Erlang/OTP
Erlang
30
star
11

aeproject

Testing framework for Sophia Smart Contract development.
JavaScript
28
star
12

whitepaper

Æternity blockchain whitepaper (Latex)
TeX
27
star
13

ae_mdw

Aeternity Middleware in Elixir
Elixir
26
star
14

aepp-sdk-python

Python SDK for the Æternity blockchain
Python
24
star
15

aepp-blockchain-explorer

Official blockchain explorer for Aeternity Blockchain
Vue
24
star
16

infrastructure

Shell
23
star
17

aepp-sophia-examples

Sophia Smart Contract examples using AEproject.
JavaScript
22
star
18

aepp-sdk-go

Golang SDK to interact with the Æternity blockchain
Go
20
star
19

mnesia_leveled

Mnesia backend plugin using Leveled (https://github.com/martinsumner/leveled)
Erlang
20
star
20

superhero-ui

Superhero UI
Vue
19
star
21

aepp-sdk-elixir

Elixir SDK targeting the æternity node implementation
Elixir
19
star
22

aepp-prototypes

Concepts of apps that will work on the top ouf æterenity blockchain
18
star
23

aebytecode

Aeternity virtual machines byte code modules
Erlang
13
star
24

tutorials

13
star
25

awesome-aeternity

A curated list of resources for the æternity blockchain
Python
12
star
26

aepp-contracts

æternity contracts editor
Vue
11
star
27

aesophia_cli

Aeternity Sophia CLI
Erlang
10
star
28

AEXs

Aeternity expansions repository — application layer standards
10
star
29

aepp-voting

JavaScript
10
star
30

aesim

Æternity p2p simulator
Erlang
9
star
31

ae-channel-service

Elixir
9
star
32

aepp-aexistence

JavaScript
8
star
33

documentation-hub

21.09.2021: The documentation-hub has been sunset in favor of awesome-aeternity
8
star
34

aeternity-reimagined

The new whitepaper
8
star
35

aestudio

Æ Studio - Aeternity's Smart Contract Editor formerly known as Fire Editor !
TypeScript
8
star
36

aepp-docs

aepps.com documentation repository
CSS
7
star
37

aepp-sdk-docs

HTML
7
star
38

conversational-machine

A state machine for conversational interfaces in nodejs
JavaScript
7
star
39

dao-fi-fusion-code-challenge

Join this code challenge and win up to $20,000 from a total prize pool of $50,000!
7
star
40

eblake2

A pure Erlang Blake2 implementation
Erlang
6
star
41

aescan

Block Explorer and Analytics Platform
Vue
6
star
42

aesophia_http

Erlang
6
star
43

dex-ui

Superhero DEX - the official UI for the first automated market maker (AMM) on æternity.
Vue
6
star
44

aerepl

A sophisticated Read-Eval-Print Loop for Sophia
Erlang
6
star
45

hyperchains-whitepaper

The specification of the aeternity's hyperchains protocol
TeX
6
star
46

dex-contracts-v2

JavaScript
5
star
47

aestratum_client

Stratum client
Erlang
5
star
48

aepp-cli-js

Command line interface for the æternity blockchain
JavaScript
5
star
49

state-channel-demo

rock, paper, scissors implemented on the æternity blockchain using state channels.
JavaScript
5
star
50

localnet

Docker-compose based configuration to easily run locally deployed dev/test network
HTML
5
star
51

aeminer

Erlang
5
star
52

aex141-nft-collection-example

Simple showcase how to create an NFT collection and mint NFTs. The repo also contains a simple-nft-viewer UI.
JavaScript
5
star
53

white-paper

2020 White paper DRAFT
TeX
4
star
54

coin-toss-game

The UI for the (developer focused) state channel demo.
TypeScript
4
star
55

tipping-contract

JavaScript
4
star
56

aepp-playground

Playground  —  unified development experience for the æternity Blockchain.
JavaScript
4
star
57

ae_plugin

Simple setup-based AE node bootstrap app for plugin development
Erlang
4
star
58

aepp-graffiti

Vue
4
star
59

deprecated-middleware

A middleware layer on top of Epoch
Python
4
star
60

aeconnector

Parent chains interface provider (Hyperchains).
Erlang
4
star
61

mdw-frontend

Vue
4
star
62

aepp-goggles

A simple interface to decode and verify encoded transactions for the æternity network.
Vue
4
star
63

aepp-response-contracts

JavaScript
4
star
64

id-manager-provider

web3 provider for dapps running in the id-manager
JavaScript
4
star
65

bounties

Rewarded contribution opportunities in the æcosystem for the æternity community.
4
star
66

ae_canary

Elixir
4
star
67

aepp-boilerplate-vue

A Vue.js boilerplate to build aepps in the æternity ecosystem.
JavaScript
3
star
68

aepp-wall

Æternal Message Wall
JavaScript
3
star
69

contraect

Aeternity AE Token Contract (ERC20)
JavaScript
3
star
70

universe-two-haeckathon

Join our running Hæckathon now and win up to $15,000!
3
star
71

superhero-utils

Superhero Tip button widget for creators
JavaScript
3
star
72

tool-paperwallets

Python
3
star
73

aepp-base-remote-connection-backend

JavaScript
3
star
74

aepp-hyperchain

Hyperchains UI
Svelte
3
star
75

beer-aepp

Beer Aepp! 🎉
Vue
3
star
76

tf-sdk-testnet

Terraform EKS Setup
HCL
3
star
77

aeserialization

Serialization formats for the aeternity node
Erlang
3
star
78

aepp-calldata-js

Aeternity data serialization library
JavaScript
3
star
79

ga-multisig-contract

This contract can be used to convert a regular, plain-old account (POA) into a MultiSig account using the Generalized Account feature of æternity.
JavaScript
3
star
80

hyperchains-contract

Staking&election contract for aeternity hyperchains
JavaScript
2
star
81

ga-multisig-backend

JavaScript
2
star
82

gitops-tools

Aeternity tools managed with gitops
Smarty
2
star
83

aevarna

Stand alone compiler for Varna contract language
Erlang
2
star
84

key-translate

Live version available at https://aeternity.github.io/key-translate/
JavaScript
2
star
85

aepp-faucet

Send Online Top-up. Instant Wallet Recharge
Python
2
star
86

aepp-gomoku-js

JavaScript
2
star
87

launcher

Graphical launcher and interface for Aeternity nodes.
Erlang
2
star
88

aesophia_aci_encoder

Erlang
2
star
89

ebip39

Erlang implementation of BIP-0039 - generating mnemonics
Erlang
2
star
90

aepps-home-microsite

Microsite to showcase the current state of æpps development on aepps.com
SCSS
2
star
91

aepp-hybrid-voting

JavaScript
2
star
92

aepp-aeproject-shape-vue

Vue
2
star
93

tipping-oracle-service

Superhero tipping oracle service
JavaScript
2
star
94

epoch-rs

æternity testnet: solving scalability problems by making sense of state-channels
2
star
95

hd-wallet-js

HD Wallet for the æternity blockchain
JavaScript
2
star
96

state-channel-workshop

The repository for the State Channel Workshop at the Æternity Universe Two conference
JavaScript
2
star
97

embedded-jitsi

HTML
2
star
98

aerepl-web

An HTTP interface for aerepl
Elixir
2
star
99

tipping-community-backend

JavaScript
2
star
100

aepp-showcases

2
star