Global lock and wait if something true - c#

I have an issue where my API receives a ColumnId from outside (its purpose is to do some updates in the database). However, if two requests with the same Id try to access it, I receive an error because two transactions can't be executed over the same row.
Since I still need to execute both nonetheless, is there a way to make a Singleton or a static class that will handle these HTTP requests so that if two requests with the same ColumnId get sent to the API, the API executes the first one and then executes the second one?
public MyClass DoStuff(MyClass2 obj, HttpRequestMessage request)
{
MyClass test = new MyClass();
// .Create() creates a session with the database
using (var sc = _sessionManager.Create())
{
try
{
var anotherObj = _repository.FindForUpdate(obj.Id);
//modify anotherObj, save it to the database and set some values for `test` based on anotherObj
}
catch
{
sc.Rollback();
}
}
return test;
}
FindForUpdate executes a query similar to this:
SELECT * FROM table WHERE id = #Id FOR UPDATE
The best I can think of is to have a singleton (as stated above) that will queue and lock the using statement in DoStuff if the Id is the same, but I don't know how to do it.

It should be quite straightforward to implement a global lock either in a static class or in a class defined with a singleton lifetime in your IoC container. You could use the lock keyword for this, or one of the many other synchronization primitives offered by .Net such as the SemaphoreSlim class.
However, as pointed out by John, this scales poorly to multiple web servers, and doesn't leverage the concurrency mechanisms offered by the database. It's hard to give concrete recommendations without knowing the specifics of your database platform and data access framework, but you should probably look into either using FOR UPDATE WAIT if your database supports it, or just an optimistic concurrency mechanism with some retry logic in your application for reapplying the update after waiting a short while.
Ideally, you will also want to change any long-running blocking operations in your application to use async/await, so that the web server thread is released back to the threadpool for serving other requests.

Related

How do I prevent the creation of multiple background processes working on the same request?

I have a simple WebApi controller method that's purpose is to trigger some background processing and return a 202 Accepted response immediately (without necessarily having completed the background processing as is consistent with a 202 response.)
public async Task<IHttpActionResult> DoSomething(string id)
{
HostingEnvironment.QueueBackgroundWorkItem(async ct =>
{
//Do work
}
return ResponseMessage(new HttpResponseMessage(HttpStatusCode.Accepted));
}
However, I want to be able to prevent multiple requests to the same endpoint with the same id from triggering multiple instances of the same background processing simultaneously.
Effectively, if two requests with the same id's were to be made at the same time (or near enough), the first one would do the processing for that id and the second one would be able to identify this and take action accordingly and not duplicate the work that's already being done.
I'd like to avoid databases/persistent storage if at all possible and I'm aware of the risks of running async tasks within an IIS worker process - for the sake of the argument, the background processing is not critical.
How can I go about doing this? Any help would be much appreciated.
You'll need some kind of storage shared between all your possible workers. This could be, for example:
A static variable. Probably the easiest to implement, but has limitations when the application does not run in only one AppDomain (especially important if you want to scale). So probably not the best way
An SQL Database: Probably the most common one. If your application already uses one, I'd say go for this route.
A No-SQL database, for example a key-value store. Might be an alternative if your application does not use a SQL database yet.
Some external component such a workflow management tool
EDIT:
Example using ConcurrentDictionary (per request of the thread starter - I stil think using a (possibly NoSQL) database would be the most elegant way) - actually you could just put Tasks into the dictionary:
private ConcurrentDictionary<string, Task<SomeType>> _cache = new //...
var task = _cache.GetOrAdd("<Key>", key => Task.Run(() => /*do some work*/));
if (task.IsCompleted)
/*result ready*/;
else
/*have to wait*/;

Accessing a method in a Singleton instance from multiple threads

I use Ninject as my IOC. I have two classes as shown below.
Customer:
public class Customer:ICustomer
{
private ICustomerRepository _CustRepo;
public Customer(ICustomerRepository custRepo){
_CustRepo = custRepo;
}
public void CustomerOperations(int custId){
var cust = _CustRepo.GetCustomer(custId);
//Do something with the cust object
if(condition1)
_CustRepo.Update(cust);
if(condition2)
_CustRepo.Delete(cust.id);
if(condition3)
_CustRepo.Insert(cust);
}
}
Repository:
public class CustomerRepository:ICustomerRepository {
private IDataAccessLayer _Dal;
//Ctor details skipped
public Customer GetCustomer(int custId) {//Get customer from DB}
public bool Update(Customer c) {//Update}
public bool Delete(int custID) {//Delete}
public bool Insert(Customer c) {//Insert}
}
Bindings:
Bind<ICustomer>().To<Customer>().InThreadScope();
Bind<ICustomerRepository>().To<CustomerRepository>().InSingletonScope();
Let's assume the scope can't be changed. I am trying to sync the calls so thread1 doesn't delete the customer while thread2 is getting ready to update the same customer.
If locking is the recommended approach, how do I make sure the repository methods are thread safe. Thanks
This is a concurrency problem that locking can't solve.
Consider the following:
Client A requests the deletion of the customer before client B can make the call to delete it. In this scenario the following occurs:
Client A acquires the lock on the customer/repository
Client B requests the lock, but has to wait until the lock is released by Client A
Client A successfully delete the customer
Client A releases the lock
Client B acquires the lock
Client B's request to update the customer fails because the customer has been deleted.
Look into the concurrency control mechanisms of your underlying data store.
If you are using SQL Server + EF, take a look at this article.
As MattTannahill says, this is a concurrency problem.
The only way to accomplish what you want to accomplish is to defer the execution of the actual point of no return (update/delete), and do the proper book keeping prior to that to provide enough information to perform the right sequence or sequences of operation(s)... You will, for lack of a better explanation, remove concurrency from the equation of record manipulation.
So basically, you'd have a "transaction log/trail" of sorts associated with the record, and the actual operations on the record do not occur until a later moment when it's safe to do so. In the meantime, a record is stored in a state of "to be updated".
Of course, this opens up other things you need to resolve, and it can get unmanageable very quickly:
How do you display an updated/deleted record that hasn't had the operation(s) performed on it yet.
What transaction has final say? What you're implying here is that updates should cancel out a concurrent delete. Which you could do if you wait to perform all of the operations and analyze the transaction trail. The rules to this kind of thing can get very complex over time because you essentially have to "mark a record for an operation" from a client interaction point of view, and then a process job to later to actually analyze and perform the operation.
If a delete is "cancelled" by an update, since the operation occurs later, there's no immediate way of letting the user know it didn't delete. You'd have to figure out a way to let that user know.
You'll need to define the "safe" time to perform these modifications. Do you do it 30 seconds after no activity on the record? Are there certain modifications that always take precedence?

Is this a safe and *relatively okay* way to log certain events asynchronously?

Working with:
.NET 4.5.1
Web Forms
Entity Framework 6 (Context Per Request)
IIS 8, Windows 2012 Datacenter
Main concern: Thread safety and reliability.
The project consist of a closed system that will be used by numerous types of users which will execute various actions. In our current project I've decided to implement something that probably most developers find absolutely necessary.
There were serious problems in the past deriving from the lack of even the simplest logging system that would allow us to track some common user actions, especially manipulating data.
I know there are popular logging frameworks, but I want to achieve something relatively simple that will not block the main thread.
The idea was to pull all data I need while on the main thread, because it turned out some of the data was harder to access from a separate thread, and then create a task that will take care of the database insert. My knowledge of multi-threading is limited, and this is what I came-up with.
public static class EventLogger
{
public static void LogEvent(int eventType, string descr, Exception ex = null)
{
//Get the page that is currently being executed;
var executingPage = HttpContext.Current.CurrentHandler as Page;
string sourcePage = executingPage != null ? executingPage.AppRelativeVirtualPath : string.Empty;
var eventToAdd = new Event()
{
Date = DateTime.Now,
EventTypeId = eventType,
isException = ex != null ? true : false,
ExceptionDetails = ex != null ? ex.Message : string.Empty,
Source = sourcePage,
UserId = UserHelper.GetUserGuid(),
UserIP = UserHelper.GetUserIP(),
UserName = UserHelper.GetUserName(),
Description = descr
};
Task.Factory.StartNew(() => LogEventAsync(eventToAdd));
}
private static void LogEventAsync(Event eventToAdd)
{
using (var context = new PlaceholderEntities())
{
context.Events.Add(eventToAdd);
context.SaveChanges();
}
}
}
Questions:
Would this be a good-enough way to log what I need and is it safe in a multi-user environment?
How would you do it if you didn't want to dig into logging frameworks?
Yes, it is safe in a multithread environment. Because you are creating a new instance of the database context everytime you insert an event into the database, you are safe. EF is not thread-safe only if you would try to reuse the same context instance across threads.
The only possible issue is that doing this in an async way possibly means that multiple connections are opened concurrently and the connection pool could be depleted. The more often you log, the higher is the possibility that this happens.
Having said this, I still recommend you use log4net or any existing logging infrastructure and just find a way to log asynchronously. People blog often how to do this with log4net, take a look here for example
http://www.ben-morris.com/using-asynchronous-log4net-appenders-for-high-performance-logging
Note that issues related to async logging are also discussed there. To workaround some issues like the possibility of misordering entries, you could possibly have a queue in such custom appender. Sticking with existing framework lets you reuse a lot of ideas that are already developed. Going on your own sooner or later will stop you for longer, when requirements change.
As mentioned by #gbjbaanb you should implement this as a queue with one or more threads actually doing the database work via EF. You can make use of BlockingCollection<T> backed by a ConcurrentQueue<T> to do this in a producer/consumer fashion. Basic premise is each caller logging something simply adds to the queue (i.e. producer). You can then have one or more threads pulling information off the queue and persisting to the database (i.e. consumer). You'd need a separate context for each thread.
There's a reasonable example on the BlockingCollection<T> documentation pages.
No, its not. EF6 is not thread-safe (well, DBContext is not thread-safe, so if you have 1 DBcontext per thread you should be ok), so adding your log event can (ha! will) interfere with another thread writing a different log event.
What you will need to do is synchronise all the calls to EF in your task, what I'd do is add the log to a collection and then have another thread that pulls these logs off the collection and writes them to the DB, one at a time. The add/remove calls to the collection must be protected with a lock to stop 2 threads adding (or removing) an entry simultaneously.
Or I'd use log4net, which is thread-safe, so you can simply call it in your thread to do the writing or the log entries.
My other advice is to write the logs to a file rather than the DB. File-logging is quick so you can do it on the main thread with minimal performance impact (log4net is very efficient too, so just use that - and I know IIS will be writing access and error logs to file anyway) which will remove your issue with threading.
One thing I really do know is that if you're not too hot on multi-threading then you need to stop doing it until you are hot on it. No disrespect to you for trying, but thread bugs can be f***** nightmares to solve, you cannot reproduce them nor easily catch them in a debugger and their cause are often unrelated to the symptoms that are reported.

Threadsafety of a method with multiple db calls

I just read this SO question Which design is most preferable: test-create, try-create, create-catch?
Regarding to the answer, seems devs prefer "Try-Create" pattern, and some of them mentioned the TryCreate(user, out resultCode) can be a threadsafe, but other patterns not.
Try-Create
enum CreateUserResultCode
{
Success,
UserAlreadyExists,
UsernameAlreadyExists
}
if (!TryCreate(user, out resultCode))
{
switch(resultCode)
{
case UserAlreadyExists: act on user exists error;
case UsernameAlreadyExists: act on username exists error;
}
}
I am thinking if the tryCreate method involves multiple db calls what is the proper way to make it thread-safe in a real practice?
Say tryCreate will do 2 things:
Check if the user name existing in db
If the name not existing then create a new one
It is very possible a thread finishes 1 but not 2, another thread start to invoke this method tryCreate and also finishes 1, which is a race-condition.
Surely, in tryCreate I can add a lock or sth, but it will make the tryCreate a hot point. If I have a high-profile website, all new registers have to wait for the lock in tryCreate.
What I see from other websites is when you type in your username,it will trigger a ajax call to check if it is existing in current db, then you go to next step to create it.(Not sure if there is a lock been created at this moment.)
Any thoughts about how to implement a proper safe tryCreate involving multiple db calls in a real life?
Updates 1:
The logic of TryCreate can be very complicated and not just 2 db calls
Thread-safety concept doesn't spawn to the databases. So whatever you do on the client side has no effect on the database side, purely because by design database supports multiple concurrent connection from many clients.
Instead use transactions to perform several actions as atomic operation on the database end.

Threading: allow one thread to access data while blocking others, and then stop blocked threads from executing the same code

imagine the simplest DB access code with some in-memory caching -
if exists in cache
return object
else
get from DB
add to cache
return object
Now, if the DB access takes a second and I have, say, 5 ASP.Net requests/threads hitting that same code within that second, how can I ensure only the first one does the DB call? I have a simple thread lock around it, but that simply queues them up in an orderly fashion, allowing each to call the DB in turn. My data repositories basically read in entire tables in one go, so we're not talking about Get by Id data requests.
Any ideas on how I can do this? Thread wait handles sound almost what I'm after but I can't figure out how to code it.
Surely this must be a common scenario?
Existing pseudocode:
lock (threadLock)
{
get collection of entities using Fluent NHib
add collection to cache
}
Thanks,
Col
You've basically answered your own question. The "lock()" is fine, it prevents the other threads proceeding into that code while any other thread is in there. Then, inside the lock perform your first pseudo-code. Check if it's cached already, if not, retrieve the value and cache it. The next thread will then come in, check the cache, find it's available and use that.
Surely this must be a common scenario?
Not necessarily as common as you may think.
In many similar caching scenarios:
the race condition you describe doesn't happen frequently (it requires multiple requests to arrive when the cache is cold)
the data returned from the database is readonly, and data returned by multiple requests is essentially interchangeable.
the cost of retrieving the database is not so prohibitive that it matters.
But if in scenario you absolutely need to prevent this race condition, then use a lock as suggested by Roger Perkins.
I'd use Monitor/Mutext over lock. Using lock u need to specify a resource (may also use this-pointer, which is not recommended).
try the following instead:
Mutext myMutex = new Mutex();
// if u want it systemwide use a named mutex
// Mutext myMutex = new Mutex("SomeUniqueName");
mutex.WaitOne();
// or
//if(mutex.WaitOne(<ms>))
//{
// //thread has access
//}
//else
//{
// //thread has no access
//}
<INSERT CODE HERE>
mutex.ReleaseMutex();
I don't know general solution or established algorithm is exist.
I personally use below code pattern to solve problem like this.
1) Define a integer variable that can be accessed by all thread.
int accessTicket = 0;
2) Modify code block
int myTicket = accessTicket;
lock (threadLock)
{
if (myTicket == accessTicket)
{
++accessTicket;
//get collection of entities using Fluent NHib
//add collection to cache
}
}
UPDATE
Purpose of this code is not prevent multiple DB access of duplicate caching. We can do it with normal thread lock.
By using the access ticket like this we can prevent other thread doing again already finished work.
UPDATE#2
LOOK THERE IS lock (threadLock)
Look before comment.
Look carefully before vote down.

Categories

Resources