Skip to content

Conversation

mdaigle
Copy link
Contributor

@mdaigle mdaigle commented Sep 30, 2025

Description

Adds enhanced routing envchange support in service of #3641
Changes can be followed commit-by-commit for ease of review.

Issues

#3641

Testing

Adds simulated server tests to verify enhanced routing behavior.

@mdaigle mdaigle requested a review from a team as a code owner September 30, 2025 22:55
Base automatically changed from dev/mdaigle/enhanced-routing-feature-extension to feat/enhanced-routing October 2, 2025 16:29

case TdsEnums.FEATUREEXT_ENHANCEDROUTINGSUPPORT:
{
SqlClientEventSource.Log.TryAdvancedTraceEvent("<sc.SqlInternalConnectionTds.OnFeatureExtAck|ADV> {0}, Received feature extension acknowledgement for ENHANCEDROUTINGSUPPORT", ObjectID);
Copy link
Contributor

Choose a reason for hiding this comment

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

This log could be misleading if the data[0] != 1. It implies that enhanced routing is enabled. Perhaps move it below line 3024 and include whether or not the feature is enabled as well.

UserServerName = string.Format(CultureInfo.InvariantCulture, "{0},{1}", routing.ServerName, routing.Port);
}

if (routing == null || routing.DatabaseName == null)
Copy link
Contributor

Choose a reason for hiding this comment

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

Would this statement be easier to understand if it were flipped?

// Always prefer a routing database name from the server, if supplied.
if (routing is not null && routing.DatabaseName is not null)
{
    ResolvedDatabaseName = routing.DatabaseName;
}
else
{
    ResolvedDatabaseName = userOptions.InitialCatalog;
}

Copy link
Contributor

Choose a reason for hiding this comment

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

Maybe FeatureExtensionBehaviour ? It would be funny if you add a 4th member later :)

Copy link
Contributor

Choose a reason for hiding this comment

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

Shouldn't this depend on how the server intends to respond? If EnableEnhancedRouting is Disabled or DoNotAcknowledge, then support isn't enabled.

Maybe this flag should be called DidClientAskForEnhancedRouting or something more succinct.


/// <summary>
/// Database name to use at the routed location.
/// Should only be set when doing enhanced routing.
Copy link
Contributor

Choose a reason for hiding this comment

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

"Setting this to a non-empty value will cause the server to include an enhanced routing ENVCHANGE token in the Login Response message. An empty value will include a legacy routing ENVCHANGE token."

// Arrange
using TdsServer server = new(new()
{
Log = Console.Out
Copy link
Contributor

Choose a reason for hiding this comment

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

Our tests should probably be using xUnit's ITestOutputHelper. Just an FYI.

Copy link
Contributor

Choose a reason for hiding this comment

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

More so, I think we should be avoiding outputting random text during a test anyhow. But I think that's more of an existing issue that we're just not touching today.

DataSource = $"localhost,{router.EndPoint.Port}",
Encrypt = false,
ConnectTimeout = 10000
}).ConnectionString;
Copy link
Contributor

Choose a reason for hiding this comment

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

This arrange stuff is common to both tests. Can you pull it out?

Copy link
Contributor

Choose a reason for hiding this comment

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

I'll counter that this doesn't meet the rule of 3 for refactoring :)

{
DataSource = $"localhost,{router.EndPoint.Port}",
Encrypt = false,
ConnectTimeout = 10000
Copy link
Contributor

Choose a reason for hiding this comment

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

Do we need to test the case where InitialCatalog is explicitly specified by the app, and ignored during routing?

What about the cases where the server ignores or explicitly denies enhanced routing?

@paulmedynski paulmedynski self-assigned this Oct 3, 2025
Copy link
Contributor

@benrr101 benrr101 left a comment

Choose a reason for hiding this comment

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

Generally speaking it's pretty good. I have a handful of style comments and a couple general questions. But I don't think they're worth holding up the PR if you're not interested (I'm kinda breaking my own rules by not taking a super hard stance on it, tho)

break;

case TdsEnums.ENV_ENHANCEDROUTING:
SqlClientEventSource.Log.TryAdvancedTraceEvent("<sc.SqlInternalConnectionTds.OnEnvChange|ADV> {0}, Received enhanced routing info", ObjectID);
Copy link
Contributor

Choose a reason for hiding this comment

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

I think the tracing format we're trying to use going forward is the xyz | abc | whatever format. But if the rest of the strings in this file are using the <abc...> asdfasdfads format, then I won't complain.


if (routing == null || routing.DatabaseName == null)
{
ResolvedDatabaseName = userOptions.InitialCatalog;
Copy link
Contributor

Choose a reason for hiding this comment

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

I can has ternary ? 👉👈🥺

if (result != TdsOperationStatus.Done)
{
return result;
ushort newLength;
Copy link
Contributor

Choose a reason for hiding this comment

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

So the whole deal with this big block o changes was you added scope {} to this case? ... If we can avoid doing that that'd be better. But, if you reallly reallllly want to use a new variable in two scopes, I'll let it slide

/// <returns>Returns a <see cref="TdsOperationStatus"/> to indicate the status of the operation.</returns>
private TdsOperationStatus TryProcessEnhancedRoutingToken(TdsParserStateObject stateObj, SqlEnvChange env)
{
ushort newLength;
Copy link
Contributor

Choose a reason for hiding this comment

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

Can we inline the declarations here?

}

/// <summary>
/// Reads an enhanced routing token from the stream and populates the provided <see cref="SqlEnvChange"/> object.
Copy link
Contributor

Choose a reason for hiding this comment

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

Not required, but I think it'd be really nice to have a layout of the token structure in a comment either a remarks or comment in the body, maybe?

DataSource = $"localhost,{router.EndPoint.Port}",
Encrypt = false,
ConnectTimeout = 10000
}).ConnectionString;
Copy link
Contributor

Choose a reason for hiding this comment

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

I'll counter that this doesn't meet the rule of 3 for refactoring :)


// Act
sqlConnection.Open();

Copy link
Contributor

Choose a reason for hiding this comment

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

nit: extra line here and in below instances

}
}

public class SimulatedServerFixture : IDisposable
Copy link
Contributor

Choose a reason for hiding this comment

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

Please put top level classes in their own files


namespace Microsoft.SqlServer.TDS.Servers
{
public enum FeatureExtensionEnablementTriState
Copy link
Contributor

Choose a reason for hiding this comment

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

Can we do this as effectively with a bool? ?

// Create ack data (1 byte: IsEnabled)
byte[] data = new byte[1];

if (EnableEnhancedRouting == FeatureExtensionEnablementTriState.Enabled)
Copy link
Contributor

Choose a reason for hiding this comment

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

Can has ternary again?

Copy link
Contributor

Choose a reason for hiding this comment

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

In fact, can just do

byte[] data = EnableEnhancedRouting is FeatureExtensionEnablementTristate.Enabled
    ? [1]
    : [0];

(and if we switch from tristate to bool? we could condense even more to)

byte[] data = IsEnhancedRoutingEnabled ? [1] : [0]

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants