I am learning microservices and trying to design an e-commerce website. I can't figure out how big shopping sites take care of the last item in the cart problem.
For example, I selected an item from Amazon which had just a single item available in stock. I logged in from two different accounts and placed the item in cart. I even reached the payment page from both the account and the site didn't restrict me anywhere saying that the item is not available. I am not sure after the payment page when payment from both the account is in progress, how Amazon handles it.
Few solutions which come to my mind are like:
Accept payment from both the accounts and later cancel transaction for one of them which paid later than the first. This will not be a good practice though as it will result in bas customer experience.
Keep few items in reserve and use them in case of overbooking.
I forget what Amazon is doing and implement quantity checks in Order service from Item service via REST calls, at every stage of the order. But these checks sometimes can fail when a lot of people are ordering the same item. for e.g. in flash sales
Please share if you guys have worked on similar problem and solved it even with few limitations. If I need to put any in more details, let me know.
I cannot answer how Amaozon does it, nor I think anyone could on a public forum I can tell you how I think this could be managed.
So you have to take lock on your inventory if you want to make sure you precisely map inventory to an order. If you intend to do that, question will be where you take lock. When an item gets added to the cart, when user goes for payment or when payment is done. But the problem with lock is that it will make you system slow.
So that is something you should avoid.
Rest all the options you have already covered in your question and it boils down to tradeoffs.
First point, user experience will suffer and you also need to incur the cost of the transaction.
Second option ask you to be ready to undersell or oversell.
When you keep reserves, you are basically saying that I will be underselling. This can also backfire because say you decide to reserve 5 items but you get 20 concurrent request foir checkout and payment, you will be back to the square one. But it can help in most scenarios, given you are willing to take a hit.
Doing inventory check at checkout can help you get better resolution on inventory but it will not help when you literally have last item in inventory and 10 people doing a checkout on it. Read calls even for two such request coincides you will give them inventory and back to square one.
So what I do in such scenarios, is
1. My inventory goes not as number but enum i.e critical, low, med, high, very high
Depending on some analytics we configure inventory check. For high and very high we will not do any check and book the item. for critical and we take the lock. (not exactly a db lock but we reserve the inventory for them), for low and medium we check the inventory and proceed if we have enough. All these values are configurable and help us mitigate the scenarios we have.
Another thing that we are trying is to distribute inventory to inventory brokers and assign inventory broker to some set of services to see this inventory. Even if we reserve the inventory on one broker others can continue selling freely. And there brokers regularly update the inventory master about the status of inventory. Its like Inventory master has 50 items, it distributes 5 each to all ten. After 10 mins they come back and if they need more inventory they ask for it, if they have left over (in case of failure) they drop back inventory to the master for it to be assigned to others.
The above approach will not help you resolve the issue precisely but it gives you certain degree of freedom as to how you can manage the inventory.
Consider doing:
On the payment page, you should re-check if the product is still available. This can be a simple HTTP GET.
If the GET call is slow for you, consider caching recent product added by user to some in-memory databases (eg. REDIS). Now if first users successfully processes the payment, decrease counter for that product-id in redis. And before proceeding payment for second user, check the counter of that product-id in redis.
(BONUS: Redis offers atomic operations, so you can successfully handle the race condition in ordering the product as well.)
Related
I am trying to write a match maker for buying and selling items. Internally, I am using 2 HashMaps, 1 for buys and one for sells for each item i.e. if a user sends me a buy request I put it in my buy HashMap and vice versa. The key is the price while the value is a queue of orders at that price (so that I can entertain requests on a FIFO basis if they have the same price. Once I receive a request e.g. buy, I look in to the sell HashMap for any matches. Users can change the quantity or price of what they want to buy but they not the item itself e.g. can change a bike buying request's price or quantity but cannot change bike to boat)
I would like to make this multithreaded so multiple requests can be handled at the same time. So I made my hashmaps in to ConcurrentHashMaps and the queue in the value a ConcurrentLinkedQueue. However, there still can be concurrency issues e.g. I am looking in to the sell map to find a match for my buy request but while I am making the match that sell request gets amended by the user to, say, a different price.
How can I synchronize the two maps with eachother? I would like to lock the same segment (i.e. the queue at that price) in both maps at the same time.
Add single Map where you keep track of available stock, do your locks only there.
Note: Also ConcurrentHashMap doesn't lock single entry it locks bucket so there may be more elements.
I think the best option for you is to use ReentrantReadWriteLock. This will help the reader thread to read unless anybody is writing to the Map. You can also achieve the same using a synchronized block to perform all the operations, But that will not scale.
for ReentrantReadWriteLock doc Please check https://docs.oracle.com/javase/7/docs/api/java/util/concurrent/locks/ReentrantReadWriteLock.html
I have a ConcurrentMap which is my in-memory cache/database for my web app.
There I have stored my entity with the id as key.
This is how my entity basically looks like.
public class MyEntity {
private int id;
private String name;
private Date start;
private Date end;
...
}
Now I have multiple Users which requests different data from my map.
User1 has a filter for the start date. So for example he only gets item 1, 2, and 3 of my map. User2 has also a filter and only gets item 2, 3, 4, 5 of the map.
So they only get a part of the complete map. I am doing the filtering on my server because the map is to big to send the complete map and I need to check other attributes.
My problem now is that the entries in the map can be updated / removed / added from some other API calls and I want to live update the entries on the user side.
For now I am sending a notification to the users that the map has been updated and then every user loads the complete data which the user needs.
For example item 8 has been updated. User1 gets a notification and loads item 1, 2, 3 again even if the update was only on item 8. So in this case it would be unnecessary to update for User1 because he doesnt need item 8.
Now i am searching for a good solution so that the User only receives the necessary updates.
One way I was thinking about was to store temporarily all items id which the user requested. So on an update notification I can check if the item updated is in the list and then send the updated item to the user only if it is in the users list.
But I am concerning the memory usage that this will create in case I have a lot of users and the user list with the item ids can be very big too.
What would be a good solution to send only the added / updated / removed item to the user and only if the user needs that item?
So something like observing only a part of the base map (cache) but with a notification for every action like adding, updating and removing item.
Basically there is no "Silver Bullet". A possible solution depends on the usage patterns, your resources and requirements.
Questions to ask:
How big is the total dataset, now and in the future?
How big is the displayed dataset?
How many users with searches?
Is it a business application with a closed user group or a public web application that needs to scale?
What is the update frequency and what kind of updates are common?
How many different search patterns are present?
The biggest question of all:
Is it really needed?
Looking at a bunch of search interfaces, the user expects an update only when there is an interaction. If it is similar to a search, then the users will not expect an instant update. An instant update can be useful and "innovative", but you do spend a lot of engineering costs into that problem.
So before jumping onto the engineering task, make sure that the benefit will justify the costs. Maybe check on these alternative approaches first:
Don't do it. Only update on a user interaction. Maybe add a reload button.
Only notify the user that an update occurred, but only update when the user hits "reload". This solves the problem that the user may not have the browser tab in focus and the transfer is a waste.
But I am concerning the memory usage that this will create in case I have a lot of users and the user list with the item ids can be very big too.
In our applications we observe that there are not so many different search patterns / filters. Let's say you might have 1000 user sessions, but only 20 different popular searches. If that is true, you can do:
For each search filter, you can store a hash value of the result.
If you do the update of the main data, you run the searches again and only send an update to the user if the hash value changed.
If it is a public web application, you should optimize more. E.g. don't send an update when the application has no focus.
I am using parse.com cloud storage, to implement level sharing/downloading and rating for built in level editor for my game, so players are allowed to built and test their own created levels, latter on they can share it with different players, that`s how I upload it to the parse.com cloud storage:
ParseObject testObject = new ParseObject("Levels");
testObject.put("file", new ParseFile(name + ".lvl", levelString.getBytes()));
testObject.put("author", authorName);
testObject.put("email", authorEmail);
testObject.saveInBackground();
It works fine, but I wanted to let players also rate downloaded levels (lets say 1-5 stars) it could be simple, by creating new two fields called rating and ratings count, so every time someone will vote, I would add it to ratings count and would ++ ratings count.
Problem is, how to prevent player from rating particular level multiple times? Thanks.
I have thought about this for a project of mine. In the end you will need two data points.
You need to track the counts per rank on the object (Level in your case)
You need to track UserLevelRating, at minimum a reference to the user, reference to the target (Level), and the rating given (if you will let people change ratings)
Depending on how you want to implement it, to prevent rating something twice, or to allow people to change the rating they have given something, you would do a query for the current user and the Level. If a record is returned they have already voted, so prevent them from voting again.
You could add some cloud code using before-safe or after-save logic to handle other things, such as changing the vote and updating the counts on the target (Level).
Here's a sample of the logic I would use for a simple single vote system without changing votes:
Test for existence of UserLevelRating record, if it exists prevent voting
Saving vote, include User=current user, Level=selected level, Rating=stars given
Cloud code, in after-save of UserLevelRating, looks at Level property, loads the level, calls increment on the property for the rating (e.g. if Rating=3, increment("Stars3") would be called)
Anytime you load a Level object you would have counts for each rating, and could produce the average.
There are many similar questions related to my question. But I didnt find any satisfactory ans for my question. So I am putting this question in this forum.
I have a question in concurrency control in inventory management system.
Say I have products A, B, C with quantity 2,3,4. And my application is multi user.
I have product page where user see the list of products and available quantity.
and I have check out and payment page which may take some time to reach after product page.
Now if it is multi user web application and say user 1 has ordered 2 quantity product A but order is not yet placed, user 2 can still see A with 2 quantities.
Should I temporarily(configurable time) lock the 2 quantities of product A until order is placed? Is it a good design. If yes, should I lock in java or in the database?
No, it's not a good design because it lend itself for abuse and problems. What if a user (competitor?) locks all your products during the whole month? What if another person fills up his/her shopping cart with product and then decide it's too much money and just turns off his/her device?
Best alternatives are:
1) Tell the user how much availability is of each product, but also tell him/her it could be gone if an order is not placed soon. This should also incentive sales. Do lock/unlock your products after the payment page, i.e. when there is a business commit to the operation. If available quantities are not enough anymore, send the user back a previous page, updating the amounts to whetever is available.
2) Similar to 1), but you could also update availability from time to time. Or send warnings like "the stock of some items in your cart is running low". Again, this also could prompt sales.
3) Reserve ("lock") items as they are moved to a shopping cart, but not forever. Release the items after being locked for certain amount of time. Keep informed the user. The time-out can be per the whole shopping cart or per item.
It is very important to notice that any "lock" mentioned above is a "business lock/reservation". It doesn't need to be implemented in the form of locks or any other particular technical solution. For example, 3) above can be implemented by adding fields locked_by and locked_until. While you check/update/manipulate these fields you will probably need to do it within a "technical lock". After the checks/updates are done, the technical locks are released. However, a "business lock" could still in place because locked_until has not elapsed yet and any other code will check this field to consider the product available or not. Why? Because business rules mandate so (not because there is a technical lock in place, which in fact is not).
"Technical locks" should be very quick. "Business locks" can be much longer (but never forever; always define a time limit for them).
It's hard to tell if you should lock "in Java" on "in the database" with the very little information you give. Are you using Entity Beans, for example? What are your fault tolerance requirements? Etc.
That said, in the general case it's probably better to keep locks (as long as they are "business locks") in the database, for these main reasons:
Persistence (in case of a power failure). You should also provide a mechanism to recover shopping carts. Perhaps also storing them in the DB?
Ability to interface with other environments (i.e. a corporate ERP or fullfillment system).
Should I temporarily(configurable time) lock the 2 quantities of product A until order is placed? Is it a good design.
It depends on the conversation rate of your site, i.e. the number of checkouts/the number of payments. If you have a high conversation rate, you can pre lock the quantities to get a better user experience.
If yes, should I lock in java or in the database?
You need a global lock to guarantee the correctness. If you have multiple application servers, you have to put the lock in the database.
Inventory Management should be handled by a Custom System Built from ground up. You cannot rely simply on ACID compliance for performance reasons. Implementing Transactions on Inventory during the order is a very bad idea and is not scalable. I propose the following solution.
An Inventory Management Backend App that updates the inventory as new items come in. Use row lock to update the inventory.
An Inventory Management Micro Service App to give the inventory to Order Management System as it needs the inventory to finish the order and keep track of time out.
a. If the order finished with Acknowledgement, The given inventory is already deducted by management system. We are good.
b. If the order is not finished and no Acknowledgement is received, the given inventory is added back to the inventory system after the timeout (Typically 2 - 3 minutes).
c. If the order has failed and acknowledgement is received that the order is failed, the given inventory is added back to the inventory system.
So, you don't lock the product rows until the order is finished. Instead, you do a soft lock on inventory and release if the order was not successful or failed due to an exception/App Failure.
I'm currently ddeveloping a hospital managment system.
My application will have multi cashiers at the hospital who working 24/7, they collecting money from the patients & issuing a receipt voucher or returning money back to patient in case of refunding.
currently i'm storing the employee user account no for each transaction, at the end of the day the employee print a list of the transaction that he made with total of money he have to send to the accountant department.
but i need some one to tell me what is the polices needed to prevent any msitake with total amount that employee have in his/her cash box.
Do i need to prevent others from deleting or revesing the record even the head of the accounting department?
I thing i need something as bank tellers, but i can't find something to help me or explain to me what exactly i need to put in my polices for this part.
I'm using: Java as GUI & programming language, MySQL (Percona) as my database, Crystal Reports As my reporting & Printing solution.
I think you need to think about this a bit more atomically. Rather than there being transactions which can be edited or removed, just have actions which occur.
So sure have your people run a transaction in, and if someone wants to edit that transaction let them but do it as changeset, a bit like how version control systems work. So you still retain the information on the original transaction but then have the change that should be applied to that transaction.
If you have some base Id for the transaction/action and then each change set points to that then at least you can tie all changes together by date order and then run them to work out the total, which you can easily cache for performance purposes later on.
Whenever money is involved its VERY risky to allow editing or removal of data, which is why most places tend to retain all information and just either archive it as out of date and put a more up to date record in its place, or have some data trail of the original record and what has changed on it.
Ultimately it all comes down to tracability...