Skip to content

Python SDK: IggyClient exposes no client configuration — no reconnection or auto-login, so session recovery is unreachable (Go/C# already expose it) #3742

Description

@bartoszkobylinski

Description

The Python binding's IggyClient accepts only a connection string, so no client
configuration can be set from Python
— in particular neither reconnection nor auto-login.
On master, foreign/python/src/client.rs:

#[new]
#[pyo3(signature = (conn=None))]
fn new(conn: Option<String>) -> PyResult<Self>

let client = IggyClientBuilder::new()
    .with_tcp()
    .with_server_address(conn.unwrap_or("127.0.0.1:8090".to_string()))
    .build()

The generated stub confirms the surface: the only exported types are AutoCommit,
AutoCommitAfter, AutoCommitWhen, IggyClient, IggyConsumer, PollingStrategy,
ReceiveMessage, SendMessage, StreamDetails, TopicDetails. There is no config or
builder type.

Why this matters — the SDK's own session recovery becomes unreachable from Python

#2880 added Unauthenticated, Disconnected, EmptyResponse and StaleClient to the
reconnect match in send_raw_with_response, so the client reconnects and replays the
stored login
. That recovery is gated on the client having reconnection enabled and an
AutoLogin credential to replay — and #3651 documents the other half:

"login_user / logout_user … never touch the state. The only writer of the session
values is connect()'s auto-login block, so a manual connect() + login_user() flow
stays at Connected"

A Python user has only the manual connect() + login_user() flow available. So the
combination is: no auto-login to replay, and a flow that does not own session state. The
practical result is that a session lost shortly after login is not recovered — it surfaces as
Unauthenticated on the next call, and the application must handle it by hand.

Observed impact

A long-running Python consumer on a restart loop, against iggy-server 0.8.0 over TCP
loopback. login_user() returned successfully; the very next call raised:

File ".../core/iggy_bus.py", line 229, in ensure_topology
    if await self._client.get_stream(self._stream) is None:
RuntimeError: Unauthenticated

Ten seconds later the same startup failed differently, then succeeded on the third attempt
and ran for ~2h48m:

File ".../core/iggy_bus.py", line 206, in connect
    await self._client.login_user(...)
RuntimeError: Disconnected

Both are errors #2880 already classifies as reconnectable — but with no reconnection/
auto-login configurable from Python, they terminate the process instead, leaving restart
supervision (systemd) as the only recovery.

Workaround a Python caller has to hand-roll today

Because login_user() returning is not sufficient evidence of an authenticated session, we
now re-verify it explicitly and re-login on failure — effectively reimplementing auto_login
plus reconnection in Python:

for attempt in range(MAX_ATTEMPTS):
    try:
        await client.connect()
        await client.login_user(username, password)
        await client.get_stream(stream)   # probe: raises Unauthenticated if the session is dead
    except Exception:
        await sleep_with_backoff(attempt)  # then retry the whole connect+login
    else:
        break

Parity — other foreign SDKs already expose this

  • Go: foreign/go/client/tcp/tcp_core.goautoLogin AutoLogin
  • C#: foreign/csharp/Iggy_SDK/Configuration/AutoLoginSettings.cs

Python is the outlier.

Suggested direction

Everything needed already exists on the Rust side; this is about exposing it:

  • core/common/src/types/configuration/tcp_config/tcp_client_config.rsTcpClientConfig
  • .../tcp_client_config_builder.rswith_auto_sign_in(AutoLogin)
  • .../tcp_client_reconnection_config.rsTcpClientReconnectionConfig
  • core/common/src/types/configuration/auth_config/auto_login.rsAutoLogin
  • core/sdk/src/clients/client_builder.rsIggyClientBuilder::with_auto_sign_in

Minimal version: optional keyword arguments on IggyClient(...) for reconnection settings
and auto-login credentials, passed through to IggyClientBuilder. Fuller version: expose a
config/builder object mirroring TcpClientConfigBuilder, which would also give the Python
SDK somewhere to hang the QUIC/HTTP/WebSocket options from #2835.

Happy to open a PR if a maintainer indicates the preferred shape (kwargs vs. exposed builder).

Affected area / component

Python SDK (foreign/python)

Related

  • #3651 — SDK client state conflates transport and session dimensions (explains why the
    manual connect() + login_user() flow does not own session state). Related, not a
    duplicate: that issue is about the state enum being unreliable; this one is about the
    configuration surface being absent in Python.
  • #2880 — added the reconnect-and-replay-login behavior that this config would unlock.
  • #1453with_auto_sign_in for the Rust builders.
  • #2835 — Python SDK transports; a config object would be the natural home for those too.

Versions

Server 0.8.0 (DockerHub apache/iggy:0.8.0), apache-iggy 0.8.0 (PyPI wheel, cp312,
manylinux), Python 3.12.13, TCP on loopback. Python constructor and builder call quoted above
were read on master, so the gap is present there too.

Scope note

This issue is deliberately limited to the missing configuration surface in the Python
binding
. Whether a session can still be lost immediately after a successful login_user()
even with auto-login and reconnection enabled is a separate Rust/server question that we
have not isolated, and it is not claimed here.

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions