Automated Job Recommendations

January 17th 2016, 4:09 amCategory: Big Data 0 comments


   One of the most important foundations to companies to properly grow is to choose the perfect employees that fit their needs. Not only the technical skills but also their culture that fits their aspects. On the other side, choosing the most appropriate job for job-seekers is very important to advance their career and quality of life.


   Recruitment process has become increasingly difficult, choosing the right employee among plenty of candidates for each job, each having different skills, cultures and ambitions.

   Recommender system technology aims to help users find items that match their personal interests. So we can use this technology to solve the recruitment problem for both sides; companies, to find appropriate candidates, and job-seekers, to find favorable positions. So let's talk about what can science offer to solve this bidirectional problem.


   In the world of data science, the more information we can get, the more accurate results we may have. So let’s start with available information we can collect about job-seekers and jobs.

Job Seeker

  • Personal information, such as language, social situation and location.
  • Information about current and past professional positions held by the candidate. This section may contain companies names, positions, companies descriptions, job start dates, and job finish dates. The company description field may further contain information about the company (for example the number of employees and industry).
  • Information about the educational background, such as university, degrees, fields of education, start and finish dates.
  • IT skills, awards and publications.
  • Relocation ability.
  • Activities (like, share, short list)


  • Required skills.
  • Nice to have skills.
  • Preferred location (onsite, work from home).
  • Company preferences.

Information extraction

   To get all this information we may face another big challenge. Most of this information may have been included in a plain text (ex. resume, job post description, etc.). So, we need to apply some knowledge extraction techniques on those texts, so we can get a complete view about requirements and skills.


Informations enrichment

   A good matching technique requires more than just looking into explicit information only. For example, a job post that is defined to be looking for a candidate who has a knowledge about Java programming language while on the other side a candidate who has claimed knowledge with Spring framework, so if we are just looking for a candidate with explicit defined Java skill then this candidate will not be shown in the view, although he had an implicit Java skill by using Spring framework. To solve this problem we need to enrich both the job and candidate information by using a knowledge base that can link these two skills or at least knows that using Spring framework implicitly imply a Java skill. This will improve the accuracy by looking into the meanings and concepts instead of the explicit information only.



Let’s define some guidelines we need to take care of when working on the matching.

  • Matching of individuals to job depends on skills and abilities that individuals should have.
  • Recommending people is a bidirectional process, it should take into account the preferences of both recruiter and candidate.
  • Recommendations should be based on the candidate’s attributes, as well as the relational aspects that determine the fit between the person and the team members/company with whom the person will collaborating (fit candidate to company not only the job).
  • Must distinguish between must-have and nice-to-have requirements and improve their contribution with dynamic weights.
  • Use ontology to categorize jobs as a knowledge base.
  • Enrich job-seeker and jobs profiles with knowledge base (knowing Cakephp framework implies knowing also PHP).
  • Data normalization to avoid domination.
  • Learning from the others job transitions.

Recommendation Techniques

Let’s list some techniques used in recommendation fields, no technique is suitable for all cases, you need first to link it with type of data you have and your whole case.

  • Collaborative filtering
    • In this technique, we are looking for a similar behavior between job-seekers, so we can find job-seekers who have similar interests, and make job recommendations from their jobs of interest.
  • Content-based filtering
    • In this technique we are looking for profile’s content for both: the job-seeker and the job post, and get the best matching between them, regardless of the behavior of the job-seeker and the company that posted the job.
  • Hybrid
    • Weighted In which, the score of item recommendation is calculated from the results of all of used recommendation techniques that are available in the system.
    • Switching The system uses some criteria to switch between recommendation techniques.
    • Mixed In which large number of recommendations are applied simultaneously, so we can mix the results from both recommenders.
    • Feature Combination uses the collaborative information as additional feature data for each item and use content-based techniques over this improved data set
    • Cascade It comprises a staged process. In this technique, one recommendation technique is used first to produce a rough ranking of candidates and a second technique refines the recommendation.
  • 3A Ranking algorithm maps (job, company and job-seeker) to a graph with relations between them (apply, favorite, post, like, similar, match, visit, … etc), then depends on relations and ranking to recommend items.
    • Content base is used to calculate similarity between jobs, job-seekers and companies, and each of them with the other one (match profile between job and job-seeker).

General recommendation system Architecture

Figure 1 - General System architecture.


   To create a self improved system you need to get feedback for the results you produced to correct yourself over time. The best feedback you can get is the feedback from the real world, so we can depend on job-seekers and companies feedback to adjust the results as desired.

  • Explicit: Ask users to rate the recommendations (jobs / candidates)
  • Implicit: Track interaction on recommendations (applied, accepted, short list and ignored)

Further Reading

  • Proceedings of the 22nd International Conference on World Wide Web. Yao Lu, Sandy El Helou, Denis Gillet (2013). A Recommender System for Job Seeking and Recruiting Website.
  • JOURNAL OF COMPUTERS, VOL. 8. Wenxing Hong, Siting Zheng, Huan Wang (2013). A Job Recommender System Based on User Clustering.
  • International Journal of the Physical Sciences Vol 7(29). Shaha T. Al-Otaibi, Mourad Ykhlef (July 2012). A survey of job recommender systems.
  • Proceedings of the fifth ACM conference on Recommender systems. Berkant Cambazoglu, Aristides Gionis (2011). Machine learning job recommendation.

It's our pleasure to highligh the initiative taken by our data team leader Ahmed Mahran to effectively contribute to the Spark Time Series project, created by Sandy Ryza, a senior data scientist at Cloudera, the leading big data solutions provider.


Time Series data has gained an increasing attention in the past few years. To quote Sandy Ryza:


Time-series analysis is becoming mainstream across multiple data-rich industries. The new Spark-TS library helps analysts and data scientists focus on business questions, not on building their own algorithms.


Find the full story here, where he introduces SparkTS, and accredits our contributor.


We are, forever, indebted to the open source community, it enabled us to create wonderful feats. It's our deep belief that we should give back to the community in order to guarantee its health and sustainability. We are proud that we effectively contributed to such great project and we are looking forward to more.

    In search space, pagination always has to happen. Solr has the feature of basic paging. In basic paging, you simply specify start and rows parameters. start indicates where the returned results should start and rows specifies how many documents are returned. With basic paging, partial index exporting and migration is a problem. Since basic paging needs to sort all the results first before returning the desired subset, it needs large amount of memory if start is of high order. For instance, start=1000000 and rows=10 causes an inefficient memory allocation to happen due to the sorting of 1000010 documents. In a distributed environment, the case is worse because the engine has to fetch 1 million documents from each shard, sort them then return the result set.

Shared storage and HDFS (pros and cons)

Shared storage in a distributed compute cluster

Figure 1 - Shared storage in a distributed compute cluster.

HDFS was built to scale out using disks directly attached to commodity servers so as to form a basis for a distributed processing framework that exploits the data locality principle - moving code to data is much more cheaper than the other way around. Hence, incorporating a shared storage system, as opposed to directly attached storage (DAS), in order to host the HDFS is not straightforward as centralized storage is not the nature of the HDFS.


Moreover, shared storage systems have some potential limitations. As a shared service, it needs to efficiently handle multiple concurrent IO requests from the numerous clients of the compute cluster. Also, the network is the channel for communicating data from and to the shared storage, the thing that would consume much of the network’s bandwidth. While in a DAS model the network is the communication channel as well for sharing data, data local computations reduce network bandwidth consumption significantly. Moreover, comparing network bandwidth to the regular internal per-machine data busses, data busses are faster as, besides the underlying hardware technology, they don’t incur the overhead imposed by the networking stack which constitutes the network infrastructure and protocols (layered protocols headers, checksum computations …).


On the other hand, shared storage systems have unique attractive features that worth to be considered either while constructing a new distributed data processing cluster or when there is a need to exploit an existing infrastructure with a pre-installed shared storage system. One important feature is separating compute from storage resources. This implies independent administration and scalability of resources; it would be possible to scale only one resource type, either compute or store, without unnecessarily scaling the other. In addition to that, compute resources will be almost fully utilized in computations rather than taking the overhead of servicing shared data to other consumers. The thing that would ensure efficiency and high availability for data sharing.


Moreover, as a plus, shared storage systems provide application agnostic features releasing more overhead from compute resources while adding more strengths to the cluster. Those features like: security, backups and snapshots, efficient capacity utilization via compression, enhanced IO performance by leveraging the latest storage and network technologies, and failure handling and high availability. So, with shared storage system, the namenode is no more a single point of failure as the metadata could be backed up safely without the need for other namenodes designed as part of the Hadoop HA (High Availability) feature. Besides, data replication, as an inherent feature in shared storage systems, relieves block replication burden off HDFS daemons saving CPU, memory and network bandwidth as lesser block replications simplifies write pipelines and downsizes block pools.


How HDFS works with shared storage

For HDFS, we can categories shared storage systems into two main groups: HDFS aware and HDFS agnostic. HDFS aware storage systems are those that provide native support for HDFS sometimes to the extent that HDFS daemons are running inside the box of the storage system. In that case, HDFS is readily tuned for the shared storage system, moreover, the rest of the cluster resources become pure compute resources. Alternatively, HDFS agnostic storage systems don’t know about the existence of the HDFS hence their incorporation has much more involvement.


For an HDFS agnostic shared storage system, aside from the recommendations of the storage system provider, a typical deployment would be to allocate separate storage partitions for each host in the cluster and to distribute HDFS daemons such that one or two master hosts to have the namenode and the secondary namenode daemons and each of the rest of the slave hosts to have at least one datanode daemon. Given such configurations, let’s inspect the cost per read and write operations.

Data flow while Host 2 performs a write with replication factor of 2

Figure 2 - Data flow while Host 2 performs a write with replication factor of 2.

Figure 2 illustrates the data flow if a client on Host 2 is writing two replicas, one local on Host 2 and another remote on Host 1. The following are the paths for the data transmission:

  1. The client on Host 2 transmits the data to the colocal data node over kernel network.
  2. The data node on Host 2 transmits the data to the shared storage over network.
  3. The data node on Host 2 transmits the data to the data node on Host 1 over network.
  4. The data node on Host 1 transmits the data to the shared storage over network.


The same data is transmitted over network three times and is written on the same storage twice. Hence, write operations are very expensive in terms of network bandwidth and storage capacity consumption. It is meaningless to consider block replication on a shared storage especially if the shared storage system readily provide data replication.

Data flow while either hosts performs a read

Figure 3 - Data flow while either hosts performs a read.

In Figure 3, if a client on Host 1 is reading a block maintained by the colocal data node, the following are the possible data transmission paths:

  1. The data is transmitted over network from the shared storage to the data node on Host 1 (link C).
  2. The data node on Host 1 transmits the data to the colocal client (link A) over kernel network.


However, if a client on Host 2 reads a block only maintained by the remote data node on Host 1, possible paths are:

  1. The data is transmitted over network from the shared storage to the data node on Host 1 (link C).
  2. The data node on Host 1 transmits the data over network to the remote client on Host 2 (link B).


In a remote read, the same data is transmitted over network twice while only once in local reads. Hence, remote reads are expensive than local reads in terms of network bandwidth consumption. The following table summarizes the cost of data transmission in the different scenarios discussed above.


Operation Data Transmission Paths (Cost)
Network Kernel Network
Local write (one replica) 1 1
Remote write (one local replica and one remote replica) 3 1
Local read 1 1
Remote read 2 0


Increasing block replication, increases data locality and read performance however decreases the write performance and wastes storage. It would be great if there is a way to minimize the length of data paths by taking shortcuts.


Short-circuit reads

Short-circuiting local and remote reads from a shared storage

Figure 4 - Short-circuiting local and remote reads from a shared storage.

Short-circuit local reads


HDFS provides one shortcut for local reads. If the client is reading a file from a collocated datanode, the client can read the file directly from the disk bypassing the datanode. In figure 4, a client on Host 1 requests to read a file from the colocal datanode. Then, the datanode redirects the client to the required block in the filesystem and the client reads the file directly from the shared storage over network.


Short-circuit remote reads

By analogy from the short-circuit local read, a short-circuit remote read is defined when a client requests a read from a remote datanode, then the remote datanode provides direct access to the file for the client. This could be a valid scenario if only the data is stored on a shared storage system to which all hosts have direct access. However, HDFS is not aware whether it’s hosted on a shared storage or not. Hence, HDFS is not providing a shortcut for remote reads but it is still possible to hack around it.


HDFS client knows whether it’s colocal with a datanode by checking the datanode IP address against the list of network interfaces on the same host as the client. If the datanode IP is one of them, the client knows that the datanode is colocal. Then, there are two possible ways for the client to get the file: the legacy short-circuit read, or the new short-circuit read. In the legacy implementation, the client requests, through TCP, the absolute path of the block from the datanode. Then, the client opens the block file and reads the content directly after receiving the path info from the datanode. While in the new implementation, the client connects to the datanode through a Unix domain socket. Then, the datanode opens the file and bypasses the file descriptor via the Unix domain socket to the client. After that, the client can consume the file directly through the descriptor.


HDFS short-circuit read mainly involves the following three actions:

  1. Datanode locality check
  2. Interprocess communication through Unix domain socket (new implementation only)
  3. File direct access


Datanode locality check: It is possible to come around this check by adding, to each host, a dummy network interface, one for each remote datanode. Each dummy interface is assigned the same IP address as the corresponding remote datanode. It’s important to hide these interfaces from network so as to be able to reach the real datanodes.


Interprocess communication through Unix domain socket: A Unix domain socket works on the kernel level and connects parties on the same host only. Processes meet at local files but share data through memory. To the extent of our knowledge, it’s possible to forward a Unix domain socket between two hosts hence enabling two remote processes to communicate through the Unix domain socket. For example, OpenSSH can do Unix domain socket forwarding. Hadoop provides only one configuration parameter for setting the socket address (dfs.domain.socket.path) such that the datanode and the DFS client on the same node use the same socket address which makes it difficult to forward the DFS client connection to the appropriate datanode. So, it’s not a straightforward task to hack the short-circuit new implementation for short-circuiting remote reads.


File direct access: Clients can have direct access to files maintained by a remote datanode if the files are hosted in a shared file system to which all hosts have access. Each datanode should be assigned an independent data directory that is hosted on a shared file system and that have the same mount point on all hosts. For example, datanode 1 on Host 1 is assigned a data directory /hdfs/data1/ and datanode 2 on Host 2 is assigned a data directory /hdfs/data2/. Both directories are hosted by a shared file system (say NFS) and are mounted on each host at the mount points /hdfs/data1/ and /hdfs/data2/, respectively.


Fortunately, we can hack around the legacy implementation of short-circuit local reads as it does not involve any Unix domain communication. The following table summarizes the cost of data transmission in the different read scenarios after enabling short-circuit remote reads. Unfortunately, HDFS does not provide a shortcut for writes hence the write cost stays the same.


Operation Data Transmission Paths (Cost)
Network Kernel Network
Local read 1 0
Remote read 1 0


Now, local and remote reads have the same reduced cost. This is a substantial improvement to HDFS for reading from a shared storage.


Proof of concept

Experiment Setup

Short-circuit remote read experiment setup (SCRR is a shorthand for Short Circuit Remote Read)

Figure 5 - Short-circuit remote read experiment setup (SCRR is a shorthand for Short Circuit Remote Read).

As illustrated in figure 5, we build a small cluster of three machines with the following configurations. SCRR3 is the master node hosting an NFS server simulating the shared storage and exposing two locations (/home/host/hdfs1 and /home/host/hdfs2) as the storage directories for the cluster datanodes. It also hosts the cluster namenode and secondary namenode. SCRR2 and SCRR1 are both slave nodes each one hosting a datanode. The datanode on SCRR1 is configured with a storage directory that is hosted on the shared storage and mounted on /home/host/hdfs1. Also, the datanode on SCRR2 has a storage directory that is hosted on the shared storage however mounted on /home/host/hdfs2. Both storage directories are mounted on both nodes (SCRR1 and SCRR2) on the same mount points such that a datanode directory could be accessed on both nodes through the same path.


In our experiment, we assign each node a specific role. SCRR3 has the shared storage role, SCRR2 has the DFS client role and SCRR1 has the datanode role. We configure the HDFS cluster with minimum replication factor of one and we enable the legacy short circuit local read. We shutdown the datanode on SCRR2 and configure a dummy network interface with the same address of the node on SCRR1. We upload a file on the HDFS cluster (that would be kept by the datanode on SCRR1). We then run a simple DFS client on the node SCRR2 that reads the file byte by byte counting the number of bytes read. After the DFS client finishes we disable the short circuit remote read configuration by deleting the dummy network interface and then we re-run the same DFS client. We repeatedly run the same DFS client alternating between the two modes (short circuit enabled and short circuit disabled) while watching the network bandwidth consumption.



Network bandwidth consumption on each node.

Figure 6 - Network bandwidth consumption on each node.

Figure 6 shows the network bandwidth consumption on each node while performing the experiment. The short circuit is enabled in runs (B, D and F) and is disabled in runs (A, C, E and G). The following table summarizes the network bandwidth consumption on each node in terms of data transmission and reception rates as well as what data is being communicated. It is clear enough how short circuiting remote reads from shared storage reduces network bandwidth consumption significantly, not to mention the applications running time.


Run Label Datandoe DFS Client Shared Storage
Receive Transmit Receive Transmit Receive Transmit
A High
(data from shared storage)
(data to DFS client)
(data from datanode)
(acks …)
(acks …)
(data to datanode)
B Idle
(short circuit)
(short circuit)
(data from shared storage)
(acks …)
(acks …)
(data to DFS client)
C, E, G Low
(acks … data in OS cache)
(data to DFS client)
(data from datanode)
(acks …)
(data cached in datanode side)
(data cached in datanode side)
D, F Idle
(short circuit)
(short circuit)
(data in OS cache)
(data in OS cache)
(data cached in DFS client side)
(data cached in DFS client side)

Appendix: Dummy network interface configuration on Linux:

Assume a cluster of three slaves


Host IP Address MAC Address
Host1 00:00:00:00:00:01
Host2 00:00:00:00:00:02
Host3 00:00:00:00:00:03

  1. On each host, create a dummy network interface and assign it a suitable name, one for each remote datanode. For example, on Host1:
    sudo ip link add dummyDataNode2 type dummy
    sudo ip link add dummyDataNode3 type dummy
  2. On each host, assign the same address of the real datanode for each device. For example, on Host 3:
    sudo ifconfig dummyDataNode1
    sudo ifconfig dummyDataNode2
  3. Make sure that all machines have the MAC address of the real datanode. For example, on Host 1, assuming eth0 is the interface through which Host 1 is supposed to communicate with HDFS cluster network:
    sudo arp -s 00:00:00:00:00:02 -i eth0
    sudo arp -s 00:00:00:00:00:03 -i eth0
  4. Make sure that all machines have a route to the real datanodes through the correct interface. For example, on Host 2:
    sudo route add -host dev eth0
    sudo route add -host dev eth0
  5. On each host, hide the dummy interface from the network by disabling ARP for the dummy device and deleting all routing entries that involve the dummy device. Use both the route and ip-route commands to query for the routing entries involving the dummy device. For example, on Host 1:
Switch off ARP on the devices so as not send nor to respond to any ARP requests:
	sudo ifconfig dummyDataNode2 -arp
	sudo ifconfig dummyDataNode3 -arp

  Query routes with route command:

	Kernel IP routing table
	Destination  Gateway    Genmask         Flags Metric Ref    Use Iface
	default         UG    0      0        0 eth0      *   U     1      0        0 eth0      *   U     1      0        0 dummyDataNode2      *   U     1      0        0 dummyDataNode3

  Delete the routing entries associated with the dummy interfaces:
	sudo route del -net netmask dev dummyDataNode2
	sudo route del -net netmask dev dummyDataNode3

  Query routes with ip-route command for each dummy interface:
	ip route show table local dev dummyDataNode2

	broadcast  proto kernel  scope link  src 
	local  proto kernel  scope host  src
	broadcast  proto kernel  scope link  src
	ip route show table local dev dummyDataNode3

	broadcast  proto kernel  scope link  src
	local  proto kernel  scope host  src
	broadcast  proto kernel  scope link  src

  Delete the routing entries associated with the dummy interfaces:
	sudo ip route del table local dev dummyDataNode2 to local
	sudo ip route del table local dev dummyDataNode2 to broadcast
	sudo ip route del table local dev dummyDataNode2 to broadcast
	sudo ip route del table local dev dummyDataNode3 to local
	sudo ip route del table local dev dummyDataNode3 to broadcast
	sudo ip route del table local dev dummyDataNode3 to broadcast


Read More:

A RAM disk is a proportion of the RAM that is treated as if being a disk storage. That is, it could host a file system through which applications can deal with files in the same way as dealing with files on other disks, using the same file API, however in a much more performant manner.


RAM is the fastest storage medium accessible by applications when compared against other storage mediums (especially HDD and SSD). However, as a very expensive storage medium, RAM is a scarce memory resource that has a relatively limited capacity (measured in gigabytes while the capacity of other storage mediums are measured in terabytes). Moreover, it’s a volatile memory that loses data after a system restart, shutdown or other power loss scenarios. It’s important to keep these limitations in mind while thinking of a way to leverage the outstanding performance of the RAM disk.

  • Scarcity limits the amount of data stored, the thing that could bring a solution idea down. However, limits could be pushed further by paying more money for more capacity as RAMs are now in hundreds of gigabytes.

  • The volatility of RAM could bring a solution idea up or down; RAM does not fit for persistent data however it fits for volatile temporary data.

  • RAM performance is always a win for the solution. It’s the reason to why we think of RAM disks.

In the big data applications domain, we list two possible generic use cases:

  • Storing temporary and intermediate discardable data

  • Storing persistent data backed by persistent storage medium

Storing temporary and intermediate discardable data:

In the big data applications domain, most of the temporary and intermediate data is volatile in nature; it is nonpermanent (by definition) and discardable such that the existence of the data is tied with the existence of the application. As long as the application is running, this data is being generated and is being kept such that its existence is mandatory for the application to finish processing. Once the application is done processing, it becomes not necessary to keep this data and hence it becomes discardable.


Moreover, the discardability of the temp data, in the majority of the big data applications, is extended much more beyond that; it is not only discardable after the termination of the application but also during the lifetime of the application as well. That is, an application can regenerate this data (as a whole or in parts) if for some reason the data is lost. In general, this is a design principle for big data frameworks.


In a mapreduce framework, shuffle files are intermediate files that are written to disk and transferred through network. They are volatile in the same way described above. However, these data are comparable in size to the input data. Hence, in almost all the situations, it’s as big as the big input data unless the processing framework is doing something more with the data; something like writing these files compressed or partitioned either across time (i.e. processing one partition at a time) or across several machines. Writing these files to a RAM disk would definitely increase the shuffle performance especially when multiple jobs with different workloads are running.

 RAM Disk VS App and OS Buffers

It worth mentioning that there are at least two levels of buffering when making disk IOs: the application level buffers and the OS level buffers. Making the appropriate configurations for both levels, it’s possible to reach the same level of performance like that of the RAM disk. However, it’s not always possible to make the appropriate configurations; the application buffers capacity differs from an application to another and from a workload to another while the OS buffers are affected by other IO operations from the same or other applications. So, having an adaptable dedicated buffer for the temp volatile data would most often assure the required performance level.


Storing persistent data backed by persistent storage medium:

There are situations when computing reusable data is very expensive in terms of the consumption of computing resources. Moreover, such expensive data could be used frequently afterwards (for example, by other analytical tasks and low-latency jobs). Hence, persisting this data becomes mandatory to save computation resources and to assure real time performance of its consumers.


In order to devise a scheme for persisting such data, a number of factors should be considered. For how long would that data be persisted? How frequently would that data be consumed? Would the data be shared across different applications? If the data is required for a relatively long period, a persistent storage medium is obligatory. However, if the data is being consumed frequently, a reasonably fast storage medium is prefered. In addition to that, sharing data efficiently across applications is not straightforward. If there is a storage medium with which we can satisfy the three aspects of persistency, usage frequency and sharing, it would be perfect for that case. 

 RAM Disk Backed by Persistent Disk

All these aspects could be satisfied by leveraging different storage types in a tiered storage model. At the top tier, the RAM disk resides while being backed at lower tiers by persistent storage. RAM disk would assure high performance while the persistent storage would assure persistency. If it happens that data in the RAM disk is lost, with a suitable failover plan, data could be restored from the persistence storage. Also, sharing data among applications becomes a matter of reading and writing files which simplifies the implementation of applications. 


A simple implementation for the tiered storage model, is to save data into two locations: a copy in the RAM disk and another copy in the persistent disk. Then for a consumer application, it could first try reading the data from the RAM disk. If it’s not found, the consumer application could read it from the persistent disk while at the same time rewriting it to the RAM disk for consequent usages.


It worth mentioning that Tachyon is an in-memory distributed file system that is backed by HDFS. Also, by the time of writing this article, HDFS is being developed in phases in order to support heterogeneous storage types (like RAM disk, regular disk, SSD …) and different data storage policies (like hot data, warm data, cold data …). Both features coupled allow the implementation of the tiered storage system we seek.


Read More: