# Ruby API

## Installation

The FoundationDB Ruby API is distributed in our Downloads.

Note

For the language binding to function, FoundationDB client binaries whose version is at least as recent must be installed. If you upgrade a language binding to a new version, you may need to upgrade the FoundationDB client binaries as well. See Installing FoundationDB client binaries.

Note

If you have a project with automatic dependency installation and have expressed a dependency on foundationdb, it may automatically install the lastest version of the language binding when you deploy your project to a new machine. If you have not also upgraded the Foundation client binary, an unplanned upgrade of the language binding may encounter an incompatibility. You should therefore configure any project dependency on foundationdb in coordination with your overall upgrade strategy.

## API versioning

When you require the `FDB` gem, it exposes only one useful method:

Note

You must call `FDB.api_version(...)` before using any other part of the API. Once you have done so, the rest of the API will become available in the `FDB` module.

Note

FoundationDB encapsulates multiple versions of its interface by requiring the client to explicitly specify the version of the API it uses. The purpose of this design is to allow you to upgrade the server, client libraries, or bindings without having to modify client code. The client libraries support all previous versions of the API. The API version specified by the client is used to control the behavior of the binding. You can therefore upgrade to more recent packages (and thus receive various improvements) without having to change your code.

Warning

When using the multi-version client API, setting an API version that is not supported by a particular client library will prevent that client from being used to connect to the cluster. In particular, you should not advance the API version of your application after upgrading your client until the cluster has also been upgraded.

For API changes between version 14 and 520 (for the purpose of porting older programs), see Release Notes.

## Opening a database

After requiring the `FDB` gem and selecting an API version, you probably want to open a :class:`Database`. The simplest way of doing this is using :func:`open`:

    require 'fdb'
    FDB.api_version 520
    db = FDB.open

## Cluster objects

## Keys and values

Keys and values in FoundationDB are simple byte strings.

To encode other data types, see Encoding data types and the tuple layer.

### `as_foundationdb_key` and `as_foundationdb_value`

In some cases, you may have objects that are used to represent specific keys or values (for example, see |subspace|). As a convenience, the language binding API can work seamlessly with such objects if they implement the |as-foundationdb-key| or |as-foundationdb-value| methods, respectively. API methods that accept a key will alternately accept an object that implements the |as-foundationdb-key| method. Likewise, API methods accepting a value will also accept an object that implements the |as-foundationdb-value| method.

Warning

|as-foundationdb-key| and |as-foundationdb-value| are not intended to implement serialization protocols for object storage. Use these functions only when your object represents a specific key or value.

## KeyValue objects

Represents a single key-value pair in the database. This is a simple value type; mutating it won’t affect your :class:`Transaction` or :class:`Database`.

## Key selectors

FoundationDB’s lexicographically ordered data model permits finding keys based on their order (for example, finding the first key in the database greater than a given key). Key selectors represent a description of a key in the database that could be resolved to an actual key by |get-key-func| or used directly as the beginning or end of a range in |get-range-func|.

For more about how key selectors work, see Key selectors.

Creates a key selector with the given reference key, equality flag, and offset. It is usually more convenient to obtain a key selector with one of the following methods:

## Database objects

A `Database` represents a FoundationDB database — a mutable, lexicographically ordered mapping from binary keys to binary values. Although `Database` provides convenience methods for reading and writing, modifications to a database are usually via transactions, which are usually created and committed automatically by |database-auto|.

Note

The convenience methods provided by `Database` have the same signature as the corresponding methods of `Transaction`. However, most of the `Database` methods are fully synchronous. (An exception is the methods for watches.) As a result, the `Database` methods do not support the use of implicit parallelism with futures.

### Database options

Database options alter the behavior of FoundationDB databases.

## Transaction objects

A `Transaction` object represents a FoundationDB database transaction. All operations on FoundationDB take place, explicitly or implicitly, through a `Transaction`.

In FoundationDB, a transaction is a mutable snapshot of a database. All read and write operations on a transaction see and modify an otherwise-unchanging version of the database and only change the underlying database if and when the transaction is committed. Read operations do see the effects of previous write operations on the same transaction. Committing a transaction usually succeeds in the absence of conflicts.

Transactions group operations into a unit with the properties of atomicity, isolation, and durability. Transactions also provide the ability to maintain an application’s invariants or integrity constraints, supporting the property of consistency. Together these properties are known as ACID.

Transactions are also causally consistent: once a transaction has been successfully committed, all subsequently created transactions will see the modifications made by it.

The most convenient way to create and use transactions is using the :meth:`Database.transact` method.

Keys and values in FoundationDB are byte strings. FoundationDB will accept Ruby strings with any encoding, but will always return strings with `ASCII-8BIT` encoding (also known as `BINARY`). To encode other data types, see the :mod:`FDB::Tuple` module and Encoding data types.

### Attributes

### Reading data

### Snapshot reads

### Writing data

### Atomic operations

An atomic operation is a single database command that carries out several logical steps: reading the value of a key, performing a transformation on that value, and writing the result. Different atomic operations perform different transformations. Like other database operations, an atomic operation is used within a transaction; however, its use within a transaction will not cause the transaction to conflict.

Atomic operations do not expose the current value of the key to the client but simply send the database the transformation to apply. In regard to conflict checking, an atomic operation is equivalent to a write without a read. It can only cause other transactions performing reads of the key to conflict.

By combining these logical steps into a single, read-free operation, FoundationDB can guarantee that the transaction will not conflict due to the operation. This makes atomic operations ideal for operating on keys that are frequently modified. A common example is the use of a key-value pair as a counter.

Warning

If a transaction uses both an atomic operation and a serializable read on the same key, the benefits of using the atomic operation (for both conflict checking and performance) are lost.

In each of the methods below, `param` should be a string appropriately packed to represent the desired value. For example:

    # wrong
    tr.add('key', 1)

    # right
    tr.add('key', [1].pack('q<'))

### Committing

### Watches

### Conflict ranges

Note

Most applications will use the serializable isolation that transactions provide by default and will not need to manipulate conflict ranges.

The following make it possible to add conflict ranges to a transaction.

### Versions

Most applications should use the read version that FoundationDB determines automatically during the transaction’s first read, and ignore all of these methods.

### Transaction options

Transaction options alter the behavior of FoundationDB transactions. FoundationDB defaults to extremely safe transaction behavior, and we have worked hard to make the performance excellent with the default setting, so you should not often need to use transaction options.

## The transact method

When performing a database transaction, any read operation, as well as the commit itself, may fail with one of a number of errors. If the error is a retryable error, the transaction needs to be restarted from the beginning. Committing a transaction is also an asynchronous operation, and the returned :class:`FutureNil` object needs to be waited on to ensure that no errors occurred.

The methods :meth:`Database.transact` and :meth:`Transaction.transact` are convenient wrappers that allow much of this complexity to be handled automatically. A call like

    db.transact do |tr|
        tr['a'] = 'A'
        tr['b'] = 'B'
    end

is equivalent to

    tr = db.create_transaction
    committed = false
    while !committed
        begin
            tr['a'] = 'A'
            tr['b'] = 'B'
            tr.commit.wait
            committed = true
        rescue FDB::Error => e
            tr.on_error(e).wait
        end
    end

The first form is considerably easier to read, and ensures that the transaction is correctly committed (and retried, when necessary).

Note

Be careful when using control flow constructs within the block passed to :meth:`transact`. `return` or `break` will exit the retry loop without committing the transaction. Use `next` to exit the block and commit the transaction.

The :meth:`Transaction.transact` method, which logically does nothing, makes it easy to write functions that operate on either a :class:`Database` or :class:`Transaction`. Consider the following method:

    def increment(db_or_tr, key)
        db_or_tr.transact do |tr|
            tr[key] = (tr[key].to_i + 1).to_s
        end
    end

This method can be called with a :class:`Database`, and it will do its job atomically:

    increment(db, 'number')

It can also be called by another transactional method with a transaction:

    def increment_both(db_or_tr, key1, key2)
        db_or_tr.transact do |tr|
            increment(tr, key1)
            increment(tr, key2)
        end
    end

In the second case, `increment` will use provided transaction and will not commit it or retry errors, since that is the responsibility of its caller, `increment_both`.

Note

In some failure scenarios, it is possible that your transaction will be executed twice. See Transactions with unknown results for more information.

## Future objects

Many FoundationDB API functions return “future” objects. A brief overview of futures is included in the class scheduling tutorial. Most future objects behave just like a normal object, but block when you use them for the first time if the asynchronous function which returned the future has not yet completed its action. A future object is considered ready when either a value is available, or when an error has occurred.

When a future object “blocks”, the ruby thread is blocked, but the global interpreter lock is released.

When used in a conditional expression, a future object will evaluate to true, even if its value is nil. To test for nil, you must explicitly use the nil?() method:

    if tr['a'].nil?

All future objects are a subclass of the :class:`Future` type.

Asynchronous methods return one of the following subclasses of :class:`Future`:

Both types are future :class:`String` objects. Objects of these types respond to the same methods as objects of type :class:`String`, and may be passed to any method that expects a :class:`String`.

An implementation quirk of :class:`Value` is that it will never evaluate to `false`, even if its value is `nil`. It is important to use `if value.nil?` rather than `if ~value` when checking to see if a key was not present in the database.

This type is a future :class:`Integer` object. Objects of this type respond to the same methods as objects of type :class:`Integer`, and may be passed to any method that expects a :class:`Integer`.

This type is a future :class:`Array` object. Objects of this type respond to the same methods as objects of type :class:`Array`, and may be passed to any method that expects a :class:`Array`.

This type is a future returned from asynchronous methods that logically have no return value.

## Streaming modes

When using |get-range-func| and similar interfaces, API clients can request large ranges of the database to iterate over. Making such a request doesn’t necessarily mean that the client will consume all of the data in the range - sometimes the client doesn’t know how far it intends to iterate in advance. FoundationDB tries to balance latency and bandwidth by requesting data for iteration in batches.

Streaming modes permit the API client to customize this performance tradeoff by providing extra information about how the iterator will be used.

The following streaming modes are available:

## Errors

Errors in the FoundationDB API are raised as exceptions of type :class:`FDB::Error`. These errors may be displayed for diagnostic purposes, but generally should be passed to :meth:`Transaction.on_error`. When using :meth:`Database.transact`, appropriate errors will be retried automatically.

Warning

You should only use the :attr:`code` attribute for programmatic comparisons, as the description of the error may change at any time. Whenever possible, use the :meth:`Transaction.on_error` method to handle :class:`FDB::Error` exceptions.

## Tuple layer

The FoundationDB API comes with a built-in layer for encoding tuples into keys usable by FoundationDB. The encoded key maintains the same sort order as the original tuple: sorted first by the first element, then by the second element, etc. This makes the tuple layer ideal for building a variety of higher-level data models.

Note

For general guidance on tuple usage, see the discussion in the document on Data Modeling.

In the FoundationDB Ruby API, a tuple is an :class:`Enumerable` of elements of the following data types:

Type | Legal Values | Canonical Value
--- | --- | ---
Null value | `nil` | `nil`
Byte string | Any value `v` where `v.kind_of? String == true` and `v.encoding` is either `Encoding::ASCII_8BIT` (aka `Encoding::BINARY`) or `Encoding::US_ASCII` (aka `Encoding::ASCII`) | `String` with encoding `Encoding::ASCII_8BIT`
Unicode string | Any value `v` where `v.kind_of? String == true` and `v.encoding` is `Encoding::UTF_8` | `String` with encoding `Encoding::UTF_8`
64-bit signed integer | Any value `v` where `v.kind_of? Integer == true` and `-2**64+1 <= v <= 2**64-1` | `Fixnum` or `Bignum` (depending on the magnitude of the value)
Floating point number (single-precision) | Any value `v` where `v.kind_of? FDB::Tuple::SingleFloat` where `v.value.kind_of? Float` and `v.value` fits inside an IEEE 754 32-bit floating-point number. | :class:`FDB::Tuple::SingleFloat`
Floating point number (double-precision) | Any value `v` where `v.kind_of? Float` | `Float`
Boolean | Any value `v` where `v.kind_of? Boolean` | `Boolean`
UUID | Any value `v` where `v.kind_of? FDB::Tuple::UUID` where `v.data.kind_of? String` and `v.data.encoding` is `Encoding::BINARY` and `v.data.length == 16` | :class:`FDB::Tuple::UUID`
Array | Any value `v` such that `v.kind_of? Array` and each element within `v` is one of the supported types with a legal value. | `Array`

Note that as Ruby does not have native support for single-precision floating point values and UUIDs, tuple elements of those types are returned instead as :class:`FDB::Tuple::SingleFloat` and :class:`FDB::Tuple::UUID` instances. These are simple classes that just wrap their underlying values, and they are not intended to offer all of the methods that a more fully-featured library for handling floating point values or UUIDs might offer. Most applications should use their library of choice for handling these values and then convert to the appropriate tuple-type when serializing for storage into the key-value store.

A single tuple element is ordered first by its type, and then by its value.

If `T` is an :class:`Enumerable` meeting these criteria, then conceptually:

    T == FDB::Tuple.unpack(FDB::Tuple.pack(T))

Note

Unpacking a tuple always returns an :class:`Array` of elements in a canonical representation, so packing and then unpacking a tuple may result in an equivalent but not identical representation.

Wrapper around a single-precision floating point value. The `value` parameter should be a `Float` that can be encoded as an IEEE 754 floating point number. If the float does not fit within a IEEE 754 floating point integer, there may be a loss of precision.

Wrapper around a 128-bit UUID. The `data` parameter should be a byte string of length 16 and is taken to be the big-endian byte representation of the UUID. If `data` is not of length 16, an exception is thrown.

## Subspaces

Subspaces provide a convenient way to use the tuple layer to define namespaces for different categories of data. The namespace is specified by a prefix tuple which is prepended to all tuples packed by the subspace. When unpacking a key with the subspace, the prefix tuple will be removed from the result.

As a best practice, API clients should use at least one subspace for application data.

Note

For general guidance on subspace usage, see the discussion in the Developer Guide.

Creates a subspace with the specified prefix tuple. If the raw prefix byte string is specified, then it will be prepended to all packed keys. Likewise, the raw prefix will be removed from all unpacked keys.

## Directories

The FoundationDB API provides directories as a tool for managing related subspaces. Directories are a recommended approach for administering applications. Each application should create or open at least one directory to manage its subspaces.

Note

For general guidance on directory usage, see the discussion in the Developer Guide.

Directories are identified by hierarchical paths analogous to the paths in a Unix-like file system. A path is represented as |dir-path-type| of strings. Each directory has an associated subspace used to store its content. The directory layer maps each path to a short prefix used for the corresponding subspace. In effect, directories provide a level of indirection for access to subspaces.

Except where noted, directory methods interpret the provided path(s) relative to the path of the directory object. When opening a directory, a byte string `layer` option may be specified as a metadata identifier.

Each instance defines a new root directory. The subspaces `node_subspace` and `content_subspace` control where the directory metadata and contents, respectively, are stored. The default root directory has a `node_subspace` with raw prefix `\xFE` and a `content_subspace` with no prefix. Specifying more restrictive values for `node_subspace` and `content_subspace` will allow using the directory layer alongside other content in a database. If `allow_manual_prefixes` is false, attempts to create a directory with a manual prefix under the directory layer will raise an exception. The default root directory does not allow manual prefixes.

### DirectorySubspace

A directory subspace represents a specific directory and its contents. It stores the `path` with which it was opened and supports all |directory-layer| methods for operating on itself and its subdirectories. It also implements all |subspace| methods for working with the contents of that directory.

## Locality information

The FoundationDB API comes with a set of functions for discovering the storage locations of keys within your cluster. This information can be useful for advanced users who wish to take into account the location of keys in the design of applications or processes.