- Provide a high throughput connection pool in SqlClient.
- Connection pool should increase the throughput when the connections are being opened successfully and in a consistent time.
- Connection pool should decrease the throughput when the connections start failing or the latency for connection establishment increases.
Async I/O in SqlClient is slow and in some cases buggy. There are many instances of cancellation of commands for Async execution, not working correctly.
There are perf degradations observed with Async APIs, without any load on the client application, when compared to their Sync counterparts.
To make changes to these two fundamentals in SqlClient, it is a lot of re-engineering of components. The current connection pool is designed to allow opening a single connection at a time.
This was a good design choice in the past, where the applications were ready to warm up the connection pool, and have it grow iteratively, instead of allow high connection throughput. However in the day and age of the cloud, where applications need to be spun up, to do the work quite fast, and then be shutdown, warming up the connection pool is considered a problem.
Also warming up the connection pool doesn't resonate with Serverless scenarios, whether it involves SQL server being serverless, or the application being serverless.
For the case for async performance being slow, a lot has to do with the following:
- Async was written on top of Sync, with some flags to toggle behaviors.
- Async had to work with the functionalities or APIs exposed from Native SNI (sni.dll), which cannot be open sourced.
- Async in Managed SNI had to stick to the APIs exposed by sni.dll and hence not written in a way, which could leverage the power of async/await in CLR.
We want to make investments in the improvements of the above two major problem areas in Sql Client.
We want to be able to contribute code to Microsoft.Data.SqlClient, but at the same time, be able to toggle between existing client and the improved client.
The improved client may not be a complete rewrite of the existing client, but change async/await to replace the current I/O. This itself is a lot of work, and will change the guts of the driver.
SqlClient needs to expose a DbDataSource due to the many benefits it can provide. DbDataSource allows configuring the connections and use them for command execution.
Assume that we provide a SqlDbDataSource
which allows for the creation of open connections. At the same time SqlDbDataSource
is can be considered a pool of connections.
A DbDataSource
can be seen as a factory of open functional connections, which can be used to execute commands.
Following the builder patter, which has been demonstrated to be quite powerful in Npgsql driver, for configuring the DbDataSource, SqlClient needs to expose a SqlDbDataSourceBuilder. When the SqlDbDataSourceBuilder is used, it offers the same features that today's SqlClient offers.
We want to propose the introduction of ExperimentalDbDataSourceBuilder
in SqlClient. This will be used to expose connections which are capable of some experimental features.
ExperimentalDbDataSourceBuilder
will provide an instance of SqlDbDataSource
but it would configure it differently with internal flags. As a result the behavior of connections provided by SqlDbDataSource created from ExperimentalDbDataSourceBuilder will have a different behavior from connections created using new SqlConnection
and the connections provided by SqlDbDataSourceBuilder
graph LR
A[ExperimentalDbDataSourceBuilder] -->|provides| B[SqlDbDataSource']
B -->|has different behavior| C{SqlConnection'}
D[SqlDbDataSourceBuilder] -->|provides| E[SqlDbDataSource]
F[new SqlConnection] -->|creates| E
E -->|provides|G
F[new SqlConnection] -->|creates| G[SqlConnection]
A -->|configures differently| B
- SqlConnection' follows a new connection pooling algorithm, which adapts to errors and eases backpressure when needed, but also provides higher throughput when needed.
- SqlConnection' creates commands which can then be used to execute readers, which also allows different implementation to be used under the covers. The driver knows about the difference in implementation since the DbDataSourceBuilder can set various parameters to signal which code path to use.
When we are at 100% feature fidelity with the current API surface and behavior of the driver, the capabilities in SqlConnection and other ADO.Net objects in SqlClient, which are enabled due to the DbDataSourceBuilder
being used to create the connection, we need to make sure that the flags become the defaults in the SqlDataSourceBuilder
.
For the connection pool, we need to decide whether the new implementation becomes the default or opt-in behavior