Expose per-attempt connection outcomes with the selected TLS client certificate
Nessuno ha ancora preso questa issue.
Valutazione
- Difficoltà
- 4/5
- Tempo stimato
- 3-5 giorni
- Idoneità per principianti
- 45/100
Direzione di ricerca
The issue is about modifying the StackExchange.Redis client's connection logic to expose per-attempt outcomes. Start by examining the PhysicalBridge class and its OnConnectionFailed method to understand the current suppression logic. Look for where LocalCertificateSelectionCallback is invoked and how to wrap it to capture the selected certificate. The goal is to design and implement a new callback, ConnectionAttemptCompleted, with the proposed EventArgs, ensuring it fires for all physical connection attempts.
Scritto dal modello di indicizzazione a partire dal testo della issue.
Descrizione
Summary
Please expose a supported, structured callback for every physical connection attempt, including:
- initial connection attempts;
- successful reconnects;
- failed reconnects that do not raise the existing
ConnectionFailedevent; - the client certificate selected for that specific attempt.
The callback should be invoked for both success and failure. This would allow certificate integrations to correlate a connection outcome with the exact certificate used.
The existing ConnectionFailed and ConnectionRestored behavior can remain unchanged for compatibility.
Motivation
We maintain a client-certificate management integration for StackExchange.Redis. It selects a client certificate for each TLS handshake and reports whether that certificate succeeded or failed, allowing fallback to a previously known-good certificate.
In short, I want to provide a client certificate and know if that certificate works.
Two limitations in the current public API prevent reliable integration.
1. Reconnect failures can be suppressed
In StackExchange.Redis, PhysicalBridge.OnConnectionFailed raises ConnectionFailed only while reportNextFailure is true.
After the first failure, the flag remains false until that bridge establishes a connection successfully. Subsequent failed reconnect attempts are therefore not reported through ConnectionFailed.
This creates the following sequence:
- Redis is connected successfully.
- The established connection closes with
SocketClosed. ConnectionFailedis raised, consuming the bridge's notification.- A newly rotated client certificate is selected for reconnect.
- The Redis server rejects that certificate.
- The reconnect fails, but no new
ConnectionFailedevent is raised because connectivity was never restored. - The integration cannot report the rejected certificate or activate its known-good fallback.
- Subsequent reconnects continue using the rejected certificate.
A custom failure classifier cannot solve this because it is never invoked when the event itself is suppressed.
2. The selected certificate cannot be correlated with the attempt
LocalCertificateSelectionCallback reports which certificate was selected, while ConnectionFailed and ConnectionRestored report outcomes independently.
The connection outcome does not identify the selected certificate. Integrations must therefore maintain a queue of selected certificates and assume connection outcomes arrive in the same order.
That assumption can fail when interactive and subscription bridges reconnect concurrently or when failures are suppressed.
For example:
- Certificate A is selected.
- The attempt fails because Redis AUTH credentials are invalid.
- Certificate A must be removed from the outstanding-attempt queue without being marked unhealthy.
- Multiple bridges and reconnect attempts can interleave selections and outcomes.
- Certificate B is later selected and rejected.
- Without an outcome carrying the selected certificate, the rejection can be incorrectly attributed to A.
This can produce incorrect fallback decisions, incorrect telemetry, and retained certificate references.
Correlation versus causation
Including the selected certificate would provide deterministic correlation:
Certificate B was selected for the connection attempt that produced exception X.
It would not by itself prove causation:
Certificate B caused exception X.
The same attempt may fail because of:
- client-certificate rejection;
- server-certificate validation;
- Redis authentication;
- socket connection failure;
- TLS protocol negotiation;
- Redis handshake failure.
Including the stage at which the attempt failed would allow integrations to classify the failure without assuming every TLS exception was caused by the client certificate.
Proposed API
Add a configuration-time callback that is invoked for every completed physical connection attempt:
public Action<ConnectionAttemptCompletedEventArgs>?
ConnectionAttemptCompleted { get; set; }
A configuration-time callback is preferable to an event that can only be attached after Connect returns because it also observes initial connection attempts.
Suggested event arguments:
public sealed class ConnectionAttemptCompletedEventArgs : EventArgs
{
public EndPoint EndPoint { get; }
public ConnectionType ConnectionType { get; }
public string? PhysicalName { get; }
public X509Certificate? ClientCertificate { get; }
public bool Succeeded { get; }
public ConnectionFailureType? FailureType { get; }
public Exception? Exception { get; }
public bool? ServerCertificateAccepted { get; }
public SslPolicyErrors? ServerCertificatePolicyErrors { get; }
}
Capturing the selected certificate
StackExchange.Redis would need to wrap the configured local-certificate selection callback and record the returned certificate on the current physical connection attempt.
This should cover certificate selection configured through both:
ConfigurationOptions.CertificateSelection;SslClientAuthenticationOptions.LocalCertificateSelectionCallback.
The captured certificate would then be included in the completion callback for the same attempt.
Optional failure stage information
If practical, the result could also expose the stage at which the attempt failed:
public ConnectionAttemptStage Stage { get; }
public enum ConnectionAttemptStage
{
SocketConnect,
TlsAuthentication,
RedisAuthentication,
RedisHandshake,
Established,
}
Example usage
configuration.ConnectionAttemptCompleted = args =>
{
if (args.ClientCertificateThumbprint is null)
{
return;
}
if (args.Succeeded)
{
// Log that the connection was successful, mark the client certificate as the LKG.
return;
}
if (IsClientCertificateRejection(args))
{
// Log that the certificate is not valid, trigger a fallback to LKG if available.
}
};
Existing alternatives
Currently we utilize a custom ILoggerFactory to detect these errors as it currently receives errors for individual physical failures before ConnectionFailed suppression. However, logging is not an appropriate application-control-flow contract, and correlation with the certificate and stage of authentication (to distinguish between TLS server and client certificate failures) is entirely missing.
Compatibility
The existing ConnectionFailed and ConnectionRestored events should retain their current suppression and notification behavior.
The proposed callback is additive and intended for integrations that require per-attempt outcomes.
- Lingua principale
- C#
- Stelle
- 6.2k
- Fork
- 1.6k
- Merge medio
- 1g 15h
- PR unite (30g)
- 43
Guida per i contributori
Apri la guida per i contributori
Come iniziare
- Leggi tutta la issue e poi la guida ai contributi del progetto.
- Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
- Fai un fork del repository e lavora su un branch.
- Apri una pull request che faccia riferimento al numero della issue.
Altre issue di StackExchange/StackExchange.Redis
-
Difficoltà 1/5 Meno di un'ora Idoneità per principianti 78/100
StackExchange/StackExchange.Redis#3249 ·
-
HotKeysClusterTests.CanUseClusterFilter skips intermittently, hiding the assertions it exists for Aperta
Difficoltà 2/5 1-3 ore Idoneità per principianti 76/100
StackExchange/StackExchange.Redis#3239 · 1 commento ·
-
Difficoltà 4/5 3-5 giorni Idoneità per principianti 45/100
StackExchange/StackExchange.Redis#3240 ·
-
Difficoltà 5/5 Più di una settimana Idoneità per principianti 35/100
StackExchange/StackExchange.Redis#3233 · 1 commento ·
-
⚙️ area:failover
StackExchange/StackExchange.Redis#3147 · 1 assegnatario ·
Tutte le issue di StackExchange/StackExchange.Redis
Issue simili
-
core dependencies
Difficoltà 1/5 Meno di un'ora Idoneità per principianti 80/100
-
bug frontend good first issue
Difficoltà 2/5 1-3 ore Idoneità per principianti 75/100
-
NavigationViewItemAutomationPeer implements IInvokeProvider but never advertises the Invoke pattern Aperta
Difficoltà 2/5 1-3 ore Idoneità per principianti 75/100
unoplatform/uno#24629 ·
-
agentic-workflows Needs: Triage :mag: State: In-PR
Difficoltà 2/5 1-3 ore Idoneità per principianti 70/100
-
Down / Waiting for removal
Difficoltà 2/5 1-3 ore Idoneità per principianti 70/100