package com.interview.multithreaded;
import java.util.Queue;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
/**
* Given a queue which gets millions of messages. Message is of form <Domain,Update>.
* You have 10000 domain tables. Also you have 50 worker threads. You can only get
* data from front of the queue. Threads get data from the front and then update the
* domain table. If work is being done on domain table you cannot apply another update.
* Update should also be applied sequentially. So an update coming later on should not
* be applied before an update coming sooner.
*/
class Data{
private String domain;
private String update;
public String getUpdate() {
return update;
}
public String getDomain() {
return domain;
}
}
interface DomainLock{
boolean acquireLock(String domain);
boolean releaseLock(String domain);
boolean isLocked(String domain);
}
class ThreadPoolManager{
private ConcurrentMap<String, Queue<Data>> domainQueueMap = new ConcurrentHashMap<>();
public ThreadPoolManager(int numOfThreads){
//initialize numOfThreads of type ThreadWorker;
}
}
interface DatabaseLayer{
public void applyUpdates(String domain,String update);
}
class ThreadWorker implements Runnable{
private QueueManager mgr;
private ConcurrentMap<String,Queue<Data>> domainQueueMap;
private DomainLock domainLock;
private DatabaseLayer databaseLayer;
public ThreadWorker(QueueManager mgr){
this.mgr = mgr;
}
@Override
public void run() {
while(true){
Pair p = mgr.getDataFromFrontOfQueue(domainLock);
if(p.yourLock){
Queue<Data> queue = domainQueueMap.get(p.data.getDomain());
if(queue != null){
while(queue.size() > 0){
Data data = queue.poll();
databaseLayer.applyUpdates(data.getDomain(), data.getUpdate());
}
}
databaseLayer.applyUpdates(p.data.getDomain(), p.data.getUpdate());
queue = domainQueueMap.get(p.data.getDomain());
if(queue != null){
while(queue.size() > 0){
Data data = queue.poll();
databaseLayer.applyUpdates(data.getDomain(), data.getUpdate());
}
}
domainLock.releaseLock(p.data.getDomain());
//check if queue is not empty
//if queue is not empty try to acquire lock again
}else{
if(domainQueueMap.containsKey(p.data.getDomain())){
Queue<Data> queue = domainQueueMap.get(p.data.getDomain());
queue.offer(p.data);
}
}
}
}
}
interface QueueHandle{
//this is a blocking call. If there is no data in the queue it just waits for data to be available
public Data getNextData();
}
class Pair{
Data data;
boolean yourLock;
}
class QueueManager{
private QueueHandle queueHandle;
public QueueManager(QueueHandle queueHandle){
this.queueHandle = queueHandle;
}
public synchronized Pair getDataFromFrontOfQueue(DomainLock domainLock){
Data data = queueHandle.getNextData();
boolean yourLock = false;
//if lock for table does not exists or if it is false lock the table
if(!domainLock.isLocked(data.getDomain())){
domainLock.acquireLock(data.getDomain());
yourLock = true;
}
Pair p = new Pair();
p.data = data;
p.yourLock = yourLock;
return p;
}
}
public class SingleQueueDomainTableUpdate {
}