<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en"><generator uri="https://jekyllrb.com/" version="3.10.0">Jekyll</generator><link href="https://www.codingnirvana.in/feed.xml" rel="self" type="application/atom+xml" /><link href="https://www.codingnirvana.in/" rel="alternate" type="text/html" hreflang="en" /><updated>2026-10-31T13:01:20+05:30</updated><id>https://www.codingnirvana.in/feed.xml</id><title type="html">Rajesh Muppalla</title><subtitle>Work, writing and hobbies. Board games, photography, badminton, quizzing and AI.</subtitle><author><name>Rajesh Muppalla</name></author><entry><title type="html">Introducing the Indix Technology Radar</title><link href="https://www.codingnirvana.in/2017/introducing-the-indix-technology-radar/" rel="alternate" type="text/html" title="Introducing the Indix Technology Radar" /><published>2017-04-10T00:00:00+05:30</published><updated>2017-04-10T00:00:00+05:30</updated><id>https://www.codingnirvana.in/2017/introducing-the-indix-technology-radar</id><content type="html" xml:base="https://www.codingnirvana.in/2017/introducing-the-indix-technology-radar/"><![CDATA[<p><img src="/assets/img/indix/tech-radar-1.png" alt="Technology Radar" /></p>

<p>About 10 years ago, I was working on Microsoft Technologies at a startup. Making technology choices was not very hard back then. Microsoft had a library, product, or framework to fulfill every developer need: database — SQL Server, IDE — Visual Studio.NET, language — C#, framework — .NET, source control — TFS, deployment — NANT scripts. We would hardly debate on these choices. Most of the technical discussions would be around design and architecture.</p>

<p>However, things have changed dramatically since then. There are a lot more choices today especially with open source technologies. Having choices is good, but the wrong choice can put a project in trouble. You can balance some of it with sound architecture or you can leave it until the last possible moment, but there is always a risk when the wrong technology choice is made.</p>

<p>In most organizations, these choices are mostly ad-hoc without a formal process. The decisions behind the choices are neither documented well nor are they communicated well to other teams or external stakeholders. And when a different team needs to choose a technology for a similar use case, they follow the same ad-hoc process and more often than not make the same mistakes.</p>

<p>We were seeing this happen at Indix as well. Given the fact that we work on some hard problems in the area of distributed systems, machine learning, and data infrastructure, we have a lot of choices to make, and not all technologies have reached a level of maturity that make them a no-brainer for most use cases.</p>

<p>So we were looking for a lightweight framework that would help us assess technology choices in a more formal and objective way rather than in an ad-hoc way. We also wanted a way to communicate these choices in the best possible way across teams as well as to non-technical folks. Finally, we wanted something that would provide us with a visual snapshot of all the technologies we were using and where they were in the adoption life cycle at any given point of time.</p>

<h3 id="indix-techradar">Indix Tech Radar?</h3>

<p><a href="https://www.thoughtworks.com/">ThoughtWorks</a> has been publishing their <a href="https://www.thoughtworks.com/radar">Technology Radar</a> since April 2010. According to their <a href="https://www.thoughtworks.com/radar/faq">FAQ</a>,</p>

<p>“The radar is a document that sets out the changes that we think are currently interesting in software development — things in motion that we think you should pay attention to and consider using in your projects. It reflects the idiosyncratic opinion of a bunch of senior technologists based on our day-to-day work and experiences.”</p>

<p>If we could take the concepts from the radar and customize them to our technology stack, it seemed like it could alleviate some of the issues around assessing and communicating technology choices across Indix. Fortunately for us, an <a href="http://nealford.com/memeagora/2013/05/28/build_your_own_technology_radar.html">article</a> by Neal Ford outlining the process to build your own technology radar and an open source <a href="https://github.com/thoughtworks/build-your-own-radar">repository</a> for visualization gave us a good head start.</p>

<h3 id="what-are-the-components-of-a-technology-radar">What Are the Components of a Technology Radar?</h3>

<p>A Technology Radar helps you visualize technologies on a radar graph. There are three major components — blips, quadrants, and rings.</p>

<p><img src="/assets/img/indix/tech-radar-2.png" alt="Quadrants, rings and blips of a technology radar" /></p>

<p>There are four quadrants in a radar — Tools, Platforms, Languages and Techniques, and Frameworks. Each technology is called a blip and is put into one of these quadrants. The quadrants can be customized according to your technology stack.</p>

<p>Each of these quadrants are further subdivided into rings. There are four rings and a blip can belong to only of these rings. The four rings are:</p>

<p>1) <strong>Hold</strong>: technologies in this ring are not to be used for starting anything new in any of our projects. These typically fall under two buckets — technologies that were once used heavily earlier but have now reached their end of life, and technologies that never went mainstream due to reasons like lack of adoption, lack of features, or availability of a better alternative. There are also cases where due to the licensing restrictions, for example GPL, we might put a framework or library on hold.</p>

<p>2) <strong>Assess</strong>: blips in this ring need research. We have heard about them in the community or our developers have tried them in their pet projects, but the judgement on whether these technologies are here to stay or not is still reserved until it we try it in a real project.</p>

<p>3) <strong>Trial</strong>: these blips are mature enough to be battle tested in projects. At least one of the teams should be using it to understand the benefits and limitations.</p>

<p>4) <strong>Adopt</strong>: blips in this ring should be followed and used. ThoughtWorks describes these as “as close to no-brainer as possible”. Also, nothing moves directly into Adopt. Every blip has to go through at least the Trial ring before it moves to Adopt.</p>

<h3 id="building-theradar">Building the Radar</h3>

<p>At Indix, we have quarterly <a href="https://www.indix.com/blog/life-at-indix/highlights-from-second-indix-internal-hackathon-of-2016/">hackathons</a> for two days, where everyone works on their own ideas that they think will make the product better or improve efficiency across a function like engineering, for example. I used that opportunity to build an alpha version of the DevOps and Front End Development radars on a mind-map (see image below), and subsequently demoed it during the hackathon.</p>

<p><img src="/assets/img/indix/tech-radar-3.png" alt="Alpha version of the radars as a mind map" /></p>

<p>Next part of the process was to get opinions from a larger audience at Indix. I used our bi-weekly engineering leads meetings as a forum for further feedback. Over the course of four such meetings, which I moderated, we debated about everything from the quadrants to the rings that blips should be in. We had some great discussions during those meetings. When opinions were shared, it was easy for me as a moderator but when they differed, my job was to make sure that we all reached a consensus.</p>

<p>The next step was to add a short description and our opinion about each of the blips in the radar. That job was taken over by the respective leads responsible for DevOps and Front End Development at Indix.</p>

<h3 id="usage-of-the-radar-atindix">Usage of the Radar at Indix</h3>

<p>One of the obvious uses of the radar at Indix is for making technology choices. The radar has helped us formalize our opinions on technologies across the entire organization. It has allowed engineers to make choices quickly in cases where earlier there would be lot of time spent in debates, discussions, or in evaluations.</p>

<p>A good example of this is the DevOps Radar under the Configuration Management (CM) section in the Tools quadrant. We were heavily invested in Chef-Server as our default choice for CM, but it had issues with stability and maintenance primarily due to multiple moving parts involved. One of the teams trialed Ansible with good success and a couple of other teams moved to it. Since we had two options for CM and because it wasn’t formally known to everyone what the official organization stand on those two was, it wasn’t obvious what to choose as CM while starting a new project. The radar helped us formalize it when Chef-Server was moved to Hold and Ansible was moved into the Adopt ring.</p>

<p>Another useful area has been around prioritizing technical debts. The radar has been used as a guidance by teams to prioritize code bases that are using technologies in the Hold ring to migrate to using technologies in the Adopt ring.</p>

<p>We have also used the radar during interviews where we compared our radar to what a candidate has worked on to understand their breadth, and then go deeper into some of those technologies to understand their opinions, thereby allowing us to assess their depth of knowledge.</p>

<h3 id="whats-next">What’s Next?</h3>

<p>We are publishing two radars today — DevOps and Front End Engineering. <a href="http://oss.indix.com/indix-radar/">Click here</a> to browse through them. The code used to build them is available <a href="https://github.com/indix/indix-radar">here</a>.</p>

<p>We would like to maintain a cadence of updating these radars every six months based on what we learn. In addition to these radars, we are also planning to release a radar for Big Data in the coming weeks. Stay tuned.</p>

<p>Please note that the opinions about technologies are our own and are based on our usage. However, feel free to raise an issue or post a comment if you think something is not right, or if you think there is a different technology we should look at as an alternative to what is already on the radar.</p>

<h3 id="acknowledgements">Acknowledgements</h3>

<p>Thanks to Ashwanth Kumar, Arijit Bhattacharya, Manoj Mahalingam, Naween Ghimire, and Vasanth Gopal for help with the content of the two radars.</p>

<p>Special thanks to Betleharaja G and Ganesh Prasannah for their work on the tech-radar repository</p>

<p><em>If you have been following this space, you must be aware that</em> <a href="https://www.indix.com/blog/developer/indix-loves-open-source/"><em>Indix loves open source</em></a><em>. We use a lot of open source projects, and are also committed to contributing back to the community. Check out our other work at</em> <a href="http://oss.indix.com/"><em>oss.indix.com</em></a><em>.</em></p>]]></content><author><name>Rajesh Muppalla</name></author><summary type="html"><![CDATA[]]></summary></entry><entry><title type="html">Microservices: What Are They and Why Should You Care?</title><link href="https://www.codingnirvana.in/2015/microservices-what-are-they/" rel="alternate" type="text/html" title="Microservices: What Are They and Why Should You Care?" /><published>2015-03-03T00:00:00+05:30</published><updated>2015-03-03T00:00:00+05:30</updated><id>https://www.codingnirvana.in/2015/microservices-what-are-they</id><content type="html" xml:base="https://www.codingnirvana.in/2015/microservices-what-are-they/"><![CDATA[<p><em>Over the last year, Microservices has emerged as a “new” approach to building large scale distributed systems. This is an introductory article that provides background on Microservices, their evolution, the pros and cons and the state of the art as it exists today.</em></p>

<p>You recently joined a brand new client project as a lead developer/architect. You’re sitting in a meeting room and after having heard the client’s requirements, you are now contemplating the right architecture and technology stack for the project.</p>

<p>Your client wants you to build an ecommerce application for consumers. They want to support multiple clients including browsers on desktop, and mobile including native mobile applications. In those cases, your system receives an HTTP request in either JSON or XML, it executes business logic, accesses database and sends back response in JSON or XML. At the same time, the system should also expose an API to integrate with third party applications.</p>

<p>“This sounds like a typical layered architecture”, you say to yourself. You have built a couple of such systems in the past, so you decide to use tried and tested architecture that you are comfortable with. The different layers of your architecture are divided into Presentation, Business and Database components. For the technology stack, you have a choice between Rails, Java, Scala, Node.js, and .NET. It really does not matter what you use as this architecture pattern is language or platform agnostic. However, your client wants you to release an MVP of the product within the next six months. In the end, you choose a technology stack that a majority of your team of four developers is at ease with.</p>

<p>Fast forward by six months. You are at a team party to celebrate the success of the MVP which was delivered on time. The team celebrates the success but also reflects on what a small team of six (you added two developers mid-way during the MVP) accomplished in a short time.
The team is thankful to you for choosing the familiar technology stack. The layered architecture also provided multiple benefits. It was very easy to make a change as all the code was in a single source code repository.  For the same reason, it was easy for the two new developers to ramp up and set up the entire application on their laptop, build it and run it locally within a couple of hours. The release was also painless given that there was a single deployment pipeline that would take the build from the CI server and deploy it on a couple of load balanced production boxes.</p>

<p>Fast forward to two years later. After the success of the MVP and a few minor pivots later, the customer base of the product has grown. Your client also boosted its sales team in the last two years to aid the growth. And your team has grown to 20 developers. However, the product is not able to scale and evolve with the customer base. You realize that the application feels like a big ship that’s very hard to steer. The application has grown to thousands of lines of code. The ramping up of new developers is taking weeks instead of days. And you are finding it hard to track down developers who can work on your “legacy” codebase. As a result of all these factors, you are not able to deliver as fast as earlier to keep up with the business requirements.</p>

<p>Let’s try to understand what’s wrong with the application that you built. Let’s give it a name first. The term that is used to describe an application like the one above is- “Monolith”.</p>

<p>Below are some of the challenges associated with a large monolithic application.</p>

<ul>
  <li>
    <p>Scaling issues – On the one hand, a monolithic application can be scaled horizontally by running multiple copies behind a load balancer. But on the other hand, each component has its own scaling requirements as some components are heavy on I/O and some on memory and hence have different resourcing requirements. With a monolithic application, you have no choice but to provide the same (higher) resources to the entire application, potentially increasing infrastructure costs.</p>
  </li>
  <li>
    <p>Technology changes and evolution – Certain problems are best solved with certain technologies. The technology choices you made when you built the monolith may or may not be the perfect fit when your application evolves at a later stage. You might want to move to a different technology stack or replace individual pieces of the architecture. It could be for scaling needs or for better developer productivity. For e.g., JVM vs C++, SQL vs NoSQL. With a monolithic application, you are married to a technology stack for a long time.</p>
  </li>
  <li>
    <p>Multiple teams – Your teams are organized as UI, Services and Database layers. Performing any cross cutting features takes time and also needs approval for the right prioritization. Ideally, you want to organize teams around business capability to reduce the communication barriers so that an individual team can take things end to end without a lot of overheads. And if the teams are across locations or time zones, it adds another level of complexity.</p>
  </li>
  <li>
    <p>Continuous delivery – Continuous delivery is about reducing the time it takes for a commitment made by a developer to hit production. It’s hard to follow continuous delivery practices when you have a single large codebase. The cycle time will be longer because the entire application has to be built, tested and deployed for any change. In addition, each of these deployments are high risk deployments.</p>
  </li>
  <li>
    <p>Ramping up new members – A large monolithic project is intimidating for new developers as it can take a long time to set up, build and run locally.</p>
  </li>
</ul>

<h3 id="is-there-a-solution">Is there a solution?</h3>

<p>Yes. Microservices – small autonomous services that work together, can be scaled and released independently with different teams potentially using different languages across different locations.</p>

<h3 id="so-how-do-we-describe-a-microservice">So, how do we describe a Microservice?</h3>

<ul>
  <li>
    <p>Autonomous – Each service can be built using the appropriate tool for the job. Multiple teams of developers can independently deliver functionality in this model. Each microservice can have its own data storage using <a href="http://martinfowler.com/bliki/PolyglotPersistence.html">polyglot persistence</a>.</p>
  </li>
  <li>
    <p>Modeled around the business domain – Microservices should be vertically aligned with business capability to provide faster delivery of business goals and outcomes.</p>
  </li>
  <li>
    <p>Small, does one thing, and does it well – Where size is a disadvantage for a monolith, it’s an advantage for a microservice. It should also follow the Single Responsibility Principle (SRP) and should strive towards loose coupling and high cohesion that are hallmarks of well-designed components.</p>
  </li>
  <li>
    <p>Owning build and deployment – A microservice should be standalone and should be able to run independently from other systems and services.</p>
  </li>
  <li>
    <p>Integrates via well-known interfaces – If the microservices expose and talk using well-known interfaces, open protocols and standards, it provides technology heterogeneity, thereby allowing teams to use the technology stack that they are comfortable with or one that is best suited for the problem at hand.</p>
  </li>
</ul>

<h3 id="whats-with-the-term-microservices">What’s with the term “Microservices”?</h3>

<p>I heard the term “Microservices” for the first time when Fred George spoke about it in his talk at <a href="https://www.youtube.com/watch?v=2rKEveL55TY">YOW 2012 – Brazil</a>. Ronald Kuhn, the tech lead on Akka, thinks that Microservices as a term is too ambiguous and likes to call it <a href="https://twitter.com/rolandkuhn/status/472024650990772224">Uniservices</a> instead, given what it does.</p>

<p>Netflix used to call its implementation of Microservices as cloud native architecture and fine grained SOA but they have also started to use the term Microservices since that has become most widely accepted.</p>

<h3 id="how-is-this-different-from-service-oriented-architecture-soa">How is this different from Service Oriented Architecture (SOA)?</h3>

<p>At the heart of it, both Microservices and SOA try to combat the challenges associated with large monolithic applications. SOA’s approach used protocols like SOAP, RPC, and a complex middleware called ESB that meant different things to different people. It had sound principles but there was a lack of consensus on how to do SOA. Moreover, implementation of SOA itself was monolithic where dumb services were integrated with a smart ESB. Proponents of Microservices look at it as a specific approach to SOA in the same way that XP or Scrum are different approaches for Agile software development.</p>

<h3 id="why-now">Why now?</h3>

<p>There was always a desire to build systems this way (even in the SOA era) but the recent advances and maturity in the areas of Cloud, DevOps and Continuous Delivery have made it easier to build them right with Microservices being the building blocks.</p>

<h3 id="moving-towards-microservices">Moving towards Microservices</h3>

<p>One has to delve into several design aspects and weigh the pros and cons of various approaches while moving towards microservices.</p>

<ul>
  <li>
    <p>Splitting the monolith – The first consideration is around splitting the monolith. And the challenge here is to identify the appropriate service boundaries. In this context, <a href="http://en.wikipedia.org/wiki/Domain-driven_design">Domain Driven Design</a> (DDD) is a nice model to help break your system into <a href="http://martinfowler.com/bliki/BoundedContext.html">Bounded Contexts</a> that allow you to set logical and physical boundaries between your services. Another challenge will be around splitting the state. For most applications that state will be the database. You should refactor your existing databases to de-normalize into one data source per table. You should also think about modeling foreign key relationships and transactions across two microservices.</p>
  </li>
  <li>
    <p>Inter-services Communication – You will have to decide the approaches for communication between microservices. REST over HTTP is a sensible default choice for both synchronous and asynchronous communication. For the payloads, you have a choice of using JSON, XML or Binary Protocols. Prefer using JSON payloads for both external and internal service communication because of its readability and integration across languages. Consider using Binary protocols like Thrift, Avro or Protobuff if performance is a consideration for your internal services.</p>
  </li>
  <li>
    <p>Testing – Start with integration tests to make sure that implicit contracts between services are codified. However, these kind of tests are hard to write and tend to be brittle when you have more than a handful of services. Use <a href="http://martinfowler.com/articles/consumerDrivenContracts.html">consumer driven contract tests</a> in those cases to detect contract breakages. Testing should also be done in post-production environments by running fake requests and asserting on the responses. Consider using a tool like <a href="https://github.com/twilio/shadow">Shadow</a> from Twilio while rolling out a newer version of a service to compare its responses with the existing service.</p>
  </li>
  <li>
    <p>Deployment – Each of the services should be independently deployable for which you should prefer a single repository for each service. Follow Continuous Delivery principles ensuring that every change within the service is built and tested including integration tests as part of a Continuous Integration (CI) pipeline. Upstream dependencies should trigger the contract tests pipeline. Once these go green, the changes should be deployed in staging environments using a deploy pipeline. Use Continuous Delivery practices like <a href="http://martinfowler.com/bliki/FeatureToggle.html">Feature Flags</a> and <a href="http://martinfowler.com/bliki/CanaryRelease.html">Canary Releasing</a> to reduce the risk of new features and to roll back problematic code if required. Use <a href="http://martinfowler.com/bliki/BlueGreenDeployment.html">Blue Green Deployments</a> to ensure zero-downtime for your services. Investigate using container technologies like <a href="http://www.docker.com/">Docker</a> with orchestration systems like <a href="https://github.com/mesosphere/marathon">Marathon</a> to help manage complex deployment workflows and to auto scale based on load.</p>
  </li>
</ul>

<p> </p>

<ul>
  <li>
    <p>Failure Isolation – Failures are common when you have more than a handful of services. If Service A calls Service B which in turn calls Service C, a problem in Service C can cause service disruption to end users. Use Patterns like Timeouts, <a href="http://martinfowler.com/bliki/CircuitBreaker.html">Circuit Breaker</a>, <a href="http://skife.org/architecture/fault-tolerance/2009/12/31/bulkheads.html">Bulk Heads</a> and Backpressure to isolate failures. In case of cascading failures, consider gracefully degrading the service rather than failing completely. The book <a href="http://www.amazon.com/Release-It-Production-Ready-Pragmatic-Programmers/dp/0978739213">Release It</a> has a chapter on Stability Patterns that offers a good introduction to this topic.</p>
  </li>
  <li>
    <p>Service Discovery – This is the equivalent of the Service Registry problem in SOA. When you have multiple services, you need a way to discover other services that you are dependent on. DNS with a Load Balancer (LB) works for most cases, but is unable to scale in a dynamic environment where nodes are overloaded and are timing out and need to be removed from the LB, or in cases where one needs to auto scale elastically according to load. <a href="http://zookeeper.apache.org/">Zookeeper</a> has been a great choice in this area for some time as it provides the primitives to build a service discovery solution. <a href="https://consul.io/">Consul</a> is emerging as an alternative that gives you all the things required for service discovery out of the box.</p>
  </li>
  <li>
    <p>Monitoring – When you have multiple services talking to each other, you need tools that can aid in understanding the system behavior and diagnose performance issues. Aggregation becomes very important to provide a unified view of various systems and metrics. Consider using a single tool like <a href="http://logstash.net/">Logstash</a> or <a href="http://www.splunk.com/">Splunk</a> to query and aggregate logs from various Microservices. Distributed request tracing provides you the visibility necessary from an end to end perspective when multiple services are involved. Use correlation IDs across various microservices to track the flow of interaction between services and tie them back to the original request. Consider using <a href="https://blog.twitter.com/2012/distributed-systems-tracing-with-zipkin">Zipkin</a> from Twitter, an open source solution inspired by <a href="http://research.google.com/pubs/pub36356.html">Dapper</a> from Google.</p>
  </li>
  <li>
    <p>Documentation – If one does not know what your service does, it’s likely that no one will use it. Use <a href="http://swagger.io/">Swagger</a> or <a href="https://www.getpostman.com/docs/collections">Postman Collections</a> to document your RESTful APIs.</p>
  </li>
  <li>
    <p>API Gateway – If your clients need to orchestrate data from multiple microservices, the API Gateway provides a way to handle cross cutting concerns like authentication, quota management, rate limiting, basic analytics etc. You can also provide a different granularity for end users by composing one or two microservices using the API Gateway.  <a href="http://techblog.netflix.com/2013/06/announcing-zuul-edge-service-in-cloud.html">Zuul</a> from Netflix is an open source alternative and <a href="http://www.3scale.net/">3scale</a>, <a href="http://apigee.com/">Apigee</a> and <a href="http://www.mashery.com/">Mashery</a> are commercial solutions.</p>
  </li>
</ul>

<h3 id="microservices-oss-stack">Microservices OSS Stack</h3>

<p>Let’s talk about some OSS that’s available to help migrate towards Microservices.</p>

<ul>
  <li>
    <p>Netflix – Netflix has a fully functional microservices stack that they have been using internally for a few years. They have open sourced several key pieces of their stack in recent times. Some notable libraries include</p>

    <ul>
      <li>
        <p>Hystrix – Fault Tolerant library</p>
      </li>
      <li>
        <p>Eureka – Service Registration and Discovery</p>
      </li>
    </ul>
  </li>
</ul>

<p>If you are using Spring, the <a href="http://cloud.spring.io/spring-cloud-netflix/">Spring Cloud Netflix</a> project integrates these libraries into the Spring ecosystem.</p>

<ul>
  <li>
    <p>Twitter – All of Twitter’s traffic until the end of 2012 was served by a single monolithic rails application, internally called “MonoRail”. Since then, they have moved to using Twitter Server for building their microservices, which is built on top of Finagle. Finatra and Finch are noteworthy frameworks from Twitter. SoundCloud and Tapas are examples of companies that are successfully using the Twitter Microservices stack in production.</p>
  </li>
  <li>
    <p>Typesafe – Typesafe’s reactive platform provides the building blocks to build a microservices platform. You can use Spray (to be superseded by Akka-HTTP), Akka-Camel, Akka-Streams to build your own microservices stack on top of the Typesafe platform. Bench.co is a company that’s using the Typesafe stack to build their microservices platform.</p>
  </li>
  <li>
    <p>Dropwizard is a JVM based microcontainer that pulls together battle tested libraries for configuration, metrics, and logging to create RESTful microservices.</p>
  </li>
  <li>
    <p>Roll Your Own – Of course, you could also roll your own stack by using various pieces from existing stacks and building on top of them.</p>
  </li>
</ul>

<p>The above list is not comprehensive and is focused on the JVM platform as majority of the Microservices at Indix run on the JVM. We are currently using Dropwizard and the Typesafe stack but are also actively evaluating the Neflix and Twitter stacks. We also have non-JVM microservices where we are using Bottle and Flask for Python, Express for Node.JS and Sinatra for Ruby.</p>

<h3 id="what-are-the-disadvantages">What are the disadvantages?</h3>

<p>The rosy picture I painted after the initial gloom might make you believe that Microservices would solve all your architectural problems. Sorry to break your heart, but like everything else in software, Microservices are not a silver bullet. It’s an architecture style that is still maturing and has its own share of challenges.</p>

<ul>
  <li>
    <p>When you are running multiple microservices, it becomes extremely important to manage the operational complexity associated with it. You need to make sure that you have the right tools in place in order to visualize and orchestrate your deployment and also ensure that you are able to monitor the complex interactions between these systems. You need to build the right set of tools as well as the skill set within your teams to handle this complexity.</p>
  </li>
  <li>
    <p>If your team is using multiple language and technology stacks to build their services, lack of standardization might slow you down especially when debugging failures.</p>
  </li>
  <li>
    <p>As you work with implicit interfaces across microservices, you will need to worry about backward compatibility and versioning. It needs co-ordination across teams and the solution to this may or may not be technical.</p>
  </li>
</ul>

<h3 id="should-i-move-to-a-microservice-architecture">Should I move to a Microservice architecture?</h3>

<p>This is one of those questions that has an YMMV (Your Mileage May Vary) answer.</p>

<p>At Indix, we started with a monolith (but modular) application which was built over the course of the last three years. As I write this article, we are migrating to microservices across all our teams. The reasons for us to move to microservices are captured in the section describing the problems with the monolith. In hindsight, starting with a monolith and breaking it down into microservices with the team growing and the system getting complex, seems like the right approach.</p>

<p>To provide a different perspective, I would refer to <a href="http://www.go.cd/">Go-CD</a> – an open source build and release management tool. I worked on the team about three years back. Right now, it’s a six+ year old project and the codebase is still a monolith. I think the reason why it’s stayed so is because there were not more than ten developers at any given point of time. The team is co-located and would ramp up only two or three new developers every year. The team uses techniques like <a href="http://martinfowler.com/bliki/BranchByAbstraction.html">Branch by Abstraction</a> and <a href="http://www.martinfowler.com/bliki/StranglerApplication.html">Strangler Approach</a> to evolve with changing technological trends. It will be interesting to see if the community decides to split Go-CD into multiple Microservices at some point.</p>

<p>If you want to move towards a Microservices architecture, you should read Martin Fowler’s article on “<a href="http://martinfowler.com/bliki/MicroservicePrerequisites.html">Pre-requisites for Microservices</a>”.</p>

<h3 id="summary">Summary</h3>

<p>Microservices is an important tool in the arsenal of an evolutionary architect. Microservices themselves have evolved out of the hard earned lessons of engineers building large scale systems. When systems and teams scale, breaking them down into Microservices decouples your systems and gives more options and choices to evolve them independently. Like any other tool, it is important to know when and how to use it. Hopefully, this article has provided you enough context to decide on both.</p>

<h3 id="further-reading">Further Reading</h3>

<p>If you are really interested in diving deeper into building Microservices, I suggest ordering a copy of the excellent book by Sam Newman called <em>Building Microservices</em>.</p>]]></content><author><name>Rajesh Muppalla</name></author><summary type="html"><![CDATA[Over the last year, Microservices has emerged as a “new” approach to building large scale distributed systems. This is an introductory article that provides background on Microservices, their evolution, the pros and cons and the state of the art as it exists today.]]></summary></entry><entry><title type="html">Lambda Architecture at Indix, Part II</title><link href="https://www.codingnirvana.in/2014/lambda-architecture-at-indix-part-ii/" rel="alternate" type="text/html" title="Lambda Architecture at Indix, Part II" /><published>2014-08-18T00:00:00+05:30</published><updated>2014-08-18T00:00:00+05:30</updated><id>https://www.codingnirvana.in/2014/lambda-architecture-at-indix-part-ii</id><content type="html" xml:base="https://www.codingnirvana.in/2014/lambda-architecture-at-indix-part-ii/"><![CDATA[<p><em>This post is the second in a series of blog posts that will go in depth over our implementation of Lambda Architecture (LA) for building the big data pipeline at Indix.</em></p>

<p><em>The first <a href="/2014/lambda-architecture-at-indix/">post</a> introduced LA, our motivation for using it and the key architectural components and principles that form the basis of implementing LA. In this post, I will talk about the framework and the abstractions that form the core of the compute part of our batch layer implementation.</em></p>

<h3 id="recap">Recap</h3>

<p><img src="/assets/img/indix/lambda_architecture_at_indix.png" alt="lambda_architecture_at_indix" /></p>

<p>In the Lambda Architecture, the batch layer is responsible for computing arbitrary functions on the master data and creating batch views so that queries can be resolved with low latency.</p>

<p>The batch layer has two key components – the compute (processing) and storage. In this particular blog post, we will focus on the compute component. We will cover the storage layer in a subsequent blog post.</p>

<h3 id="batch-layer--compute-requirements">Batch layer – Compute Requirements</h3>

<p>The compute component is responsible for running precomputes on the entire master data set which in our case is in several tera bytes. Our machine learning algorithms and models are constantly changing. To make it predictable we run it daily.</p>

<p>Given the above, there are three key requirements of the compute component in the batch layer
– Scalability – It should scale linearly i.e. maintain the performance with increased load by adding more resources in proportion to the increase in load. – Fault Tolerance – It should be tolerant to faults associated with distributed systems like network partitions, disk failures and server crashes. – Generality – It should be expressive enough to support any arbitrary functions on the master data.</p>

<h3 id="batch-layer-compute-framework--mapreduce">Batch layer compute framework – MapReduce</h3>

<p>MapReduce satisifies all the requirements of compute part of the batch layer I described above.</p>

<p>MapReduce is a programming model and infrastructure for doing parallelized distributed computing on server clusters. It has its roots in the functional programming world, specifically, higher order functions. Google took it from there and showed how these abstractions can be used to do massive parallelized computations. This is the basis of Apache Hadoop, the open source Big Data infrastructure platform that is used by several companies world-wide.</p>

<p>I will not be covering MapReduce in this blog post as there are several tutorials out there that can explain the fundamentals. However, the rest of the post assumes an understanding of core ideas behing MapReduce. So if you are not aware of MapReduce or need to brush up on its basics, I highly recommend the two links below.</p>

<ul>
  <li><a href="http://static.googleusercontent.com/media/research.google.com/en//archive/mapreduce-osdi04.pdf">Google Paper Introducing MapReduce (2005)</a></li>
  <li><a href="https://developer.yahoo.com/hadoop/tutorial/module4.html">Yahoo Developer Tutorial</a></li>
</ul>

<h3 id="mapreduce--indix">MapReduce @ Indix</h3>

<p>By May 2012, we had a 10-node hadoop cluster setup on AWS that was available for the team to run MapReduce tasks. In about a month, we had several MapReduce jobs running that were</p>

<p>By June 2014, we have around 120 MapReduce jobs runnning on a 100 node AWS cluster doing data transformations, training and evaluating models, running precomputations, creating indexes, finding duplicates among other things.</p>

<h3 id="first-cut--using-vanilla-mapreduce-api">First cut – Using vanilla MapReduce API</h3>

<p>The initial 30 or so MapReduce jobs were written by us using the vanilla MapReduce Java API. After writing a handful of these jobs we noticed that there was lot of boiler plate code being written for each job and we had to write quite a bit of plumbing code to express complex data workflows. We were unable to compose and hence re-use lot of the logic that was contained in those jobs.We soon realized that there was a need for a different paradigm,library or language to express high level user defined functions.</p>

<p>We would still not trade the time we spent using the vanilla API. It taught us some valuable lessons and we gained deep knowledge into the inner workings of Hadoop MapReduce infrastructure that helped us while working with higher level abstractions. Its a dangerous thing to ignore the details while working on higher level abstractions.</p>

<h3 id="second-cut--using-pig">Second cut – Using PIG</h3>

<p>The first such abstraction we tried was PIG. PIG from Yahoo is a relational data-flow language. It provides a DSL, called Pig Latin, on top of vanilla MapReduce. The interface it provides has nothing to do with “map” and “reduce”. The Pig Latin DSL commands are interpreted into a series of MapReduce jobs. Its very similar to a query planner in an RDBMS that translates SQL into a series of operations on data. It supports UDFs(User Defined Functions), loadable via Java classes for extending functionality thats not provided out of the box. We tried PIG for running ad-hoc jobs on our entire data. It was easy to read and learn. It exceled at simple data flows and was good for running ad-hoc tasks. However, it was inefficient for implementing non-trivial algorithms that could be used in production pipelines. Writing UDFs, the main building blocks in PIG, was painful because they needed to be written and compiled in a language (Java) other than the main query language (Pig Latin).</p>

<p>We did not use HIVE, an SQL like dialect on MapReduce, but the same arguments can be made for it.</p>

<h3 id="enter-scalding">Enter Scalding</h3>

<p>Scalding is a domain-specific language (DSL), that offers a higher level of abstraction on MapReduce by leveraging the power of Scala. Its an open source library from Twitter, and is built on top of Cascading. The Scalding DSL is very similar to the Scala collections API. The same code that works on a small list of records could be used to work on a stream of billions of tuples. The best part is that you can use functions in Scala to incorporate your business logic in the queries.</p>

<h3 id="useful-links">Useful Links</h3>

<p>To understand Scalding better, one needs a basic understanding of the Scala language and also a high level overview of the important concepts used in Cascading. I will recommend reading the following links</p>

<ul>
  <li>Scala
    <ul>
      <li><a href="http://twitter.github.com/scala_school/">Twitter Scala School</a></li>
      <li><a href="https://www.coursera.org/course/progfun">Scala Functional Programming Course</a></li>
    </ul>
  </li>
  <li>Cascading
    <ul>
      <li><a href="http://docs.cascading.org/impatient/">Cascading for the Impatient</a></li>
    </ul>
  </li>
</ul>

<h3 id="motivating-example-for-scalding">Motivating Example for Scalding</h3>

<p>To get a sense of the benefits of using Scalding, let’s try to look at the solution of a simple problem in both Scalding and the Java MapReduce API.</p>

<p>The problem we will solve is an important building block in Text Analysis. Given a list of sentences, find the frequency of words and pick the top K when sorted in descending order of count of frequency. You should also filter stop words (for simplicity, we will define stop words as the words that have a length that is 2 or less)</p>

<p>For example, given the below input and a K value of 5</p>

<div class="language-scala highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">mary</span> <span class="n">had</span> <span class="n">a</span> <span class="n">little</span> <span class="n">lamb</span>
<span class="n">twinkle</span> <span class="n">twinkle</span> <span class="n">little</span> <span class="n">star</span>
</code></pre></div></div>

<p>we need to return the following output</p>

<div class="language-scala highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="mi">2</span>   <span class="n">twinkle</span>
<span class="mi">2</span>   <span class="n">little</span>
<span class="mi">1</span>   <span class="n">star</span>
<span class="mi">1</span>   <span class="n">mary</span>
<span class="mi">1</span>   <span class="n">lamb</span>
</code></pre></div></div>

<p>The final requirement is that this code should scale to work on a data set containing billions of words.</p>

<h3 id="using-java-map-reduce-api">Using Java Map Reduce API</h3>

<p>Lets try to see how you would write a job to do for this using the Java MapReduce API. The map function gets each line as an input. It splits the line into words (using space as the delimiter), filters stop words and emits a key value tuple containing the word and its count as 1.</p>

<div class="language-scala highlighter-rouge"><div class="highlight"><pre class="highlight"><code>    <span class="n">public</span> <span class="n">static</span> <span class="k">class</span> <span class="nc">FrequencyCounterMapper</span> <span class="k">extends</span> <span class="nc">Mapper</span><span class="o">&lt;</span><span class="nc">IntWritable</span><span class="o">,</span> <span class="nc">Text</span><span class="o">,</span> <span class="nc">Text</span><span class="o">,</span> <span class="nc">IntWritable</span><span class="o">&gt;</span> <span class="o">{</span>

        <span class="nd">@Override</span>
        <span class="k">protected</span> <span class="n">void</span> <span class="nf">map</span><span class="o">(</span><span class="nc">IntWritable</span> <span class="n">key</span><span class="o">,</span> <span class="nc">Text</span> <span class="n">value</span><span class="o">,</span> <span class="nc">Context</span> <span class="n">context</span><span class="o">)</span> <span class="n">throws</span> <span class="nc">IOException</span><span class="o">,</span> <span class="nc">InterruptedException</span> <span class="o">{</span>
            <span class="nc">String</span> <span class="n">line</span> <span class="k">=</span> <span class="nv">value</span><span class="o">.</span><span class="py">toString</span><span class="o">();</span>
            <span class="nc">String</span><span class="o">[]</span> <span class="n">words</span> <span class="k">=</span> <span class="nv">line</span><span class="o">.</span><span class="py">split</span><span class="o">(</span><span class="s">" "</span><span class="o">);</span>
            <span class="nc">List</span><span class="o">&lt;</span><span class="nc">String</span><span class="o">&gt;</span> <span class="n">cleanedWords</span> <span class="k">=</span> <span class="k">new</span> <span class="nc">ArrayList</span><span class="o">&lt;</span><span class="nc">String</span><span class="o">&gt;();</span>

            <span class="nf">for</span> <span class="o">(</span><span class="nc">String</span> <span class="n">word</span> <span class="k">:</span> <span class="kt">words</span><span class="o">)</span> <span class="o">{</span>
                <span class="nf">if</span><span class="o">(</span><span class="nv">word</span><span class="o">.</span><span class="py">trim</span><span class="o">().</span><span class="py">length</span><span class="o">()</span> <span class="o">&gt;</span> <span class="mi">2</span><span class="o">)</span>
                    <span class="nv">cleanedWords</span><span class="o">.</span><span class="py">add</span><span class="o">(</span><span class="nv">word</span><span class="o">.</span><span class="py">trim</span><span class="o">().</span><span class="py">toLowerCase</span><span class="o">());</span>
            <span class="o">}</span>

            <span class="nf">for</span> <span class="o">(</span><span class="nc">String</span> <span class="n">cleanedWord</span> <span class="k">:</span> <span class="kt">cleanedWords</span><span class="o">)</span> <span class="o">{</span>
                <span class="nv">context</span><span class="o">.</span><span class="py">write</span><span class="o">(</span><span class="k">new</span> <span class="nc">Text</span><span class="o">(</span><span class="n">cleanedWord</span><span class="o">),</span> <span class="k">new</span> <span class="nc">IntWritable</span><span class="o">(</span><span class="mi">1</span><span class="o">));</span>
            <span class="o">}</span>
        <span class="o">}</span>
    <span class="o">}</span>
</code></pre></div></div>

<p>The reduce function gets a list of tuples grouped by the same word. It then sums up the values in the key value tuple to get the frequency for each word.</p>

<div class="language-scala highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">public</span> <span class="n">static</span> <span class="k">class</span> <span class="nc">FrequencyCounterReducer</span> <span class="k">extends</span> <span class="nc">Reducer</span><span class="o">&lt;</span><span class="nc">Text</span><span class="o">,</span> <span class="nc">IntWritable</span><span class="o">,</span> <span class="nc">Text</span><span class="o">,</span> <span class="nc">IntWritable</span><span class="o">&gt;</span> <span class="o">{</span>
    <span class="nd">@Override</span>
    <span class="k">protected</span> <span class="n">void</span> <span class="nf">reduce</span><span class="o">(</span><span class="nc">Text</span> <span class="n">key</span><span class="o">,</span> <span class="nc">Iterable</span><span class="o">&lt;</span><span class="nc">IntWritable</span><span class="o">&gt;</span> <span class="n">values</span><span class="o">,</span> <span class="nc">Context</span> <span class="n">context</span><span class="o">)</span> <span class="n">throws</span> <span class="nc">IOException</span><span class="o">,</span> <span class="nc">InterruptedException</span> <span class="o">{</span>
        <span class="n">int</span> <span class="n">sum</span> <span class="k">=</span> <span class="mi">0</span><span class="o">;</span>
        <span class="nf">for</span> <span class="o">(</span><span class="nc">IntWritable</span> <span class="n">value</span> <span class="k">:</span> <span class="kt">values</span><span class="o">)</span> <span class="o">{</span>
            <span class="n">sum</span> <span class="o">+=</span> <span class="nv">value</span><span class="o">.</span><span class="py">get</span><span class="o">();</span>
        <span class="o">}</span>
        <span class="nv">context</span><span class="o">.</span><span class="py">write</span><span class="o">(</span><span class="n">key</span><span class="o">,</span> <span class="k">new</span> <span class="nc">IntWritable</span><span class="o">(</span><span class="n">sum</span><span class="o">));</span>
    <span class="o">}</span>
<span class="o">}</span>
</code></pre></div></div>

<p>The main function is responsible for executing the job by calling the run function. Within the run function, we create the job by giving it a name, specifying the mapper and reducer classes,setting the input and output file paths and providing classes describing how the input and output will be formatted.</p>

<div class="language-scala highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">public</span> <span class="n">static</span> <span class="n">void</span> <span class="nf">main</span><span class="o">(</span><span class="nc">String</span><span class="o">[]</span> <span class="n">args</span><span class="o">)</span> <span class="n">throws</span> <span class="nc">Exception</span> <span class="o">{</span>
    <span class="k">new</span> <span class="nc">WordFrequencyJob</span><span class="o">().</span><span class="py">run</span><span class="o">(</span><span class="n">args</span><span class="o">);</span>
<span class="o">}</span>

<span class="k">private</span> <span class="nc">Job</span> <span class="nf">createJob</span><span class="o">(</span><span class="nc">String</span> <span class="n">inPath</span><span class="o">,</span> <span class="nc">String</span> <span class="n">outPath</span><span class="o">)</span> <span class="n">throws</span> <span class="nc">IOException</span> <span class="o">{</span>
    <span class="nc">String</span> <span class="n">jobName</span> <span class="k">=</span> <span class="s">"Word Frequency in Vanilla MR"</span><span class="o">;</span>
    <span class="nc">Job</span> <span class="n">job</span> <span class="k">=</span> <span class="k">new</span> <span class="nc">Job</span><span class="o">(</span><span class="nf">getConf</span><span class="o">());</span>
    <span class="nv">job</span><span class="o">.</span><span class="py">setJobName</span><span class="o">(</span><span class="nv">jobName</span><span class="o">.</span><span class="py">trim</span><span class="o">());</span>

    <span class="nv">job</span><span class="o">.</span><span class="py">setMapSpeculativeExecution</span><span class="o">(</span><span class="kc">false</span><span class="o">);</span>
    <span class="nv">job</span><span class="o">.</span><span class="py">setMapperClass</span><span class="o">(</span><span class="nv">FrequencyCounterMapper</span><span class="o">.</span><span class="py">class</span><span class="o">);</span>
    <span class="nv">job</span><span class="o">.</span><span class="py">setReducerClass</span><span class="o">(</span><span class="nv">FrequencyCounterReducer</span><span class="o">.</span><span class="py">class</span><span class="o">);</span>

    <span class="nv">job</span><span class="o">.</span><span class="py">setInputFormatClass</span><span class="o">(</span><span class="nv">TextInputFormat</span><span class="o">.</span><span class="py">class</span><span class="o">);</span>
    <span class="nv">TextInputFormat</span><span class="o">.</span><span class="py">setInputPaths</span><span class="o">(</span><span class="n">job</span><span class="o">,</span> <span class="n">inPath</span><span class="o">);</span>

    <span class="nv">job</span><span class="o">.</span><span class="py">setOutputFormatClass</span><span class="o">(</span><span class="nv">TextOutputFormat</span><span class="o">.</span><span class="py">class</span><span class="o">);</span>
    <span class="nv">TextOutputFormat</span><span class="o">.</span><span class="py">setOutputPath</span><span class="o">(</span><span class="n">job</span><span class="o">,</span> <span class="k">new</span> <span class="nc">Path</span><span class="o">(</span><span class="n">outPath</span><span class="o">));</span>

    <span class="nv">job</span><span class="o">.</span><span class="py">setJarByClass</span><span class="o">(</span><span class="k">this</span><span class="o">.</span><span class="py">getClass</span><span class="o">());</span>
    <span class="k">return</span> <span class="n">job</span><span class="o">;</span>
<span class="o">}</span>

<span class="nd">@Override</span>
<span class="n">public</span> <span class="n">int</span> <span class="nf">run</span><span class="o">(</span><span class="nc">String</span><span class="o">[]</span> <span class="n">args</span><span class="o">)</span> <span class="n">throws</span> <span class="nc">Exception</span> <span class="o">{</span>
    <span class="nc">Job</span> <span class="n">job</span> <span class="k">=</span> <span class="nf">createJob</span><span class="o">(</span><span class="n">args</span><span class="o">[</span><span class="err">0</span><span class="o">],</span> <span class="n">args</span><span class="o">[</span><span class="err">1</span><span class="o">]);</span>
    <span class="nf">if</span><span class="o">(</span><span class="nv">job</span><span class="o">.</span><span class="py">waitForCompletion</span><span class="o">(</span><span class="kc">true</span><span class="o">))</span>
        <span class="k">return</span> <span class="mi">0</span><span class="o">;</span>
    <span class="k">else</span>
        <span class="k">return</span> <span class="o">-</span><span class="mi">1</span><span class="o">;</span>
<span class="o">}</span>
<span class="o">}</span>
</code></pre></div></div>

<p>Now, we’ve the word frequencies as output. We should be able to process this and get top-k frequent words from this. Note that, we also want to know what these top-k words’ frequencies are. We would be writing another MR job that gives us Top-K words based on their frequencies.</p>

<p>The map function for this MR job is a little involved. The Mapper class maintains an in-memory TreeMap which allows the map input to be sorted based on its key. We use it so that, we can maintain only top K words in the mapper class and keep removing words which fall below the top K. What this has done to us is, reduced the amount of shuffle that would otherwise happen b/w the map and the reduce phase. Note the Mapper task Type – It takes Text, IntWritable as its input K,V and produces NullWritable, WordFrequencyWritable(a custom writable that would contain both word and its frequency) as the output K,V. NullWritable is the Writable equivalent of null.</p>

<div class="language-scala highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">class</span> <span class="nc">WordFrequencyWritable</span> <span class="n">implements</span> <span class="nc">Writable</span> <span class="o">{</span>
    <span class="n">int</span> <span class="n">count</span><span class="o">;</span>
    <span class="nc">String</span> <span class="n">word</span><span class="o">;</span>

    <span class="nd">@Override</span>
    <span class="n">public</span> <span class="n">void</span> <span class="nf">write</span><span class="o">(</span><span class="nc">DataOutput</span> <span class="n">dataOutput</span><span class="o">)</span> <span class="n">throws</span> <span class="nc">IOException</span> <span class="o">{</span>
        <span class="nv">dataOutput</span><span class="o">.</span><span class="py">writeInt</span><span class="o">(</span><span class="n">count</span><span class="o">);</span>
        <span class="nv">dataOutput</span><span class="o">.</span><span class="py">writeInt</span><span class="o">(</span><span class="nv">word</span><span class="o">.</span><span class="py">length</span><span class="o">());</span>
        <span class="nv">dataOutput</span><span class="o">.</span><span class="py">write</span><span class="o">(</span><span class="nv">word</span><span class="o">.</span><span class="py">getBytes</span><span class="o">());</span>
    <span class="o">}</span>

    <span class="nd">@Override</span>
    <span class="n">public</span> <span class="n">void</span> <span class="nf">readFields</span><span class="o">(</span><span class="nc">DataInput</span> <span class="n">dataInput</span><span class="o">)</span> <span class="n">throws</span> <span class="nc">IOException</span> <span class="o">{</span>
        <span class="n">count</span> <span class="k">=</span> <span class="nv">dataInput</span><span class="o">.</span><span class="py">readInt</span><span class="o">();</span>
        <span class="n">int</span> <span class="n">length</span> <span class="k">=</span> <span class="nv">dataInput</span><span class="o">.</span><span class="py">readInt</span><span class="o">();</span>
        <span class="n">byte</span><span class="o">[]</span> <span class="n">b</span> <span class="k">=</span> <span class="k">new</span> <span class="n">byte</span><span class="o">[</span><span class="kt">length</span><span class="o">];</span>
        <span class="nv">dataInput</span><span class="o">.</span><span class="py">readFully</span><span class="o">(</span><span class="n">b</span><span class="o">);</span>
        <span class="n">word</span> <span class="k">=</span> <span class="k">new</span> <span class="nc">String</span><span class="o">(</span><span class="n">b</span><span class="o">);</span>
    <span class="o">}</span>

    <span class="c1">// Needed For the deserialization at reducer</span>
    <span class="nc">WordFrequencyWritable</span><span class="o">(){}</span>
    <span class="nc">WordFrequencyWritable</span><span class="o">(</span><span class="n">int</span> <span class="n">count</span><span class="o">,</span> <span class="nc">String</span> <span class="n">word</span><span class="o">)</span> <span class="o">{</span>
        <span class="k">this</span><span class="o">.</span><span class="py">count</span> <span class="k">=</span> <span class="n">count</span><span class="o">;</span>
        <span class="k">this</span><span class="o">.</span><span class="py">word</span> <span class="k">=</span> <span class="n">word</span><span class="o">;</span>
    <span class="o">}</span>
<span class="o">}</span>

<span class="n">public</span> <span class="n">static</span> <span class="k">class</span> <span class="nc">TopKMapper</span> <span class="k">extends</span> <span class="nc">Mapper</span><span class="o">&lt;</span><span class="nc">Text</span><span class="o">,</span> <span class="nc">IntWritable</span><span class="o">,</span> <span class="nc">NullWritable</span><span class="o">,</span> <span class="nc">WordFrequencyWritable</span><span class="o">&gt;</span> <span class="o">{</span>
    <span class="nc">TreeMap</span><span class="o">&lt;</span><span class="nc">IntWritable</span><span class="o">,</span> <span class="nc">List</span><span class="o">&lt;</span><span class="nc">Text</span><span class="o">&gt;&gt;</span> <span class="n">freqMap</span> <span class="k">=</span> <span class="k">new</span> <span class="nc">TreeMap</span><span class="o">&lt;</span><span class="nc">IntWritable</span><span class="o">,</span> <span class="nc">List</span><span class="o">&lt;</span><span class="nc">Text</span><span class="o">&gt;&gt;();</span>
    <span class="nd">@Override</span>
    <span class="k">protected</span> <span class="n">void</span> <span class="nf">map</span><span class="o">(</span><span class="nc">Text</span> <span class="n">key</span><span class="o">,</span> <span class="nc">IntWritable</span> <span class="n">value</span><span class="o">,</span> <span class="nc">Context</span> <span class="n">context</span><span class="o">)</span> <span class="n">throws</span> <span class="nc">IOException</span><span class="o">,</span> <span class="nc">InterruptedException</span> <span class="o">{</span>
        <span class="nf">if</span> <span class="o">(</span><span class="nv">freqMap</span><span class="o">.</span><span class="py">containsKey</span><span class="o">(</span><span class="n">value</span><span class="o">)){</span>
            <span class="nv">freqMap</span><span class="o">.</span><span class="py">get</span><span class="o">(</span><span class="n">value</span><span class="o">).</span><span class="py">add</span><span class="o">(</span><span class="n">key</span><span class="o">);</span>
        <span class="o">}</span><span class="k">else</span><span class="o">{</span>
            <span class="nc">List</span><span class="o">&lt;</span><span class="nc">Text</span><span class="o">&gt;</span> <span class="n">words</span> <span class="k">=</span> <span class="k">new</span> <span class="nc">ArrayList</span><span class="o">&lt;</span><span class="nc">Text</span><span class="o">&gt;();</span>
            <span class="nv">words</span><span class="o">.</span><span class="py">add</span><span class="o">(</span><span class="n">key</span><span class="o">);</span>
            <span class="nv">freqMap</span><span class="o">.</span><span class="py">put</span><span class="o">(</span><span class="n">value</span><span class="o">,</span> <span class="n">words</span><span class="o">);</span>
        <span class="o">}</span>

        <span class="c1">// We don't have to exactly do Top K on mappers.</span>
        <span class="c1">// So, we are approximating things here.</span>
        <span class="nf">if</span><span class="o">(</span><span class="nv">freqMap</span><span class="o">.</span><span class="py">size</span><span class="o">()</span> <span class="o">&gt;</span> <span class="n">K</span><span class="o">){</span>
            <span class="nv">freqMap</span><span class="o">.</span><span class="py">remove</span><span class="o">(</span><span class="nv">freqMap</span><span class="o">.</span><span class="py">firstKey</span><span class="o">());</span>
        <span class="o">}</span>
        <span class="c1">// keep alive</span>
        <span class="nv">context</span><span class="o">.</span><span class="py">progress</span><span class="o">();</span>
    <span class="o">}</span>

    <span class="nd">@Override</span>
    <span class="k">protected</span> <span class="n">void</span> <span class="nf">cleanup</span><span class="o">(</span><span class="nc">Context</span> <span class="n">context</span><span class="o">)</span> <span class="n">throws</span> <span class="nc">IOException</span><span class="o">,</span> <span class="nc">InterruptedException</span> <span class="o">{</span>
        <span class="nf">for</span> <span class="o">(</span><span class="nc">IntWritable</span> <span class="n">key</span> <span class="k">:</span> <span class="kt">freqMap.keySet</span><span class="o">())</span> <span class="o">{</span>
            <span class="nc">List</span><span class="o">&lt;</span><span class="nc">Text</span><span class="o">&gt;</span> <span class="n">words</span> <span class="k">=</span> <span class="nv">freqMap</span><span class="o">.</span><span class="py">get</span><span class="o">(</span><span class="n">key</span><span class="o">);</span>
            <span class="nf">for</span> <span class="o">(</span><span class="nc">Text</span> <span class="n">word</span> <span class="k">:</span> <span class="kt">words</span><span class="o">)</span> <span class="o">{</span>
                <span class="c1">// because we want to have the word and its frequency; we construct the Writable</span>
                <span class="nv">context</span><span class="o">.</span><span class="py">write</span><span class="o">(</span><span class="kc">null</span><span class="o">,</span> <span class="k">new</span> <span class="nc">WordFrequencyWritable</span><span class="o">(</span><span class="nv">key</span><span class="o">.</span><span class="py">get</span><span class="o">(),</span> <span class="nv">word</span><span class="o">.</span><span class="py">toString</span><span class="o">()));</span>
            <span class="o">}</span>
        <span class="o">}</span>
        <span class="nv">super</span><span class="o">.</span><span class="py">cleanup</span><span class="o">(</span><span class="n">context</span><span class="o">);</span>
    <span class="o">}</span>
<span class="o">}</span>
</code></pre></div></div>

<p> </p>

<p>The reducer(only one in this case) runs through all the Top-K WordFrequencyWritables got from all the mappers and produces Top K amongst them. Note that, since the key from the mappers were all null, all output would go to only one reducer and hence we are able to do Top K with total ordering.</p>

<div class="language-scala highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">public</span> <span class="n">static</span> <span class="k">class</span> <span class="nc">TopKReducer</span> <span class="k">extends</span> <span class="nc">Reducer</span><span class="o">&lt;</span><span class="nc">NullWritable</span><span class="o">,</span> <span class="nc">WordFrequencyWritable</span><span class="o">,</span> <span class="nc">Text</span><span class="o">,</span> <span class="nc">IntWritable</span><span class="o">&gt;</span> <span class="o">{</span>
    <span class="nc">TreeMap</span><span class="o">&lt;</span><span class="nc">Integer</span><span class="o">,</span> <span class="nc">List</span><span class="o">&lt;</span><span class="nc">String</span><span class="o">&gt;&gt;</span> <span class="n">freqMap</span> <span class="k">=</span> <span class="k">new</span> <span class="nc">TreeMap</span><span class="o">&lt;</span><span class="nc">Integer</span><span class="o">,</span> <span class="nc">List</span><span class="o">&lt;</span><span class="nc">String</span><span class="o">&gt;&gt;();</span>

    <span class="nd">@Override</span>
    <span class="k">protected</span> <span class="n">void</span> <span class="nf">reduce</span><span class="o">(</span><span class="nc">NullWritable</span> <span class="n">key</span><span class="o">,</span> <span class="nc">Iterable</span><span class="o">&lt;</span><span class="nc">WordFrequencyWritable</span><span class="o">&gt;</span> <span class="n">values</span><span class="o">,</span> <span class="nc">Context</span> <span class="n">context</span><span class="o">)</span> <span class="n">throws</span> <span class="nc">IOException</span><span class="o">,</span> <span class="nc">InterruptedException</span> <span class="o">{</span>
        <span class="nf">for</span> <span class="o">(</span><span class="nc">WordFrequencyWritable</span> <span class="n">value</span> <span class="k">:</span> <span class="kt">values</span><span class="o">)</span> <span class="o">{</span>
            <span class="nf">if</span> <span class="o">(</span><span class="nv">freqMap</span><span class="o">.</span><span class="py">containsKey</span><span class="o">(</span><span class="nv">value</span><span class="o">.</span><span class="py">count</span><span class="o">)){</span>
                <span class="nc">List</span><span class="o">&lt;</span><span class="nc">String</span><span class="o">&gt;</span> <span class="n">words</span> <span class="k">=</span> <span class="nv">freqMap</span><span class="o">.</span><span class="py">get</span><span class="o">(</span><span class="nv">value</span><span class="o">.</span><span class="py">count</span><span class="o">);</span>
                <span class="nv">words</span><span class="o">.</span><span class="py">add</span><span class="o">(</span><span class="nv">value</span><span class="o">.</span><span class="py">word</span><span class="o">);</span>
            <span class="o">}</span><span class="k">else</span><span class="o">{</span>
                <span class="nc">List</span><span class="o">&lt;</span><span class="nc">String</span><span class="o">&gt;</span> <span class="n">words</span> <span class="k">=</span> <span class="k">new</span> <span class="nc">ArrayList</span><span class="o">&lt;</span><span class="nc">String</span><span class="o">&gt;();</span>
                <span class="nv">words</span><span class="o">.</span><span class="py">add</span><span class="o">(</span><span class="nv">value</span><span class="o">.</span><span class="py">word</span><span class="o">);</span>
                <span class="nv">freqMap</span><span class="o">.</span><span class="py">put</span><span class="o">(</span><span class="nv">value</span><span class="o">.</span><span class="py">count</span><span class="o">,</span> <span class="n">words</span><span class="o">);</span>
            <span class="o">}</span>

            <span class="nf">if</span><span class="o">(</span><span class="nv">freqMap</span><span class="o">.</span><span class="py">size</span><span class="o">()</span> <span class="o">&gt;</span> <span class="n">K</span><span class="o">){</span>
                <span class="nv">freqMap</span><span class="o">.</span><span class="py">remove</span><span class="o">(</span><span class="nv">freqMap</span><span class="o">.</span><span class="py">firstKey</span><span class="o">());</span>
            <span class="o">}</span>
            <span class="c1">// keep alive</span>
            <span class="nv">context</span><span class="o">.</span><span class="py">progress</span><span class="o">();</span>
        <span class="o">}</span>
    <span class="o">}</span>

    <span class="nd">@Override</span>
    <span class="k">protected</span> <span class="n">void</span> <span class="nf">cleanup</span><span class="o">(</span><span class="nc">Context</span> <span class="n">context</span><span class="o">)</span> <span class="n">throws</span> <span class="nc">IOException</span><span class="o">,</span> <span class="nc">InterruptedException</span> <span class="o">{</span>
        <span class="nc">NavigableSet</span><span class="o">&lt;</span><span class="nc">Integer</span><span class="o">&gt;</span> <span class="n">keys</span> <span class="k">=</span> <span class="nv">freqMap</span><span class="o">.</span><span class="py">descendingKeySet</span><span class="o">();</span>
        <span class="n">int</span> <span class="n">count</span> <span class="k">=</span> <span class="mi">0</span><span class="o">;</span>
        <span class="nf">for</span> <span class="o">(</span><span class="nc">Integer</span> <span class="n">key</span> <span class="k">:</span> <span class="kt">keys</span><span class="o">)</span> <span class="o">{</span>
            <span class="nc">List</span><span class="o">&lt;</span><span class="nc">String</span><span class="o">&gt;</span> <span class="n">words</span> <span class="k">=</span> <span class="nv">freqMap</span><span class="o">.</span><span class="py">get</span><span class="o">(</span><span class="n">key</span><span class="o">);</span>
            <span class="nf">for</span> <span class="o">(</span><span class="nc">String</span> <span class="n">word</span> <span class="k">:</span> <span class="kt">words</span><span class="o">)</span> <span class="o">{</span>
                <span class="n">count</span><span class="o">++;</span>
                <span class="nf">if</span><span class="o">(</span><span class="n">count</span> <span class="o">&gt;</span> <span class="n">K</span><span class="o">){</span>
                    <span class="n">break</span><span class="o">;</span>
                <span class="o">}</span>
                <span class="nv">context</span><span class="o">.</span><span class="py">write</span><span class="o">(</span><span class="k">new</span> <span class="nc">Text</span><span class="o">(</span><span class="n">word</span><span class="o">),</span> <span class="k">new</span> <span class="nc">IntWritable</span><span class="o">(</span><span class="n">key</span><span class="o">));</span>
            <span class="o">}</span>

            <span class="nf">if</span><span class="o">(</span><span class="n">count</span> <span class="o">&gt;</span> <span class="n">K</span><span class="o">)</span>
                <span class="n">break</span><span class="o">;</span>
        <span class="o">}</span>
    <span class="o">}</span>
<span class="o">}</span>
</code></pre></div></div>

<p> </p>

<p>The main function is responsible for executing the job by calling the run function. Within the run function, we create the Word Frequency job by giving it a name, specifying the mapper and reducer classes, setting the input and output file paths and providing classes describing how the input and output will be formatted. Once this job completes, based on the status code, we create another job responsible for providing us the TopK result. This pattern is typically called Chaining. We finally return an exit code based on the success/failure of the job.</p>

<div class="language-scala highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">public</span> <span class="n">static</span> <span class="n">void</span> <span class="nf">main</span><span class="o">(</span><span class="nc">String</span><span class="o">[]</span> <span class="n">args</span><span class="o">)</span> <span class="n">throws</span> <span class="nc">Exception</span> <span class="o">{</span>
    <span class="k">new</span> <span class="nc">WordFrequencyJob</span><span class="o">().</span><span class="py">run</span><span class="o">(</span><span class="n">args</span><span class="o">);</span>
<span class="o">}</span>

<span class="k">private</span> <span class="nc">Job</span> <span class="nf">createJob</span><span class="o">(</span><span class="nc">String</span> <span class="n">inPath</span><span class="o">,</span> <span class="nc">String</span> <span class="n">outPath</span><span class="o">)</span> <span class="n">throws</span> <span class="nc">IOException</span> <span class="o">{</span>
    <span class="nc">String</span> <span class="n">jobName</span> <span class="k">=</span> <span class="s">"Word Frequency in Vanilla MR"</span><span class="o">;</span>
    <span class="nc">Job</span> <span class="n">job</span> <span class="k">=</span> <span class="k">new</span> <span class="nc">Job</span><span class="o">(</span><span class="nf">getConf</span><span class="o">());</span>
    <span class="nv">job</span><span class="o">.</span><span class="py">setJobName</span><span class="o">(</span><span class="nv">jobName</span><span class="o">.</span><span class="py">trim</span><span class="o">());</span>

    <span class="nv">job</span><span class="o">.</span><span class="py">setMapSpeculativeExecution</span><span class="o">(</span><span class="kc">false</span><span class="o">);</span>
    <span class="nv">job</span><span class="o">.</span><span class="py">setMapperClass</span><span class="o">(</span><span class="nv">FrequencyCounterMapper</span><span class="o">.</span><span class="py">class</span><span class="o">);</span>
    <span class="nv">job</span><span class="o">.</span><span class="py">setReducerClass</span><span class="o">(</span><span class="nv">FrequencyCounterReducer</span><span class="o">.</span><span class="py">class</span><span class="o">);</span>

    <span class="nv">job</span><span class="o">.</span><span class="py">setInputFormatClass</span><span class="o">(</span><span class="nv">TextInputFormat</span><span class="o">.</span><span class="py">class</span><span class="o">);</span>
    <span class="nv">TextInputFormat</span><span class="o">.</span><span class="py">setInputPaths</span><span class="o">(</span><span class="n">job</span><span class="o">,</span> <span class="n">inPath</span><span class="o">);</span>

    <span class="nv">job</span><span class="o">.</span><span class="py">setOutputFormatClass</span><span class="o">(</span><span class="nv">TextOutputFormat</span><span class="o">.</span><span class="py">class</span><span class="o">);</span>
    <span class="nv">TextOutputFormat</span><span class="o">.</span><span class="py">setOutputPath</span><span class="o">(</span><span class="n">job</span><span class="o">,</span> <span class="k">new</span> <span class="nc">Path</span><span class="o">(</span><span class="n">outPath</span><span class="o">));</span>

    <span class="nv">job</span><span class="o">.</span><span class="py">setJarByClass</span><span class="o">(</span><span class="k">this</span><span class="o">.</span><span class="py">getClass</span><span class="o">());</span>
    <span class="k">return</span> <span class="n">job</span><span class="o">;</span>
<span class="o">}</span>

<span class="k">private</span> <span class="nc">Job</span> <span class="nf">createTopKJob</span><span class="o">(</span><span class="nc">String</span> <span class="n">inPath</span><span class="o">,</span> <span class="nc">String</span> <span class="n">outPath</span><span class="o">)</span> <span class="n">throws</span> <span class="nc">IOException</span> <span class="o">{</span>
    <span class="nc">String</span> <span class="n">jobName</span> <span class="k">=</span> <span class="s">"Top K Vanilla MR"</span><span class="o">;</span>
    <span class="nc">Job</span> <span class="n">job</span> <span class="k">=</span> <span class="k">new</span> <span class="nc">Job</span><span class="o">(</span><span class="nf">getConf</span><span class="o">());</span>
    <span class="nv">job</span><span class="o">.</span><span class="py">setJobName</span><span class="o">(</span><span class="nv">jobName</span><span class="o">.</span><span class="py">trim</span><span class="o">());</span>

    <span class="nv">job</span><span class="o">.</span><span class="py">setMapSpeculativeExecution</span><span class="o">(</span><span class="kc">false</span><span class="o">);</span>
    <span class="nv">job</span><span class="o">.</span><span class="py">setMapperClass</span><span class="o">(</span><span class="nv">TopKMapper</span><span class="o">.</span><span class="py">class</span><span class="o">);</span>
    <span class="nv">job</span><span class="o">.</span><span class="py">setReducerClass</span><span class="o">(</span><span class="nv">TopKReducer</span><span class="o">.</span><span class="py">class</span><span class="o">);</span>
    <span class="c1">// Because we want only the TopK, it should be reasonable to assume that,</span>
    <span class="c1">// sending data to one reducer would not create any scale problems</span>
    <span class="nv">job</span><span class="o">.</span><span class="py">setNumReduceTasks</span><span class="o">(</span><span class="mi">1</span><span class="o">);</span>

    <span class="nv">job</span><span class="o">.</span><span class="py">setInputFormatClass</span><span class="o">(</span><span class="nv">TextInputFormat</span><span class="o">.</span><span class="py">class</span><span class="o">);</span>
    <span class="nv">TextInputFormat</span><span class="o">.</span><span class="py">setInputPaths</span><span class="o">(</span><span class="n">job</span><span class="o">,</span> <span class="n">inPath</span><span class="o">);</span>

    <span class="nv">job</span><span class="o">.</span><span class="py">setOutputFormatClass</span><span class="o">(</span><span class="nv">TextOutputFormat</span><span class="o">.</span><span class="py">class</span><span class="o">);</span>
    <span class="nv">TextOutputFormat</span><span class="o">.</span><span class="py">setOutputPath</span><span class="o">(</span><span class="n">job</span><span class="o">,</span> <span class="k">new</span> <span class="nc">Path</span><span class="o">(</span><span class="n">outPath</span><span class="o">));</span>

    <span class="nv">job</span><span class="o">.</span><span class="py">setJarByClass</span><span class="o">(</span><span class="k">this</span><span class="o">.</span><span class="py">getClass</span><span class="o">());</span>
    <span class="k">return</span> <span class="n">job</span><span class="o">;</span>
<span class="o">}</span>

<span class="nd">@Override</span>
<span class="n">public</span> <span class="n">int</span> <span class="nf">run</span><span class="o">(</span><span class="nc">String</span><span class="o">[]</span> <span class="n">args</span><span class="o">)</span> <span class="n">throws</span> <span class="nc">Exception</span> <span class="o">{</span>
    <span class="nc">Job</span> <span class="n">job</span> <span class="k">=</span> <span class="nf">createJob</span><span class="o">(</span><span class="n">args</span><span class="o">[</span><span class="err">0</span><span class="o">],</span> <span class="n">args</span><span class="o">[</span><span class="err">1</span><span class="o">]);</span>
    <span class="nf">if</span><span class="o">(</span><span class="nv">job</span><span class="o">.</span><span class="py">waitForCompletion</span><span class="o">(</span><span class="kc">true</span><span class="o">))</span>
        <span class="k">return</span> <span class="nf">createTopKJob</span><span class="o">(</span><span class="n">args</span><span class="o">[</span><span class="err">1</span><span class="o">],</span> <span class="n">args</span><span class="o">[</span><span class="err">2</span><span class="o">]).</span><span class="py">waitForCompletion</span><span class="o">(</span><span class="kc">true</span><span class="o">)</span> <span class="o">?</span> <span class="mi">0</span> <span class="k">:</span> <span class="kt">-</span><span class="err">1</span><span class="o">;</span>
    <span class="k">else</span>
        <span class="k">return</span> <span class="o">-</span><span class="mi">1</span><span class="o">;</span>
<span class="o">}</span>
<span class="o">}</span>
</code></pre></div></div>

<p> </p>

<h3 id="using-scalding">Using Scalding</h3>

<p>Now lets look at writing the same job using Scalding. The code below reads in a text file, outputs every word in a line, filters the stop words, counts the frequency of each word, sorts them in the descending order of frequency and writes these word frequency tuples to a TSV file.</p>

<div class="language-scala highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">class</span> <span class="nc">WordFrequencyJob</span><span class="o">(</span><span class="n">args</span><span class="k">:</span> <span class="kt">Args</span><span class="o">)</span> <span class="k">extends</span> <span class="nc">Job</span><span class="o">(</span><span class="n">args</span><span class="o">)</span> <span class="o">{</span>

  <span class="nc">TextLine</span><span class="o">(</span><span class="nf">args</span><span class="o">(</span><span class="s">"input"</span><span class="o">))</span>
  <span class="o">.</span><span class="py">flatMap</span><span class="o">(</span><span class="ss">'line</span> <span class="o">-&gt;</span> <span class="ss">'word</span><span class="o">)(</span><span class="n">tokenize</span><span class="o">)</span>
  <span class="o">.</span><span class="py">filter</span><span class="o">(</span><span class="ss">'line</span><span class="o">)(!</span><span class="n">stopWord</span><span class="o">)</span>
  <span class="o">.</span><span class="py">groupBy</span><span class="o">(</span><span class="ss">'word</span><span class="o">)</span> <span class="o">{</span> <span class="nv">_</span><span class="o">.</span><span class="py">size</span><span class="o">(</span><span class="ss">'frequency</span><span class="o">)</span> <span class="o">}</span>
  <span class="o">.</span><span class="py">groupAll</span><span class="o">(</span><span class="nv">_</span><span class="o">.</span><span class="py">sortedReverseTake</span><span class="o">((</span><span class="ss">'frequency</span><span class="o">,</span> <span class="ss">'word</span><span class="o">),</span> <span class="nf">args</span><span class="o">(</span><span class="s">"K"</span><span class="o">)))</span>
  <span class="o">.</span><span class="py">write</span><span class="o">(</span><span class="nc">Tsv</span><span class="o">(</span><span class="nf">args</span><span class="o">(</span><span class="s">"output"</span><span class="o">),</span> <span class="o">(</span><span class="ss">'word</span><span class="o">,</span> <span class="ss">'frequency</span><span class="o">)))</span>

  <span class="k">def</span> <span class="nf">tokenize</span><span class="o">(</span><span class="n">line</span><span class="k">:</span> <span class="kt">String</span><span class="o">)</span> <span class="k">=</span> <span class="nv">line</span><span class="o">.</span><span class="py">split</span><span class="o">(</span><span class="s">" "</span><span class="o">).</span><span class="py">map</span><span class="o">(</span><span class="nv">_</span><span class="o">.</span><span class="py">trim</span><span class="o">).</span><span class="py">filterNot</span><span class="o">(</span><span class="nv">_</span><span class="o">.</span><span class="py">isEmpty</span><span class="o">)</span>
  <span class="k">def</span> <span class="nf">stopWord</span><span class="o">(</span><span class="n">word</span><span class="k">:</span> <span class="kt">String</span><span class="o">)</span> <span class="k">=</span> <span class="nv">word</span><span class="o">.</span><span class="py">length</span> <span class="o">&lt;=</span> <span class="mi">2</span>

<span class="o">}</span>
</code></pre></div></div>

<p> </p>

<p>Let’s try to dissect this code line by line.</p>

<h3 id="textline">Textline</h3>

<p>Textline is an example of a Scalding source which contains the input data. In this case the source is a text file. You could also read from a TSV file, Thrift or Protobuf encoded records from HDFS or database records from MySQL.</p>

<div class="language-scala highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">//args input contains the filename to read from</span>
<span class="nc">TextLine</span><span class="o">(</span><span class="nf">args</span><span class="o">(</span><span class="s">"input"</span><span class="o">))</span>
</code></pre></div></div>

<p> </p>

<h3 id="flatmap">flatMap</h3>

<p>flatMap applies the function gives as is argument to a stream of tuples. We define a symbol line, the placeholder for the input and another symbol word, the placeholder for the output. We then specify the tokenize function that describes how to flatmap over the input. The tokenize function takes the line as input and returns a list of words as output.</p>

<div class="language-scala highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">.</span><span class="py">flatMap</span><span class="o">(</span><span class="ss">'line</span> <span class="o">-&gt;</span> <span class="ss">'word</span><span class="o">)</span> <span class="o">(</span><span class="n">tokenize</span><span class="o">)</span>
</code></pre></div></div>

<div class="language-scala highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">tokenize</span><span class="o">(</span><span class="n">line</span><span class="k">:</span> <span class="kt">String</span><span class="o">)</span> <span class="k">=</span> <span class="nv">line</span><span class="o">.</span><span class="py">split</span><span class="o">(</span><span class="s">" "</span><span class="o">).</span><span class="py">map</span><span class="o">(</span><span class="nv">_</span><span class="o">.</span><span class="py">trim</span><span class="o">).</span><span class="py">filterNot</span><span class="o">(</span><span class="nv">_</span><span class="o">.</span><span class="py">isEmpty</span><span class="o">)</span>
</code></pre></div></div>

<p> </p>

<p><em>Note</em> – This is a good example where the UDF equivalent function is defined inline in Scala itself.</p>

<h3 id="filter">filter</h3>

<p>filter applies the predicate function passed to it and filters out all tuples that do not satisfy that predicate. In this case, all words that are not stop words are filtered out.</p>

<div class="language-scala highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">.</span><span class="py">filter</span><span class="o">(!</span><span class="n">stopWord</span><span class="o">)</span>
</code></pre></div></div>

<div class="language-scala highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">stopWord</span><span class="o">(</span><span class="n">word</span><span class="k">:</span> <span class="kt">String</span><span class="o">)</span> <span class="k">=</span> <span class="nv">word</span><span class="o">.</span><span class="py">length</span> <span class="o">&lt;=</span> <span class="mi">2</span>
</code></pre></div></div>

<p> </p>

<h3 id="groupby">groupBy</h3>

<p>groupBy groups similar words together and counts the size of each group. We also specify a new field frequency that contains the size for each word.</p>

<div class="language-scala highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">.</span><span class="py">groupBy</span><span class="o">(</span><span class="ss">'word</span><span class="o">)</span> <span class="o">{</span> <span class="nv">_</span><span class="o">.</span><span class="py">size</span><span class="o">(</span><span class="ss">'frequency</span><span class="o">)</span> <span class="o">}</span>
</code></pre></div></div>

<p> </p>

<h3 id="groupall">groupAll</h3>

<p>groupAll groups all records together and sends them to one reducer. After we’ve all records at one place, we sort them by the frequency field and then the word field in descending order(note the reverse) and pick the first K from it.</p>

<div class="language-scala highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">.</span><span class="py">groupAll</span><span class="o">(</span><span class="nv">_</span><span class="o">.</span><span class="py">sortedReverseTake</span><span class="o">((</span><span class="ss">'frequency</span><span class="o">,</span> <span class="ss">'word</span><span class="o">),</span> <span class="nf">args</span><span class="o">(</span><span class="s">"K"</span><span class="o">)))</span>
</code></pre></div></div>

<p> </p>

<h3 id="write">write</h3>

<p>write allows you to write the data to a source. In this case we are writing to a TSV source.</p>

<div class="language-scala highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">.</span><span class="py">write</span><span class="o">(</span><span class="nc">Tsv</span><span class="o">(</span><span class="nf">args</span><span class="o">(</span><span class="s">"output"</span><span class="o">),</span> <span class="o">(</span><span class="ss">'word</span><span class="o">,</span> <span class="ss">'frequency</span><span class="o">)))</span>
</code></pre></div></div>

<p> </p>

<p>You can run this job by using <a href="https://github.com/twitter/scalding/wiki/Scald.rb">scald.rb</a> or in the newly released <a href="https://github.com/twitter/scalding/wiki/Scalding-REPL">Scalding REPL</a>.</p>

<p>As you can see, Scalding brings you the advantages of the Scala language like conciseness and higher order functions to your MapReduce jobs. We were able to write a job that took 100+ lines of code in Java MapReduce using Scalding in about 10 lines.</p>

<h3 id="everything-is-not-rosy-with-abstractions">Everything is not rosy with abstractions</h3>

<p>There are some practical issues that arise when using higher level abstractions. Its important to be aware of these issues when you are working with one. Most importantly, the full power of the underlying API may not be available for various reasons. For example, because of certain design choices made in the implementation of the abstraction, its either harder, in which case it takes time or sometimes even not possible, to implement certain features. Another example is when the development speed of the abstraction lags significantly behind the underlying API.</p>

<p>Specifically with Scalding, we faced the following two issues</p>

<ul>
  <li>We started using Scalding 0.8 for developing the initial set of our jobs. Job counter support, which provides useful metrics for a job, was not easy to implement in Scalding 0.8. In order to get the counters exposed at the job layer with the map/filter/groupBy abstractions, we had to expose a new set of ‘rich’ apis that provided an instance of FlowProcess(from the Cascading layer) that would provide access to counters. We ended up writing our own Scalding extension library which has richMap, richFilter and richGroupBy apis that allow the jobs to update counters. This library is now obsolete as Scalding version 0.9 onwards have first class support for job counters.</li>
  <li>Since Scalding is built on top of Cascading, which itself was built on the hadoop MapReduce, there as an extra level of indirection between Scalding and the Hadoop MapReduce API. What that means is that the features that are in Hadoop right now will take anywhere from few weeks to months to make it into Scalding. For example, Cascading 3.0 has Apache Tez support, its still not available in Scalding yet because its using Cascading 2.5.5 and has not upgraded to Cascading 3.0. Similarly, Hadoop YARN was available for a while before Cascading 2.5 and hence Scalding got support for it.</li>
</ul>

<h3 id="summary">Summary</h3>

<p>Using the MapReduce paradigm, we were able to scale our batch layer to work with terabytes of data. The linear scalabilty feature of MapReduce made it easy for us to maintain predictable job run times by adding the corresponding number of nodes in our compute cluster. In addition, the generality of MapReduce allows us to use it for multiple jobs running several differet data transformations and varied set of algorithms.</p>

<p>Finally, by using Scalding to express our MapReduce computations we are able to write code that is less complex, composable and error free.</p>]]></content><author><name>Rajesh Muppalla</name></author><summary type="html"><![CDATA[This post is the second in a series of blog posts that will go in depth over our implementation of Lambda Architecture (LA) for building the big data pipeline at Indix.]]></summary></entry><entry><title type="html">Lambda Architecture at Indix, Part I</title><link href="https://www.codingnirvana.in/2014/lambda-architecture-at-indix/" rel="alternate" type="text/html" title="Lambda Architecture at Indix, Part I" /><published>2014-07-17T00:00:00+05:30</published><updated>2014-07-17T00:00:00+05:30</updated><id>https://www.codingnirvana.in/2014/lambda-architecture-at-indix</id><content type="html" xml:base="https://www.codingnirvana.in/2014/lambda-architecture-at-indix/"><![CDATA[<p><em>This post is the first in a series of blog posts that will go in depth over our implementation of Lambda Architecture for building the big data pipeline at Indix.</em></p>

<h3 id="background">Background</h3>

<p><a href="http://www.indix.com">Indix</a> is a product intelligence platform. We are building the world’s largest product database and APIs to enable brands, retailers and developers to deliver the right product to the right customer at the right place, every time.</p>

<p>During the last two years, we have built a catalog of several million products and billions of price points collected from thousands of e-commerce websites. We collect product data as semi-structured HTML via crawling product pages from these websites. Our parsers extract product attributes from the pages. The resultant structured data is then run through a series of machine learning algorithms to classify and extract deeper product attributes, and products get matched across stores. Our analytics engine uses this data to compute aggregates across multiple dimensions and derive actionable insights. This data is also indexed by our search engine. All this data is then consumed by our apps, API and mobile platforms.</p>

<p>The use cases above pose unique and interesting challenges on our data platform in terms of scale, performance, availability, manageability and cost.</p>

<h3 id="data-platform-v10">Data Platform v1.0</h3>

<p>After couple of false starts, the first “stable” version of our data platform is represented in the diagram below.</p>

<p><img src="/assets/img/indix/data_platform_v1.0.png" alt="data_platform_v1.0" /></p>

<p>We were looking for a data model that would allow us to keep a copy of millions of web pages collected by our crawler and related information that would be used by our downstream systems – Machine Learning, Analytics and Search.</p>

<p>We finally ended up using <a href="http://hbase.apache.org/">HBase</a>, an open source implementation of <a href="http://static.googleusercontent.com/media/research.google.com/en//archive/bigtable-osdi06.pdf">Google BigTable</a> to implement our storage layer. The URL of a web page was the row key. We used multiple column families to store additional information related to the URL. For example, the crawlers would store the HTML page contents in the content column family. Likewise, parsers that are responsible for extracting key price and product attributes from a product web page stored the parsed data as a JSON in another column family.</p>

<p>Our processing platform consisted of a series of <a href="http://hadoop.apache.org/">Hadoop</a> MapReduce jobs that would run various ML algorithms on the HBase table to augment the URL row with semantic content including category annotations and tags. These MR jobs would scan the entire table, and were chained together so that each job would read the content from the previous job’s output and write the output to their corresponding column families. We were able to store multiple versions of most of this content using the timestamp based versioning provided by HBase. For example, we were storing the last five versions of the crawled product page and all the versions of the parsed price and product data.</p>

<p>This data would then be exported daily to the analytics system which would run another series of MapReduce jobs to aggregate, calculate derived fields and run precomputations across several dimensions. The output of these jobs was bulk-imported into a HBase table. A snapshot of this data was used as the input to generate the Lucene index used by Solr. Finally, all this data was exposed through service endpoints to be consumed by Web, API and mobile customers.</p>

<h3 id="challenges">Challenges</h3>

<p>This version of our data platform was in production for about a year. During the course of the year, we faced several challenges.</p>

<ul>
  <li><em>Operational Issues</em> – HBase is able to provide high write throughput by writing everything first to an in-memory store (memstore). Once this memstore reaches a certain size, it is flushed to disk into a store file (it also writes to a log file for durability). The store files created on disk are immutable and need to be merged together to improve random read times. This is done by a process called compaction. Compactions are highly disk and CPU intensive and as a result there is high variability of HBase read-write performance and sometimes even server lockup when compactions are running. We followed the best practices around configuration to avoid issues due to compaction but we still ran into regular operational issues with HBase.</li>
  <li><em>Data Corruption</em> – Our data model made it non-trivial to handle URL redirects as that involved mutating data across two or more rows in HBase. When a web page redirects to another web page, we used to copy all the data corresponding to the older web page to the new one and mark the row corresponding to the old web page for deletion. We wrote a daily job that would do this. During one such run, we deployed an incorrect algorithm that caused data corruption. We had to run a migration job to restore some of the data.</li>
  <li><em>Data Loss</em> – As mentioned earlier, we were storing all our parsed data in one of the HBase column families in JSON. We were using the Google GSON libary for converting our POJO objects to JSON. Due to a <a href="https://code.google.com/p/google-gson/issues/detail?id=457">bug</a> in the library, we ended up storing null instead of valid JSON. This was not detected during the write time but was detected later by our metrics system. The only way to recover the parsed data was to re-parse the original HTML. In some cases, we did not have all the versions of the HTML data and as a result, we lost the data for those URLs.</li>
  <li><em>Wrong Choice of MapReduce Abstractions</em> – The first version of our jobs was written using the Java MapReduce API. After writing a handful of jobs, we realized that there was a lot of boiler plate code being written for each job and there was quite a bit of plumbing code to express complex data workflows. We were looking for an abstraction on top of the vanilla MapReduce, that would hide the underlying complexity. We used PIG briefly but we had to learn a new language (PIG Latin) and for anything that the language did not support, we had to write UDFs in Java.</li>
</ul>

<h3 id="data-platform-v20">Data Platform v2.0</h3>

<p>We soon realized that we had to re-think our data system from the ground up. Our existing system was too complex to manage, understand and extend. We needed a simpler approach that would scale, be easier to reason, be tolerant to human errors and could evolve with our product.</p>

<h3 id="lambda-architecture">Lambda Architecture</h3>

<p>Lambda architecture, coined by <a href="http://www.manning.com/marz/">Nathan Marz</a>, an ex-Twitter engineer and the creator of <a href="http://storm.incubator.apache.org/">Storm</a>, seemed like a step in the right direction for us.</p>

<p>Lambda architecture, at its core, is a set of architecture principles and components that allows both batch and real-time to work together while building immutability, recomputation and human fault tolerance into the system.</p>

<h3 id="components">Components</h3>

<p>To better understand how it solved each of the challenges highlighted earlier in the post, I will first try to explain the key components that make up Lambda Architecture. Here’s a diagram that shows the components with an overlay of the technologies we used to implement them at Indix.</p>

<p><img src="/assets/img/indix/lambda_architecture_at_indix.png" alt="lambda_architecture_at_indix" /></p>

<p>The entire architecture can be decomposed into three layers – batch, serving and speed.</p>

<ol>
  <li>All the new data that enters into the system is stored in the batch layer and is also sent to the speed layer. New data enters into our system using our Crawlers, which are implemented using <a href="http://www.akka.io">Akka</a>, an Actor based concurrent library in Scala.</li>
  <li>The batch layer is responsible for computing arbitrary functions on the master data. Our master data is an immutable store in HDFS on which we compute views using a series of MapReduce jobs using <a href="http://github.com/twitter/scalding">Scalding</a>, a Scala library from Twitter that allows you to write MapReduce jobs in the Scala collections API, and <a href="http://spark.apache.org/">Spark</a>, a framework for in-memory distributed computing. Our batch system runs recomputation every day on our entire data set.</li>
  <li>The serving layer indexes and exposes precomputed views to serve ad-hoc queries with low latency. We use HBase, <a href="http://lucene.apache.org/solr/">Solr</a> and Oogway, our own in-memory analytics engine, for the serving layer. The data in this layer is ingested using bulk updates and there are no random writes. When new batch views are available, we swap the existing views with the new ones.</li>
  <li>The speed layer deals only with new data and compensates for the high latency updates of the serving layer by creating real-time views. Currently, our real-time latency requirements are in hours and not in seconds, allowing us to use a micro-batch architecture that is a stripped down version of our batch layer and re-uses the same code and processing framework.</li>
  <li>To get the final result, the batch and real-time views must be queried and the results merged together.</li>
</ol>

<h3 id="principles">Principles</h3>

<p>Lambda architecture also consists of a core set of architectural principles that help you build simple and robust big data systems. I have highlighted the ones that we think are the most important and how these principles address some of the challenges we faced earlier.</p>

<ul>
  <li><em>Immutability and Human Fault Tolerance</em> – Because the master data set contains the ‘rawest’ form of data, it’s easy to recover from mistakes. If you write bad data, you can remove the data and recompute the views or if there are bugs in the functions that compute the view, we just recompute the view. At any point of time, you have the previous versions of the batch and serving layer. So if you can’t wait for the recomputes because of the time it takes, you can use the older non-corrupted version of batch and serving data, thereby trading correctness for latency.</li>
  <li><em>Complexity Isolation</em> – The operational complexity of HBase, as I described earlier, was high because of random writes leading to compactions. In the Lambda architecture implementation, we use HBase in our speed layer. All that complexity is isolated to the speed layer which is much smaller in size (only a few hours of data) compared to the entire batch data (potentially hundreds of days worth of data).</li>
  <li><em>Enforceable Schemas</em> – Lambda architecture advocates the use of enforceable schemas to ensure read time validation of data to avoid data loss. These schemas are implemented by language-neutral serialization frameworks like Thrift, Avro, Protobuf etc. We use Thrift that allows us to enforce schemas which were not possible using the declarative model of JSON.</li>
</ul>

<h3 id="conclusion">Conclusion</h3>

<p>Lambda Architecture is technology and domain agnostic. The trick is to break down the various stages in your data pipeline into the layers of the architecture and choose technologies and frameworks that satisfy the specific requirements of each layer.</p>

<p>The current system has been in production for more than a year now, handling three times more data than our older system and most importantly is more robust and simple. Our batch and serving layers have matured during the last year and are stable. However, our speed and merge layers are still a work in progress and I expect them to evolve significantly over the course of the next year to handle lower latencies.</p>]]></content><author><name>Rajesh Muppalla</name></author><summary type="html"><![CDATA[This post is the first in a series of blog posts that will go in depth over our implementation of Lambda Architecture for building the big data pipeline at Indix.]]></summary></entry><entry><title type="html">Scaling Our Data Platform Using Scala</title><link href="https://www.codingnirvana.in/2014/scaling-our-data-platform-using-scala/" rel="alternate" type="text/html" title="Scaling Our Data Platform Using Scala" /><published>2014-06-23T00:00:00+05:30</published><updated>2014-06-23T00:00:00+05:30</updated><id>https://www.codingnirvana.in/2014/scaling-our-data-platform-using-scala</id><content type="html" xml:base="https://www.codingnirvana.in/2014/scaling-our-data-platform-using-scala/"><![CDATA[<p>At Indix, we are building the world’s largest product database. We collect product data by crawling product pages from e-commerce web sites. This data goes through a series of data pipelines that extract product attributes and run various machine learning algorithms to classify and match similar products across stores. This data is finally fed into our analytics engine and is exposed via an app and API to customers.</p>

<p>During the last two years, we have scaled our catalog to over hundreds of millions of products, crawled from thousands of sites and we are processing billions of price points daily. In this blog post, I will talk about our experiences in scaling our data platform using Scala.</p>

<h3 id="history">HISTORY</h3>

<p>We started as a Java shop in Jan 2012.</p>

<p>Why Java? – When you are starting your company and you know that the first couple of iterations of your product will most probably be throwaway prototypes, it’s very important to choose a language that the core engineering team is familiar with. For us, Java was that language.</p>

<p>Don’t worry – if you survive for a year, you will get lots of opportunities to play with the cool languages around (like we did!).</p>

<h3 id="tech-stack-evolution">TECH STACK EVOLUTION</h3>

<h3 id="march-2013">MARCH 2013</h3>

<p>This is how our data platform stack looked like in March 2013, with Java being the core language
<img src="/assets/img/indix/Tech-Stack-2013.png" alt="Data platform tech stack, March 2013" /></p>

<h3 id="march-2014">MARCH 2014</h3>

<p>And this is how it looked like a year later (March 2014)</p>

<p><img src="/assets/img/indix/Tech-Stack-2014.png" alt="Data platform tech stack, March 2014" /></p>

<p>Since the middle of 2013, Scala and its ecosystem of tools and frameworks have become an integral part of our tech stack.</p>

<h3 id="why-scala">WHY SCALA?</h3>

<p>After about a year of making mistakes and learning the domain, we were looking for ways to fix the pain points in our existing data platform. Our data platform consists of a distributed crawler responsible for data ingestion, and a series of data pipelines that process this data using various algorithms and transformations. Scala, the language, with its mix of functional and object oriented paradigms was an ideal choice to build data pipelines. The Scala ecosystem provided us with a rich set of libraries and frameworks that provided the right abstractions to handle most of our problems.</p>

<p>Here’s a brief overview of the Scala frameworks we are currently using.</p>

<h3 id="akka">AKKA</h3>

<p>The initial version of our crawler, which we wrote in the first couple of months after we started, was in production for about a year. It wasn’t feature rich but allowed us to iterate faster and understand the crawling domain. During this time, we also realized that none of the available open source crawlers would be able to support our custom use cases. We decided to build a new crawler from scratch using various open source distributed components as building blocks for storage, fetching and clustering. For clustering, we were looking for a framework that would provide us features like load-balancing, redundancy and no-single-point-of-failure fault tolerance support. We evaluated Gearman, Apache YARN, Netflix Hystrix and Akka on the above criteria. We chose Akka not only because its cluster module satisified all our requirements, but also for its Actor based concurrent framework.</p>

<p>I have built distributed systems earlier and I learned a very important lesson from that experience – building distributed systems is hard because it involves writing concurrent, multi-threaded code, where you end up solving for concurrency bugs that are very difficult to reason. Akka makes it easy to write concurrent code by decoupling your business logic from low-level mechanisms like threads, locks and non-blocking I/O. The secret sauce are Actors, inspired from Erlang.</p>

<p>The new crawler system has been in production for about many months now and crawls millions of URLs per day with an average of a few TB data crawled daily. It runs on a nodes cluster on EC2.</p>

<h3 id="scalding">SCALDING</h3>

<p>We have over a 100 batch jobs that run on our Hadoop cluster every day as part of the data pipelines. These jobs structure and analyze our product and price data. The first version of the jobs we wrote used the Java MapReduce API. After writing a handful of jobs we realized that there was lot of boiler plate code being written for each job and we had to write quite a bit of plumbing code to express complex data workflows. We were looking for an abstraction on top of the vanilla MapReduce, that would hide the underlying complexity. We used PIG briefly but we had to learn a new language (PIG Latin) and for anything that the language did not support we had to write UDFs in Java. That’s when we evaluated Scalding.</p>

<p>Scalding, an open source library from Twitter and built on top of Cascading, allows us to use idiomatic Scala – using the collections’ API like map, filter, groupBy etc. to define our MapReduce jobs.</p>

<p>Scalding has made it super easy for us to express complex batch jobs (sometimes involving as many as 8-way joins). It’s become the default choice for writing any MapReduce jobs at Indix.</p>

<h3 id="spark">SPARK</h3>

<p>Spark is the newest entrant into the Scala big data ecosystem at Indix. In cases where your batch algorithms needs to be iterative, MapReduce will not scale well because of the heavy disk I/O required for the intermediate output of each iteration. Spark, a standalone data processing framework, keeps data in memory and provides better execution times for iterative algorithms. We use Spark to cluster products for our Matching engine, that deduplicates similar products across stores. We have a 10 node cluster on EC2 running Spark.</p>

<p>We will cover our detailed experiences of using Akka, Scalding and Spark in subsequent blog posts.</p>

<h3 id="other-scala-frameworks">OTHER SCALA FRAMEWORKS</h3>

<p>All the UI that’s required by our data platforms for adminstering and monitoring our data pipelines uses the Play Framework. We are currently investigating SummingBird – another library from Twitter, that allows us to run the same code in both batch and real time modes.</p>

<h3 id="migration-to-scala">MIGRATION TO SCALA</h3>

<p>The good thing with Scala is that it runs on JVM and allows you to reuse existing Java libraries. We did not have to migrate a lot of existing code. In several instances, we had both Java and Scala code being built as part of the same module.</p>

<p>Following is a list of resources that were most helpful for our team to quickly ramp up on the language.</p>

<ul>
  <li><a href="http://aperiodic.net/phil/scala/s-99/">99 problems in Scala</a></li>
  <li><a href="https://www.coursera.org/course/progfun">Functional programming course</a> on Coursera by Martin Odersky (designer of Scala)</li>
  <li><a href="http://twitter.github.io/scala_school/">Twitter’s scala school</a></li>
</ul>

<p>Any new engineer or intern joining our data platform team goes through these resources as part of their on-boarding.</p>

<h3 id="pain-points">PAIN POINTS</h3>

<p>Not everything was smooth sailing for us. There were some pain points during the migration and usage of Scala and its associated frameworks which can be broadly classified into the following three areas:</p>

<ul>
  <li><em>Version incompatibility</em> – This has been one of the major pain points for us. We use Scala 2.10 for most of our internal Scala libraries. The version of Akka that we were using was also built with Scala 2.10. However, the versions of Scalding and Spark were still using 2.9 which made it difficult for us to share our data models. Over the course of the last year, these incompatibilites have accumulated as technical debts. Once every few weeks, we take a couple of days off to migrate our libraries from 2.9 and 2.10. We are doing one of those exercises as I write this blog post.</li>
  <li><em>Message passing using Actors in Akka</em> – There was a learning curve when we started using Akka, primarily because of how Actors communicate. They use asynchronous message passing. You cannot call any methods on Actors. The only way to communicate with them is by passing messages. Unlike RPC, you have to write additonal code to manage reliabilty and synchrony, which becomes an additional burden on the developer. During deployment, we ran into a couple of issues running our crawler on EC2, one of which is described in <a href="https://groups.google.com/forum/#!msg/akka-user/RYxaPl_nby4/PYEb0SqgTa8J">this</a> mail thread on the Akka forums. We eventually solved it by tweaking the failure detector parameters after multiple trials and errors spanning couple of weeks.</li>
  <li><em>Compile times</em> – We definitely miss the instant feedback we got when we would compile or run unit tests in Java. Having said that, we are not terribly unhappy with the compile times and feedback we get with Scala. I think the primary reason is that we do not have very large projects. Our project respositories do not have more than 3 or 4 modules. Also, external compiler and incremental compilation support provide by the IDE (we use IntelliJ Idea) has been a big help.</li>
</ul>

<h3 id="summary">SUMMARY</h3>

<p>Scala, the language, and the frameworks and tools available in its ecosystem have made the developers at Indix more productive than they were a year ago. The shift to Scala and as a result to functional thinking has allowed us to build more scalable, robust and fault tolerant systems.</p>]]></content><author><name>Rajesh Muppalla</name></author><summary type="html"><![CDATA[At Indix, we are building the world’s largest product database. We collect product data by crawling product pages from e-commerce web sites. This data goes through a series of data pipelines that extract product attributes and run various machine learning algorithms to classify and match similar products across stores. This data is finally fed into our analytics engine and is exposed via an app and API to customers.]]></summary></entry></feed>