Skip to main content

lsm

Fast Python bindings for SQLite's LSM key/value store. The LSM storage engine was initially written as part of the experimental SQLite4 rewrite (now abandoned). More recently, the LSM source code was moved into the SQLite3 source tree and has seen some improvements and fixes. This project uses the LSM code from the SQLite3 source tree.

Features:

  • Embedded zero-conf database.
  • Keys support in-order traversal using cursors.
  • Transactional (including nested transactions).
  • Single writer/multiple reader MVCC based transactional concurrency model.
  • On-disk database stored in a single file.
  • Data is durable in the face of application or power failure.
  • Thread-safe.
  • Releases GIL for read and write operations (each connection has own mutex)
  • Page compression (lz4 or zstd)
  • Zero dependency static library
  • Python 3.x.

Limitations:

The source for Python lsm is hosted on GitHub.

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

Quick-start

Below is a sample interactive console session designed to show some of the basic features and functionality of the lsm Python library.

To begin, instantiate a LSM object, specifying a path to a database file.

from lsm import LSM
db = LSM('test.ldb')
assert db.open()

More pythonic variant is using context manager:

from lsm import LSM
with LSM("test.ldb") as db:
    assert db.info()

Not opened database will raise a RuntimeError:

import pytest
from lsm import LSM

db = LSM('test.ldb')

with pytest.raises(RuntimeError):
    db.info()

Binary/string mode

You should select mode for opening the database with binary: bool = True argument.

For example when you want to store strings just pass binary=False:

from lsm import LSM
with LSM("test_0.ldb", binary=False) as db:
    # must be str for keys and values
    db['foo'] = 'bar'
    assert db['foo'] == "bar"

Otherwise, you must pass keys and values ad bytes (default behaviour):

from lsm import LSM

with LSM("test.ldb") as db:
    db[b'foo'] = b'bar'
    assert db[b'foo'] == b'bar'

Key/Value Features

lsm is a key/value store, and has a dictionary-like API:

from lsm import LSM
with LSM("test.ldb", binary=False) as db:
    db['foo'] = 'bar'
    assert db['foo'] == 'bar'

Database apply changes as soon as possible:

import pytest
from lsm import LSM

with LSM("test.ldb", binary=False) as db:
    for i in range(4):
         db[f'k{i}'] = str(i)

    assert 'k3' in db
    assert 'k4' not in db
    del db['k3']

    with pytest.raises(KeyError):
        print(db['k3'])

By default, when you attempt to look up a key, lsm will search for an exact match. You can also search for the closest key, if the specific key you are searching for does not exist:

import pytest
from lsm import LSM, SEEK_LE, SEEK_GE, SEEK_LEFAST


with LSM("test.ldb", binary=False) as db:
    for i in range(4):
        db[f'k{i}'] = str(i)

    # Here we will match "k1".
    assert db['k1xx', SEEK_LE] == '1'

    # Here we will match "k1" but do not fetch a value
    # In this case the value will always be ``True`` or there will
    # be an exception if the key is not found
    assert db['k1xx', SEEK_LEFAST] is True

    with pytest.raises(KeyError):
        print(db['000', SEEK_LEFAST])

    # Here we will match "k2".
    assert db['k1xx', SEEK_GE] == "2"

LSM supports other common dictionary methods such as:

  • keys()
  • values()
  • items()
  • update()

Slices and Iteration

The database can be iterated through directly, or sliced. When you are slicing the database the start and end keys need not exist -- lsm will find the closest key (details can be found in the LSM.fetch_range() documentation).

from lsm import LSM

with LSM("test_slices.ldb", binary=False) as db:

    # clean database
    for key in db.keys():
        del db[key]

    db['foo'] = 'bar'

    for i in range(3):
        db[f'k{i}'] = str(i)

    # Can easily iterate over the database items
    assert (
        sorted(item for item in db.items()) == [
            ('foo', 'bar'), ('k0', '0'), ('k1', '1'), ('k2', '2')
        ]
    )

    # However, you will not read the entire database into memory, as special
    # iterator objects are used.
    assert str(db['k0':'k99']).startswith("<lsm_slice object at")

    # But you can cast it to the list for example
    assert list(db['k0':'k99']) == [('k0', '0'), ('k1', '1'), ('k2', '2')]

You can use open-ended slices. If the lower- or upper-bound is outside the range of keys an empty list is returned.

with LSM("test_slices.ldb", binary=False, readonly=True) as db:
    assert list(db['k0':]) == [('k0', '0'), ('k1', '1'), ('k2', '2')]
    assert list(db[:'k1']) == [('foo', 'bar'), ('k0', '0'), ('k1', '1')]
    assert list(db[:'aaa']) == []

To retrieve keys in reverse order or stepping over more than one item, simply use a third slice argument as usual. Negative step value means reverse order, but first and second arguments must be ordinarily ordered.

with LSM("test_slices.ldb", binary=False, readonly=True) as db:
    assert list(db['k0':'k99':2]) == [('k0', '0'), ('k2', '2')]
    assert list(db['k0'::-1]) == [('k2', '2'), ('k1', '1'), ('k0', '0')]
    assert list(db['k0'::-2]) == [('k2', '2'), ('k0', '0')]
    assert list(db['k0'::3]) == [('k0', '0')]

You can also delete slices of keys, but note that delete will not include the keys themselves:

with LSM("test_slices.ldb", binary=False) as db:
    del db['k0':'k99']

    # Note that 'k0' still exists.
    assert list(db.items()) == [('foo', 'bar'), ('k0', '0')]

Cursors

While slicing may cover most use-cases, for finer-grained control you can use cursors for traversing records.

from lsm import LSM, SEEK_GE, SEEK_LE

with LSM("test_cursors.ldb", binary=False) as db:
    del db["a":"z"]

    db["spam"] = "spam"

    with db.cursor() as cursor:
        cursor.seek('spam')
        key, value = cursor.retrieve()
        assert key == 'spam'
        assert value == 'spam'

Seeking over cursors:

with LSM("test_cursors.ldb", binary=False) as db:
    db.update({'k0': '0', 'k1': '1', 'k2': '2', 'k3': '3', 'foo': 'bar'})

    with db.cursor() as cursor:

        cursor.first()
        key, value = cursor.retrieve()
        assert key == "foo"
        assert value == "bar"

        cursor.last()
        key, value = cursor.retrieve()
        assert key == "spam"
        assert value == "spam"

        cursor.previous()
        key, value = cursor.retrieve()
        assert key == "k3"
        assert value == "3"

Finding the first match that is greater than or equal to 'k0' and move forward until the key is less than 'k99'

with LSM("test_cursors.ldb", binary=False) as db:
    with db.cursor() as cursor:
        cursor.seek("k0", SEEK_GE)
        results = []

        while cursor.compare("k99") > 0:
            key, value = cursor.retrieve()
            results.append((key, value))
            cursor.next()

    assert results == [('k0', '0'), ('k1', '1'), ('k2', '2'), ('k3', '3')]

Finding the last match that is lower than or equal to 'k99' and move backward until the key is less than 'k0'

with LSM("test_cursors.ldb", binary=False) as db:
    with db.cursor() as cursor:
        cursor.seek("k99", SEEK_LE)
        results = []

        while cursor.compare("k0") >= 0:
            key, value = cursor.retrieve()
            results.append((key, value))
            cursor.previous()

    assert results == [('k3', '3'), ('k2', '2'), ('k1', '1'), ('k0', '0')]

It is very important to close a cursor when you are through using it. For this reason, it is recommended you use the LSM.cursor() context-manager, which ensures the cursor is closed properly.

Transactions

lsm supports nested transactions. The simplest way to use transactions is with the LSM.transaction() method, which returns a context-manager:

from lsm import LSM

with LSM("test_tx.ldb", binary=False) as db:
    del db["a":"z"]
    for i in range(10):
        db[f"k{i}"] = f"{i}"


with LSM("test_tx.ldb", binary=False) as db:
    with db.transaction() as tx1:
        db['k1'] = '1-mod'

        with db.transaction() as tx2:
            db['k2'] = '2-mod'
            tx2.rollback()

    assert db['k1'] == '1-mod'
    assert db['k2'] == '2'

You can commit or roll-back transactions part-way through a wrapped block:

from lsm import LSM

with LSM("test_tx_2.ldb", binary=False) as db:
    del db["a":"z"]
    for i in range(10):
        db[f"k{i}"] = f"{i}"

with LSM("test_tx_2.ldb", binary=False) as db:
    with db.transaction() as txn:
        db['k1'] = 'outer txn'

        # The write operation is preserved.
        txn.commit()

        db['k1'] = 'outer txn-2'

        with db.transaction() as txn2:
            # This is committed after the block ends.
            db['k1'] = 'inner-txn'

        assert db['k1'] == "inner-txn"

        # Rolls back both the changes from txn2 and the preceding write.
        txn.rollback()

        assert db['k1'] == 'outer txn', db['k1']

If you like, you can also explicitly call LSM.begin(), LSM.commit(), and LSM.rollback().

from lsm import LSM

# fill db
with LSM("test_db_tx.ldb", binary=False) as db:
    del db["k":"z"]
    for i in range(10):
        db[f"k{i}"] = f"{i}"


with LSM("test_db_tx.ldb", binary=False) as db:
    # start transaction
    db.begin()
    db['k1'] = '1-mod'

    # nested transaction
    db.begin()
    db['k2'] = '2-mod'
    # rolling back nested transaction
    db.rollback()

    # comitting top-level transaction
    db.commit()

    assert db['k1'] == '1-mod'
    assert db['k2'] == '2'

Thanks to

Release files for lsm 0.5.10

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for lsm 0.5.10
File Size Uploaded
lsm-0.5.10.tar.gz 896.2 kB Details

Built distributions (wheels)

Table of built distributions (wheels) for lsm 0.5.10
File
lsm-0.5.10-cp313-cp313-win_amd64.whl CPython 3.13 CPython 3.13 Windows x86-64 Details
lsm-0.5.10-cp313-cp313-manylinux_2_34_x86_64.whl CPython 3.13 CPython 3.13 Linux glibc 2.34+ x86-64 Details
lsm-0.5.10-cp313-cp313-macosx_10_13_universal2.whl CPython 3.13 CPython 3.13 macOS 10.13+ universal2 (ARM64, x86-64) Details
lsm-0.5.10-cp312-cp312-win_amd64.whl CPython 3.12 CPython 3.12 Windows x86-64 Details
lsm-0.5.10-cp312-cp312-manylinux_2_34_x86_64.whl CPython 3.12 CPython 3.12 Linux glibc 2.34+ x86-64 Details
lsm-0.5.10-cp312-cp312-macosx_10_13_universal2.whl CPython 3.12 CPython 3.12 macOS 10.13+ universal2 (ARM64, x86-64) Details
lsm-0.5.10-cp311-cp311-win_amd64.whl CPython 3.11 CPython 3.11 Windows x86-64 Details
lsm-0.5.10-cp311-cp311-manylinux_2_34_x86_64.whl CPython 3.11 CPython 3.11 Linux glibc 2.34+ x86-64 Details
lsm-0.5.10-cp311-cp311-macosx_10_9_universal2.whl CPython 3.11 CPython 3.11 macOS 10.9+ universal2 (ARM64, x86-64) Details
lsm-0.5.10-cp310-cp310-win_amd64.whl CPython 3.10 CPython 3.10 Windows x86-64 Details
lsm-0.5.10-cp310-cp310-manylinux_2_34_x86_64.whl CPython 3.10 CPython 3.10 Linux glibc 2.34+ x86-64 Details
lsm-0.5.10-cp310-cp310-macosx_10_9_universal2.whl CPython 3.10 CPython 3.10 macOS 10.9+ universal2 (ARM64, x86-64) Details
lsm-0.5.10-cp39-cp39-win_amd64.whl CPython 3.9 CPython 3.9 Windows x86-64 Details
lsm-0.5.10-cp39-cp39-manylinux_2_34_x86_64.whl CPython 3.9 CPython 3.9 Linux glibc 2.34+ x86-64 Details
lsm-0.5.10-cp39-cp39-macosx_10_9_universal2.whl CPython 3.9 CPython 3.9 macOS 10.9+ universal2 (ARM64, x86-64) Details

Total release size: 13.5 MB

Release files / lsm-0.5.10.tar.gz

Download URL lsm-0.5.10.tar.gz
Size 896.2 kB
Tags Source
SHA-256 checksum
How to use checksums
809f75d1a1e96c6e980ad51836e61f72c5221a53ff58e915a266c6ffe7ce20d9
BLAKE2b-256 checksum
How to use checksums
af05029d5a563e734878fed1d081801155ba4ec458c538eec1dfe1acaf894c2f
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.1.0 CPython/3.12.9

Release files / lsm-0.5.10-cp313-cp313-win_amd64.whl

Download URL lsm-0.5.10-cp313-cp313-win_amd64.whl
Size 249.7 kB
Tags CPython 3.13 Windows x86-64
SHA-256 checksum
How to use checksums
116b66034ac0650d0e965356bc2e88ac69a753187b0c2593f33665ccd3b99bf2
BLAKE2b-256 checksum
How to use checksums
acbff78d11e05ea8855d95fa200de6f378050f204058533b56480ad43c89e332
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.1.0 CPython/3.13.2

Release files / lsm-0.5.10-cp313-cp313-manylinux_2_34_x86_64.whl

Download URL lsm-0.5.10-cp313-cp313-manylinux_2_34_x86_64.whl
Size 1.1 MB
Tags CPython 3.13 Linux glibc 2.34+ x86-64
SHA-256 checksum
How to use checksums
32033522de2b72f33d7badefa369e430f2987c9c6f7aee5a8430f44c61e7e43e
BLAKE2b-256 checksum
How to use checksums
8049158cef2c5c10d13a3cd20862554e11f0e430b278a980ee231fe1e828e4da
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.1.0 CPython/3.9.21

Release files / lsm-0.5.10-cp313-cp313-macosx_10_13_universal2.whl

Download URL lsm-0.5.10-cp313-cp313-macosx_10_13_universal2.whl
Size 1.2 MB
Tags CPython 3.13 macOS 10.13+ universal2 (ARM64, x86-64)
SHA-256 checksum
How to use checksums
194dba0ccfe02e6dcb7b8abc50a2f559ac2ad469af8f38ae81a56f3abba72364
BLAKE2b-256 checksum
How to use checksums
d33a774c2bd145ff10bd0a1bde4879fbfd7346260c5e8deb0615d1b28535d0dd
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.1.0 CPython/3.13.1

Release files / lsm-0.5.10-cp312-cp312-win_amd64.whl

Download URL lsm-0.5.10-cp312-cp312-win_amd64.whl
Size 249.6 kB
Tags CPython 3.12 Windows x86-64
SHA-256 checksum
How to use checksums
f5476e680357683393d56bb128ed46cf0c7ca1e6435cc1771435eda8010f65fb
BLAKE2b-256 checksum
How to use checksums
55958c1e7f2143ea419c9729bba30041214a14e954bec678a6237a59006ece00
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.1.0 CPython/3.12.9

Release files / lsm-0.5.10-cp312-cp312-manylinux_2_34_x86_64.whl

Download URL lsm-0.5.10-cp312-cp312-manylinux_2_34_x86_64.whl
Size 1.1 MB
Tags CPython 3.12 Linux glibc 2.34+ x86-64
SHA-256 checksum
How to use checksums
1f7c8db72ffd2bdc930785a0c4897be5766b36d8d9914a5ad88a8919c4abba5c
BLAKE2b-256 checksum
How to use checksums
1d52df9925a88b52b541313699c58d5c953e48fec3b50c5e41bf5cbfcbcb353f
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.1.0 CPython/3.9.21

Release files / lsm-0.5.10-cp312-cp312-macosx_10_13_universal2.whl

Download URL lsm-0.5.10-cp312-cp312-macosx_10_13_universal2.whl
Size 1.2 MB
Tags CPython 3.12 macOS 10.13+ universal2 (ARM64, x86-64)
SHA-256 checksum
How to use checksums
6ac95c245b11a1bdcc5f047f9cacdbf3fc4a0141d9592be51eae76b5a4b4aee6
BLAKE2b-256 checksum
How to use checksums
7cb6f2c2d4fff96d26a1df5ca1c677ccbf515fe20cdb4b23f403329981508304
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.1.0 CPython/3.12.8

Release files / lsm-0.5.10-cp311-cp311-win_amd64.whl

Download URL lsm-0.5.10-cp311-cp311-win_amd64.whl
Size 249.3 kB
Tags CPython 3.11 Windows x86-64
SHA-256 checksum
How to use checksums
6e88f8d784a442c64f1fbf78b96eadc6e0327ad229c386a95a9454a633699411
BLAKE2b-256 checksum
How to use checksums
147c5d53cb5742220181f030d75521067c0520a88af21b5cc645c0dcf06bdaef
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.1.0 CPython/3.11.9

Release files / lsm-0.5.10-cp311-cp311-manylinux_2_34_x86_64.whl

Download URL lsm-0.5.10-cp311-cp311-manylinux_2_34_x86_64.whl
Size 1.1 MB
Tags CPython 3.11 Linux glibc 2.34+ x86-64
SHA-256 checksum
How to use checksums
3d152942c47a911d18cb151b21e84802f92d162881438f8c833903f66b85d610
BLAKE2b-256 checksum
How to use checksums
d2acc97dd47eaa94c823b2a0b768ee72e70a52d3639502a6ec5e5d26977fbc09
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.1.0 CPython/3.9.21

Release files / lsm-0.5.10-cp311-cp311-macosx_10_9_universal2.whl

Download URL lsm-0.5.10-cp311-cp311-macosx_10_9_universal2.whl
Size 1.2 MB
Tags CPython 3.11 macOS 10.9+ universal2 (ARM64, x86-64)
SHA-256 checksum
How to use checksums
137e1001a16e26f5cd2c87060c342a30c789ba0e2d173448222b847f1acb8d87
BLAKE2b-256 checksum
How to use checksums
69d1201978c03999cde64e2a203cd097831c10b85b61576825f7e1844e4e4aae
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.1.0 CPython/3.11.9

Release files / lsm-0.5.10-cp310-cp310-win_amd64.whl

Download URL lsm-0.5.10-cp310-cp310-win_amd64.whl
Size 249.1 kB
Tags CPython 3.10 Windows x86-64
SHA-256 checksum
How to use checksums
93eaf0b9c0fa7621aff8025985768fe5b0b03dd7fd03905ee916c03ea7e6cad3
BLAKE2b-256 checksum
How to use checksums
6ebb97075074ee34f8e9b96a57a5de8309e332aa909899e3d925be404a9eaece
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.1.0 CPython/3.10.11

Release files / lsm-0.5.10-cp310-cp310-manylinux_2_34_x86_64.whl

Download URL lsm-0.5.10-cp310-cp310-manylinux_2_34_x86_64.whl
Size 1.1 MB
Tags CPython 3.10 Linux glibc 2.34+ x86-64
SHA-256 checksum
How to use checksums
63d549b3397f4d8ad1d95c59bda3345be11571af88c8d05b8d860e369c2db165
BLAKE2b-256 checksum
How to use checksums
772d6e4feb6707e1ae2e0f2c27ba4ef761479c634370f9b22f13403c4c4600c8
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.1.0 CPython/3.9.21

Release files / lsm-0.5.10-cp310-cp310-macosx_10_9_universal2.whl

Download URL lsm-0.5.10-cp310-cp310-macosx_10_9_universal2.whl
Size 1.2 MB
Tags CPython 3.10 macOS 10.9+ universal2 (ARM64, x86-64)
SHA-256 checksum
How to use checksums
2735e8def90e2c3ddf71552e0a21dfbd90a3bd2d55c462ca03f8130b0010b0d4
BLAKE2b-256 checksum
How to use checksums
520f8fa0ff7a67fd959b1d1c493cd7a888698cbb2378f601d4f23b5068256d28
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.1.0 CPython/3.10.11

Release files / lsm-0.5.10-cp39-cp39-win_amd64.whl

Download URL lsm-0.5.10-cp39-cp39-win_amd64.whl
Size 339.9 kB
Tags CPython 3.9 Windows x86-64
SHA-256 checksum
How to use checksums
85208bff639387d112dcfd4d278a4adc1dcff5c860110ecd14047feeec71900e
BLAKE2b-256 checksum
How to use checksums
0da4cd5f47079c1b66357dad3e522923e32fa08724fc9f2b844309948bc90172
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.1.0 CPython/3.9.13

Release files / lsm-0.5.10-cp39-cp39-manylinux_2_34_x86_64.whl

Download URL lsm-0.5.10-cp39-cp39-manylinux_2_34_x86_64.whl
Size 1.1 MB
Tags CPython 3.9 Linux glibc 2.34+ x86-64
SHA-256 checksum
How to use checksums
8f0792bebe41bf2f24d460c0179d3b75b6de831eb10800be0fc36851c5af4ec8
BLAKE2b-256 checksum
How to use checksums
d55c62e8195417803797fecdfee6538c0b4632249efa4ced7fec56c8a689c5c8
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.1.0 CPython/3.9.21

Release files / lsm-0.5.10-cp39-cp39-macosx_10_9_universal2.whl

Download URL lsm-0.5.10-cp39-cp39-macosx_10_9_universal2.whl
Size 1.2 MB
Tags CPython 3.9 macOS 10.9+ universal2 (ARM64, x86-64)
SHA-256 checksum
How to use checksums
3bc1eb331877563b7061e81ae765adcf775e44f7e86ebba9d23aedb5d4db26e1
BLAKE2b-256 checksum
How to use checksums
839699145e7a94d8b2763d5c33dec6e8e7e06f69695e9ced00a97fb23df5d230
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.1.0 CPython/3.9.13

Release history Release notifications | RSS feed

This release

0.5.10 This release

16 release files

0.5.9

11 release files

0.5.7

7 release files

0.5.6

6 release files

0.5.5

7 release files

0.5.3

26 release files

0.4.9

16 release files

0.4.8

20 release files

0.4.7

20 release files

0.4.6

27 release files

0.4.5

11 release files

0.4.4

24 release files

0.4.3

16 release files

0.4.2

16 release files

0.4.1

16 release files

0.4.0

16 release files

0.3.9

13 release files

0.3.8

13 release files

0.3.7

13 release files

0.3.6

13 release files

0.3.5

13 release files

0.3.2

13 release files

0.2.4

13 release files

0.2.1

13 release files

0.2.0

13 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page