Skip to content

Commit 7b7224d

Browse files
committed
Completed async documentation
1 parent 9612657 commit 7b7224d

File tree

2 files changed

+146
-4
lines changed

2 files changed

+146
-4
lines changed

docs/async.rst

Lines changed: 145 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,148 @@
11
Async API Execution
22
-------------------
33

4-
In **asynchronous API executions**, python-arango-async sends API requests to ArangoDB in
5-
fire-and-forget style. The server processes the requests in the background, and
6-
the results can be retrieved once available via `AsyncJob` objects.
4+
In **asynchronous API executions**, the driver sends API requests to ArangoDB in
5+
fire-and-forget style. The server processes them in the background, and
6+
the results can be retrieved once available via :class:`arangoasync.job.AsyncJob` objects.
7+
8+
**Example:**
9+
10+
.. code-block:: python
11+
12+
import time
13+
from arangoasync import ArangoClient
14+
from arangoasync.auth import Auth
15+
from arangoasync.errno import HTTP_BAD_PARAMETER
16+
from arangoasync.exceptions import (
17+
AQLQueryExecuteError,
18+
AsyncJobCancelError,
19+
AsyncJobClearError,
20+
)
21+
22+
# Initialize the client for ArangoDB.
23+
async with ArangoClient(hosts="http://localhost:8529") as client:
24+
auth = Auth(username="root", password="passwd")
25+
26+
# Connect to "test" database as root user.
27+
db = await client.db("test", auth=auth)
28+
29+
# Begin async execution. This returns an instance of AsyncDatabase, a
30+
# database-level API wrapper tailored specifically for async execution.
31+
async_db = db.begin_async_execution(return_result=True)
32+
33+
# Child wrappers are also tailored for async execution.
34+
async_aql = async_db.aql
35+
async_col = async_db.collection("students")
36+
37+
# API execution context is always set to "async".
38+
assert async_db.context == "async"
39+
assert async_aql.context == "async"
40+
assert async_col.context == "async"
41+
42+
# On API execution, AsyncJob objects are returned instead of results.
43+
job1 = await async_col.insert({"_key": "Neal"})
44+
job2 = await async_col.insert({"_key": "Lily"})
45+
job3 = await async_aql.execute("RETURN 100000")
46+
job4 = await async_aql.execute("INVALID QUERY") # Fails due to syntax error.
47+
48+
# Retrieve the status of each async job.
49+
for job in [job1, job2, job3, job4]:
50+
# Job status can be "pending" or "done".
51+
assert await job.status() in {"pending", "done"}
52+
53+
# Let's wait until the jobs are finished.
54+
while await job.status() != "done":
55+
time.sleep(0.1)
56+
57+
# Retrieve the results of successful jobs.
58+
metadata = await job1.result()
59+
assert metadata["_id"] == "students/Neal"
60+
61+
metadata = await job2.result()
62+
assert metadata["_id"] == "students/Lily"
63+
64+
cursor = await job3.result()
65+
assert await cursor.next() == 100000
66+
67+
# If a job fails, the exception is propagated up during result retrieval.
68+
try:
69+
result = await job4.result()
70+
except AQLQueryExecuteError as err:
71+
assert err.http_code == HTTP_BAD_PARAMETER
72+
73+
# Cancel a job. Only pending jobs still in queue may be cancelled.
74+
# Since job3 is done, there is nothing to cancel and an exception is raised.
75+
try:
76+
await job3.cancel()
77+
except AsyncJobCancelError as err:
78+
print(err.message)
79+
80+
# Clear the result of a job from ArangoDB server to free up resources.
81+
# Result of job4 was removed from the server automatically upon retrieval,
82+
# so attempt to clear it raises an exception.
83+
try:
84+
await job4.clear()
85+
except AsyncJobClearError as err:
86+
print(err.message)
87+
88+
# List the IDs of the first 100 async jobs completed.
89+
jobs_done = await db.async_jobs(status="done", count=100)
90+
91+
# List the IDs of the first 100 async jobs still pending.
92+
jobs_pending = await db.async_jobs(status='pending', count=100)
93+
94+
# Clear all async jobs still sitting on the server.
95+
await db.clear_async_jobs()
96+
97+
Cursors returned from async API wrappers will no longer send async requests when they fetch more results, but behave
98+
like regular cursors instead. This makes sense, because the point of cursors is iteration, whereas async jobs are meant
99+
for one-shot requests. However, the first result retrieval is still async, and only then the cursor is returned, making
100+
async AQL requests effective for queries with a long execution time.
101+
102+
**Example:**
103+
104+
.. code-block:: python
105+
106+
from arangoasync import ArangoClient
107+
from arangoasync.auth import Auth
108+
109+
# Initialize the client for ArangoDB.
110+
async with ArangoClient(hosts="http://localhost:8529") as client:
111+
auth = Auth(username="root", password="passwd")
112+
113+
# Connect to "test" database as root user.
114+
db = await client.db("test", auth=auth)
115+
116+
# Get the API wrapper for "students" collection.
117+
students = db.collection("students")
118+
119+
# Insert some documents into the collection.
120+
await students.insert_many([{"_key": "Neal"}, {"_key": "Lily"}])
121+
122+
# Begin async execution.
123+
async_db = db.begin_async_execution(return_result=True)
124+
125+
aql = async_db.aql
126+
job = await aql.execute(
127+
f"FOR d IN {students.name} SORT d._key RETURN d",
128+
count=True,
129+
batch_size=1,
130+
ttl=1000,
131+
)
132+
await job.wait()
133+
134+
# Iterate through the cursor.
135+
# Although the request to fetch the cursor is async, its underlying executor is no longer async.
136+
# Next batches will be fetched in real-time.
137+
doc_cnt = 0
138+
cursor = await job.result()
139+
async with cursor as ctx:
140+
async for _ in ctx:
141+
doc_cnt += 1
142+
assert doc_cnt == 2
143+
144+
.. note::
145+
Be mindful of server-side memory capacity when issuing a large number of
146+
async requests in small time interval.
147+
148+
See :class:`arangoasync.database.AsyncDatabase` and :class:`arangoasync.job.AsyncJob` for API specification.

tests/test_async.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,7 @@ async def test_async_cursor(db, doc_col, docs):
126126
)
127127
await job.wait()
128128

129-
# Get the cursor. Bear in mind that its underlying executor is async.
129+
# Get the cursor. Bear in mind that its underlying executor is no longer async.
130130
doc_cnt = 0
131131
cursor = await job.result()
132132
async with cursor as ctx:

0 commit comments

Comments
 (0)