Skip to content

arcadedb.aio.AsyncDatabase: await query, command, run_in_transaction and find_nearest from async def code (refs #306) - #379

Merged
tae898 merged 3 commits into
mainfrom
feat/306-async-database
Nov 12, 2026
Merged

tae898 merged 3 commits into
mainfrom
feat/306-async-database

Conversation

@tae898

@tae898 tae898 commented Nov 12, 2026

Copy link
Copy Markdown
Collaborator

What does this PR do?

The asyncio facade of #306, item 1, as the owner decided on the issue. arcadedb.aio.AsyncDatabase(db, max_workers=8) wraps an open Database and owns a bounded thread pool of its own.

  • await adb.query(...) and await adb.command(...) run the blocking call on a worker and return the rows as a list of dict (ResultSet.to_list()), or None for a command that has no result set.
  • await adb.run_in_transaction(fn, retries, backoff_s) runs the plain function fn from begin to commit, with every retry, on one worker. A transaction belongs to its thread and two awaits of one task can run on two workers, so there is no async with transaction. An exception of fn rolls back and reaches the awaiting task.
  • await adb.find_nearest(index, query_vector, k, ...) runs index.find_nearest. await adb.run(fn, *args) runs any function.
  • Cancellation is not exposed. Cancelling the awaiting task abandons the result, a call that started finishes, a call still waiting for a worker does not run. await adb.close() refuses new calls and waits for the ones that were submitted, the queued ones included, without blocking the loop. It does not close the Database.
  • The docstring and the docs page say how AsyncDatabase differs from the engine's AsyncExecutor (db.async_executor()).

The module is one file of the shared tree, so both wheels carry it. New page how-to/asyncio.adoc (its Python blocks run in tests/test_docs_asyncio_page.py), a nav entry, and pointers from the concurrency, vectors and async-executor pages.

Tests on both wheels (CI runs them): tests/test_aio.py has rows and errors reaching the task, commit on one worker, rollback on an exception with the worker usable afterwards, 200 tasks writing and reading at once with no lost update, the pool running max_workers calls at once (a barrier) and never more, close() waiting for a running call and for queued ones and refusing new calls, both cancel cases, context variables, and the max_workers check. The two read-your-writes tests of #373 now also run through the facade. I ran the pool, close and cancel logic once without the engine to check the tests' own logic. The engine-side tests run in CI only.

Related issues

Refs #306. Items 3 (numpy in, arrays out) and 4 (collection size, graph index) still need measurements, so no closing keyword.

Docs

how-to/asyncio.adoc (new), nav.adoc, and one sentence each in how-to/concurrency.adoc, how-to/vectors.adoc and how-to/async-executor.adoc.

Checklist

  • A regression test fails on the old code and passes on the new: this is a new module, so its tests fail on main with an import error.
  • The test suite that the change can affect passes: CI runs it on both wheels.
  • uvx pre-commit run --files <changed files> passes.

🤖 Generated with Claude Code

tae898 and others added 2 commits November 12, 2026 12:17
…and find_nearest from async def code (refs #306)

An asyncio facade over the blocking calls of an open Database, on a bounded thread pool of its own (max_workers, default 8). Decided on #306:
a transaction belongs to one thread and two awaits of one task can run on two workers, so there is no async with transaction;
run_in_transaction(fn) runs the plain function fn, begin to commit and every retry, on one worker. Cancellation is not exposed: cancelling
the awaiting task abandons the result, a call that started finishes, a call still waiting for a worker does not run, and close() waits for
the calls that were submitted (it does not close the Database). The docstring and the docs page say how AsyncDatabase differs from the
engine's AsyncExecutor.

tests/test_aio.py pins, on both wheels: rows and errors reach the awaiting task, run_in_transaction commits on one worker and rolls back on
an exception, 200 tasks write and read at once without a lost update, the pool runs max_workers calls at once and never more, close() waits
and refuses new calls, a cancelled await abandons the result. tests/test_docs_asyncio_page.py runs the blocks of the new page how-to/asyncio.adoc.
Items 3 and 4 of #306 still need measurements.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…d vectors pages point to the facade (refs #306)

The two read-your-writes tests of #373 (the writing task, and other tasks that search after the commit) run again through
arcadedb.aio.AsyncDatabase: an insert is run_in_transaction and a search is find_nearest, on both wheels. The asyncio section of the
concurrency page and the vectors page name the facade next to asyncio.to_thread.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
@tae898 tae898 self-assigned this Nov 12, 2026
The count helper builds its SQL from a fixed type name of the tests, backtick-quoted, with no values. CI's Bandit run flags it at low confidence; the repo's convention is a nosec with the reason.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
@tae898
tae898 merged commit 65e7c8f into main Nov 12, 2026
43 checks passed
@tae898
tae898 deleted the feat/306-async-database branch November 12, 2026 05:25
@github-actions github-actions Bot added this to the 26.12.1 milestone Nov 12, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant