Skip to content
Open
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
9 changes: 9 additions & 0 deletions CHANGES.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,15 @@
Changes for crate
=================

Unreleased
==========

- Added ``connect(..., probe=False)``, which creates a connection without
contacting the servers. The lowest server version is then resolved when it
is first read, and reading it raises ``ConnectionError`` while no server
responds. Use it when the cluster may not respond yet, for example in tests
and during startup.

2026/09/17 2.3.0
================
- Added ``DefaultTypeConverter`` support that decodes ``DataType.UUID``
Expand Down
7 changes: 7 additions & 0 deletions docs/by-example/connection.rst
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,13 @@ This section sets up a connection object, and inspects some of its attributes.
>>> connection.lowest_server_version.version
(2, 0, 0)

With ``probe=False``, the connection is created without contacting any server.
The lowest server version is then resolved when it is first read::

>>> connection = connect(client=ClientMocked(), probe=False)
>>> connection.lowest_server_version.version
(2, 0, 0)

cursor()
========

Expand Down
30 changes: 28 additions & 2 deletions src/crate/client/connection.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ def __init__(
time_zone=None,
jwt_token=None,
compress: Union[int, bool] = 8192,
probe: bool = True,
):
"""
:param servers:
Expand Down Expand Up @@ -139,6 +140,13 @@ def __init__(
``False`` disables compression entirely.
``True`` compresses every request regardless of size.
An integer compresses only when the payload exceeds that many bytes.
:param probe:
(optional, defaults to ``True``)
Contact the servers while the connection is created, and raise
``ConnectionError`` when none of them responds.
With ``False``, creating the connection performs no request. The
server version is resolved when it is first read, and reading it
raises ``ConnectionError`` while no server responds.
""" # noqa: E501

self._converter = converter
Expand Down Expand Up @@ -168,9 +176,27 @@ def __init__(
jwt_token=jwt_token,
compress=compress,
)
self.lowest_server_version = self._lowest_server_version()
self._version_cache: Union[Version, None] = None
if probe:
self._version_cache = self._lowest_server_version()
self._closed = False

@property
def lowest_server_version(self) -> Version:
"""
The lowest CrateDB version among the servers of this connection.

With ``probe=False``, the servers are contacted on the first read, so
this raises ``ConnectionError`` when none of them responds.
"""
if self._version_cache is None:
self._version_cache = self._lowest_server_version()
return self._version_cache

@lowest_server_version.setter
def lowest_server_version(self, version: Version) -> None:
self._version_cache = version

def cursor(self, **kwargs) -> Cursor:
"""
Return a new Cursor Object using the connection.
Expand Down Expand Up @@ -208,7 +234,7 @@ def get_blob_container(self, container_name):
"""
return BlobContainer(container_name, self)

def _lowest_server_version(self):
def _lowest_server_version(self) -> Version:
lowest = None
servers = self.client.active_servers
connection_errors = []
Expand Down
25 changes: 25 additions & 0 deletions tests/client/test_connection.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,31 @@ def test_lowest_server_version():
assert (1, 0, 3) == connection.lowest_server_version.version


def test_connect_without_probe_does_not_need_a_server():
"""
Verify `probe=False` creates a connection without contacting any server.
"""
client = Client(servers="localhost:1234")
connection = connect(client=client, probe=False)

with pytest.raises(crate.client.exceptions.ConnectionError) as excinfo:
connection.lowest_server_version # noqa: B018
assert excinfo.match("Server not available")


def test_lowest_server_version_resolved_on_first_read():
"""
Verify the server version is resolved when read, not when connecting.
"""
client = Client(servers="localhost:4200")
client.server_infos = lambda server: (None, None, "5.5.2")

connection = connect(client=client, probe=False)
assert connection._version_cache is None
assert (5, 5, 2) == connection.lowest_server_version.version
assert (5, 5, 2) == connection._version_cache.version


def test_connection_closes_access():
"""
Verify that a connection closes on exit and that it also closes
Expand Down
Loading