Skip to main content

fast bindings for the sophia database

Project description

sophy, fast Python bindings for Sophia embedded database, v2.2.

About sophy

  • Written in Cython for speed and low-overhead
  • Clean, memorable APIs
  • Extensive support for Sophia's features
  • Python 2 and Python 3 support
  • No 3rd-party dependencies besides Cython
  • Documentation on readthedocs

About Sophia

  • Ordered key/value store
  • Keys and values can be composed of multiple fieldsdata-types
  • ACID transactions
  • MVCC, optimistic, non-blocking concurrency with multiple readers and writers.
  • Multiple databases per environment
  • Multiple- and single-statement transactions across databases
  • Prefix searches
  • Automatic garbage collection and key expiration
  • Hot backup
  • Compression
  • Multi-threaded compaction
  • mmap support, direct I/O support
  • APIs for variety of statistics on storage engine internals
  • BSD licensed

Some ideas of where Sophia might be a good fit

  • Running on application servers, low-latency / high-throughput
  • Time-series
  • Analytics / Events / Logging
  • Full-text search
  • Secondary-index for external data-store

Limitations

  • Not tested on Windoze.

If you encounter any bugs in the library, please open an issue, including a description of the bug and any related traceback.

Installation

The sophia sources are bundled with the sophy source code, so the only thing you need to install is Cython. You can install from GitHub or from PyPI.

Pip instructions:

$ pip install sophy

Or to install the latest code from master:

$ pip install -e git+https://github.com/coleifer/sophy#egg=sophy

Git instructions:

$ pip install Cython
$ git clone https://github.com/coleifer/sophy
$ cd sophy
$ python setup.py build
$ python setup.py install

To run the tests:

$ python tests.py


Overview

Sophy is very simple to use. It acts like a Python dict object, but in addition to normal dictionary operations, you can read slices of data that are returned efficiently using cursors. Similarly, bulk writes using update() use an efficient, atomic batch operation.

Despite the simple APIs, Sophia has quite a few advanced features. There is too much to cover everything in this document, so be sure to check out the official Sophia storage engine documentation.

The next section will show how to perform common actions with sophy.

Using Sophy

Let's begin by import sophy and creating an environment. The environment can host multiple databases, each of which may have a different schema. In this example our database will store arbitrary binary data as the key and value. Finally we'll open the environment so we can start storing and retrieving data.

from sophy import Sophia, Schema, StringIndex

# Instantiate our environment by passing a directory path which will store the
# various data and metadata for our databases.
env = Sophia('/path/to/store/data')

# We'll define a very simple schema consisting of a single utf-8 string for the
# key, and a single utf-8 string for the associated value.
schema = Schema(key_parts=[StringIndex('key')],
                value_parts=[StringIndex('value')])

# Create a key/value database using the schema above.
db = env.add_database('example_db', schema)

if not env.open():
    raise Exception('Unable to open Sophia environment.')

CRUD operations

Sophy databases use the familiar dict APIs for CRUD operations:

db['name'] = 'Huey'
db['animal_type'] = 'cat'
print db['name'], 'is a', db['animal_type']  # Huey is a cat

'name' in db  # True
'color' in db  # False

db['temp_val'] = 'foo'
del db['temp_val']
print db['temp_val']  # raises a KeyError.

Use update() for bulk-insert, and multi_get() for bulk-fetch. Unlike __getitem__(), calling multi_get() with a non-existant key will not raise an exception and return None instead.

db.update(k1='v1', k2='v2', k3='v3')

for value in db.multi_get('k1', 'k3', 'kx'):
    print value
# v1
# v3
# None

result_dict = db.multi_get_dict(['k1', 'k3', 'kx'])
# {'k1': 'v1', 'k3': 'v3'}

Other dictionary methods

Sophy databases also provides efficient implementations for keys(), values() and items(). Unlike dictionaries, however, iterating directly over a Sophy database will return the equivalent of the items() (as opposed to the just the keys):

db.update(k1='v1', k2='v2', k3='v3')

list(db)
# [('k1', 'v1'), ('k2', 'v2'), ('k3', 'v3')]


db.items()
# same as above.


db.keys()
# ['k1', 'k2', 'k3']


db.values()
# ['v1', 'v2', 'v3']

There are two ways to get the count of items in a database. You can use the len() function, which is not very efficient since it must allocate a cursor and iterate through the full database. An alternative is the index_count property, which may not be exact as it includes transactional duplicates and not-yet-merged duplicates.

print(len(db))
# 4

print(db.index_count)
# 4

Fetching ranges

Because Sophia is an ordered data-store, performing ordered range scans is efficient. To retrieve a range of key-value pairs with Sophy, use the ordinary dictionary lookup with a slice instead.

db.update(k1='v1', k2='v2', k3='v3', k4='v4')


# Slice key-ranges are inclusive:
db['k1':'k3']
# [('k1', 'v1'), ('k2', 'v2'), ('k3', 'v3')]


# Inexact matches are fine, too:
db['k1.1':'k3.1']
# [('k2', 'v2'), ('k3', 'v3')]


# Leave the start or end empty to retrieve from the first/to the last key:
db[:'k2']
# [('k1', 'v1'), ('k2', 'v2')]

db['k3':]
# [('k3', 'v3'), ('k4', 'v4')]


# To retrieve a range in reverse order, use the higher key first:
db['k3':'k1']
# [('k3', 'v3'), ('k2', 'v2'), ('k1', 'v1')]

To retrieve a range in reverse order where the start or end is unspecified, you can pass in True as the step value of the slice to also indicate reverse:

db[:'k2':True]
# [('k2', 'k1'), ('k1', 'v1')]

db['k3'::True]
# [('k4', 'v4'), ('k3', 'v3')]

db[::True]
# [('k4', 'v4'), ('k3', 'v3'), ('k2', 'v2'), ('k1', 'v1')]

Cursors

For finer-grained control over iteration, or to do prefix-matching, Sophy provides a cursor interface.

The cursor() method accepts 5 parameters:

  • order (default=>=) -- semantics for matching the start key and ordering results.
  • key -- the start key
  • prefix -- search for prefix matches
  • keys -- (default=True) -- return keys while iterating
  • values -- (default=True) -- return values while iterating

Suppose we were storing events in a database and were using an ISO-8601-formatted date-time as the key. Since ISO-8601 sorts lexicographically, we could retrieve events in correct order simply by iterating. To retrieve a particular slice of time, a prefix could be specified:

# Iterate over events for July, 2017:
for timestamp, event_data in db.cursor(key='2017-07-01T00:00:00',
                                       prefix='2017-07-'):
    do_something()

Transactions

Sophia supports ACID transactions. Even better, a single transaction can cover operations to multiple databases in a given environment.

Example usage:

account_balance = env.add_database('balance', ...)
transaction_log = env.add_database('transaction_log', ...)

# ...

def transfer_funds(from_acct, to_acct, amount):
    with env.transaction() as txn:
        # To write to a database within a transaction, obtain a reference to
        # a wrapper object for the db:
        txn_acct_bal = txn[account_balance]
        txn_log = txn[transaction_log]

        # Transfer the asset by updating the respective balances. Note that we
        # are operating on the wrapper database, not the db instance.
        from_bal = txn_acct_bal[from_acct]
        txn_acct_bal[to_account] = from_bal + amount
        txn_acct_bal[from_account] = from_bal - amount

        # Log the transaction in the transaction_log database. Again, we use
        # the wrapper for the database:
        txn_log[from_account, to_account, get_timestamp()] = amount

Multiple transactions are allowed to be open at the same time, but if there are conflicting changes, an exception will be thrown when attempting to commit the offending transaction:

# Create a basic k/v store. Schema.key_value() is a convenience/factory-method.
kv = env.add_database('main', Schema.key_value())

# ...

# Instead of using the context manager, we'll call begin() explicitly so we
# can show the interaction of 2 open transactions.
txn = env.transaction().begin()

t_kv = txn[kv]
t_kv['k1'] = 'v1'

txn2 = env.transaction().begin()
t2_kv = txn2[kv]

t2_kv['k1'] = 'v1-x'

txn2.commit()  # ERROR !!
# SophiaError('txn is not finished, waiting for concurrent txn to finish.')

txn.commit()  # OK

# Try again?
txn2.commit()  # ERROR !!
# SophiaError('transasction rolled back by another concurrent transaction.')

Index types, multi-field keys and values

Sophia supports multi-field keys and values. Additionally, the individual fields can have different data-types. Sophy provides the following field types:

  • StringIndex - stores UTF8-encoded strings, e.g. text.
  • BytesIndex - stores bytestrings, e.g. binary data.
  • JsonIndex - stores arbitrary objects as UTF8-encoded JSON data.
  • MsgPackIndex - stores arbitrary objects using msgpack serialization.
  • PickleIndex - stores arbitrary objects using Python pickle library.
  • UUIDIndex - stores UUIDs.
  • U64Index and reversed, U64RevIndex
  • U32Index and reversed, U32RevIndex
  • U16Index and reversed, U16RevIndex
  • U8Index and reversed, U8RevIndex
  • SerializedIndex - which is basically a BytesIndex that accepts two functions: one for serializing the value to the db, and another for deserializing.

To store arbitrary data encoded using msgpack, you could use MsgPackIndex:

schema = Schema(StringIndex('key'), MsgPackIndex('value'))
db = sophia_env.add_database('main', schema)

To declare a database with a multi-field key or value, you will pass the individual fields as arguments when constructing the Schema object. To initialize a schema where the key is composed of two strings and a 64-bit unsigned integer, and the value is composed of a string, you would write:

key = [StringIndex('last_name'), StringIndex('first_name'), U64Index('area_code')]
value = [StringIndex('address_data')]
schema = Schema(key_parts=key, value_parts=value)

address_book = sophia_env.add_database('address_book', schema)

To store data, we use the same dictionary methods as usual, just passing tuples instead of individual values:

sophia_env.open()

address_book['kitty', 'huey', 66604] = '123 Meow St'
address_book['puppy', 'mickey', 66604] = '1337 Woof-woof Court'

To retrieve our data:

huey_address = address_book['kitty', 'huey', 66604]

To delete a row:

del address_book['puppy', 'mickey', 66604]

Indexing and slicing works as you would expect.

Note: when working with a multi-part value, a tuple containing the value components will be returned. When working with a scalar value, instead of returning a 1-item tuple, the value itself is returned.

Configuring and Administering Sophia

Sophia can be configured using special properties on the Sophia and Database objects. Refer to the configuration document for the details on the available options, including whether they are read-only, and the expected data-type.

For example, to query Sophia's status, you can use the status property, which is a readonly setting returning a string:

print(env.status)
"online"

Other properties can be changed by assigning a new value to the property. For example, to read and then increase the number of threads used by the scheduler:

nthreads = env.scheduler_threads
env.scheduler_threads = nthread + 2

Database-specific properties are available as well. For example to get the number of GET and SET operations performed on a database, you would write:

print(db.stat_get, 'get operations')
print(db.stat_set, 'set operations')

Refer to the documentation for complete lists of settings. Dotted-paths are translated into underscore-separated attributes.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

sophy-0.7.0.tar.gz (205.8 kB view details)

Uploaded Source

Built Distributions

If you're not sure about the file name format, learn more about wheel file names.

sophy-0.7.0-cp314-cp314t-musllinux_1_2_x86_64.whl (2.0 MB view details)

Uploaded CPython 3.14tmusllinux: musl 1.2+ x86-64

sophy-0.7.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl (2.0 MB view details)

Uploaded CPython 3.14tmanylinux: glibc 2.17+ x86-64manylinux: glibc 2.28+ x86-64

sophy-0.7.0-cp314-cp314-musllinux_1_2_x86_64.whl (1.9 MB view details)

Uploaded CPython 3.14musllinux: musl 1.2+ x86-64

sophy-0.7.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl (2.0 MB view details)

Uploaded CPython 3.14manylinux: glibc 2.17+ x86-64manylinux: glibc 2.28+ x86-64

sophy-0.7.0-cp313-cp313-musllinux_1_2_x86_64.whl (1.9 MB view details)

Uploaded CPython 3.13musllinux: musl 1.2+ x86-64

sophy-0.7.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl (2.0 MB view details)

Uploaded CPython 3.13manylinux: glibc 2.17+ x86-64manylinux: glibc 2.28+ x86-64

sophy-0.7.0-cp312-cp312-musllinux_1_2_x86_64.whl (2.0 MB view details)

Uploaded CPython 3.12musllinux: musl 1.2+ x86-64

sophy-0.7.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl (2.1 MB view details)

Uploaded CPython 3.12manylinux: glibc 2.17+ x86-64manylinux: glibc 2.28+ x86-64

sophy-0.7.0-cp311-cp311-musllinux_1_2_x86_64.whl (2.0 MB view details)

Uploaded CPython 3.11musllinux: musl 1.2+ x86-64

sophy-0.7.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl (2.1 MB view details)

Uploaded CPython 3.11manylinux: glibc 2.17+ x86-64manylinux: glibc 2.28+ x86-64

sophy-0.7.0-cp310-cp310-musllinux_1_2_x86_64.whl (1.9 MB view details)

Uploaded CPython 3.10musllinux: musl 1.2+ x86-64

sophy-0.7.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl (2.0 MB view details)

Uploaded CPython 3.10manylinux: glibc 2.17+ x86-64manylinux: glibc 2.28+ x86-64

sophy-0.7.0-cp39-cp39-musllinux_1_2_x86_64.whl (1.9 MB view details)

Uploaded CPython 3.9musllinux: musl 1.2+ x86-64

sophy-0.7.0-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl (2.0 MB view details)

Uploaded CPython 3.9manylinux: glibc 2.17+ x86-64manylinux: glibc 2.28+ x86-64

File details

Details for the file sophy-0.7.0.tar.gz.

File metadata

  • Download URL: sophy-0.7.0.tar.gz
  • Upload date:
  • Size: 205.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for sophy-0.7.0.tar.gz
Algorithm Hash digest
SHA256 61474c40cde0f82860313e0893716c7f15eb81695cc92d5bc03012aa58ffea06
MD5 82137541c3049295d520a249e5159a38
BLAKE2b-256 e7ae2a0873a0a226d9dd4f210681e547f7609b4af1c3ff38ce4c9f282d550f78

See more details on using hashes here.

File details

Details for the file sophy-0.7.0-cp314-cp314t-musllinux_1_2_x86_64.whl.

File metadata

File hashes

Hashes for sophy-0.7.0-cp314-cp314t-musllinux_1_2_x86_64.whl
Algorithm Hash digest
SHA256 6a39cb9a493f862dc927433a1b69a8d32b83dbd103b5c15403f6c348ee64eac7
MD5 97492d2990e113f0d9bc0eba74c806f0
BLAKE2b-256 c59c584f6e82dc67f8a7df9079ee0f48851d324c3318355329c07eb766cc2efb

See more details on using hashes here.

File details

Details for the file sophy-0.7.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl.

File metadata

File hashes

Hashes for sophy-0.7.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl
Algorithm Hash digest
SHA256 06c2ff32508222ed8072b3fbb56ad330ba727215ccb0a12476a1f7085330ba2e
MD5 c0dc9ca9037cba14d09b51cf6cd0a9cb
BLAKE2b-256 f06550614e85f21a807cbe2be337965e0a40ca4cd10a03eb300948ee0f4cdbd0

See more details on using hashes here.

File details

Details for the file sophy-0.7.0-cp314-cp314-musllinux_1_2_x86_64.whl.

File metadata

File hashes

Hashes for sophy-0.7.0-cp314-cp314-musllinux_1_2_x86_64.whl
Algorithm Hash digest
SHA256 0d884ddf0dcc6bde6c2c68b31de7d238452e16f89f4e865acbcb90e5e1bf7525
MD5 4a343b93c4770db9b1ca6c594a0183cd
BLAKE2b-256 ebbb36f7bd72a1487f82e90abc18d52c040a39ad98a4942028047676ee1be160

See more details on using hashes here.

File details

Details for the file sophy-0.7.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl.

File metadata

File hashes

Hashes for sophy-0.7.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl
Algorithm Hash digest
SHA256 427e9bf20a356e99ac3d4b46ed60a4d5039f6f5ca86f763cfac24357d2d2bcaf
MD5 c075fd1edcc2537cae979c21b183843a
BLAKE2b-256 9fc944975bc6367e35636c2bd895b3aa1456d54aefab260ab085c853a20f9614

See more details on using hashes here.

File details

Details for the file sophy-0.7.0-cp313-cp313-musllinux_1_2_x86_64.whl.

File metadata

File hashes

Hashes for sophy-0.7.0-cp313-cp313-musllinux_1_2_x86_64.whl
Algorithm Hash digest
SHA256 9bc9317cc5d72b399c8a73b437c08c676c7c4ce0edf554ee38aa9c6721919c67
MD5 3510dffc50411b18c173eb80f06d5b52
BLAKE2b-256 65b23932a76537729b86e6afaa934e9592f49a9fa874526a363d51e4b0262774

See more details on using hashes here.

File details

Details for the file sophy-0.7.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl.

File metadata

File hashes

Hashes for sophy-0.7.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl
Algorithm Hash digest
SHA256 e519c61e754285c9824f1ea1719e2562c35a1387b3d300d938b6e2ea7797c931
MD5 70eb546f4854fe986f986b0d351f86e9
BLAKE2b-256 78cda82877f4b66dec83b5f72b4104a6ca9ce51f96ff6d029cdab28ace951020

See more details on using hashes here.

File details

Details for the file sophy-0.7.0-cp312-cp312-musllinux_1_2_x86_64.whl.

File metadata

File hashes

Hashes for sophy-0.7.0-cp312-cp312-musllinux_1_2_x86_64.whl
Algorithm Hash digest
SHA256 b61f239fd558964c85ff61785eddfdcea8b5ba3352ebcc9238492cc3c393cc78
MD5 19b79f680d8310ab076fb23f15fcafb5
BLAKE2b-256 f179d1c975439d00a3715487aadc8020d24428b6e7e3564fb445537aeab8a6ca

See more details on using hashes here.

File details

Details for the file sophy-0.7.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl.

File metadata

File hashes

Hashes for sophy-0.7.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl
Algorithm Hash digest
SHA256 35e377cf7d8b98ce027f173e259b924b5c3db9506a0dda272d375b6ba212dd60
MD5 87ed331c66b356fb51bd932ec63d955d
BLAKE2b-256 55c5cc09c074614153631d50b873a012c2aad6629f00a5a582b27fcecd10c2ab

See more details on using hashes here.

File details

Details for the file sophy-0.7.0-cp311-cp311-musllinux_1_2_x86_64.whl.

File metadata

File hashes

Hashes for sophy-0.7.0-cp311-cp311-musllinux_1_2_x86_64.whl
Algorithm Hash digest
SHA256 ce57ebffd7330bbeb578eaabee3b09f82ededb94371b4cceb9355e33ec330695
MD5 46da70052f5452ec344d0792c88288dc
BLAKE2b-256 18b9c9249dcc224e6430e6b46ee0c86c578be8ff2744532ce36ddf33aeecde9a

See more details on using hashes here.

File details

Details for the file sophy-0.7.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl.

File metadata

File hashes

Hashes for sophy-0.7.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl
Algorithm Hash digest
SHA256 9bd5878350f1faf769431b7cde172e63f79089eca6750e57996f669d172da114
MD5 0034b3b162005f3aef6261bf800c98ad
BLAKE2b-256 8232c9457c35aad1bff93d41a218b65907bb0c674f34ada0509d75ad2fd1a37f

See more details on using hashes here.

File details

Details for the file sophy-0.7.0-cp310-cp310-musllinux_1_2_x86_64.whl.

File metadata

File hashes

Hashes for sophy-0.7.0-cp310-cp310-musllinux_1_2_x86_64.whl
Algorithm Hash digest
SHA256 4298622d502b7f39305040f46aa846ba4f7f33f8b1b17f1c9427c991cd7ef045
MD5 667c984912680143fa76760fb8252f17
BLAKE2b-256 1914b8f813152bf622ce65a3574d33d23061fd1f2a1aa1901584b518a8cd03e9

See more details on using hashes here.

File details

Details for the file sophy-0.7.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl.

File metadata

File hashes

Hashes for sophy-0.7.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl
Algorithm Hash digest
SHA256 d18c060dbca567bd6926e785fd9f92c7b718d97f2472a43647e60f0b15b1d6cb
MD5 e37d510a64edb2e129ebbc7c5cfc9354
BLAKE2b-256 fb6b95e9916183afd48eae002caf9705f1b91e7584440cb67f341029b02cddca

See more details on using hashes here.

File details

Details for the file sophy-0.7.0-cp39-cp39-musllinux_1_2_x86_64.whl.

File metadata

File hashes

Hashes for sophy-0.7.0-cp39-cp39-musllinux_1_2_x86_64.whl
Algorithm Hash digest
SHA256 33f6f397da55eead10f7ed456094a5187379a61b0c20575a094436570cf0f9aa
MD5 d66b2c691c03efe53ea04fd17b83d69d
BLAKE2b-256 bbe829265d5b530b396cbcf5542e14bb87961ee66123654332c6b4cab8a30edb

See more details on using hashes here.

File details

Details for the file sophy-0.7.0-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl.

File metadata

File hashes

Hashes for sophy-0.7.0-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl
Algorithm Hash digest
SHA256 c354af8ff0347bdde967ba3f0f2df03a4dbb462b05b8e48caae8ff3cd177203e
MD5 be3330ba73b05f34e4581a0e0f625ee3
BLAKE2b-256 cc9bf14af226870838657ab2b684e5ce28439bf811342f9565e5a6932ecdf0f7

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page