Skip to content

Threads and processes

A Database object is safe to share between threads, with no lock of your own:

  • every method releases the GIL, so other Python threads keep running;
  • searches run in parallel;
  • writes (add, upsert, delete, persist, compact) wait for each other and for running searches instead of failing.
from concurrent.futures import ThreadPoolExecutor
with ThreadPoolExecutor(8) as pool:
results = list(pool.map(lambda q: db.search(q, 10), queries))

For many queries at once, search_batch(queries, k) parallelises inside Rust.

A database directory can be open in one place at a time. A second open, from this process or another, raises BlockingIOError:

a = srvdb.Database("./db", dimension=384)
b = srvdb.Database("./db", dimension=384) # BlockingIOError: already open

Share one Database object instead of opening the same path twice. The lock is released when the object is garbage-collected or the process exits, even after a crash. For multiple processes, put one process in charge of the database and send it requests.

SrvDB is Send + Sync. Searches take &self; writes take &mut self. Wrap it in Arc<RwLock<SrvDB>> to mix readers and a writer across threads. Design: ADR 0005.