I am developing an eCommerce website which will be accessed by multiple users. I have a method UpdateUserAmount() which must be synchronized for a specific user i.e. we should not access the method UpdateUserAmount() simultaneously for the same user.
I am thinking of using a new static lock object for each user.I will create this object when the user enters the critical section and delete it when he leaves.
This seems inefficient to me as we are creating as many objects as there are simultaneous users. Is there an efficient method of achieving the same?
UpdateUserAmount(int amt) {
user.amount+=amt // critical section
}
I am thinking of using a new [...] lock object for each user.
That's exactly what you'll need to do.
Related
I have ASP.NET Core application which communicates with web api.
There is an business operation involving a few steps (do sth on one page, go to next and next). This multi-step operation is being done in context of one element.
So let's say you have a list of some business objects and your task is to accept object 3 from this list. Accepting is our multistep operation and if I am currently accepting object 3 no one else should be able to enter accepting operation for object 3. When I finish operation, it should be unlocked.
Hope the problem is understandable.
We don't want very time-consuming solution, the simplest idea was to create a database table which indicates when user starts operation, it saves id of the object and id of the user and automatcally remove itself after for example 5 minutes, if someone else want to access operation we check if it is blocked for this object. But it is kind of hacky and not very clean (what if user will go for a coffee and continue operation after 10 minutes?)
I'm looking for a better way to implement this kind of behaviour and appreciate any ideas
If I were to implement that behavior, I'll also use database, but kinda different way. I'll make a table of objects (object 3 is one of its row), adding a column for UserId, boolean OnProcess (to mark if the object is on process or not) and timestamp for StartProcess.
For a user to be able to enter the operation, run query like:
UPDATE Objects SET UserId = <CurrentUser>, StartProcess = <NOW>, OnProcess = true
OUTPUT Object.Id
WHERE Object.Id == 3 AND
(
OnProcess == false
OR ( OnProcess == true AND UserId == <CurrentUser> )
OR ( StartProcess <is more than 15 minutes ago>)
)
disclaimer: the query above is not an executable query, but it should be clear enough to understand what it does.
With the query above, the Object.Id will be returned when:
the object is not being processed by another user
the object is being processed by the CurrentUser itself, also resetting the StartProcess (some kind of sliding behavior). This way, if CurrentUser AFK for a given time (but not exceeding the threshold time) and comes back, he/she can comfortably continue the operation
the object is not being processed for the last 15 minutes. This is actually the threshold that I mention in previous point. As for how long (15 minutes in my example), it is really up to you.
If the Object.Id is returned for a user, then he/she are able to enter the operation.
You're looking for a semaphore. The lock keyword is the most basic of semaphores, but you can also use Semaphore/SemaphoreSlim, which provide the ability to do things like rate-limiting, whereas lock will literally gate one op at a time. However, your goal is to gate one op at a time, for a particular resource, which makes SemaphoreSlim a better choice, specifically a ConcurrentDictionary<string, SemaphoreSlim>.
You'll need a class with singleton lifetime (one instance for the entire life of the application). There, you'll add an ivar:
private readonly ConcurrentDictionary<string, SemaphoreSlim> _semaphores = new ConcurrentDictionary<string, SemaphoreSlim>();
Then, you'll add the following code around the operation you want to gate:
var semaphore = _semaphores.GetOrAdd("object3", _ => new SemaphoreSlim(1, 1));
await semaphore.WaitAsync();
// do something
semaphore.Release();
The "object3" there is obviously just a placeholder. You'll want to use whatever makes sense (ID, etc.) - something that uniquely identifies the particular resource you're gating. This then will only hold operations for that particular resource if there's an existing operation on that particular resource. A different resource would get its own semaphore and thus its own gate.
Say you have an Action in ASP.NET MVC in a multi-instance environment that looks something like this*:
public void AddLolCat(int userId)
{
var user = _Db.Users.ById(userId);
user.LolCats.Add( new LolCat() );
user.LolCatCount = user.LolCats.Count();
_Db.SaveChanges();
}
When a user repeatedly presses a button or refreshes, race conditions will occur, making it possible that LolCatCount is not similar to the amount of LolCats.
Question
What is the common way to fix these issues? You could fix it client side in JavaScript, but that might not always be possible. I.e. when something happens on a page refresh, or because someone is screwing around in Fiddler.
I guess you have to make some kind of a network based lock?
Do you really have to suffer the extra latency per call?
Can you tell an Action that it is only allowed to be executed once per User?
Is there any common pattern already in place that you can use? Like a Filter or attribute?
Do you return early, or do you really lock the process?
When you return early, is there an 'established' response / response code I should return?
When you use a lock, how do you prevent thread starvation with (semi) long running processes?
* just a stupid example shown for brevity. Real world examples are a lot more complicated.
Answer 1: (The general approach)
If the data store supports transactions you could do the following:
using(var trans = new TransactionScope(.., ..Serializable..)) {
var user = _Db.Users.ById(userId);
user.LolCats.Add( new LolCat() );
user.LolCatCount = user.LolCats.Count();
_Db.SaveChanges();
trans.Complete();
}
this will lock the user record in the database making other requests wait until the transaction has been committed.
Answer 2: (Only possible with single process)
Enabling sessions and using session will cause implicit locking between requests from the same user (session).
Session["TRIGGER_LOCKING"] = true;
Answer 3: (Example specific)
Deduce the number of LolCats from the collection instead of keeping track of it in a separate field and thus avoid inconsistency issues.
Answers to your specific questsions:
I guess you have to make some kind of a network based lock?
yes, database locks are common
Do you really have to suffer the extra latency per call?
say what?
Can you tell an Action that it is only allowed to be executed once per User
You could implement an attribute that uses the implicit session locking or some custom variant of it but that won't work between processes.
Is there any common pattern already in place that you can use? Like a Filter or attribute?
Common practice is to use locks in the database to solve the multi instance issue. No filter or attribute that I know of.
Do you return early, or do you really lock the process?
Depends on your use case. Commonly you wait ("lock the process"). However if your database store supports the async/await pattern you would do something like
var user = await _Db.Users.ByIdAsync(userId);
this will free the thread to do other work while waiting for the lock.
When you return early, is there an 'established' response / response code I should return?
I don't think so, pick something that fits your use case.
When you use a lock, how do you prevent thread starvation with (semi) long running processes?
I guess you should consider using queues.
By "multi-instance" you're obviously referring to a web farm or maybe a web garden situation where just using a mutex or monitor isn't going to be sufficient to serialize requests.
So... do you you have just one database on the back end? Why not just use a database transaction?
It sounds like you probably don't want to force serialized access to this one section of code for all user id's, right? You want to serialize requests per user id?
It seems to me that the right thinking about this is to serialize access to the source data, which is the LolCats records in the database.
I do like the idea of disabling the button or link in the browser for the duration of a request, to prevent the user from hammering away on the button over and over again before previous requests finish processing and return. That seems like an easy enough step with a lot of benefit.
But I doubt that is enough to guarantee the serialized access you want to enforce.
You could also implement shared session state and implement some kind of a lock on a session-based object, but it would probably need to be a collection (of user id's) in order to enforce the serializable-per-user paradigm.
I'd vote for using a database transaction.
I suggest, and personally use mutex on this case.
I have write here : Mutex release issues in ASP.NET C# code , a class that handle mutex but you can make your own.
So base on the class from this answer your code will be look like:
public void AddLolCat(int userId)
{
// I add here some text in front of the number, because I see its an integer
// so its better to make it a little more complex to avoid conflicts
var gl = new MyNamedLock("SiteName." + userId.ToString());
try
{
//Enter lock
if (gl.enterLockWithTimeout())
{
var user = _Db.Users.ById(userId);
user.LolCats.Add( new LolCat() );
user.LolCatCount = user.LolCats.Count();
_Db.SaveChanges();
}
else
{
// log the error
throw new Exception("Failed to enter lock");
}
}
finally
{
//Leave lock
gl.leaveLock();
}
}
Here the lock is base on the user, so different users will not block each other.
About Session Lock
If you use the asp.net session on your call then you may win a free lock "ticket" from the session. The session is lock each call until the page is return.
Read about that on this q/a:
Web app blocked while processing another web app on sharing same session
Does ASP.NET Web Forms prevent a double click submission?
jQuery Ajax calls to web service seem to be synchronous
Well MVC is stateless meaning that you'll have to handle with yourself manually. From a purist perspective I would recommend preventing the multiple presses by using a client-side lock, although my preference is to disable the button and apply an appropriate CSSClass to demonstrate its disabled state. I guess my reasoning is we cannot fully determine the consumer of the action so while you provide the example of Fiddler, there is no way to truly determine whether multiple clicks are applicable or not.
However, if you wanted to pursue a server-side locking mechanism, this article provides an example storing the requester's information in the server-side cache and returns an appropriate response depending on the timeout / actions you would want to implement.
HTH
One possible solution is to avoid the redundancy which can lead to inconsistent data.
i.e. If LolCatCount can be determined at runtime, then determine it at runtime instead of persisting this redundant information.
I am writing a web service that allows users to create jobs within the system. Each user has an allowance of the number of jobs they can create. I have a method which checks that the user has some remaining credits which looks like this:
private bool CheckRemainingCreditsForUser(string userId)
{
lock(lockObj)
{
var user = GetUserFromDB(userId);
if (user.RemaingCredit == 0) return false;
RemoveOneCreditFromUser(user);
SaveUserToDB(user);
}
}
The problem I can see with this approach is that if multiple different users make a request at the same time they will only get processed one at a time which could cause performance issues to the client. Would it be possible to do something like this?
private bool CheckRemainingCreditsForUser(string userId)
{
//If there is a current lock on the value of userId then wait
//If not get a lock on the value of userId
var user = GetUserFromDB(userId);
if (user.RemaingCredit == 0) return false;
RemoveOneCreditFromUser(user);
SaveUserToDB(user);
//Release lock on the value of userId
}
This would mean that requests with different userIds could be processed at the same time, but requests with the same userId would have to wait for the previous request to finish
Yes, you could do that with a Dictionary<string, object>. To link a lockObject to every userId.
The problem would be cleaning up that Dictionary every so often.
But I would verify first that there really is a bottleneck here. Don't fix problems you don't have.
The alternative is to have a (optimistic) concurrency check in your db and just handle the (rare) conflict cases.
Instead of locking in every methods, why aren't you using a Singleton that will manage the User's rights ?
It will be responsible from giving the remaining allowances AND manage them at the same time without loosing the thread-safe code.
By the way, a method named CheckRemainingCreditsForUser should not remove allowances since the name isn't implying it, you may be the only developer on this project but it won't hurt to make 2 methods to manage this for re-useability and code comprehension.
EDIT : And this object should also hold the Users dictionary
what is the best way to prevent user double click or refresh page that would cause execute SQL insert statement twice, I've tried to disable the button after click, but the result is not really good. I am expecting that it is possible to do it from code-behind. something more like SQL commit and rollback
Perhaps PRG wikipedia article can help to you:
Post/Redirect/Get (PRG) is a common design pattern for web developers
to help avoid certain duplicate form submissions and allow user agents
to behave more intuitively with bookmarks and the refresh button.
If you wish to protect against this you're going to need the server to be aware that the current user has already begun an action, and can't begin it again until a condition is met.
You need to be able to identify that user amongst the many that are potentially visiting your site. This is most simply done using SessionState, but if you have no other need for SessionState and wish to scale your application massively, a simple random cookie to identify the user can be used as a prefix for any keys that you use to place items into the server cache.
Let's say you used SessionState. You'd do something like the following (pseudo):
public void StartAction()
{
var inProgress = HttpContext.Current.Session["actionInProgress"] as bool;
if (!inProgress)
{
try
{
HttpContext.Current.Session["actionInProgress"] = true;
MySqlController.DoWork();
}
finally
{
HttpContext.Current.Session["actionInProgress"] = false;
}
}
}
The above does not account for the following:
Catching exceptions and/or closing any connections in your finally block
Queueing up subsequent actions as a result of the next clicks on your client (this pseudo-code just returns if the action is already in progress)
I've gone for the simplest solution, but in reality a better practise would be to have this encompassed as a service which runs asynchronously so that you can monitor the progress both for the benefit of the user, and for the prevention of multiple parallel processes.
In my web project, I am using Static List. So say suppose I have 2 users (A, B) logged in to my website at the same time, then this List will store some information about A and as well as B. But say when I process B List's records, A's List's are getting processed instead of B's.
Can somebody point out the problem please?, also please suggest me some possible solution to avoid this problem.
I am using ASP.NET C# 3.5.
Thank you in advance :)
Edit:
Now I have changed the data type from Dictionary to List, but still the same problem...
A static variable is one that is the same for all instances of a particular class. So this means your website uses the exact same dictionary for User A, B, C, D, etc. Essentially whichever user last writes to the dictionary is the one whose content you will see, regardless of which user is looking.
As other's have suggested, you can use Session variables. These are stored in the server's memory, and are related to a specific browser 'session' (i.e. user).
Personally, I prefer to use the ViewState as this is stored in the response to the browser, and returned to you whenever the page makes a postback. This means less memory usage on the server, the viewstate is not volatile across sessions (or subject to application resets like session). However this also means that whatever you are storing is sent across the wire, so you would never want to store things like credit card numbers, SSN's, etc.
It also means that if you're storing a lot of very large objects, you're going to have a very large response and postback (possibly slowing the cycle), so you have to be more careful about how and what you store.
So that's a few different options for you, you should do the research and decide which is best for your requirements.
Storing values in session is like:
Session["mykey"] = value;
And reading values from session is like:
Object value = Session["mykey"];
The session will time out after a couple of minutes and the value would then be null.
To avoid this consider using:
Viewstate["mykey"] = value;
Viewstate is used exactly like session except that the value has to be serializable.
The viewstate is send to the client and back again so consider the amount of data that you want to store this way. The viewstate is stored in "__VIEWSTATE" and encoded in base64.
Don't use a static dictionary. Use Session variables to store information about a user's session.
For better information, you will have to provide us with a bit more information: what records? in what format? etc.
When a variable is declared static, that means there is exactly 1 instance of that variable per class (this also includes classes in web applications).
Thus, a static variable is not tied to a user. If you need to store data specific to a user, consider using a Session variable.
You could store the dictionary in the Session, that way each user would have their own and the code wouldn't have access to others.
Or if you sometimes want to be able to access other users' info, you could declare it as
static Dictionary<String, Dictionary<T, T>>
where the string key is the unique user id.