Skip to main content

Typed collections backed by NATS JetStream KV

Project description

Brainless DB

Typed collections backed by NATS JetStream KV. In-memory with background sync.

Quick Start

from typing import Annotated
from msgspec import Meta
from brainlessdb import BrainlessDB, BrainlessDBFeat, BrainlessStruct

class UserV1(BrainlessStruct):
    id: Annotated[int, Meta(extra={"brainlessdb_flags": BrainlessDBFeat.INDEX})]
    name: Annotated[str, Meta()] = ""

db = BrainlessDB(nats, namespace="app")
await db.start()

users = db.collection(UserV1)
await users.watch()  # start watching for remote changes

# Create
user = users.add(UserV1(id=1, name="Alice"))

# Update
user.name = "Bob"
user.save()  # marks dirty, schedules flush

# Query (async, auto-loads from NATS)
user = await users.find(id=1)  # uses index
all_users = await users.filter(lambda u: u.id > 0)

await db.stop()

When to Use What

Class Use for Has UUID Stored in
BrainlessStruct Main entities (User, Order, etc.) Yes Own bucket(s)
Struct (msgspec) Nested data, app-local data No Inside parent entity
from msgspec import Struct
from brainlessdb import BrainlessStruct

# App-local data - plain Struct (no UUID, not an entity)
class UcsLocal(Struct):
    sid: int = 0
    pointer: int = 0

# Nested data - plain Struct
class Address(Struct):
    street: str = ""
    city: str = ""

# Main entity - BrainlessStruct (has UUID, stored in NATS)
class UserV1(BrainlessStruct):
    id: Annotated[int, Meta()]
    address: Optional[Address] = None  # nested struct
    _: Optional[UcsLocal] = None       # app-local struct

Global API

import brainlessdb

await brainlessdb.setup(nats, namespace="app")
users = brainlessdb.collection(UserV1)
await brainlessdb.flush()  # manual flush
await brainlessdb.stop()

Field Types

Config Fields (default)

Persistent data synced across all instances:

class UserV1(BrainlessStruct):
    id: Annotated[int, Meta()]
    name: Annotated[str, Meta()] = ""

State Fields

Ephemeral data (separate bucket, faster sync):

class UserV1(BrainlessStruct):
    id: Annotated[int, Meta()]
    status: Annotated[int, Meta(extra={"brainlessdb_flags": BrainlessDBFeat.STATE})] = 0

Indexed Fields

Fast O(1) lookups:

class UserV1(BrainlessStruct):
    id: Annotated[int, Meta(extra={"brainlessdb_flags": BrainlessDBFeat.INDEX})]

Combine flags with |:

counter: Annotated[int, Meta(extra={"brainlessdb_flags": BrainlessDBFeat.INDEX | BrainlessDBFeat.STATE})] = 0

App-Local Fields

Data private to each namespace. Use plain Struct (not BrainlessStruct):

from msgspec import Struct

# Plain Struct - just data, no UUID
class UcsLocal(Struct):
    sid: int = 0

class AriLocal(Struct):
    channel_id: str = ""

# Entity with app-local field
class UserV1(BrainlessStruct):
    id: Annotated[int, Meta()]
    _: Union[UcsLocal, AriLocal, None] = None

Each namespace only sees its own local data:

# In UCS app (namespace="ucs")
user = UserV1(id=1, _=UcsLocal(sid=123))
users.add(user)
user._.sid  # 123

# In ARI app (namespace="ari")  
user = await users.find(id=1)
user._  # None - no ARI local data yet

CRUD Operations

# Add/update
item = coll.add(MyStruct(...))

# Update via save()
item.field = value
item.save()

# Get by UUID
item = coll.get(uuid_str)

# Delete
coll.delete(item)
coll.delete(uuid_str)

# Clear all
coll.clear()

# Dict-style access
item = coll[uuid_str]
del coll[item]
len(coll)
for item in coll: ...
item in coll

Filtering

# By predicate
items = await coll.filter(lambda i: i.priority > 5)

# By field (uses index if available)
items = await coll.filter(status=1)

# Nested fields
items = await coll.filter(address__city="Prague")

# Combined
items = await coll.filter(lambda i: i.active, status=1, limit=10)

# Find single
item = await coll.find(id=123)

# Sort
items = await coll.order_by("priority", reverse=True)

Events

Callbacks fire on remote changes by default. Set trigger_local=True to also fire on local changes.

# Any change
coll.on_change(lambda old, new: print(f"{old} -> {new}"))

# Deletion
coll.on_delete(lambda item: print(f"deleted: {item}"))

# Specific property
coll.on_property_change(
    status=lambda item, field, old, new: print(f"{field}: {old} -> {new}")
)

# Also trigger on local changes
coll.on_change(my_callback, trigger_local=True)

Watching

# Watch single collection
await coll.watch()
await coll.unwatch()

# Watch all collections
await db.watch()
await db.unwatch()

Watch updates in-memory entities automatically when remote changes arrive.

Flush Scheduling

  • Changes schedule flush after flush_interval (default 100ms)
  • Multiple changes batch into single flush
  • flush_interval=0 flushes immediately
  • await db.flush() forces immediate flush

Multi-Bucket Architecture

Each struct uses up to 3 NATS KV buckets:

  • {StructName} - config fields (persistent)
  • {StructName}-State - state fields (ephemeral)
  • {StructName}-{LocalClass} - app-local fields (per namespace)

Example: UserV1 with UCS namespace creates:

  • UserV1 (config)
  • UserV1-State (if state fields exist)
  • UserV1-UcsLocal (local data for UCS app)

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

brainlessdb-0.2.0.tar.gz (40.6 kB view details)

Uploaded Source

Built Distribution

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

brainlessdb-0.2.0-py3-none-any.whl (11.8 kB view details)

Uploaded Python 3

File details

Details for the file brainlessdb-0.2.0.tar.gz.

File metadata

  • Download URL: brainlessdb-0.2.0.tar.gz
  • Upload date:
  • Size: 40.6 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.9.26 {"installer":{"name":"uv","version":"0.9.26","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Arch Linux","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for brainlessdb-0.2.0.tar.gz
Algorithm Hash digest
SHA256 914f835cce04914d3f39fe2a97e4accf67654d24d5e768ca4784301f9073d91f
MD5 4c1dcea8aac62f97f6989a0965c6256f
BLAKE2b-256 5ceaacadfa87f10de2b672729be4563bedc6b37d017980b1d88d5023b3c677a1

See more details on using hashes here.

File details

Details for the file brainlessdb-0.2.0-py3-none-any.whl.

File metadata

  • Download URL: brainlessdb-0.2.0-py3-none-any.whl
  • Upload date:
  • Size: 11.8 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.9.26 {"installer":{"name":"uv","version":"0.9.26","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Arch Linux","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for brainlessdb-0.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 4fbf645015ca4c23ba7999fda2131348bb6e2d0fa141b0899d494fbf0842bda9
MD5 efafc36db0c0b0e93abbc2e8bb1f292a
BLAKE2b-256 07bb37fcbdb92bf87a9bcd67383585b6d00dbf6e9959f0fc1b578d8cadd18405

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