Low level design: webpage visits counter
See the problem statement here: https://codezym.com/question/6
This is an easy question yet it gives you a taste of basic data structures to use in a multithreaded environment. This is an ideal question to start your low-level design interview preparation.
Basically, you have the keep count of webpage visits for all webpages of a website and fetch counts.
Increment page visit count:
incrementVisitCount(int pageIndex)
Get visit count of a page:
getVisitCount(int pageIndex)
Data Storage:
Data storage for this problem can be done in multiple different ways, let's discuss them one by one.
Let's assume that total number of webpages on the website is
n
.Keep all the counts in an
ArrayList
of sizen
:ArrayList<Integer> pageVisitCounts
We can lock the
pageVisitCounts
usingsynchronized
keyword for eachincrementVisitCount
andgetVisitCount
request.This solution will work correctly in a multithreaded environment but it's inefficient.
Since at any time, only one thread can increment or fetch visit count of a webpage. So we are missing the benefits of our multiprocessor hardware.
Use a
ConcurrentHashMap
to keep countsConcurrentHashMap<Integer, Integer> pageVisitCounts
This is basically a page index vs visit count map and it is more efficient than above
ArrayList
ofIntegers
.
Although ConcurrentHashMap
takes more space than an ArrayList
of integers but its default concurrency level is 16, which means at max 16 threads can call incrementVisitCount
for different webpages at the same time, while using above ArrayList
with synchronized
, only one thread was able to update page visit count at a time, so this is much more efficient.
Use an ArrayList of AtomicInteger
ArrayList<AtomicInteger> pageVisitCounts
This data structure allows even more parallelism than a ConcurrentHashMap.
Now you can update visit counts for each page parallelly, e.g. if there are 1000 pages in the website then at a time at max 1000 threads can call
incrementVisitCount
for different webpages.AtomicInteger
takes care of the update in a multi-threaded environment.In this case this data structure is simple to use and more efficient than other two data structures we discussed above. We have used this in our final solution.
Please do jump into comments for further discussion on other ways to solve this problem.
Complete Code :
- You can run and test below code on CodeZym: https://codezym.com/question/6
// ****** It's better to write code in your local code editor and paste it back here *********
// put all import statements at the top, else it will give compiler error
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.ConcurrentHashMap;
/**
* - All the methods of this class will be tested in a MULTI-THREADED environment.
* - If you are creating new interfaces, then they have to be declared on the top, even before class Solution, else it will give class not found error for classes implementing them.
* - use helper.print("") or helper.println("") for printing logs else logs will not be visible.
*/
public class Solution implements Q06WebpageVisitCounterInterface {
private Helper06 helper;
private ArrayList<AtomicInteger> pageVisitCounts;
// constructor must always be public, don't remove the public keyword
public Solution(){}
/**
* Use this method to initialize your instance variables
*/
public void init(int totalPages, Helper06 helper){
this.helper=helper;
pageVisitCounts = new ArrayList<>();
ConcurrentHashMap<Integer, Integer> map = new ConcurrentHashMap<>();
for(int i=0;i<totalPages;i++)pageVisitCounts.add(new AtomicInteger(0));
// helper.println("restaurant rating module initialized");
}
/**
* increment visit count for pageIndex by 1
*/
public void incrementVisitCount(int pageIndex) {
pageVisitCounts.get(pageIndex).incrementAndGet();
}
/**
* return total visit count for a given page
*/
public int getVisitCount(int pageIndex) {
return pageVisitCounts.get(pageIndex).get();
}
}
// uncomment below code in case you are using your local ide like intellij, eclipse etc and
// comment it back again back when you are pasting completed solution in the online CodeZym editor.
// if you don't comment it back, you will get "java.lang.AssertionError: java.lang.LinkageError"
// This will help avoid unwanted compilation errors and get method autocomplete in your local code editor.
/**
interface Q06WebpageVisitCounterInterface {
void init(int totalPages, Helper06 helper);
void incrementVisitCount(int pageIndex);
int getVisitCount(int pageIndex);
}
class Helper06 {
void print(String s){System.out.print(s);}
void println(String s){System.out.println(s);}
}
*/