Thread safe WebApi put requests - c#

I have a webapi and I want to make my logic inside this controller thread safe.
I want user can only update payroll when the last one updated and two update at the same time should not be happend.
As you can see in the code, I added a column in Payroll entity with the name of IsLock as boolean and try to handle multiple request for update in this way but it is not thread-safe.
How can I make it thread-safe ?
[HttpPut("{year}/{month}")]
public async Task<NoContentResult> Approve([FromRoute] int year, [FromRoute] int month)
{
var payroll = _dataContext.Payrolls
.SingleOrDefaultAsync(p =>
p.Month == month && p.Year == year);
if (payroll.IsLock)
{
throw new ValidationException(
$"The payroll {payroll.Id} is locked.");
}
try
{
payroll.IsLock = true;
_dataContext.Payrolls.Update(payroll);
await _dataContext.SaveChangesAsync(cancellationToken);
payroll.Status = PayrollStatus.Approved;
_dataContext.Payrolls.Update(payroll);
await _dataContext.SaveChangesAsync(cancellationToken);
payroll.IsLock = false;
_dataContext.Payrolls.Update(payroll);
await _dataContext.SaveChangesAsync(cancellationToken);
return NoContent();
}
catch (Exception)
{
payroll.IsLock = false;
_dataContext.Payrolls.Update(payroll);
await _dataContext.SaveChangesAsync(cancellationToken);
throw;
}
}

You are looking for Concurrency Tokens. Each row in the payroll table would have one. When a user loaded the edit interface for a payroll, the concurrency token would be sent to the client. The client would include the concurrency token in the request to update the payroll. The update would only succeed of the concurrency token had not changed - meaning that the data had not changed since the user fetched it to start the edit.
Entity Framework uses the concurrency tokens internally, as well, so it won't save changes from a stale entity (where the data has changed since it was loaded).
The current IsLocked solution has some flaws. If two API requests are received at the same time, both may read the payroll data and see that it isn't locked. Both requests would then lock the row, make competing changes, and release the lock without ever realizing there were simultaneous edits.

Related

Convert to async/await

public async Task<JobViewModel> Handle(EditJobCommand command, CancellationToken cancellationToken)
{
if (command.JobViewModel == null) throw new System.InvalidOperationException("Empty request.");
var jobViewModel = command.JobViewModel;
try
{
var job = _mapper.Map<DataAccess.Domain.Lab.Job>(jobViewModel);
job.Id = command.Id;
OnBeforeAttach(job);
_context.Set<DataAccess.Domain.Lab.Job>().Attach(job);
//_context.MarkModified(job);
await OnBeforeSave(job);
await _context.SaveChangesAsync();
return jobViewModel;
}
catch (DbUpdateConcurrencyException ex)
{
Console.WriteLine(ex.Message);
throw;
}
catch (DbEntityValidationException e)
{
foreach (var eve in e.EntityValidationErrors)
{
Console.WriteLine("Entity of type \"{0}\" in state \"{1}\" has the following validation errors:",
eve.Entry.Entity.GetType().Name, eve.Entry.State);
foreach (var ve in eve.ValidationErrors)
{
Console.WriteLine("- Property: \"{0}\", Error: \"{1}\"",
ve.PropertyName, ve.ErrorMessage);
}
}
throw e;
}
catch (DbUpdateException e)
{
Console.WriteLine(e.Message);
throw;
}
}
private async Task OnBeforeSave(Job job)
{
var entry = _context.Entry(job);
entry.State = EntityState.Unchanged;
entry.Property(j => j.Name).IsModified = true;
entry.Property(j => j.Description).IsModified = true;
entry.Property(j => j.SAPCode).IsModified = true;
entry.Property(j => j.InitiatedOn).IsModified = true;
entry.Property(j => j.EndDate).IsModified = true;
entry.Property(j => j.ProjectId).IsModified = true;
entry.Property(j => j.TimeStamp).IsModified = true;
if (job.IsNew)
{
entry.State = EntityState.Added;
}
else
{
entry.State = EntityState.Modified;
entry.Property(e => e.CreatedBy).IsModified = false;
entry.Property(e => e.CreatedOn).IsModified = false;
}
}
}
I want to use await for below line
var entry = _context.Entry(job); but using await gives below error DbEntityEntry does not contain definition for getawaiter
Aslo ,please not i want to use await as calling this method onBeforesave () is giving me error of optimistic concurrency.
Optimistic concurrency errors implies that your code is using something like a RowVersion Timestamp column for cuncurrency checks. These are used to help guard against "last in wins" data overwrites.
Take the scenario where Bob and Jane both running queries and happen to pick the same record to be updated. Bob makes a change to one field and Saves, then Jane makes a change to a second field and Saves. In your model you are taking all fields from a serialized copy at the time of their Read with their specific edits, attaching that as an entity, setting the fields to modified, and attempting to save the changes. Without optimistic concurrency, Jane's changes would be saved, while Bob's changes would be lost, overwritten by Jane's stale data, completely silently. With a RowVersion, the original record would have a RowVersion stamp of "AAAAAAA", when Bob saves his changes, his copy of the RowVersion would be "AAAAAA" which is checked by EF against the current database row. They match, so the update goes through and the row version is updated to "BBBBBB". Now when Jane goes to save her changes, her RowVersion value is still "AAAAA". When EF checks that against the DB row now "BBBBBB" Jane's operation fails due to the concurrency issue detected. Jane's data is stale.
Optimistic concurrency is a safeguard to ensure these types of problems don't happen silently. It is up to the developers as to what the system should do in these scenarios. Such as, Log the fact that it happened and allow the overwrite; Attempt to automatically merge the change; Reject the change and give Jane the current data state to review and apply her change again; etc.
This comes down to how you are performing your update. Whether optimistic concurrency or not, it is not a good idea to deserialize data from a client into an entity, attach it to the DbContext and call SaveChanges. The good thing is that your code is being pessimistic with the fields that it is marking as IsModified rather than setting the entire EntityState to Modified, but that is ultimately a lot of boilerplate code to write for every update. It also means your UPDATE statements will include every column nominated whether those values changed or not.
A better way to handle the Update, and concurrency:
(Add in appropriate exception handling, I trimmed out to show the relevant bits)
public async Task<JobViewModel> Handle(EditJobCommand command, CancellationToken cancellationToken)
{
if (command.JobViewModel == null) throw new System.InvalidOperationException("Empty request.");
var job = _mapper.Map<DataAccess.Domain.Lab.Job>(jobViewModel);
var job = await _context.Jobs.SingleAsync(x => x.Id == command.JobViewModel.Id);
if(job.RowVersion != command.JobViewModel.RowVersion)
{ // Concurrency issue! Handle here.
}
_mapper.Map(command.JobViewModel, job);
await _context.SaveChangesAsync();
}
The key differences here in approach:
Fetch the job asynchronously from the DB. This ensures we are inspecting and updating the current state of the record. This also ensures that our Edit command actually has a valid Job ID. From here we can also validate that the Job is in an editable state (I.e. not Inactive) and can assert that the current user has permissions to edit that Job etc.
Check the RowVersion. This might be a different column name, but essentially our concurrency marker which needs to be passed to the View, and returned in our Edit ViewModel. If these values match, the data hasn't changed so we can continue with the edit. If they don't match you can handle that whether to refresh the client view and alert them that data has possibly changed, or allow the overwrite after logging the concurrency issue.
Leverage Automapper to copy allowed values across from the ViewModel to the Entity. In your mapping for VM -> Entity you can configure what fields can be copied across then call Mapper.Map(src, destination). Aside from being a single line of code, the benefit of using the Change Tracking on the entity is that the resulting UPDATE statement will only include fields that actually change (and only if anything actually changed).

How much can I put into one transaction?

This builds on "Should I always use transactions in nhibernate (even for simple reads and writes)?
".
The gist is, we should always use a transaction, even for a simple get like this:
using (ITransaction transaction = session.BeginTransaction())
{
var printerJob2 = (PrinterJob) session.Get(typeof (PrinterJob), id);
transaction.Commit();
return printerJob2;
}
Let's consider this code:
User user = session.Get<User>(userId);
if(user == null) return UnAuthorizedResult();
Order order = session.Get<Order>(orderId);
if(order == null) return BadRequestResult();
session.Delete<Order>(order);
transaction.Commit();
return OkResult();
I am assuming that I am not meant to create a new transaction for every DB access, as that becomes quite messy:
User user;
using (ITransaction transaction = session.BeginTransaction())
{
user = session.Get<User>(userId);
transaction.Commit();
}
if(user == null) return UnAuthorizedResult();
Order order;
using (ITransaction transaction = session.BeginTransaction())
{
order = session.Get<Order>(orderId);
transaction.Commit();
}
if(order == null) return BadRequestResult();
using (ITransaction transaction = session.BeginTransaction())
{
session.Delete<Order>(order);
transaction.Commit();
return OkResult();
}
I am assuming it's all supposed to go into one transaction.
Are there limits to this?
How much can I put into one transaction before it "breaks down" in one way or another?
You use transactions when you want a bunch of updates to all succeed or all fail. Generally things are starting to move away from it as a modus operandi, and towards operations that are tolerant of failures, pick up where they left off if tried again (rather than inserting duplicate records - look up idempotency) etc and are generally more forgiving of a world full of latency, transient and fickle network reliability, disparate systems and the inability to establish unifying transactions (look up sagas) etc but in essence, you use a transaction when you want it to be as if it were a one hit operation that worked out, or not.
There's no point making a transaction for a read, and there's nearly no point using one for a single update (unless you want to be able to undo that update later), but if you had to collect a whole load of data about a person and insert it into 17 different tables, you might use a transaction to ensure it all worked or all failed, so you don't have partial data lying around. You don't need to worry about unpicking 9 out of the 17 inserts if there was a fail; you just roll back the transaction and it's as it the insert never happened, though autoincrement numbers might remain bumped on by one
Are there limits to how much data you can ball up in a transaction? Technically yes but I think it unlikely you'd breach them with an enterprise DB if you're adhering to the next rule of thumb, which is..
Realistically, you probably want to keep the transaction sets as small as possible to leave someone the minimum headache of sorting things out when it goes wrong. Don't save every new customer you acquire for a whole year in the same transaction and then then decide to commit the transaction just because it's Christmas; ten thousand inserts thwarted by one name that's too long or a server crash on Christmas eve isn't ideal
So that code from your example is kind of a mess littered with transactions opening and closing within a given controller method. The code in your example is basically the same as using no transactions because each step commits itself. I'm assuming and MVC app because I see an OKResult near the end of your code.
Transactions are atomic units of work. If you've got 3 steps and one fails everything should be rolled back to the last known state before the transaction started. In a web scenario this is usually the request. You'll see a lot of information is you google "session per request" nhibernate. With that said I do a couple of things to ensure I'm adhering to this pattern.
In global.asax I have these helper methods
public static ISession CurrentSession
{
get { return (ISession) HttpContext.Current.Items[Sessionkey]; }
private set { HttpContext.Current.Items[Sessionkey] = value; }
}
protected void Application_BeginRequest() { CurrentSession = SessionFactory.OpenSession(); }
protected void Application_EndRequest()
{
if (CurrentSession != null)
CurrentSession.Dispose();
}
Then I also have this Attribute that I can used at the method or controller level to ensure each controller action is transactionally sound
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class)]
public class TransactionAttribute : ActionFilterAttribute
{
private ITransaction Transaction { get; set; }
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
Transaction = MvcApplication.CurrentSession.BeginTransaction(System.Data.IsolationLevel.ReadCommitted);
}
public override void OnActionExecuted(ActionExecutedContext filterContext)
{
if (!Transaction.IsActive)
return;
if (filterContext.Exception == null)
{
Transaction.Commit();
return;
}
Transaction.Rollback();
}
}
And now my transaction code fairly well contained and doesn't have to be littered all over your controller methods.

Update quantity issue with Concurrent Transactions C#

I have developed an application to online purchasing my products.
I have a product "Umbrellas" in my store with 100 pieces. I have developed an application to online purchasing my products.
But there is an issue when there is a concurrent purchasing.
If there is a two concurrent purchasing happening the AvailableQty will update incorrectly. Let's say there are two transactions happening concurrently with Purchasing Qty as 100 & 50. Ideally, the first transaction (purchase qty is 100) should be successful as we have 100 stocks available. But the second transaction should return an error because the stock is insufficient to process as with the first transaction the balance is 0. (100 - 100). But above scenario both transactions are successful and the balance shows as -50 now.
This will work correctly when there are two separate transactions. But this is an issue when this two transactions happening CONCURRENTLY. The reason for this problem is, when concurrent transactions the condition to check the availability hits same time, in that time the condition is satisfied as the DB table has not updated with the latest qty.
How can I correct this?
public bool UpdateStock(int productId, int purchaseQty)
{
using(var db = new MyEntities())
{
var stock = db.Products.Find(productId);
if (stock.AvailableQty >= purchaseQty) // Condition to check the availablity
{
stock.AvailableQty = stock.AvailableQty - purchaseQty;
db.SaveChanges();
return true;
}
else
{
return false;
}
}
}
This is typical thread concurrency issue which can be resolved in multiple ways, one of them is using simple lock statement:
public class StockService
{
private readonly object _availableQtyLock = new object();
public bool UpdateStock(int productId, int purchaseQty)
{
using (var db = new MyEntities())
{
lock (_availableQtyLock)
{
var stock = db.Products.Find(productId);
if (stock.AvailableQty >= purchaseQty) // Condition to check the availablity
{
stock.AvailableQty = stock.AvailableQty - purchaseQty;
db.SaveChanges();
return true;
}
return false;
}
}
}
}
Only one thread can get a exclusive rights to get a lock on _availableQtyLock, which means other thread will have to wait for the first thread to release lock on that object.
Take into account this is the simplest (and possibly slowest) way of dealing with concurrency, there are other ways to do thread synchronization, e.g. Monitor, Semaphore, fast SlimLock etc... Since it's hard to tell which one will suit your needs the best, you'll need to do proper performance/stress testing, but my advice would be to start with simplest.
Note: As others mentioned in comments, concurrency issues can be done on DB level as well, which indeed would be more suitable, but if you don't want/can't introduce any DB changes, this would be way to go

EntityFramework and handling duplicate primary key/concurrency/race conditions situations

I wrote a library, referenced by numerous applications, that tracks who is online and which application and page they are viewing.
The data is stored, using EF6, in a Sql Server 2008 table which tracks their username (primary key), application, page and timestamp. I only want to store the latest request for each person so each username should only be stored once.
The library code, which is called from the Global.asax of each application looks like this:
public static void Add(ApplicationType application, string username, string pageRequested)
{
using (var db = new CommonDAL()) // EF context
{
var exists = db.ActiveUsers.Find(username);
if (exists != null)
db.ActiveUsers.Remove(exists);
var activeUser = new ActiveUser() { ApplicationID = application.Value(), Username = username, PageRequested = pageRequested, TimeRequested = DateTime.Now };
db.ActiveUsers.Add(activeUser);
db.SaveChanges();
}
}
I'm intermittently getting the error Violation of PRIMARY KEY constraint 'PK_tblActiveUser_Username'. Cannot insert duplicate key in object 'dbo.tblActiveUser'. The duplicate key value is (xxxxxxxx)
What I can only guess is happening is Request A comes in, removes the existing username. Request B (from same user) then comes in, tries to remove the username, sees nothing exists. Request A then adds the username. Request B then tries to add the username. The error frequently seems to be triggered when a web server sends a client a 401 status, which again points to multiple requests within a short period of time triggering this.
I'm having trouble mocking this race condition using unit tests as I haven't done much async programming before, but tried to create async tests with delays to mock multiple simultaneous slow requests. I've tried to use using (var transaction = new TransactionScope()) and using (var transaction = db.Database.BeginTransaction(System.Data.IsolationLevel.ReadCommitted)) to lock the requests so request A can complete before request B begins but can't verify either one fixes the issue as I can't mock the situation reliably.
1) Which is the right way to prevent the exception (Most recent request is the one that ultimately is stored)?
2) Which is the right way to to write a unit test to prove this is working?
Since you only want to store the latest item, you could use a last update wins and avoid the race condition on who can insert first, the database handles the locks and the last to call update (which is the most recent) is what is in the table.
Something like the following should handle any primary key errors if you run into concurrency issues on the edge case that a brand new user has 2 requests at the same time and avoid an "infinite" loop of errors (well until a stack overflow exception any way).
public static void Add(ApplicationType application,
string username,
string pageRequested,
int recursionCount = 0)
{
using (var db = new CommonDAL()) // EF context
{
var exists = db.ActiveUsers.Find(username);
if (exists != null)
{
exists.propa = "someVal";
}
else
{
var activeUser = new ActiveUser
{
ApplicationID = application.Value(),
Username = username,
PageRequested = pageRequested,
TimeRequested = DateTime.Now
};
db.ActiveUsers.Add(activeUser);
}
try
{
db.SaveChanges();
}
catch(<Primary Key Violation>)
{
if(recursionCount < x)
{
Add(application, username, pageRequested, recursionCount++)
}
else
{
throw;
}
}
}
}
As for unit testing this, it will be very hard unless you insert an artificial delay or can force both threads to run at the same time. Sometimes the timing on the race conditions is in the millisecond range depending on the issue. Tasks may not work because they are not guaranteed to run at the same time, you throw them to the background thread pool and they run when they can. Old school threads may work but I don't know how to force it since the time between read and remove & create are most likely in the 5 ms range or less.

Avoid fast post on webapi c#

I have problem in when user post the data. Some times the post run so fast and this make problem in my website.
The user want to register a form about 100$ and have 120$ balance.
When the post (save) button pressed sometimes two post come to server very fast like:
2018-01-31 19:34:43.660 Register Form 5760$
2018-01-31 19:34:43.663 Register Form 5760$
Therefore my client balance become negative.
I use If in my code to check balance but the code run many fast and I think both if happen together and I missed them.
Therefore I made Lock Controll class to avoid concurrency per user but not work well.
I made global Action Filter to control the users this is my code:
public void OnActionExecuting(ActionExecutingContext context)
{
try
{
var controller = (Controller)context.Controller;
if (controller.User.Identity.IsAuthenticated)
{
bool jobDone = false;
int delay = 0;
int counter = 0;
do
{
delay = LockControllers.IsRequested(controller.User.Identity.Name);
if (delay == 0)
{
LockControllers.AddUser(controller.User.Identity.Name);
jobDone = true;
}
else
{
counter++;
System.Threading.Thread.Sleep(delay);
}
if (counter >= 10000)
{
context.HttpContext.Response.StatusCode = 400;
jobDone = true;
context.Result = new ContentResult()
{
Content = "Attack Detected"
};
}
} while (!jobDone);
}
}
catch (System.Exception)
{
}
}
public void OnActionExecuted(ActionExecutedContext context)
{
try
{
var controller = (Controller)context.Controller;
if (controller.User.Identity.IsAuthenticated)
{
LockControllers.RemoveUser(controller.User.Identity.Name);
}
}
catch (System.Exception)
{
}
}
I made list static list of user and sleep their thread until previous task happen.
Is there any better way to manage this problem?
So the original question has been edited so this answer is invalid.
so the issue isn't that the code runs too fast. Fast is always good :) The issue is that the account is going into negative funds. If the client decides to post a form twice that is the clients fault. It maybe that you only want the client to pay only once which is an other problem.
So for the first problem, I would recommend a using transactions (https://en.wikipedia.org/wiki/Database_transaction) to lock your table. Which means that the add update/add a change (or set of changes) and you force other calls to that table to wait until those operations have been done. You can always begin your transaction and check that the account has the correct amount of funds.
If the case is that they are only ever meant to pay once then.. then have a separate table that records if the user has payed (again within a transaction), before processing the update/add.
http://www.entityframeworktutorial.net/entityframework6/transaction-in-entity-framework.aspx
(Edit: fixing link)
You have a few options here
You implement ETag functionality in your app which you can use for optimistic concurrency. This works well, when you are working with records, i.e. you have a database with a data record, return that to the user and then the user changes it.
You could add an required field with a guid to your view model which you pass to your app and add it to in memory cache and check it on each request.
public class RegisterViewModel
{
[Required]
public Guid Id { get; set; }
/* other properties here */
...
}
and then use IMemoryCache or IDistributedMemoryCache (see ASP.NET Core Docs) to put this Id into the memory cache and validate it on request
public Task<IActioNResult> Register(RegisterViewModel register)
{
if(!ModelState.IsValid)
return BadRequest(ModelState);
var userId = ...; /* get userId */
if(_cache.TryGetValue($"Registration-{userId}", register.Id))
{
return BadRequest(new { ErrorMessage = "Command already recieved by this user" });
}
// Set cache options.
var cacheEntryOptions = new MemoryCacheEntryOptions()
// Keep in cache for 5 minutes, reset time if accessed.
.SetSlidingExpiration(TimeSpan.FromMinutes(5));
// when we're here, the command wasn't executed before, so we save the key in the cache
_cache.Set($"Registration-{userId}", register.Id, cacheEntryOptions );
// call your service here to process it
registrationService.Register(...);
}
When the second request arrives, the value will already be in the (distributed) memory cache and the operation will fail.
If the caller do not sets the Id, validation will fail.
Of course all that Jonathan Hickey listed in his answer below applies to, you should always validate that there is enough balance and use EF-Cores optimistic or pessimistic concurrency

Categories