Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 11 additions & 9 deletions coordinode/coordinode/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -2011,9 +2011,10 @@ async def create_constraint(
returned with its running build, so a retry after a lost reply
does not start a second build.
wait: Seconds to wait for the build of the owned index. ``None``:
the server's default. ``0``: return once the constraint and its
build are admitted. The call's deadline is extended by it, so
the deadline does not expire while the server is still waiting.
as long as the client's timeout. ``0``: return once the
constraint and its build are admitted. The call's deadline is
extended by it, so the deadline does not expire while the
server is still waiting.
"""
from coordinode._proto.coordinode.v2.graph.schema_pb2 import ( # type: ignore[import]
ConstraintKind,
Expand Down Expand Up @@ -2043,12 +2044,13 @@ async def create_constraint(
)
if property_type is not None:
req.property_type.CopyFrom(self._property_type_proto(property_type, "the constraint"))
timeout = self._timeout
if wait is not None:
# A zero wait is sent too: absent takes the server's default.
req.wait.FromTimedelta(datetime.timedelta(seconds=wait))
timeout += wait
created = await self._schema_stub.CreateConstraint(req, timeout=timeout)
# Always sent: an absent wait takes the server's default, which this
# client cannot see and may exceed its deadline. Without one named,
# the server waits as long as this client's timeout.
if wait is None:
wait = self._timeout
req.wait.FromTimedelta(datetime.timedelta(seconds=wait))
created = await self._schema_stub.CreateConstraint(req, timeout=self._timeout + wait)
return ConstraintInfo(created)

async def drop_constraint(self, name: str, *, if_exists: bool = False) -> None:
Expand Down
19 changes: 17 additions & 2 deletions tests/unit/test_schema_crud.py
Original file line number Diff line number Diff line change
Expand Up @@ -411,8 +411,23 @@ async def _inner() -> None:
assert (sent.name, sent.label, list(sent.properties)) == ("l_key", "L", ["a", "b"])
assert sent.kind == schema_pb2.CONSTRAINT_KIND_NODE_KEY and sent.if_not_exists
assert not sent.HasField("property_type")
# No wait named: the server's own default applies, so none is sent.
assert not sent.HasField("wait")

asyncio.run(_inner())

def test_default_wait_ends_before_the_deadline(self):
# With no wait named, the server's own default applies (60 s on
# 0.7.2, operator-tunable), longer than the client's 30 s timeout: a
# build between the two ended the call with DEADLINE_EXCEEDED while
# the server went on to create the constraint. The client's timeout
# is sent as the wait instead, and the deadline covers it.
async def _inner() -> None:
client = self._client()
await client.create_constraint("L", "email", "unique")
call = client._schema_stub.CreateConstraint.call_args
sent = call.args[0]
assert sent.HasField("wait")
assert sent.wait.ToTimedelta().total_seconds() == pytest.approx(client._timeout)
assert call.kwargs["timeout"] > client._timeout

asyncio.run(_inner())

Expand Down
Loading