MDBX::

Database class

The primary class for interacting with an MDBX database.

Attributes

deserializer RW

A Proc for automatically deserializing values. Defaults to Marshal.load.

options R

Options used when instantiating this database handle.

path R

The path on disk of the database.

serializer RW

A Proc for automatically serializing values. Defaults to Marshal.dump.

Public Class Methods

MDBX::Database.open( path ) => db
MDBX::Database.open( path, options ) => db

Open an existing (or create a new) mdbx database at filesystem path. In block form, the database is automatically closed when the block exits.

MDBX::Database.open( path, options ) do |db|
    db[ 'key' ] = value
end # closed!

Passing options modify various database behaviors. See the libmdbx documentation for detailed information.

Options

Unless otherwise mentioned, option keys are symbols, and values are boolean.

:coalesce

Attempt to coalesce items for the garbage collector, potentialy increasing the chance of unallocating storage earlier.

:compatible

Skip compatibility checks when opening an in-use database with unknown or mismatched flag values.

:exclusive

Access is restricted to the first opening process. Other attempts to use this database (even in readonly mode) are denied.

:lifo_reclaim

Recycle garbage collected items via LIFO, instead of FIFO. Depending on underlying hardware (disk write-back cache), this could increase write performance.

:max_collections

Set the maximum number of “subdatabase” collections allowed. By default, collection support is disabled.

:max_readers

Set the maximum number of allocated simultaneous reader slots.

:max_size

Set an upper boundary (in bytes) for the database map size. The default is 10485760 bytes.

:mode

Whe creating a new database, set permissions to this 4 digit octal number. Defaults to ‘0644`. Set to `0` to never automatically create a new file, only opening existing databases.

:no_memory_init

Skip initializing malloc’ed memory to zeroes before writing.

:no_metasync

A system crash may sacrifice the last commit for a potentially large write performance increase. Database integrity is maintained.

:no_subdir

When creating a new database, don’t put the data and lock file under a dedicated subdirectory.

:no_readahead

Disable all use of OS readahead. Potentially useful for random reads wunder low memory conditions. Default behavior is to dynamically choose when to use or omit readahead.

:no_threadlocal

Parallelize read-only transactions across threads. Writes are always thread local. (See MDBX documentation for details.)

:readonly

Reject any write attempts while using this database handle.

:writemap

Trade safety for speed for databases that fit within available memory. (See MDBX documentation for details.)

# File lib/mdbx/database.rb, line 96
def self::open( *args, &block )
        db = new( *args )

        db.serializer   = ->( v ) { Marshal.dump( v ) }
        db.deserializer = ->( v ) { Marshal.load( v ) }

        if block_given?
                begin
                        yield db
                ensure
                        db.close
                end
        end

        return db
end

Public Instance Methods

db[ 'key' ] => value

Return a single value for key immediately.

VALUE
rmdbx_get_val( VALUE self, VALUE key )
{
        UNWRAP_DB( self, db );

        CHECK_HANDLE();
        rmdbx_open_txn( db, MDBX_TXN_RDONLY );

        MDBX_val ckey;
        MDBX_val data;

        rmdbx_key_for( key, &ckey );
        int rc = mdbx_get( db->txn, db->dbi, &ckey, &data );
        rmdbx_close_txn( db, RMDBX_TXN_ROLLBACK );
        xfree( ckey.iov_base );

        VALUE rv;
        switch ( rc ) {
                case MDBX_SUCCESS:
                        rv = rb_str_new( data.iov_base, data.iov_len );
                        return rb_funcall( self, rb_intern("deserialize"), 1, rv );

                case MDBX_NOTFOUND:
                        return Qnil;

                default:
                        rmdbx_close( self );
                        rb_raise( rmdbx_eDatabaseError, "Unable to fetch value: (%d) %s", rc, mdbx_strerror(rc) );
        }
}
db[ 'key' ] = value

Set a single value for key. If the value is nil, the key is removed.

VALUE
rmdbx_put_val( VALUE self, VALUE key, VALUE val )
{
        int rc;
        UNWRAP_DB( self, db );

        CHECK_HANDLE();
        rmdbx_open_txn( db, MDBX_TXN_READWRITE );

        MDBX_val ckey;
        rmdbx_key_for( key, &ckey );

        if ( NIL_P(val) ) { /* remove if set to nil */
                rc = mdbx_del( db->txn, db->dbi, &ckey, NULL );
        }
        else {
                MDBX_val data;
                rmdbx_val_for( self, val, &data );
                rc = mdbx_put( db->txn, db->dbi, &ckey, &data, 0 );
                xfree( data.iov_base );
        }

        rmdbx_close_txn( db, RMDBX_TXN_COMMIT );
        xfree( ckey.iov_base );

        switch ( rc ) {
                case MDBX_SUCCESS:
                        return val;
                case MDBX_NOTFOUND:
                        return Qnil;
                default:
                        rb_raise( rmdbx_eDatabaseError, "Unable to update value: (%d) %s", rc, mdbx_strerror(rc) );
        }
}
abort()
Alias for: rollback
clear

Empty the current collection on disk. If collections are not enabled or the database handle is set to the top-level (main) db - this deletes *all records* from the database.

VALUE
rmdbx_clear( VALUE self )
{
        UNWRAP_DB( self, db );

        rmdbx_open_txn( db, MDBX_TXN_READWRITE );
        int rc = mdbx_drop( db->txn, db->dbi, false );

        if ( rc != MDBX_SUCCESS ) {
                rmdbx_close_txn( db, RMDBX_TXN_ROLLBACK );
                rb_raise( rmdbx_eDatabaseError, "mdbx_drop: (%d) %s", rc, mdbx_strerror(rc) );
        }

        rmdbx_close_txn( db, RMDBX_TXN_COMMIT );

        return Qnil;
}
close => true

Cleanly close an opened database.

VALUE
rmdbx_close( VALUE self )
{
        UNWRAP_DB( self, db );
        rmdbx_close_all( db );
        return Qtrue;
}
closed? => false

Predicate: return true if the database environment is closed.

VALUE
rmdbx_closed_p( VALUE self )
{
        UNWRAP_DB( self, db );
        return db->state.open == 1 ? Qfalse : Qtrue;
}
collection( name=nil ) { |self| ... }

Gets or sets the sub-database “collection” that read/write operations apply to. If a block is passed, the collection automatically reverts to the prior collection when it exits.

db.collection #=> (collection name, or nil if in main)
db.collection( 'collection_name' ) #=> db

db.collection( 'collection_name' ) do
    [ ... ]
end # reverts to the previous collection name
# File lib/mdbx/database.rb, line 149
def collection( name=nil )
        current = self.get_subdb
        return current unless name

        self.set_subdb( name.to_s )
        yield( self ) if block_given?

        return self

ensure
        self.set_subdb( current ) if name && block_given?
end
Also aliased as: namespace
commit()

Close any open transaction, writing all changes.

# File lib/mdbx/database.rb, line 219
def commit
        return self.close_transaction( true )
end
Also aliased as: save
delete( key, &block )

Deletes the entry for the given key and returns its associated value. If no block is given and key is found, deletes the entry and returns the associated value. If no block given and key is not found, returns nil.

If a block is given and key is found, ignores the block, deletes the entry, and returns the associated value. If a block is given and key is not found, calls the block and returns the block’s return value.

# File lib/mdbx/database.rb, line 281
def delete( key, &block )
        val = self[ key ]
        return block.call( key ) if block_given? && val.nil?

        self[ key ] = nil
        return val
end
drop( collection ) → db

Destroy a collection. You must be in the top level database to call this method.

VALUE
rmdbx_drop( VALUE self, VALUE name )
{
        UNWRAP_DB( self, db );

        /* Provide a friendlier error message if max_collections is 0. */
        if ( db->settings.max_collections == 0 )
                rb_raise( rmdbx_eDatabaseError, "Unable to drop collection: collections are not enabled." );

        /* All transactions must be closed when dropping a database. */
        if ( db->txn )
                rb_raise( rmdbx_eDatabaseError, "Unable to drop collection: transaction open" );

        /* A drop can only be performed from the top-level database. */
        if ( db->subdb != NULL )
                rb_raise( rmdbx_eDatabaseError, "Unable to drop collection: switch to top-level db first" );

        name = rb_funcall( name, rb_intern("to_s"), 0 );
        db->subdb = StringValueCStr( name );

        rmdbx_close_dbi( db ); /* ensure we're reopening within the new subdb */
        rmdbx_open_txn( db, MDBX_TXN_READWRITE );
        int rc = mdbx_drop( db->txn, db->dbi, true );

        if ( rc != MDBX_SUCCESS ) {
                rmdbx_close_txn( db, RMDBX_TXN_ROLLBACK );
                rb_raise( rmdbx_eDatabaseError, "mdbx_drop: (%d) %s", rc, mdbx_strerror(rc) );
        }

        rmdbx_close_txn( db, RMDBX_TXN_COMMIT );

        /* Reset the current collection to the top level. */
        db->subdb = NULL;
        rmdbx_close_dbi( db ); /* ensure next access is not in the defunct subdb */

        /* Force populate the new db->dbi handle.  Under 0.12.x, getting a
         * 'permission denied' doing this for the first access with a RDONLY
         * for some reason. */
        rmdbx_open_txn( db, MDBX_TXN_READWRITE );
        rmdbx_close_txn( db, RMDBX_TXN_ROLLBACK );

        return self;
}
each()
Alias for: each_pair
each_key {|key| block } => self

Calls the block once for each key, returning self. A transaction must be opened prior to use.

VALUE
rmdbx_each_key( VALUE self )
{
        UNWRAP_DB( self, db );
        int state;

        CHECK_HANDLE();
        rmdbx_open_cursor( db );
        RETURN_ENUMERATOR( self, 0, 0 );

        rb_protect( rmdbx_each_key_i, self, &state );

        mdbx_cursor_close( db->cursor );
        db->cursor = NULL;

        if ( state ) rb_jump_tag( state );

        return self;
}
each_pair {|key, value| block } => self

Calls the block once for each key and value, returning self. A transaction must be opened prior to use.

VALUE
rmdbx_each_pair( VALUE self )
{
        UNWRAP_DB( self, db );
        int state;

        CHECK_HANDLE();
        rmdbx_open_cursor( db );
        RETURN_ENUMERATOR( self, 0, 0 );

        rb_protect( rmdbx_each_pair_i, self, &state );

        mdbx_cursor_close( db->cursor );
        db->cursor = NULL;

        if ( state ) rb_jump_tag( state );

        return self;
}
Also aliased as: each
each_value {|value| block } => self

Calls the block once for each value, returning self. A transaction must be opened prior to use.

VALUE
rmdbx_each_value( VALUE self )
{
        UNWRAP_DB( self, db );
        int state;

        CHECK_HANDLE();
        rmdbx_open_cursor( db );
        RETURN_ENUMERATOR( self, 0, 0 );

        rb_protect( rmdbx_each_value_i, self, &state );

        mdbx_cursor_close( db->cursor );
        db->cursor = NULL;

        if ( state ) rb_jump_tag( state );

        return self;
}
empty?()

Returns true if the current collection has no data.

# File lib/mdbx/database.rb, line 250
def empty?
        return self.size.zero?
end
fetch( key, &block )

Returns the value for the given key, if found. If key is not found and no block was given, returns nil. If key is not found and a block was given, yields key to the block and returns the block’s return value.

# File lib/mdbx/database.rb, line 260
def fetch( key, &block )
        val = self[ key ]
        if block_given?
                return block.call( key ) if val.nil?
        else
                return val if val
                raise KeyError, "key not found: %p" % [ key ]
        end
end
has_key?(p1)
Alias for: include?
in_transaction? => false

Predicate: return true if a transaction (or snapshot) is currently open.

VALUE
rmdbx_in_transaction_p( VALUE self )
{
        UNWRAP_DB( self, db );
        return db->txn ? Qtrue : Qfalse;
}
include?( 'key' ) => bool

Returns true if the current collection contains key.

VALUE
rmdbx_include( VALUE self, VALUE key )
{
        UNWRAP_DB( self, db );

        CHECK_HANDLE();
        rmdbx_open_txn( db, MDBX_TXN_RDONLY );

        MDBX_val ckey;
        MDBX_val data;
        rmdbx_key_for( key, &ckey );

        int rc = mdbx_get( db->txn, db->dbi, &ckey, &data );
        rmdbx_close_txn( db, RMDBX_TXN_ROLLBACK );
        xfree( ckey.iov_base );

        switch ( rc ) {
                case MDBX_SUCCESS:
                        return Qtrue;

                case MDBX_NOTFOUND:
                        return Qfalse;

                default:
                        rmdbx_close( self );
                        rb_raise( rmdbx_eDatabaseError, "Unable to fetch key: (%d) %s", rc, mdbx_strerror(rc) );
        }
}
Also aliased as: has_key?
keys()

Returns a new Array containing all keys in the collection.

# File lib/mdbx/database.rb, line 292
def keys
        return self.conditional_snapshot do
                self.each_key.to_a
        end
end
length → Integer

Returns the count of keys in the currently selected collection.

VALUE
rmdbx_length( VALUE self )
{
        UNWRAP_DB( self, db );
        MDBX_stat mstat;

        CHECK_HANDLE();
        rmdbx_open_txn( db, MDBX_TXN_RDONLY );

        int rc = mdbx_dbi_stat( db->txn, db->dbi, &mstat, sizeof(mstat) );
        if ( rc != MDBX_SUCCESS )
                rb_raise( rmdbx_eDatabaseError, "mdbx_dbi_stat: (%d) %s", rc, mdbx_strerror(rc) );

        VALUE rv = LONG2FIX( mstat.ms_entries );
        rmdbx_close_txn( db, RMDBX_TXN_ROLLBACK );

        return rv;
}
Also aliased as: size
main()

Switch to the top-level collection.

# File lib/mdbx/database.rb, line 166
def main
        return self.set_subdb( nil )
end
namespace( name=nil )
Alias for: collection
reopen()

Open the DB environment handle.

VALUE
rmdbx_open_env( VALUE self )
{
        int rc;
        UNWRAP_DB( self, db );
        rmdbx_close_all( db );

        /* Allocate an mdbx environment.
         */
        rc = mdbx_env_create( &db->env );
        if ( rc != MDBX_SUCCESS )
                rb_raise( rmdbx_eDatabaseError, "mdbx_env_create: (%d) %s", rc, mdbx_strerror(rc) );

        /* Set the maximum number of named databases for the environment. */
        mdbx_env_set_maxdbs( db->env, db->settings.max_collections );

        /* Customize the maximum number of simultaneous readers. */
        if ( db->settings.max_readers )
                mdbx_env_set_maxreaders( db->env, db->settings.max_readers );

        /* Set an upper boundary (in bytes) for the database map size. */
        if ( db->settings.max_size )
                mdbx_env_set_geometry( db->env, -1, -1, db->settings.max_size, -1, -1, -1 );

        rc = mdbx_env_open( db->env, db->path, db->settings.env_flags, db->settings.mode );
        if ( rc != MDBX_SUCCESS ) {
                rmdbx_close_all( db );
                rb_raise( rmdbx_eDatabaseError, "mdbx_env_open: (%d) %s", rc, mdbx_strerror(rc) );
        }

        /* Force populate the db->dbi handle.  Under 0.12.x, getting a
         * 'permission denied' doing this for the first access with a RDONLY
         * for some reason. */
        rmdbx_open_txn( db, MDBX_TXN_READWRITE );
        rmdbx_close_txn( db, RMDBX_TXN_ROLLBACK );

        db->state.open = 1;
        return Qtrue;
}
rollback()

Close any open transaction, abandoning all changes.

# File lib/mdbx/database.rb, line 211
def rollback
        return self.close_transaction( false )
end
Also aliased as: abort
save()
Alias for: commit
size()
Alias for: length
slice( *keys )

Returns a new Hash object containing the entries for the given keys. Any given keys that are not found are ignored.

# File lib/mdbx/database.rb, line 302
def slice( *keys )
        return self.conditional_snapshot do
                keys.each_with_object( {} ) do |key, acc|
                        val = self[ key ]
                        acc[ key ] = val if val
                end
        end
end
snapshot( &block )

Open a new mdbx read only snapshot. In block form, the snapshot is automatically closed when the block ends.

# File lib/mdbx/database.rb, line 204
def snapshot( &block )
        self.transaction( commit: false, &block )
end
statistics()

Return a hash of various metadata for the current database.

# File lib/mdbx/database.rb, line 338
def statistics
        raw = self.raw_stats

        # Place build options in their own hash.
        #
        build_opts = raw.delete( :build_options ).split.each_with_object( {} ) do |opt, acc|
                key, val = opt.split( '=' )
                acc[ key.to_sym ] = Integer( val ) rescue val
        end

        stats = {
                build: {
                        compiler: raw.delete( :build_compiler ),
                        flags:    raw.delete( :build_flags ),
                        options:  build_opts,
                        target:   raw.delete( :build_target )
                }
        }
        stats.merge!( raw )

        return stats
end
to_a()

Return the entirety of database contents as an Array of array pairs.

# File lib/mdbx/database.rb, line 232
def to_a
        return self.conditional_snapshot do
                self.each_pair.to_a
        end
end
to_h()

Return the entirety of database contents as a Hash.

# File lib/mdbx/database.rb, line 241
def to_h
        return self.conditional_snapshot do
                self.each_pair.to_h
        end
end
transaction( commit: true ) { |self| ... }

Open a new mdbx read/write transaction. In block form, the transaction is automatically committed when the block ends.

Raising a MDBX::Rollback exception from within the block automatically rolls the transaction back.

# File lib/mdbx/database.rb, line 181
def transaction( commit: true, &block )
        self.open_transaction( commit )
        yield self if block_given?

        return self

rescue MDBX::Rollback
        commit = false
        self.rollback
rescue
        commit = false
        self.rollback
        raise
ensure
        if block_given?
                commit ? self.commit : self.rollback
        end
end
values()

Returns a new Array containing all values in the collection.

# File lib/mdbx/database.rb, line 314
def values
        return self.conditional_snapshot do
                self.each_value.to_a
        end
end
values_at( *keys )

Returns a new Array containing values for the given keys.

# File lib/mdbx/database.rb, line 323
def values_at( *keys )
        return self.conditional_snapshot do
                keys.each_with_object( [] ) do |key, acc|
                        acc << self[ key ]
                end
        end
end

Protected Instance Methods

conditional_snapshot() { || ... }

Yield and return the block, opening a snapshot first if there isn’t already a transaction in progress. Closes the snapshot if this method opened it.

# File lib/mdbx/database.rb, line 396
def conditional_snapshot
        in_txn = self.in_transaction?
        self.snapshot unless in_txn

        return yield
ensure
        self.abort unless in_txn
end
deserialize( val )

Safely deserialize a value, closing any open transaction and re-raising if necessary.

# File lib/mdbx/database.rb, line 382
def deserialize( val )
        return val unless self.deserializer
        return self.deserializer.call( val )

rescue => err
        self.close_transaction( false )
        raise err
end
serialize( val )

Safely serialize a value, closing any open transaction and re-raising if necessary.

# File lib/mdbx/database.rb, line 369
def serialize( val )
        return val unless self.serializer
        return self.serializer.call( val )

rescue => err
        self.close_transaction( false )
        raise err
end