Skip to content

Add TLS session resumption via SSLSessionCache - #789

Open
sylwiaszunejko wants to merge 5 commits into
scylladb:masterfrom
sylwiaszunejko:tls-ticket
Open

Add TLS session resumption via SSLSessionCache#789
sylwiaszunejko wants to merge 5 commits into
scylladb:masterfrom
sylwiaszunejko:tls-ticket

Conversation

@sylwiaszunejko

@sylwiaszunejko sylwiaszunejko commented Apr 3, 2026

Copy link
Copy Markdown
Collaborator

Summary

This PR implements TLS session resumption for the Python driver. After the first
successful TLS handshake with a node, the negotiated session is stored in a
thread-safe cache and reused on subsequent connections, skipping the full
handshake.

Both TLS 1.2 (session IDs) and TLS 1.3 (session tickets / PSK) are supported.

Changes

cassandra/connection.pySSLSessionCache class & endpoint keys

  • _SessionCacheEntry namedtuple stores (session, timestamp) for TTL tracking.
  • SSLSessionCache: a thread-safe OrderedDict-based cache with LRU eviction,
    TTL expiration, and periodic cleanup (every 100 set() calls), keyed by
    endpoint tls_session_cache_key.
  • Configurable max_size (default 100) and ttl (default 3600 s).
  • Base EndPoint class provides a default tls_session_cache_key property
    returning (address, port). Subclasses override for context-specific keys:
    • DefaultEndPoint: (address, port) — inherits default
    • SniEndPoint: (address, port, server_name) — prevents proxy collisions
    • UnixSocketEndPoint: (unix_socket_path,)
    • ClientRoutesEndPoint: (host_id, address, port)

cassandra/connection.pyConnection wiring

  • Connection gains _ssl_session_cache attribute, set via ssl_session_cache
    kwarg in __init__.
  • _wrap_socket_from_context() restores a cached session via
    ssl_sock.session = ... after wrap_socket(); gracefully handles
    ssl.SSLError / AttributeError if the server rejects the session.
  • _ssl_session_cache_key() helper delegates to
    endpoint.tls_session_cache_key.
  • _cache_tls_session_if_needed() stores socket.session in the cache when
    ssl_context is set and the session is non-None.
  • Sessions are cached at three points to cover both TLS 1.2 and 1.3:
    1. After _initiate_connection() in _connect_socket() — TLS 1.2 sessions
      are available immediately after connect.
    2. After ReadyMessage in _handle_startup_response() — TLS 1.3 tickets
      arrive asynchronously after the first application-data exchange.
    3. After AuthSuccessMessage in _handle_auth_response() — same TLS 1.3
      coverage for authenticated connections.

cassandra/cluster.pyCluster integration

  • Imports SSLSessionCache.
  • Adds ssl_session_cache class attribute with docstring.
  • __init__ accepts ssl_session_cache=_NOT_SET parameter.
  • Auto-creates an SSLSessionCache() when ssl_context or ssl_options are
    set; no configuration required for the common case.
  • Pass ssl_session_cache=None explicitly to opt out.
  • A custom SSLSessionCache(max_size=…, ttl=…) can be supplied.
  • _make_connection_kwargs() passes the cache to every Connection via
    kwargs_dict.setdefault('ssl_session_cache', self.ssl_session_cache).

cassandra/io/eventletreactor.py — Eventlet (PyOpenSSL) support

  • _wrap_socket_from_context() restores cached PyOpenSSL sessions via
    set_session() before the handshake.
  • _initiate_connection() calls _cache_pyopenssl_session() after
    do_handshake().
  • New _cache_pyopenssl_session() helper stores the session via
    get_session(), logs whether the session was reused
    (session_reused()), and catches all exceptions silently.

cassandra/io/twistedreactor.py — Twisted (PyOpenSSL) support

  • _SSLCreator.__init__ accepts an optional ssl_session_cache parameter.
  • clientConnectionForTLS() restores cached sessions via set_session().
  • info_callback() stores sessions after SSL_CB_HANDSHAKE_DONE via
    get_session(), logs reuse status.
  • TwistedConnection.add_connection() passes ssl_session_cache=self._ssl_session_cache
    to _SSLCreator.

Tests

tests/unit/test_connection.py

  • TestSSLSessionCache — empty lookup, set/get, key isolation by
    address/port/SNI, overwrite, thread safety, TTL expiration, LRU eviction,
    max_size enforcement, clear(), clear_expired(), automatic periodic
    cleanup, None session handling.
  • TestEndPointTLSSessionCacheKey — cache key correctness for
    DefaultEndPoint, SniEndPoint, UnixSocketEndPoint,
    ClientRoutesEndPoint, plus isolation between different paths/addresses.
  • TestConnectionSSLSessionRestore — session restore from cache,
    tolerance when cache is None, ssl.SSLError on session setter,
    SNI-specific cached session lookup.
  • TestConnectionCacheTLSSession — session stored after connect,
    no-op when session=None, no-op when cache=None, no-op when
    ssl_context=None, SNI-specific key used for storage.

tests/unit/test_cluster.py

  • TestSSLSessionCacheAutoCreation — auto-create with ssl_context,
    auto-create with ssl_options, no cache without TLS, explicit None
    opt-out, custom cache injection, cache passed to connection_factory.

Fixes: https://scylladb.atlassian.net/browse/DRIVER-165

Pre-review checklist

  • I have split my patch into logically separate commits.
  • All commit messages clearly explain what they change and why.
  • I added relevant tests for new features and bug fixes.
  • All commits compile, pass static checks and pass test.
  • PR description sums up the changes and reasons why they should be introduced.
  • I have provided docstrings for the public items that I want to introduce.
  • I have adjusted the documentation in ./docs/source/.
  • I added appropriate Fixes: annotations to PR description.

@Lorak-mmk

Copy link
Copy Markdown

This reduces reconnection latency and CPU overhead, especially in
deployments with short-lived connections or frequent reconnects.

Such claims would ideally be supported by benchmarks. Could you try to create some?
I very vaguely remember this feature being postponed because the performance gains were underwhelming (but perhaps memory is failing me).

@sylwiaszunejko

Copy link
Copy Markdown
Collaborator Author

This reduces reconnection latency and CPU overhead, especially in
deployments with short-lived connections or frequent reconnects.

Such claims would ideally be supported by benchmarks. Could you try to create some? I very vaguely remember this feature being postponed because the performance gains were underwhelming (but perhaps memory is failing me).

That's the goal, but you're right, I don't have any tests to prove that, removed this claim from the PR description. If I manage to create proper benchmarks I will update on that

@mykaul

mykaul commented Apr 3, 2026

Copy link
Copy Markdown

We could, if it helps, only support this for TLS 1.3.

@sylwiaszunejko

Copy link
Copy Markdown
Collaborator Author

@dkropachev @Lorak-mmk I pushed changes with improvement from older Dmitry's PR, will update PR description soon

@dkropachev dkropachev left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I rechecked the TLS session-resumption path against the current branch. The ssl_options configuration still builds a fresh SSLContext per Connection, and a cached stdlib session from the previous connection is incompatible with that new context. I reproduced the failure locally on Python 3.10.12; the session restore path raises ValueError: Session refers to a different SSLContext. Since the new code only catches AttributeError and ssl.SSLError, reconnects fail instead of falling back to a full handshake, and the regression is enabled by default because Cluster auto-creates SSLSessionCache for ssl_options.

Comment thread cassandra/connection.py Outdated

@dkropachev dkropachev left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Two blocking issues from local validation:

  1. Twisted caches a TLS session even after hostname verification has already failed, which lets an untrusted peer populate the resumption cache.
  2. SSLSessionCache accepts max_size <= 0 and then crashes on the first insert (KeyError from popitem() on an empty OrderedDict).

Comment thread cassandra/io/twistedreactor.py Outdated
transport = connection.get_app_data()
transport.failVerification(Failure(ConnectionException("Hostname verification failed", self.endpoint)))
# Store TLS session after successful handshake (PyOpenSSL)
if self.ssl_session_cache is not None:

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

failVerification() should short-circuit this callback. As written, a hostname mismatch still falls through and caches the just-negotiated session, so an untrusted peer can seed the resumption cache. I reproduced this locally with a mocked _SSLCreator: failVerification was called and the session still landed in SSLSessionCache.

Comment thread cassandra/connection.py Outdated
self._sessions.move_to_end(key)
return

if len(self._sessions) >= self._max_size:

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SSLSessionCache(max_size=0) currently crashes on the first insert: len(self._sessions) >= self._max_size is already true for an empty cache, so popitem(last=False) raises KeyError. Since this is now a public tuning knob, please validate max_size > 0 (and probably ttl > 0) or define zero as a disabled cache, and cover it with a unit test.

@dkropachev dkropachev left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Two correctness issues need attention before this lands: the PyOpenSSL TLS 1.3 cache point is too early to capture the resumable session, and the cache can evict a live entry while expired ones remain resident.

Comment thread cassandra/io/twistedreactor.py Outdated
Comment thread cassandra/connection.py Outdated
Comment thread cassandra/io/twistedreactor.py Outdated
Comment thread tests/integration/standard/test_tls_resumption.py
Comment thread cassandra/cluster.py Outdated
Comment thread tests/integration/standard/test_tls_resumption.py
@coderabbitai

coderabbitai Bot commented Jul 15, 2026

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds a thread-safe LRU/TTL SSLSessionCache, endpoint-specific cache keys, and optional Cluster configuration with automatic cache creation. Connections and Eventlet/Twisted TLS reactors restore cached sessions before handshakes and store negotiated sessions afterward. Unit and integration tests cover cache behavior, endpoint identity, cluster wiring, reactor verification, and reconnection-based TLS ticket reuse.

Sequence Diagram(s)

sequenceDiagram
  participant Cluster
  participant Connection
  participant SSLSessionCache
  participant TLSReactor
  Cluster->>Connection: create with resolved ssl_session_cache
  Connection->>SSLSessionCache: retrieve endpoint session
  SSLSessionCache-->>Connection: cached session
  Connection->>TLSReactor: restore session before handshake
  TLSReactor-->>Connection: complete TLS handshake
  Connection->>SSLSessionCache: store negotiated session
Loading

Suggested reviewers: lorak-mmk, dkropachev, mykaul

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 42.67% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly names the main change: adding TLS session resumption via SSLSessionCache.
Description check ✅ Passed The PR description covers the required summary, changes, tests, and Fixes link; only the docstring/docs checklist items are left unchecked.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai
coderabbitai Bot requested a review from dkropachev July 15, 2026 08:11

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@tests/integration/standard/test_tls_resumption.py`:
- Around line 176-193: Update _server_supports_tls_resumption to replace the
blanket except Exception with an exception tuple containing only the expected
socket and SSL connection/handshake failure types, while preserving the existing
return False behavior for those failures and allowing unexpected programming
errors to propagate.
- Around line 259-286: Register class cleanup in setUpClass before stopping the
shared CCM cluster, ensuring failure during configuration or startup restores
the cluster state. Update the cleanup to restart or remove the CCM cluster as
appropriate, while preserving the existing stop, configuration, and start
sequence.
- Line 41: Guard the module-level OpenSSL import in the TLS resumption test so
missing pyOpenSSL does not fail test collection. Set an unavailable marker for
SSL and skip the Twisted/Eventlet execution path when SSL cannot be imported,
while preserving the existing behavior when the import succeeds.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 3267e3fb-7cc3-4650-a701-56702bea5c6a

📥 Commits

Reviewing files that changed from the base of the PR and between f2faf64 and 7f65023.

📒 Files selected for processing (1)
  • tests/integration/standard/test_tls_resumption.py

Comment thread tests/integration/standard/test_tls_resumption.py Outdated
Comment thread tests/integration/standard/test_tls_resumption.py
Comment thread tests/integration/standard/test_tls_resumption.py
Copilot AI review requested due to automatic review settings July 22, 2026 13:43

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 9 out of 9 changed files in this pull request and generated 6 comments.

Comment thread cassandra/connection.py Outdated
Comment thread cassandra/io/eventletreactor.py
Comment thread cassandra/io/twistedreactor.py Outdated
Comment thread cassandra/io/twistedreactor.py Outdated
Comment thread tests/integration/standard/test_tls_resumption.py Outdated
Comment thread tests/integration/standard/test_tls_resumption.py Outdated
Copilot AI review requested due to automatic review settings July 23, 2026 10:18

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 9 out of 9 changed files in this pull request and generated 4 comments.

Comments suppressed due to low confidence (2)

cassandra/connection.py:1881

  • TLS 1.3 does not guarantee that NewSessionTicket arrives before ReadyMessage/AuthSuccessMessage. This is the final cache attempt, so if has_ticket is still false here, later TLS reads can process the ticket but never retry caching it, silently disabling resumption for servers that delay tickets. Retry opportunistically after subsequent TLS reads until a ticketed session is stored, or hook post-handshake ticket processing.
            self._cache_tls_session_if_needed()

cassandra/cluster.py:1574

  • The PR description says a cache is auto-created when either ssl_context or ssl_options is set (and lists an ssl_options auto-creation test), but this implementation intentionally excludes the legacy ssl_options path. Please align the advertised support and test summary with the actual behavior so users do not expect legacy TLS resumption.
        if ssl_session_cache is _NOT_SET:
            if ssl_context is not None and self._connection_class_supports_tls_resumption():
                self.ssl_session_cache = SSLSessionCache()
            else:
                self.ssl_session_cache = None

Comment thread cassandra/io/twistedreactor.py Outdated
Comment thread tests/integration/standard/test_tls_resumption.py Outdated
Comment thread tests/integration/standard/test_tls_resumption.py Outdated
Comment thread tests/integration/standard/test_tls_resumption.py
Copilot AI review requested due to automatic review settings July 23, 2026 11:24

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 9 out of 9 changed files in this pull request and generated 1 comment.

Comments suppressed due to low confidence (1)

cassandra/cluster.py:1565

  • The PR description promises automatic cache creation when either ssl_context or ssl_options is set, but this branch deliberately disables it for ssl_options-only clusters (and the new unit test asserts None). Please either implement the documented ssl_options behavior or update the PR description so users and reviewers are not given a contradictory API contract.
        # Auto-create a session cache when TLS is enabled via ssl_context,
        # unless the caller explicitly passed ssl_session_cache (including None
        # to opt out).  The legacy ssl_options-only path is excluded because it
        # builds a fresh SSLContext per connection, making session reuse

Comment on lines +239 to +244
# info_callback reads the cache from the connection (not the creator);
# mirror clientConnectionForTLS() here.
connection._cassandra_ssl_session_cache = ssl_session_cache
transport = Mock()
connection.get_app_data.return_value = transport
connection._cassandra_endpoint = endpoint
Copilot AI review requested due to automatic review settings July 23, 2026 12:28

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 9 out of 9 changed files in this pull request and generated 1 comment.

Comments suppressed due to low confidence (1)

cassandra/cluster.py:1571

  • The PR summary says automatic cache creation applies when either ssl_context or ssl_options is set, but this implementation (and its new test) intentionally excludes the legacy ssl_options path. Please update the PR description to state that automatic resumption is ssl_context-only, or implement the promised legacy behavior, so the documented activation semantics are unambiguous.
            if ssl_context is not None and self._connection_class_supports_tls_resumption():

Comment thread cassandra/connection.py
Copilot AI review requested due to automatic review settings July 23, 2026 13:47

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 9 out of 9 changed files in this pull request and generated 1 comment.

Comments suppressed due to low confidence (1)

cassandra/cluster.py:1574

  • The PR description says a cache is auto-created when either ssl_context or ssl_options is set, but this branch intentionally creates one only for ssl_context; the added test and public docstring likewise state that legacy ssl_options is unsupported. Please update the PR description to match the implemented contract (or implement the promised legacy behavior).
        if ssl_session_cache is _NOT_SET:
            if ssl_context is not None and self._connection_class_supports_tls_resumption():
                self.ssl_session_cache = SSLSessionCache()
            else:
                self.ssl_session_cache = None

Comment thread cassandra/connection.py Outdated
Comment on lines +912 to +915
if key in self._sessions:
self._sessions[key] = _SessionCacheEntry(session, current_time)
self._sessions.move_to_end(key)
return
Introduce SSLSessionCache in connection.py: a thread-safe OrderedDict-based
cache with LRU eviction (max_size, default 100) and TTL expiration (default
3600s), keyed by endpoint tls_session_cache_key.

Add tls_session_cache_key property to all EndPoint subclasses:
  - DefaultEndPoint: (address, port)
  - SniEndPoint: (address, port, server_name) — prevents proxy collisions
  - UnixSocketEndPoint: (unix_socket_path,)
  - ClientRoutesEndPoint: (host_id, address, port)

Includes unit tests for basic ops, key isolation, SNI keys, overwrite,
thread safety, TTL expiration, LRU eviction, clear/clear_expired,
automatic cleanup, custom parameters, and endpoint cache key tests.
- Add _ssl_session_cache attribute on Connection, set via ssl_session_cache param
- Restore cached TLS sessions in _wrap_socket_from_context with error tolerance
- Add _cache_tls_session_if_needed helper (delegates to endpoint.tls_session_cache_key)
- Cache sessions at 3 points: after connect, ReadyMessage, AuthSuccessMessage
  (handles TLS 1.3 async ticket delivery)
- Add TestConnectionSSLSessionRestore and TestConnectionCacheTLSSession tests
- Import SSLSessionCache in cluster.py
- Add ssl_session_cache attribute with comprehensive docstring
- Add ssl_session_cache parameter to Cluster.__init__ (default _NOT_SET)
- Auto-create SSLSessionCache when ssl_context or ssl_options are set
- Pass ssl_session_cache to connection factory via _make_connection_kwargs
- Add TestSSLSessionCacheAutoCreation tests (6 tests)
- EventletConnection: restore cached session before handshake via set_session()
- TwistedConnection: pass ssl_session_cache to _SSLCreator, restore cached
  session in clientConnectionForTLS()
- Both reactors: defer session storage to _cache_tls_session_if_needed() override
  called at ReadyMessage / AuthSuccessMessage time, ensuring TLS 1.3 session
  tickets (which arrive after the first application-data exchange) are captured
- Skip caching when session_reused() is True (abbreviated handshake)
- All operations wrapped in try/except for error tolerance
- Debug logging for session reuse and restore/store failures
Tests TLS ticket resumption end-to-end using a dynamically generated
CA + server certificate pair. The test spins up a single-node CCM
cluster configured for TLS, opens multiple connections, and verifies
that subsequent connections reuse the TLS session rather than
performing a full handshake.

Skips automatically when the Scylla CCM node does not support
server-side TLS session resumption (i.e. does not echo the session
ticket back on reconnect).
Copilot AI review requested due to automatic review settings July 23, 2026 15:44

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 9 out of 9 changed files in this pull request and generated 4 comments.

Comments suppressed due to low confidence (3)

cassandra/io/eventletreactor.py:190

  • get_session() can still return a non-resumable TLS 1.3 session here—the docstring already notes that non-None does not prove a NewSessionTicket arrived. Ready/AuthSuccess is not a protocol timing guarantee, and setting _tls_session_cached permanently suppresses refresh when a later ticket arrives. Keep refreshing TLS 1.3 sessions on later reads or otherwise detect ticket arrival before setting the guard.
            session = self._socket.get_session()
            if session is not None:
                self._ssl_session_cache.set(
                    self._ssl_session_cache_key(), session)
                self._tls_session_cached = True

cassandra/io/twistedreactor.py:370

  • A non-None pyOpenSSL session does not establish that the TLS 1.3 NewSessionTicket has arrived. If it arrives after Ready/AuthSuccess, this caches a ticketless session and sets both guards, preventing any later refresh. Keep refreshing TLS 1.3 sessions after later reads or otherwise wait for post-handshake ticket processing before setting the guards.
            session = ssl_conn.get_session()
            if session is not None:
                self._ssl_session_cache.set(
                    self.endpoint.tls_session_cache_key, session)
                self._tls_session_cached = True

cassandra/cluster.py:1572

  • The PR description says a cache is auto-created for ssl_context or ssl_options, but this condition—and the new unit test—explicitly excludes the ssl_options-only path. The code rationale about per-connection contexts makes the exclusion intentional, so update the PR description to avoid promising unsupported behavior.
        if ssl_session_cache is _NOT_SET:
            if ssl_context is not None and self._connection_class_supports_tls_resumption():
                self.ssl_session_cache = SSLSessionCache()

Comment thread cassandra/connection.py
Comment on lines +1350 to +1351
if tls_version == 'TLSv1.3' and (
initial or not getattr(session, 'has_ticket', False)):
session = object()
with patch('cassandra.connection.time.monotonic', return_value=1000.0):
cache.set(self._key('127.0.0.1', 9042), session)
assert cache.get(self._key('127.0.0.1', 9042)) is session
Comment thread cassandra/cluster.py
kwargs_dict.setdefault('sockopts', self.sockopts)
kwargs_dict.setdefault('ssl_options', self.ssl_options)
kwargs_dict.setdefault('ssl_context', self.ssl_context)
kwargs_dict.setdefault('ssl_session_cache', self.ssl_session_cache)
Comment thread cassandra/connection.py
Returns the cache key for Client Routes endpoints.
Uses host_id and original address for uniqueness.
"""
return (str(self._host_id), self._original_address, self._original_port)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants