Thread safety when different processes access same resources - c#

I am working with windows services to get some data from an API
service#1 gets data from "http://api.provider.com/Entity1"
service#2 gets data from "http://api.provider.com/Entity2"
and I have both these services in one .csproj and I use a singleton httpClient to retrieve data from API:
public sealed class Client : HttpClient{
private static readonly object padlock = new object();
private static Client instance = null;
public static Client Instance
{
get
{
if (instance == null)
{
lock (padlock)
{
if (instance == null)
{
instance = new Client();
}
}
}
return instance;
}
}
private Client()
{
DefaultRequestHeaders.Accept.Clear();
DefaultRequestHeaders.Accept.Add(MediaTypeWithQualityHeaderValue.Parse("application/json"));
DefaultRequestHeaders.Add("...", "...");
}
public async Task<string> Get(string url)
{
var result = await GetStringAsync(url);
return result;
}}
but these processes are parallel so the singleton class is a shared static class between the two.And Then I have this class as the consumer fo the first:
class APIHAndler{
public List<obj1> f1()
{
var jsonResult = Client.Instance.Get(url1).Result;
//make list of obj1 out of json
}
public List<obj2> f2()
{
var jsonResult = Client.Instance.Get(url2).Result;
//make list of obj2 out of json
}
}
What I did is to create one instance of the class APIHAndler in each of my services and call f1 and f2 based on the ongoing business and I get this error:
Response status code does not indicate success: 429 (Too Many Requests).
I think it is probably due to the fact of having two different connections open at the same time. but I don't know how to avoid this. If you can help me fix this or have a better solution I will be very happy to hear about.

I don't know how to avoid this
The service you're calling should have documentation that explains what causes a 429. Sometimes it's only a single request at a time; more often it's a certain number of requests within a time window. Either way, you'll need to throttle your requests, and you can build throttling with a SemaphoreSlim.

Related

Publish/Subcribe Notification Service Over gRPC Using protobuf-net.grpc

I'm trying to write a notification system between a server and multiple clients using gRPC server streaming in protobuf-net.grpc (.NET Framework 4.8).
I based my service off of this example. However, if I understand the example correctly, it is only able to handle a single subscriber (as _subscriber is a member variable of the StockTickerService class).
My test service looks like this:
private readonly INotificationService _notificationService;
private readonly Channel<Notification> _channel;
public ClientNotificationService(INotificationService notificationService)
{
_notificationService = notificationService;
_notificationService.OnNotification += OnNotification;
_channel = Channel.CreateUnbounded<Notification>();
}
private async void OnNotification(object sender, Notification notification)
{
await _channel.Writer.WriteAsync(notification);
}
public IAsyncEnumerable<Notification> SubscribeAsync(CallContext context = default)
{
return _channel.AsAsyncEnumerable(context.CancellationToken);
}
INotificationService just has an event OnNotification, which is fired when calling its Notify method.
I then realized that System.Threading.Channels implements the Producer/Consumer pattern, but I need the Publisher/Subscriber pattern. When trying it out, indeed only one of the clients gets notified, instead of all of them.
It would also be nice if the server knew when a client disconnects, which seems impossible when returning _channel.AsAsyncEnumerable.
So how can I modify this in order to
serve multiple clients, with all of them being notified when OnNotification is called
and log when a client disconnects?
For 1, you'd need an implementation of a publisher/subscriber API; each call to SubscribeAsync will always represent a single conversation between gRPC endpoints, so you'll need your own mechanism for broadcasting that to multiple consumers. Maybe RX is worth investigating there
For 2, context.CancellationToken should be triggered by client-disconnect
Many thanks to Marc Gravell
I rewrote the NotificationService like this, using System.Reactive.Subjects (shortened) - no need for an event, use an Action instead:
public class NotificationService<T>
{
private readonly Subject<T> _stream = new Subject<T>();
public void Publish(T notification)
{
_stream.OnNext(notification);
}
public IDisposable Subscribe(Action<T> onNext)
return _stream.Subscribe(onNext);
}
}
My updated ClientNotificationService, which is exposed as a code-first gRPC service:
public class ClientNotificationService : IClientNotificationService
{
private readonly INotificationService<Notification> _notificationService;
public ClientNotificationService(INotificationService<Notification> notificationService)
{
_notificationService = notificationService;
}
public async IAsyncEnumerable<Notification> SubscribeAsync(CallContext context = default)
{
try
{
Channel<Notification> channel = Channel.CreateUnbounded<Notification>(
new UnboundedChannelOptions { SingleReader = true, SingleWriter = true });
CancellationTokenSource cts = CancellationTokenSource.CreateLinkedTokenSource(context.CancellationToken);
using (_notificationService.Subscribe(n => channel.Writer.WriteAsync(n, cts.Token)))
{
await foreach (Notification notification in channel.AsAsyncEnumerable(cts.Token))
{
yield return notification;
}
}
}
finally
{
// canceled -> log, cleanup, whatever
}
}
}
Note: Solution provided by OP on question section.

.NET client-side WCF with queued requests

Background
I'm working on updating legacy software library. The legacy code uses an infinitely looping System.Threading.Thread that executes processes in the queue. These processes perform multiple requests with another legacy system that can only process one request at a time.
I'm trying to modernize, but I'm new to WCF services and there may be a big hole in my knowledge that'd simplify things.
WCF Client-Side Host
In modernizing, I'm trying to move to a client-side WCF service. The WCF service allows requests to be queued from multiple a applications. The service takes a request and returns a GUID back so that I can properly associate via the callbacks.
public class SomeService : ISomeService
{
public Guid AddToQueue(Request request)
{
// Code to add the request to a queue, return a Guid, etc.
}
}
public interface ISomeCallback
{
void NotifyExecuting(Guid guid)
void NotifyComplete(Guid guid)
void NotifyFault(Guid guid, byte[] data)
}
WCF Client Process Queues
The problem I'm having is that the legacy processes can include more than one request. Process 1 might do Request X then Request Y, and based on those results follow up with Request Z. With the legacy system, there might be Processes 1-10 queued up.
I have a cludgy model where the process is executed. I'm handling events on the process to know when it's finished or fails. But, it just feels really cludgy...
public class ActionsQueue
{
public IList<Action> PendingActions { get; private set; }
public Action CurrentAction { get; private set; }
public void Add(Action action)
{
PendingAction.Add(action)
if (CurrentAction is null)
ExecuteNextAction();
}
private void ExecuteNextAction()
{
if (PendingActions.Count > 0)
{
CurrentAction = PendingActions[0];
PendingActions.RemoveAt(0);
CurrentAction.Completed += OnActionCompleted;
CurrentAction.Execute();
}
}
private OnActionCompleted(object sender, EventArgs e)
{
CurrentAction = default;
ExecuteNextAction();
}
}
public class Action
{
internal void Execute()
{
// Instantiate the first request
// Add handlers to the first request
// Send it to the service
}
internal void OnRequestXComplete()
{
// Use the data that's come back from the request
// Proceed with future requests
}
}
With the client-side callback the GUID is matched up to the original request, and it raises a related event on the original requests. Again, the implementation here feels really cludgy.
I've seen example of Async methods for the host, having a Task returned, and then using an await on the Task. But, I've also seen recommendations not to do this.
Any recommendations on how to untangle this mess into something more usable are appreciated. Again, it's possible that there's a hole in my knowledge here that's keeping me from a better solutiong.
Thanks
Queued communication between the client and the server of WCF is usually possible using a NetMsmqbinding, which ensures persistent communication between the client and the server. See this article for specific examples.
If you need efficient and fast message processing, use a non-transactional queue and set the ExactlyOnce attribute to False, but this has a security impact. Check this docs for further info.
In case anyone comes along later with a similar issue, this is a rough sketch of what I ended up with:
[ServiceContract(Name="MyService", SessionMode=Session.Required]
public interface IMyServiceContract
{
[OperationContract()]
Task<string> ExecuteRequestAsync(Action action);
}
public class MyService: IMyServiceContract
{
private TaskQueue queue = new TaskQueue();
public async Task<string> ExecuteRequestAsync(Request request)
{
return await queue.Enqueue(() => request.Execute());
}
}
public class TaskQueue
{
private SemaphoreSlim semaphore;
public TaskQueue()
{
semaphore = new SemaphoreSlim(1);
}
Task<T> Enqueue<T>(Func<T> function)
{
await semaphore.WaitAsync();
try
{
return await Task.Factory.StartNew(() => function.invoke();)
}
finally
{
semaphore.Release();
}
}
}

How to ensure thread safe usage when returning an AmazonS3Client instance from a singleton in .net core

I am trying to abstract away the create of an AmazonS3Client. For context this is done using this mechanism rather than the standard DI mechanism as the credentials needed for the client are only known at runtime after the credentials have been retrieved to assume a role. These credentials have a temporary access token.
I have created the following factory, which aims to return the same client to the caller until the credentials used within the client are invalid. After this I dispose of the client and instantiate a new one.
I want to ensure this is thread safe. As in caller one could have got the client and was about to use it. At the same time caller two tries to get a client, which was then deemed to have invalid credentials. The logic below then tries to dispose of the client. So I am concerned on the impact on caller one.
To add the following code belongs in a singleton service within .net core.
public class AmazonS3ClientFactory : IAmazonS3ClientFactory
{
private readonly IAmazonAssumeRoleService assumeRoleService;
private readonly Config config;
public AmazonS3ClientFactory(
IOptions<Config> config,
IAmazonAssumeRoleService assumeRoleService)
{
if (string.IsNullOrWhiteSpace(config?.Value.AssumedRoleArn))
{
throw new ArgumentNullException(nameof(config));
}
this.config = config?.Value;
this.assumeRoleService = assumeRoleService;
}
private IAmazonS3 Client { get; set; }
public async Task<IAmazonS3> GetClient(RegionEndpoint region)
{
if (this.Client != null && this.assumeRoleService.CredentialsExistAndAreValid())
{
return this.Client;
}
else
{
this.Client?.Dispose();
var credentials= await this.assumeRoleService.AssumeRoleAsync(this.config.AssumedRoleArn);
this.Client = new AmazonS3Client(assumeRoleCredentials, region);
return this.Client;
}
}
}
yes its thread safe
i asked same question from amazon's employee and his answer was yes
just enjoy :D

Dependency Injection not resolving fast enough for use when a service relies on another service

I am injecting two services into my dot net core web api, the main service relies on data in the helper service. The helper service populates this data in the constructor, however when the main service goes to use this data it is not ready because the constructor of the helper service has not finished by the time it is needed.
I thought DI and the compiler would resolve and chain these services properly so the helper service would not be used until it was fully instantiated.
How I tell the main service to wait until the helper service is fully resolved and instantiated?
Generic sample code of what I am doing. I call the DoSomething() in MainSerice the HelperService calls out to an external API to get some data, that data is needed in the MainService.
StartUp.cs
public void ConfigureServices(IServiceCollection services)
{
services.AddSingleton<IHelperService, HelperService);
services.Scoped<IMainService, MainService);
}
MainService.cs
public class MainService : IMainService
{
private readonly IHelperServuce _helper;
public MainService(IHelperService HelperService)
{
_helper = HelperService;
}
public void DoSomething()
{
string helperParameter = _helper.Param1; //This fails because the constructor of HelperService has not finished
}
}
HelperService.cs
public class HelperService : IHelperService
{
public HelperService()
{
GetParamData();
}
private async void GetParamData()
{
var response = await CallExternalAPIForParameters(); //This may take a second.
Params1 = response.value;
}
private string _param1;
public string Param1
{
get
{
return _param1;
}
private set
{
_param1 = value;
}
}
}
You are not awaiting the async method GetParamData() data in the constructor. That is, ofcourse, not possible. Your constructor should only initialize simple data. You could fix this by, instead of using a property to return, you could also return a Task from a method called (for example) Task<string> GetParam1(). Which could cache the string value.
for example:
public class HelperService : IHelperService
{
private string _param1;
// note: this is not threadsafe.
public async Task<string> GetParam1()
{
if(_param1 != null)
return _param1;
var response = await CallExternalAPIForParameters(); //This may take a second.
_params1 = response.value;
return _param1;
}
}
You could even return a ValueTask<string> because most of the calls can be executed synchronously.
Pass a lambda to the helper service that initializes the variable in your main service, as in...
Helper service.getfirstparam( (response) ->
{ firstparam = response.data;});
While (firstparam == null)
sleep
// now do your processing

Consumer Producer- Producer thread never executes assigned function

I have .NET Core Web API solution. In each call, I need to perform some database operations. The issue is at a time multiple db connections get opened & close. So to avoid it, I want to implement Queue of objects to be sent to database and then want a separate thread to perform db operation.
I've tried some code as below. But here, Consumer thread never executes assigned function. There is no separate thread for Producer, I am simply feeding queue with object.
What modifications I should do? Need some guidance as I'm new to Threading stuff.
public static class BlockingQueue
{
public static Queue<WebServiceLogModel> queue;
static BlockingQueue()
{
queue = new Queue<WebServiceLogModel>();
}
public static object Dequeue()
{
lock (queue)
{
while (queue.Count == 0)
{
Monitor.Wait(queue);
}
return queue.Dequeue();
}
}
public static void Enqueue(WebServiceLogModel webServiceLog)
{
lock (queue)
{
queue.Enqueue(webServiceLog);
Monitor.Pulse(queue);
}
}
public static void ConsumerThread(IConfiguration configuration)
{
WebServiceLogModel webServiceLog = (WebServiceLogModel)Dequeue();
webServiceLog.SaveWebServiceLog(configuration);
}
public static void ProducerThread(WebServiceLogModel webServiceLog)
{
Enqueue(webServiceLog);
Thread.Sleep(100);
}
}
I've created and started thread in StartUp.cs:
public Startup(IConfiguration configuration)
{
Thread t = new Thread(() => BlockingQueue.ConsumerThread(configuration));
t.Start();
}
In Controller, I've written code to feed the queue:
[HttpGet]
[Route("abc")]
public IActionResult GetData()
{
BlockingQueue.ProducerThread(logModel);
return StatusCode(HttpContext.Response.StatusCode = (int)HttpStatusCode.NotFound, ApplicationConstants.Message.NoBatchHistoryInfo);
}
First of all, try to avoid static classes and methods. Use pattern singleton in that case (and if you really need this).
Second, try to avoid lock, Monitor - those concurrency primitives significantly lower your performance.
In such situation, you can use BlockingCollection<> as 'Adam G' mentioned above, or you can develop your own solution.
public class Service : IDisposable
{
private readonly BlockingCollection<WebServiceLogModel> _packets =
new BlockingCollection<WebServiceLogModel>();
private Task _task;
private volatile bool _active;
private static readonly TimeSpan WaitTimeout = TimeSpan.FromSeconds(1);
public Service()
{
_active = true;
_task = ExecTaskInternal();
}
public void Enqueue(WebServiceLogModel model)
{
_packets.Add(model);
}
public void Dispose()
{
_active = false;
}
private async Task ExecTaskInternal()
{
while (_active)
{
if (_packets.TryTake(out WebServiceLogModel model))
{
// TODO: whatever you need
}
else
{
await Task.Delay(WaitTimeout);
}
}
}
}
public class MyController : Controller
{
[HttpGet]
[Route("abc")]
public IActionResult GetData([FromServices] Service service)
{
// receive model form somewhere
WebServiceLogModel model = FetchModel();
// enqueue model
service.Enqueue(model);
// TODO: return what you need
}
}
And in Startup:
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddSingleton<Service>();
// TODO: other init staffs
}
}
You even can add Start/Stop methods to the service instead of implementing IDisposable and start your service in the startup class in the method Configure(IApplicationBuilder app).
I think your consumer thread is executed just once if there is something in the queue and then immediately returns. If you want to have a thread doing work in background, which is started just once, it should never return and should catch all exceptions. Your thread from BlockingQueue.ConsumerThread is invoked once in Stratup and returns.
Also please be aware that doing such solution is not safe. ASP.NET doesn't guarantee background threads to be running if there are no requests coming in. Your application pool can recycle (and by default it recycles after 20 minutes of inactivity or every 27 hours), so there is a chance that your background code won't be executed for some queue items.
Also, while it doesn't solve all issues, I would suggest using https://www.hangfire.io/ to do background tasks in ASP.NET server. It has persistence layer, can retry jobs and has simple API's. In your request handler you can push new jobs to Hangfire and then have just 1 job processor thread.

Categories

Resources