a11oy / kipu_qillqaq /pool.py
betterwithage's picture
sync(space): full source mirror — resolve all GitHub<->Space drift (CTO)
a6a5d8e verified
Raw
History Blame
5.3 kB
"""KipuPool — the shared, content-addressed receipt-cell substrate.
Every organ reads/writes ReceiptCells here. Each write:
1. computes/verifies the content address (cid),
2. persists the cell (LMDB if available, else a JSON-file store — same API),
3. optionally stores a Reed-Solomon shard manifest for durability,
4. publishes a ("write", cell) event on the in-process EventBus.
Each read publishes a ("read", cid) event and emits a read-receipt cell, so there is no
silent access. This is a Linda-style tuple space + event sourcing + content addressing.
HONEST: durability is Reed-Solomon erasure coding, not "holographic QEC".
"""
from __future__ import annotations
import json
import os
import threading
from pathlib import Path
from typing import Iterable, Optional
from .cell import ReceiptCell
from .coding import decode_shards, encode_cell
from .events import EventBus
class _Store:
"""Key-value persistence: LMDB backend if installed, else JSON-file fallback."""
def __init__(self, path: str):
self.path = path
self.backend = "json"
self._lmdb = None
self._lock = threading.RLock()
try:
import lmdb # type: ignore
os.makedirs(path, exist_ok=True)
self._lmdb = lmdb.open(path, map_size=256 * 1024 * 1024)
self.backend = "lmdb"
except Exception:
self._file = Path(path)
self._file.parent.mkdir(parents=True, exist_ok=True)
if not self._file.exists():
self._file.write_text("{}")
def put(self, key: str, value: bytes) -> None:
with self._lock:
if self.backend == "lmdb":
with self._lmdb.begin(write=True) as txn:
txn.put(key.encode(), value)
else:
d = json.loads(self._file.read_text())
d[key] = value.decode("utf-8")
self._file.write_text(json.dumps(d))
def get(self, key: str) -> Optional[bytes]:
with self._lock:
if self.backend == "lmdb":
with self._lmdb.begin() as txn:
v = txn.get(key.encode())
return bytes(v) if v is not None else None
d = json.loads(self._file.read_text())
v = d.get(key)
return v.encode("utf-8") if v is not None else None
def keys(self) -> Iterable[str]:
with self._lock:
if self.backend == "lmdb":
with self._lmdb.begin() as txn:
return [k.decode() for k, _ in txn.cursor()]
return list(json.loads(self._file.read_text()).keys())
class KipuPool:
"""The shared receipt-cell substrate."""
def __init__(self, path: str = "/tmp/kipu", durability: bool = True,
rs_n: int = 10, rs_k: int = 6):
self.bus = EventBus()
self._cells = _Store(os.path.join(path, "cells"))
self._shards = _Store(os.path.join(path, "shards"))
self.durability = durability
self.rs_n, self.rs_k = rs_n, rs_k
@property
def store_backend(self) -> str:
return self._cells.backend
def write(self, cell: ReceiptCell) -> str:
"""Persist a cell, optionally RS-encode it, publish a write event. Returns cid."""
if not cell.verify():
raise ValueError("ReceiptCell failed content-address verification")
cb = cell.to_bytes()
self._cells.put(cell.cid, cb)
if self.durability:
manifest = encode_cell(cb, self.rs_n, self.rs_k)
self._shards.put(cell.cid, json.dumps(manifest).encode())
self.bus.publish("write", cell)
self.bus.publish(f"organ:{cell.organ}", cell)
return cell.cid
def read(self, cid: str, reader: str = "anon", emit_receipt: bool = True) -> Optional[ReceiptCell]:
"""Read a cell by cid. Publishes a read event and (optionally) a read-receipt."""
raw = self._cells.get(cid)
cell = ReceiptCell.from_bytes(raw) if raw is not None else None
self.bus.publish("read", {"cid": cid, "reader": reader, "hit": cell is not None})
if emit_receipt and cell is not None:
rr = ReceiptCell(organ=reader, kind="read_receipt",
payload={"read_cid": cid}, parents=(cid,))
self._cells.put(rr.cid, rr.to_bytes())
return cell
def recover(self, cid: str, drop: Optional[list[int]] = None) -> Optional[ReceiptCell]:
"""Recover a cell from its Reed-Solomon shards, optionally simulating lost shards."""
raw = self._shards.get(cid)
if raw is None:
return None
manifest = json.loads(raw.decode())
if drop:
shards = list(manifest["shards"])
for i in drop:
shards[i] = None
manifest = {**manifest, "shards": shards}
cb = decode_shards(manifest)
return ReceiptCell.from_bytes(cb)
def all_cids(self) -> list[str]:
return list(self._cells.keys())
def stats(self) -> dict:
return {
"store_backend": self._cells.backend,
"cells": len(list(self._cells.keys())),
"durability": self.durability,
"rs_code": f"RS({self.rs_n},{self.rs_k})",
}