Skip to content

Repository files navigation

PooledServiceClientFactory

Custom pool for Dataverse Service Client

Background

Recently, we are facing some challenges from socket depletion while connecting to Dynamics 365 instance through ServiceClient in nuget package Microsoft.PowerPlatform.Dataverse.Client.
After some deep dive, we find out that when you dispose a ServiceClient instance, the underline socket connection is NOT closed. Based on MS knowledge article, that is prepared for connection pool usage.
Then, we also have tried shared (or static) instances, which leads to unsafe thread issues. More specific, we are following MS suggestion that we connection to Dynamics 365 as one Application User (not individual named user), then set up CallID to identify who is actually doing the work. After switch to shared (or static) instances, we found all read / write threads are messed up with unpredictable user access.

Solution

We must find out a way to use ServiceClient as scoped instances, while not dispose instances after each usage, but put back to a pool for future.
Luckily, after some research we find out that .NET core has a built-in BlockingCollection generic class for this kind of purpose.
Therefore, we come up with the idea of this pooled Service Client factory.

Explanation of Codes

Most codes are self-explanatory, just a few sentences to outline the main idea here.
The public interface is prepare for CDI whether for a .NET Core website or an Azure Function.
The main class should be initialized with a valid Dynamics 365 connection string, and a capacity that satisfy normal work load.
We assume at the peak time, you might need four times of normal work load, certainly please change that for your situation.
If resource is running low and we are within peak capacity limit. We will increase number or resources in the pool to 50% of the original capacity, for sure you can adjust that based on your need as well.
Every 5 minutes, we will check to see if pool is running at a high resource count, while resource usage is low. If so, we will scale back 50% of the original capacity. On the other end, we will increase the capacity by 50% of the original if resources are running low. Again, you can change that for your own situation; for us, this is NOT a real-time system, so 5 minutes is (far) more than enough.

Delayed Initializer

For Azure Function in particular, we have added delayed initializer for the resource pool.
This will allow fast and simple start host and Azure function with minimial information.
When host started, we will then initialize the heavy services / resources for the resource pool.

Sample usage

In program.cs, start services.

services.AddSingleton<IPooledServiceClientFactory>(new PooledServiceClientFactory(YOUR_CRM_CONNECTION_STRING));
services.AddHostedService<HostStartupHandler>();

Add a class for host service (event)

public class HostStartupHandler : IHostedService
{
    private readonly IPooledServiceClientFactory _pooledServiceClientFactory;

    public HostStartupHandler(IPooledServiceClientFactory pooledServiceClientFactory)
    {
        _pooledServiceClientFactory = pooledServiceClientFactory;
    }

    public Task StartAsync(CancellationToken cancellationToken)
    {
        _pooledServiceClientFactory.Initialize();
        return Task.CompletedTask;
    }

    public Task StopAsync(CancellationToken cancellationToken)
    {
        return Task.CompletedTask;
    }
}

Where-ever you need a scoped instance, insert codes like following, assuming you have done CDI for your class.

ServiceClient client = _clientFactory.Acquire();
try
{
	client.CallerId = YOUR_CALLER_GUID;
	...
}
finally
{
	_clientFactory.Release(client);
}

Conclusion

Follow the pattern as shown above, you can avoid short-lived ServiceClient, and still using them as scoped instance to guarantee thread safe.
Just pull down the source codes into VS2022 and compile them, then you are ready to go.

Further Improvements

  1. Make initial capacity, max capacity and capacity increase / decrease step all configurable.
  2. Make better algorithm for scale up and scale back the resources.

License

Free software, absolutely no warranty, use at your own risk!

About

Custom pool for Dataverse Service Client

Resources

Stars

0 stars

Watchers

1 watching

Forks

Releases

Packages

Used by

Contributors

Languages