<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">

  <title><![CDATA[Shady Minds]]></title>
  <link href="http://dyagilev.org/atom.xml" rel="self"/>
  <link href="http://dyagilev.org/"/>
  <updated>2022-07-25T20:53:02-07:00</updated>
  <id>http://dyagilev.org/</id>
  <author>
    <name><![CDATA[Oleksii Diagiliev]]></name>
    
  </author>
  <generator uri="http://octopress.org/">Octopress</generator>

  
  <entry>
    <title type="html"><![CDATA[Building SQL driver for in-memory datagrid. Slides]]></title>
    <link href="http://dyagilev.org/blog/2018/02/02/how-we-built-sql-driver-for-in-memory-datagrid-slides/"/>
    <updated>2018-02-02T17:57:37+03:00</updated>
    <id>http://dyagilev.org/blog/2018/02/02/how-we-built-sql-driver-for-in-memory-datagrid-slides</id>
    <content type="html"><![CDATA[<p>What it takes to build a SQL driver for distributed data storage with Apache Calcite. Sharing my experience in <a href="https://speakerdeck.com/fe2s/how-we-built-sql-driver-for-in-memory-data-grid">the deck</a>.</p>

<!-- more -->

]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[How we built InsightEdge. Slides and talk recording]]></title>
    <link href="http://dyagilev.org/blog/2017/12/06/how-we-built-insightedge/"/>
    <updated>2017-12-06T12:36:00-08:00</updated>
    <id>http://dyagilev.org/blog/2017/12/06/how-we-built-insightedge</id>
    <content type="html"><![CDATA[<p>Sharing my slides and talk recording (in Russian) from JavaDay 2016 conference.</p>

<p>In this talk I discuss how we built an open-source Spark distribution <a href="http://insightedge.io">http://insightedge.io</a> that runs on top of in-memory database.</p>

<!-- more -->

<p>The agenda of the talk:</p>

<ul>
  <li>a need of hybrid transactional and analytical processing</li>
  <li>an overview of in-memory datagrid features</li>
  <li>how we designed InsightEdge RDD partitions to make it scalable</li>
  <li>implementation of Spark DataSource API to support DataFrame/SQL</li>
  <li>optimization techniques: predicates push-down and columns pruning</li>
  <li>how InsightEdge can run 30 times faster that regular Spark</li>
  <li>designing API with Scala, the good and unpleasant parts</li>
  <li>extending Spark API with geo spatial queries</li>
  <li>testing with Docker</li>
</ul>

<p>Video recording (in Russian):</p>

<iframe width="560" height="315" src="https://www.youtube.com/embed/RGoIlpB13U0?rel=0" frameborder="0" gesture="media" allow="encrypted-media" allowfullscreen=""></iframe>

<p>Slides:</p>

<script async="" class="speakerdeck-embed" data-id="67f31bb62d8e42feafdcc1c24db17524" data-ratio="1.41436464088398" src="//speakerdeck.com/assets/embed.js"></script>

]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Real-time Spatial Analytics with InsightEdge Spark: Taxi Price Surge Use Case]]></title>
    <link href="http://dyagilev.org/blog/2016/09/29/real-time-spatial-analytics-with-insightedge-spark-taxi-price-surge-use-case/"/>
    <updated>2016-09-29T16:35:41+03:00</updated>
    <id>http://dyagilev.org/blog/2016/09/29/real-time-spatial-analytics-with-insightedge-spark-taxi-price-surge-use-case</id>
    <content type="html"><![CDATA[<p>A couple of weeks ago we <a href="http://insightedge.io/blog/insightedge-1-0-ga-real-time-insights-actionable-analytics/">launched InsightEdge</a>, introducing you to our high performance Spark distribution with enterprise-grade OLTP capabilities. In this blog post, we will create a demo application for a taxi price surge use case that runs real-time analytics on a streaming geospatial data.</p>

<!-- more -->

<p>We take a fundamental supply and demand economic model of price determination in a market. We will then compute price in real-time based on the current supply and demand.</p>

<p>To make our demo even more fun, we will create a taxi price surge use case. We will consider the transportation business domain, and taxi companies like Uber or Lyft in particular.</p>

<p>In taxi services, the order requests and available drivers represent the supply and demand data correspondingly. It is interesting that this data is bound to geographical location, which introduces additional complexity. Comparing to business areas like retail, where the product demand is linked to either offline store or a well known list of warehouses, the order requests are geographically distributed.</p>

<p>With services like Uber, the fare rates automatically increase when the taxi demand is higher than drivers around you. The Uber prices are surging to ensure reliability and availability for those who agree to pay a bit more.</p>

<h2 id="taxi-price-surge-use-case--3-key-architectural-questions">Taxi Price Surge Use Case – 3 Key Architectural Questions</h2>

<ul>
  <li>How do we handle the events like an ‘Order Request’ event or a ‘Pickup’ event?</li>
  <li>How do we compute the price accounting the nearby requests? We will need to find an efficient way to execute geospatial queries.</li>
  <li>How can we scale technology to run business in many cities, states or countries?</li>
</ul>

<h2 id="architecture">Architecture</h2>

<p>The following diagram illustrates the application architecture:</p>

<p><img src="http://dyagilev.org/images/ie-geospatial/geo-demo-arch-diagram.jpg" /></p>

<h2 id="how-does-this-architecture-addresses-our-3-key-questions">How Does this Architecture Addresses Our 3 Key Questions?</h2>

<p>With InsightEdge Geospatial API we are able to efficiently find nearby orders and, therefore, minimize the time required to compute the price. The efficiency comes from the ability to index order request location in the datagrid.</p>

<p>Kafka allows to handle a high throughput of incoming raw events. Even if the computation layer starts processing slower(say during the peak hour), all the events will be reliably buffered in Kafka. The seamless and proven integration with Spark makes it a good choice for streaming applications.</p>

<p>InsightEdge Data Grid also plays a role of a serving layer handling any operational/transactional queries from web/mobile apps.</p>

<p>All the components(Kafka and InsightEdge) can scale out almost linearly;</p>

<p>To scale to many cities, we can leverage data locality principle through a full pipeline (Kafka, Spark, Data Grid) partitioning by the <code>city</code> or even with a more granular geographical units of scale. In this case the geospatial search query will be limited to a single Data Grid partition. We leave this enhancement out of the scope of the demo.</p>

<h2 id="building-a-demo-application-for-a-taxi-price-surge-use-case">Building a Demo Application for a Taxi Price Surge Use Case</h2>

<p>To simulate the taxi orders we took a <a href="https://github.com/fivethirtyeight/uber-tlc-foil-response">csv dataset</a> with Uber pickups in New York City. The demo application consists of following components:</p>

<ul>
  <li>Feeder application, reads the csv file and sends order and pickup events to Kafka</li>
  <li>InsightEdge processing, a Spark Streaming application that reads from Kafka, computes price and saves to datagrid</li>
  <li>Web app, reads orders from datagrid and visualizes them on a map</li>
</ul>

<p><img src="http://dyagilev.org/images/ie-geospatial/demo_screenshot.jpg" /></p>

<h2 id="coding-processing-logic-with-insightedge-api">Coding Processing Logic with InsightEdge API</h2>

<p>Let’s see how InsightEdge API can be used to calculate the price:</p>

<div class="bogus-wrapper"><notextile><figure class="code"><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
<span class="line-number">2</span>
<span class="line-number">3</span>
<span class="line-number">4</span>
<span class="line-number">5</span>
<span class="line-number">6</span>
<span class="line-number">7</span>
<span class="line-number">8</span>
<span class="line-number">9</span>
<span class="line-number">10</span>
<span class="line-number">11</span>
<span class="line-number">12</span>
<span class="line-number">13</span>
<span class="line-number">14</span>
<span class="line-number">15</span>
<span class="line-number">16</span>
<span class="line-number">17</span>
<span class="line-number">18</span>
<span class="line-number">19</span>
<span class="line-number">20</span>
<span class="line-number">21</span>
<span class="line-number">22</span>
</pre></td><td class="code"><pre><code class="scala"><span class="line"><span class="k">val</span> <span class="n">ordersStream</span> <span class="k">=</span> <span class="n">initKafkaStream</span><span class="o">(</span><span class="n">ssc</span><span class="o">,</span> <span class="s">&quot;orders&quot;</span><span class="o">)</span> <span class="c1">// step 1</span>
</span><span class="line">
</span><span class="line"><span class="n">ordersStream</span>
</span><span class="line">  <span class="o">.</span><span class="n">map</span><span class="o">(</span><span class="n">message</span> <span class="k">=&gt;</span> <span class="nc">Json</span><span class="o">.</span><span class="n">parse</span><span class="o">(</span><span class="n">message</span><span class="o">).</span><span class="n">as</span><span class="o">[</span><span class="kt">OrderEvent</span><span class="o">])</span> <span class="c1">// step 2</span>
</span><span class="line">  <span class="o">.</span><span class="n">transform</span> <span class="o">{</span> <span class="n">rdd</span> <span class="k">=&gt;</span>  <span class="c1">// step 3</span>
</span><span class="line">    <span class="k">val</span> <span class="n">query</span> <span class="k">=</span> <span class="s">&quot;location spatial:within ? AND status = ?&quot;</span>
</span><span class="line">    <span class="k">val</span> <span class="n">radius</span> <span class="k">=</span> <span class="mf">0.5</span> <span class="o">*</span> <span class="nc">DistanceUtils</span><span class="o">.</span><span class="nc">KM_TO_DEG</span>
</span><span class="line">    <span class="k">val</span> <span class="n">queryParamsConstructor</span> <span class="k">=</span> <span class="o">(</span><span class="n">e</span><span class="k">:</span> <span class="kt">OrderEvent</span><span class="o">)</span> <span class="k">=&gt;</span> <span class="nc">Seq</span><span class="o">(</span><span class="n">circle</span><span class="o">(</span><span class="n">point</span><span class="o">(</span><span class="n">e</span><span class="o">.</span><span class="n">longitude</span><span class="o">,</span> <span class="n">e</span><span class="o">.</span><span class="n">latitude</span><span class="o">),</span> <span class="n">radius</span><span class="o">),</span> <span class="nc">NewOrder</span><span class="o">)</span>
</span><span class="line">    <span class="k">val</span> <span class="n">projections</span> <span class="k">=</span> <span class="nc">Some</span><span class="o">(</span><span class="nc">Seq</span><span class="o">(</span><span class="s">&quot;id&quot;</span><span class="o">))</span>
</span><span class="line">    <span class="n">rdd</span><span class="o">.</span><span class="n">zipWithGridSql</span><span class="o">[</span><span class="kt">OrderRequest</span><span class="o">](</span><span class="n">query</span><span class="o">,</span> <span class="n">queryParamsConstructor</span><span class="o">,</span> <span class="n">projections</span><span class="o">)</span>
</span><span class="line">  <span class="o">}</span>
</span><span class="line">  <span class="o">.</span><span class="n">map</span> <span class="o">{</span> <span class="k">case</span> <span class="o">(</span><span class="n">e</span><span class="k">:</span> <span class="kt">OrderEvent</span><span class="o">,</span> <span class="n">nearOrders</span><span class="k">:</span> <span class="kt">Seq</span><span class="o">[</span><span class="kt">OrderRequest</span><span class="o">])</span> <span class="k">=&gt;</span> <span class="c1">// step 4</span>
</span><span class="line">    <span class="k">val</span> <span class="n">location</span> <span class="k">=</span> <span class="n">point</span><span class="o">(</span><span class="n">e</span><span class="o">.</span><span class="n">longitude</span><span class="o">,</span> <span class="n">e</span><span class="o">.</span><span class="n">latitude</span><span class="o">)</span>
</span><span class="line">    <span class="k">val</span> <span class="n">nearOrderIds</span> <span class="k">=</span> <span class="n">nearOrders</span><span class="o">.</span><span class="n">map</span><span class="o">(</span><span class="k">_</span><span class="o">.</span><span class="n">id</span><span class="o">)</span>
</span><span class="line">    <span class="k">val</span> <span class="n">priceFactor</span> <span class="k">=</span> <span class="k">if</span> <span class="o">(</span><span class="n">nearOrderIds</span><span class="o">.</span><span class="n">length</span> <span class="o">&gt;</span> <span class="mi">3</span><span class="o">)</span> <span class="o">{</span>
</span><span class="line">      <span class="mf">1.0</span> <span class="o">+</span> <span class="o">(</span><span class="n">nearOrderIds</span><span class="o">.</span><span class="n">length</span> <span class="o">-</span> <span class="mi">3</span><span class="o">)</span> <span class="o">*</span> <span class="mf">0.1</span>
</span><span class="line">    <span class="o">}</span> <span class="k">else</span> <span class="o">{</span>
</span><span class="line">      <span class="mf">1.0</span>
</span><span class="line">    <span class="o">}</span>
</span><span class="line">    <span class="nc">OrderRequest</span><span class="o">(</span><span class="n">e</span><span class="o">.</span><span class="n">id</span><span class="o">,</span> <span class="n">e</span><span class="o">.</span><span class="n">time</span><span class="o">,</span> <span class="n">location</span><span class="o">,</span> <span class="n">priceFactor</span><span class="o">,</span> <span class="n">nearOrderIds</span><span class="o">,</span> <span class="nc">NewOrder</span><span class="o">)</span>
</span><span class="line">  <span class="o">}</span>
</span><span class="line">  <span class="o">.</span><span class="n">saveToGrid</span><span class="o">()</span> <span class="c1">// step 5</span>
</span></code></pre></td></tr></table></div></figure></notextile></div>

<ul>
  <li>Step 1: Initialize a stream of Kafka <code>orders</code> topic</li>
  <li>Step 2: Parse Kafka message that is in Json format (in real app you may want to use formats like Avro)</li>
  <li>Step 3: For every order we find other non-processed orders within 0.3 km using InsightEdge’s <code>zipWithGridSql()</code> function</li>
  <li>Step 4: Given near orders, we calculate the price with a simple linear function</li>
  <li>Step 5: Finally we save the order details including price and near order ids into the data grid with <code>saveToGrid()</code>function</li>
</ul>

<p>The full source of the application is available on <a href="https://github.com/InsightEdge/insightedge-geo-demo">github</a></p>

<h2 id="taxi-price-surge-use-case-summary">Taxi Price Surge Use Case Summary</h2>

<p>In this blog post we created a demo application that processes the data stream using InsightEdge geospatial features.</p>

<p>An alternative approach for implementing dynamic price surging can use machine learning clustering algorithms to split order requests into clusters and calculate if the demand within a cluster is higher than the supply. This streaming application saves the cluster details in the datagrid. Then, to determine the price we execute a geospatial datagrid query to find which cluster the given location belongs to.</p>

]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Scalable machine learning with InsightEdge: mobile advertisement clicks prediction]]></title>
    <link href="http://dyagilev.org/blog/2016/05/30/scalable-machine-learning-with-insightedge-mobile-advertisement-clicks-prediction/"/>
    <updated>2016-05-30T20:34:42+03:00</updated>
    <id>http://dyagilev.org/blog/2016/05/30/scalable-machine-learning-with-insightedge-mobile-advertisement-clicks-prediction</id>
    <content type="html"><![CDATA[<p>This blog post will provide an introduction into using machine learning algorithms with <a href="http://insightedge.io/">InsightEdge</a>. We will go through an exercise to predict mobile advertisement click-through rate with <a href="https://www.kaggle.com/c/avazu-ctr-prediction">Avazu’s dataset</a>.</p>

<!-- more -->

<ul id="markdown-toc">
  <li><a href="#overview" id="markdown-toc-overview">Overview</a></li>
  <li><a href="#loading-the-data" id="markdown-toc-loading-the-data">Loading the data</a></li>
  <li><a href="#exploring-the-data" id="markdown-toc-exploring-the-data">Exploring the data</a></li>
  <li><a href="#processing-and-transforming-the-data" id="markdown-toc-processing-and-transforming-the-data">Processing and transforming the data</a></li>
  <li><a href="#saving-preprocessed-data-to-the-data-grid" id="markdown-toc-saving-preprocessed-data-to-the-data-grid">Saving preprocessed data to the data grid</a></li>
  <li><a href="#a-simple-algorithm" id="markdown-toc-a-simple-algorithm">A simple algorithm</a></li>
  <li><a href="#experimenting-with-more-features" id="markdown-toc-experimenting-with-more-features">Experimenting with more features</a></li>
  <li><a href="#tuning-algorithm-parameters" id="markdown-toc-tuning-algorithm-parameters">Tuning algorithm parameters</a></li>
  <li><a href="#architecture" id="markdown-toc-architecture">Architecture</a></li>
  <li><a href="#summary" id="markdown-toc-summary">Summary</a></li>
</ul>

<h2 id="overview">Overview</h2>

<p>There are several compensation models in online advertising industry, probably the most notable is CPC (Cost Per Click), in which an advertiser pays a publisher when the ad is clicked.
Search engine advertising is one of the most popular forms of CPC. It allows advertisers to bid for ad placement in a search engine’s sponsored links when someone searches on a keyword that is related to their business offering.</p>

<p>For the search engines like Google, advertising is one of the main sources of their revenue. The challenge for the advertising system is to determine what ad should be displayed for each query that the search engine receives.</p>

<p>The revenue search engine can get is essentially:</p>

<p><code>revenue = bid * probability_of_click</code></p>

<p>The goal is to maximize the revenue for every search engine query. Whereis the <code>bid</code> is a known value, the <code>probability_of_click</code> is not. Thus predicting the probability of click becomes the key task.</p>

<p>Working on a machine learning problem involves a lot of experiments with feature selection, feature transformation, training different models and tuning parameters.While there are a few excellent machine learning libraries for Python and R, like scikit-learn, their capabilities are typically limited to relatively small datasets that you fit on a single machine.</p>

<p>With the large datasets and/or CPU intensive workloads you may want to scale out beyond a single machine. This is one of the key benefits of InsightEdge, since it’s able to scale the computation and data storage layers across many machines under one unified cluster.</p>

<h2 id="loading-the-data">Loading the data</h2>

<p>The <a href="https://www.kaggle.com/c/avazu-ctr-prediction/data">dataset</a> consists of:</p>

<ul>
  <li>train (5.9G) - Training set. 10 days of click-through data, ordered chronologically. Non-clicks and clicks are subsampled according to different strategies.</li>
  <li>test (674M) - Test set. 1 day of ads to test model predictions.</li>
</ul>

<p>At first, we want to launch InsightEdge.</p>

<p>To get the first data insights quickly, one can <a href="http://insightedge.io/docs/010/0_quick_start.html">launch InsightEdge on a laptop</a>.
Though for the big datasets or compute-intensive tasks the resources of a single machine might not be enough.</p>

<p>For this problem we will <a href="http://insightedge.io/docs/010/13_cluster_setup.html">setup a cluster</a> with four workers and place the downloaded files on HDFS.</p>

<p><img src="http://dyagilev.org/images/ie-ml/0_cluster.png" /></p>

<p>Let’s open the interactive <a href="http://insightedge.io/docs/010/14_notebook.html">Web Notebook</a> and start exploring our dataset.</p>

<p>The dataset is in csv format, so we will use databricks csv library to load it from hdfs into the Spark dataframe:</p>

<div class="bogus-wrapper"><notextile><figure class="code"><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
<span class="line-number">2</span>
</pre></td><td class="code"><pre><code class="scala"><span class="line"><span class="o">%</span><span class="n">dep</span>
</span><span class="line"><span class="n">z</span><span class="o">.</span><span class="n">load</span><span class="o">(</span><span class="s">&quot;com.databricks:spark-csv_2.10:1.3.0&quot;</span><span class="o">)</span>
</span></code></pre></td></tr></table></div></figure></notextile></div>

<p>Load the dataframe into Spark memory and cache:</p>

<div class="bogus-wrapper"><notextile><figure class="code"><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
<span class="line-number">2</span>
<span class="line-number">3</span>
<span class="line-number">4</span>
<span class="line-number">5</span>
<span class="line-number">6</span>
<span class="line-number">7</span>
</pre></td><td class="code"><pre><code class="scala"><span class="line"><span class="k">val</span> <span class="n">df</span> <span class="k">=</span> <span class="n">sqlContext</span><span class="o">.</span><span class="n">read</span>
</span><span class="line">      <span class="o">.</span><span class="n">format</span><span class="o">(</span><span class="s">&quot;com.databricks.spark.csv&quot;</span><span class="o">)</span>
</span><span class="line">      <span class="o">.</span><span class="n">option</span><span class="o">(</span><span class="s">&quot;header&quot;</span><span class="o">,</span> <span class="s">&quot;true&quot;</span><span class="o">)</span>
</span><span class="line">      <span class="o">.</span><span class="n">option</span><span class="o">(</span><span class="s">&quot;inferSchema&quot;</span><span class="o">,</span> <span class="s">&quot;false&quot;</span><span class="o">)</span>
</span><span class="line">      <span class="o">.</span><span class="n">load</span><span class="o">(</span><span class="s">&quot;hdfs://10.8.1.116/data/avazu_ctr/train&quot;</span><span class="o">)</span>
</span><span class="line">
</span><span class="line"><span class="n">df</span><span class="o">.</span><span class="n">cache</span><span class="o">()</span>
</span></code></pre></td></tr></table></div></figure></notextile></div>

<h2 id="exploring-the-data">Exploring the data</h2>

<p>Now that we have the dataset in Spark memory, we can read the first rows:</p>

<div class="bogus-wrapper"><notextile><figure class="code"><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
</pre></td><td class="code"><pre><code class="scala"><span class="line"><span class="n">df</span><span class="o">.</span><span class="n">show</span><span class="o">(</span><span class="mi">10</span><span class="o">)</span>
</span></code></pre></td></tr></table></div></figure></notextile></div>

<div class="bogus-wrapper"><notextile><figure class="code"><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
<span class="line-number">2</span>
<span class="line-number">3</span>
<span class="line-number">4</span>
<span class="line-number">5</span>
<span class="line-number">6</span>
<span class="line-number">7</span>
<span class="line-number">8</span>
<span class="line-number">9</span>
<span class="line-number">10</span>
<span class="line-number">11</span>
<span class="line-number">12</span>
<span class="line-number">13</span>
<span class="line-number">14</span>
<span class="line-number">15</span>
</pre></td><td class="code"><pre><code class="scala"><span class="line"><span class="o">+--------------------+-----+--------+----+----------+--------+-----------+-------------+--------+----------+------------+---------+---------+------------+-----------+----------------+-----+---+---+----+---+---+------+---+</span>
</span><span class="line"><span class="o">|</span>                  <span class="n">id</span><span class="o">|</span><span class="n">click</span><span class="o">|</span>    <span class="n">hour</span><span class="o">|</span>  <span class="n">C1</span><span class="o">|</span><span class="n">banner_pos</span><span class="o">|</span> <span class="n">site_id</span><span class="o">|</span><span class="n">site_domain</span><span class="o">|</span><span class="n">site_category</span><span class="o">|</span>  <span class="n">app_id</span><span class="o">|</span><span class="n">app_domain</span><span class="o">|</span><span class="n">app_category</span><span class="o">|</span><span class="n">device_id</span><span class="o">|</span><span class="n">device_ip</span><span class="o">|</span><span class="n">device_model</span><span class="o">|</span><span class="n">device_type</span><span class="o">|</span><span class="n">device_conn_type</span><span class="o">|</span>  <span class="n">C14</span><span class="o">|</span><span class="n">C15</span><span class="o">|</span><span class="n">C16</span><span class="o">|</span> <span class="n">C17</span><span class="o">|</span><span class="n">C18</span><span class="o">|</span><span class="n">C19</span><span class="o">|</span>   <span class="n">C20</span><span class="o">|</span><span class="n">C21</span><span class="o">|</span>
</span><span class="line"><span class="o">+--------------------+-----+--------+----+----------+--------+-----------+-------------+--------+----------+------------+---------+---------+------------+-----------+----------------+-----+---+---+----+---+---+------+---+</span>
</span><span class="line"><span class="o">|</span> <span class="mi">1000009418151094273</span><span class="o">|</span>    <span class="mi">0</span><span class="o">|</span><span class="mi">14102100</span><span class="o">|</span><span class="mi">1005</span><span class="o">|</span>         <span class="mi">0</span><span class="o">|</span><span class="mi">1</span><span class="n">fbe01fe</span><span class="o">|</span>   <span class="n">f3845767</span><span class="o">|</span>     <span class="mi">28905</span><span class="n">ebd</span><span class="o">|</span><span class="n">ecad2386</span><span class="o">|</span>  <span class="mi">7801</span><span class="n">e8d9</span><span class="o">|</span>    <span class="mi">07</span><span class="n">d7df22</span><span class="o">|</span> <span class="n">a99f214a</span><span class="o">|</span> <span class="n">ddd2926e</span><span class="o">|</span>    <span class="mi">44956</span><span class="n">a24</span><span class="o">|</span>          <span class="mi">1</span><span class="o">|</span>               <span class="mi">2</span><span class="o">|</span><span class="mi">15706</span><span class="o">|</span><span class="mi">320</span><span class="o">|</span> <span class="mi">50</span><span class="o">|</span><span class="mi">1722</span><span class="o">|</span>  <span class="mi">0</span><span class="o">|</span> <span class="mi">35</span><span class="o">|</span>    <span class="o">-</span><span class="mi">1</span><span class="o">|</span> <span class="mi">79</span><span class="o">|</span>
</span><span class="line"><span class="o">|</span><span class="mi">10000169349117863715</span><span class="o">|</span>    <span class="mi">0</span><span class="o">|</span><span class="mi">14102100</span><span class="o">|</span><span class="mi">1005</span><span class="o">|</span>         <span class="mi">0</span><span class="o">|</span><span class="mi">1</span><span class="n">fbe01fe</span><span class="o">|</span>   <span class="n">f3845767</span><span class="o">|</span>     <span class="mi">28905</span><span class="n">ebd</span><span class="o">|</span><span class="n">ecad2386</span><span class="o">|</span>  <span class="mi">7801</span><span class="n">e8d9</span><span class="o">|</span>    <span class="mi">07</span><span class="n">d7df22</span><span class="o">|</span> <span class="n">a99f214a</span><span class="o">|</span> <span class="mi">96809</span><span class="n">ac8</span><span class="o">|</span>    <span class="mi">711</span><span class="n">ee120</span><span class="o">|</span>          <span class="mi">1</span><span class="o">|</span>               <span class="mi">0</span><span class="o">|</span><span class="mi">15704</span><span class="o">|</span><span class="mi">320</span><span class="o">|</span> <span class="mi">50</span><span class="o">|</span><span class="mi">1722</span><span class="o">|</span>  <span class="mi">0</span><span class="o">|</span> <span class="mi">35</span><span class="o">|</span><span class="mi">100084</span><span class="o">|</span> <span class="mi">79</span><span class="o">|</span>
</span><span class="line"><span class="o">|</span><span class="mi">10000371904215119486</span><span class="o">|</span>    <span class="mi">0</span><span class="o">|</span><span class="mi">14102100</span><span class="o">|</span><span class="mi">1005</span><span class="o">|</span>         <span class="mi">0</span><span class="o">|</span><span class="mi">1</span><span class="n">fbe01fe</span><span class="o">|</span>   <span class="n">f3845767</span><span class="o">|</span>     <span class="mi">28905</span><span class="n">ebd</span><span class="o">|</span><span class="n">ecad2386</span><span class="o">|</span>  <span class="mi">7801</span><span class="n">e8d9</span><span class="o">|</span>    <span class="mi">07</span><span class="n">d7df22</span><span class="o">|</span> <span class="n">a99f214a</span><span class="o">|</span> <span class="n">b3cf8def</span><span class="o">|</span>    <span class="mi">8</span><span class="n">a4875bd</span><span class="o">|</span>          <span class="mi">1</span><span class="o">|</span>               <span class="mi">0</span><span class="o">|</span><span class="mi">15704</span><span class="o">|</span><span class="mi">320</span><span class="o">|</span> <span class="mi">50</span><span class="o">|</span><span class="mi">1722</span><span class="o">|</span>  <span class="mi">0</span><span class="o">|</span> <span class="mi">35</span><span class="o">|</span><span class="mi">100084</span><span class="o">|</span> <span class="mi">79</span><span class="o">|</span>
</span><span class="line"><span class="o">|</span><span class="mi">10000640724480838376</span><span class="o">|</span>    <span class="mi">0</span><span class="o">|</span><span class="mi">14102100</span><span class="o">|</span><span class="mi">1005</span><span class="o">|</span>         <span class="mi">0</span><span class="o">|</span><span class="mi">1</span><span class="n">fbe01fe</span><span class="o">|</span>   <span class="n">f3845767</span><span class="o">|</span>     <span class="mi">28905</span><span class="n">ebd</span><span class="o">|</span><span class="n">ecad2386</span><span class="o">|</span>  <span class="mi">7801</span><span class="n">e8d9</span><span class="o">|</span>    <span class="mi">07</span><span class="n">d7df22</span><span class="o">|</span> <span class="n">a99f214a</span><span class="o">|</span> <span class="n">e8275b8f</span><span class="o">|</span>    <span class="mi">6332421</span><span class="n">a</span><span class="o">|</span>          <span class="mi">1</span><span class="o">|</span>               <span class="mi">0</span><span class="o">|</span><span class="mi">15706</span><span class="o">|</span><span class="mi">320</span><span class="o">|</span> <span class="mi">50</span><span class="o">|</span><span class="mi">1722</span><span class="o">|</span>  <span class="mi">0</span><span class="o">|</span> <span class="mi">35</span><span class="o">|</span><span class="mi">100084</span><span class="o">|</span> <span class="mi">79</span><span class="o">|</span>
</span><span class="line"><span class="o">|</span><span class="mi">10000679056417042096</span><span class="o">|</span>    <span class="mi">0</span><span class="o">|</span><span class="mi">14102100</span><span class="o">|</span><span class="mi">1005</span><span class="o">|</span>         <span class="mi">1</span><span class="o">|</span><span class="n">fe8cc448</span><span class="o">|</span>   <span class="mi">9166</span><span class="n">c161</span><span class="o">|</span>     <span class="mi">0569</span><span class="n">f928</span><span class="o">|</span><span class="n">ecad2386</span><span class="o">|</span>  <span class="mi">7801</span><span class="n">e8d9</span><span class="o">|</span>    <span class="mi">07</span><span class="n">d7df22</span><span class="o">|</span> <span class="n">a99f214a</span><span class="o">|</span> <span class="mi">9644</span><span class="n">d0bf</span><span class="o">|</span>    <span class="mi">779</span><span class="n">d90c2</span><span class="o">|</span>          <span class="mi">1</span><span class="o">|</span>               <span class="mi">0</span><span class="o">|</span><span class="mi">18993</span><span class="o">|</span><span class="mi">320</span><span class="o">|</span> <span class="mi">50</span><span class="o">|</span><span class="mi">2161</span><span class="o">|</span>  <span class="mi">0</span><span class="o">|</span> <span class="mi">35</span><span class="o">|</span>    <span class="o">-</span><span class="mi">1</span><span class="o">|</span><span class="mi">157</span><span class="o">|</span>
</span><span class="line"><span class="o">|</span><span class="mi">10000720757801103869</span><span class="o">|</span>    <span class="mi">0</span><span class="o">|</span><span class="mi">14102100</span><span class="o">|</span><span class="mi">1005</span><span class="o">|</span>         <span class="mi">0</span><span class="o">|</span><span class="n">d6137915</span><span class="o">|</span>   <span class="n">bb1ef334</span><span class="o">|</span>     <span class="n">f028772b</span><span class="o">|</span><span class="n">ecad2386</span><span class="o">|</span>  <span class="mi">7801</span><span class="n">e8d9</span><span class="o">|</span>    <span class="mi">07</span><span class="n">d7df22</span><span class="o">|</span> <span class="n">a99f214a</span><span class="o">|</span> <span class="mi">05241</span><span class="n">af0</span><span class="o">|</span>    <span class="mi">8</span><span class="n">a4875bd</span><span class="o">|</span>          <span class="mi">1</span><span class="o">|</span>               <span class="mi">0</span><span class="o">|</span><span class="mi">16920</span><span class="o">|</span><span class="mi">320</span><span class="o">|</span> <span class="mi">50</span><span class="o">|</span><span class="mi">1899</span><span class="o">|</span>  <span class="mi">0</span><span class="o">|</span><span class="mi">431</span><span class="o">|</span><span class="mi">100077</span><span class="o">|</span><span class="mi">117</span><span class="o">|</span>
</span><span class="line"><span class="o">|</span><span class="mi">10000724729988544911</span><span class="o">|</span>    <span class="mi">0</span><span class="o">|</span><span class="mi">14102100</span><span class="o">|</span><span class="mi">1005</span><span class="o">|</span>         <span class="mi">0</span><span class="o">|</span><span class="mi">8</span><span class="n">fda644b</span><span class="o">|</span>   <span class="mi">25</span><span class="n">d4cfcd</span><span class="o">|</span>     <span class="n">f028772b</span><span class="o">|</span><span class="n">ecad2386</span><span class="o">|</span>  <span class="mi">7801</span><span class="n">e8d9</span><span class="o">|</span>    <span class="mi">07</span><span class="n">d7df22</span><span class="o">|</span> <span class="n">a99f214a</span><span class="o">|</span> <span class="n">b264c159</span><span class="o">|</span>    <span class="n">be6db1d7</span><span class="o">|</span>          <span class="mi">1</span><span class="o">|</span>               <span class="mi">0</span><span class="o">|</span><span class="mi">20362</span><span class="o">|</span><span class="mi">320</span><span class="o">|</span> <span class="mi">50</span><span class="o">|</span><span class="mi">2333</span><span class="o">|</span>  <span class="mi">0</span><span class="o">|</span> <span class="mi">39</span><span class="o">|</span>    <span class="o">-</span><span class="mi">1</span><span class="o">|</span><span class="mi">157</span><span class="o">|</span>
</span><span class="line"><span class="o">|</span><span class="mi">10000918755742328737</span><span class="o">|</span>    <span class="mi">0</span><span class="o">|</span><span class="mi">14102100</span><span class="o">|</span><span class="mi">1005</span><span class="o">|</span>         <span class="mi">1</span><span class="o">|</span><span class="n">e151e245</span><span class="o">|</span>   <span class="mi">7</span><span class="n">e091613</span><span class="o">|</span>     <span class="n">f028772b</span><span class="o">|</span><span class="n">ecad2386</span><span class="o">|</span>  <span class="mi">7801</span><span class="n">e8d9</span><span class="o">|</span>    <span class="mi">07</span><span class="n">d7df22</span><span class="o">|</span> <span class="n">a99f214a</span><span class="o">|</span> <span class="n">e6f67278</span><span class="o">|</span>    <span class="n">be74e6fe</span><span class="o">|</span>          <span class="mi">1</span><span class="o">|</span>               <span class="mi">0</span><span class="o">|</span><span class="mi">20632</span><span class="o">|</span><span class="mi">320</span><span class="o">|</span> <span class="mi">50</span><span class="o">|</span><span class="mi">2374</span><span class="o">|</span>  <span class="mi">3</span><span class="o">|</span> <span class="mi">39</span><span class="o">|</span>    <span class="o">-</span><span class="mi">1</span><span class="o">|</span> <span class="mi">23</span><span class="o">|</span>
</span><span class="line"><span class="o">|</span><span class="mi">10000949271186029916</span><span class="o">|</span>    <span class="mi">1</span><span class="o">|</span><span class="mi">14102100</span><span class="o">|</span><span class="mi">1005</span><span class="o">|</span>         <span class="mi">0</span><span class="o">|</span><span class="mi">1</span><span class="n">fbe01fe</span><span class="o">|</span>   <span class="n">f3845767</span><span class="o">|</span>     <span class="mi">28905</span><span class="n">ebd</span><span class="o">|</span><span class="n">ecad2386</span><span class="o">|</span>  <span class="mi">7801</span><span class="n">e8d9</span><span class="o">|</span>    <span class="mi">07</span><span class="n">d7df22</span><span class="o">|</span> <span class="n">a99f214a</span><span class="o">|</span> <span class="mi">37</span><span class="n">e8da74</span><span class="o">|</span>    <span class="mi">5</span><span class="n">db079b5</span><span class="o">|</span>          <span class="mi">1</span><span class="o">|</span>               <span class="mi">2</span><span class="o">|</span><span class="mi">15707</span><span class="o">|</span><span class="mi">320</span><span class="o">|</span> <span class="mi">50</span><span class="o">|</span><span class="mi">1722</span><span class="o">|</span>  <span class="mi">0</span><span class="o">|</span> <span class="mi">35</span><span class="o">|</span>    <span class="o">-</span><span class="mi">1</span><span class="o">|</span> <span class="mi">79</span><span class="o">|</span>
</span><span class="line"><span class="o">|</span><span class="mi">10001264480619467364</span><span class="o">|</span>    <span class="mi">0</span><span class="o">|</span><span class="mi">14102100</span><span class="o">|</span><span class="mi">1002</span><span class="o">|</span>         <span class="mi">0</span><span class="o">|</span><span class="mi">84</span><span class="n">c7ba46</span><span class="o">|</span>   <span class="n">c4e18dd6</span><span class="o">|</span>     <span class="mi">50</span><span class="n">e219e0</span><span class="o">|</span><span class="n">ecad2386</span><span class="o">|</span>  <span class="mi">7801</span><span class="n">e8d9</span><span class="o">|</span>    <span class="mi">07</span><span class="n">d7df22</span><span class="o">|</span> <span class="n">c357dbff</span><span class="o">|</span> <span class="n">f1ac7184</span><span class="o">|</span>    <span class="mi">373</span><span class="n">ecbe6</span><span class="o">|</span>          <span class="mi">0</span><span class="o">|</span>               <span class="mi">0</span><span class="o">|</span><span class="mi">21689</span><span class="o">|</span><span class="mi">320</span><span class="o">|</span> <span class="mi">50</span><span class="o">|</span><span class="mi">2496</span><span class="o">|</span>  <span class="mi">3</span><span class="o">|</span><span class="mi">167</span><span class="o">|</span><span class="mi">100191</span><span class="o">|</span> <span class="mi">23</span><span class="o">|</span>
</span><span class="line"><span class="o">+--------------------+-----+--------+----+----------+--------+-----------+-------------+--------+----------+------------+---------+---------+------------+-----------+----------------+-----+---+---+----+---+---+------+---+</span>
</span><span class="line"><span class="n">only</span> <span class="n">showing</span> <span class="n">top</span> <span class="mi">10</span> <span class="n">rows</span>
</span></code></pre></td></tr></table></div></figure></notextile></div>

<p>The data fields are:</p>

<ul>
  <li>id: ad identifier</li>
  <li>click: 0/1 for non-click/click</li>
  <li>hour: format is YYMMDDHH</li>
  <li>C1: anonymized categorical variable</li>
  <li>banner_pos</li>
  <li>site_id</li>
  <li>site_domain</li>
  <li>site_category</li>
  <li>app_id</li>
  <li>app_domain</li>
  <li>app_category</li>
  <li>device_id</li>
  <li>device_ip</li>
  <li>device_model</li>
  <li>device_type</li>
  <li>device_conn_type</li>
  <li>C14-C21 – anonymized categorical variables</li>
</ul>

<p>Let’s see how many rows are in the training dataset:</p>

<div class="bogus-wrapper"><notextile><figure class="code"><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
<span class="line-number">2</span>
<span class="line-number">3</span>
</pre></td><td class="code"><pre><code class="scala"><span class="line"><span class="k">val</span> <span class="n">totalCount</span> <span class="k">=</span> <span class="n">df</span><span class="o">.</span><span class="n">count</span><span class="o">()</span>
</span><span class="line">
</span><span class="line"><span class="n">totalCount</span><span class="k">:</span> <span class="kt">Long</span> <span class="o">=</span> <span class="mi">40428967</span>
</span></code></pre></td></tr></table></div></figure></notextile></div>

<p>There are about 40M+ rows in the dataset.</p>

<p>Let’s now calculate the CTR (click-through rate) of the dataset. The click-through rate is the number of times a click is made on the advertisement divided by the total impressions (the number of times an advertisement was served):</p>

<div class="bogus-wrapper"><notextile><figure class="code"><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
<span class="line-number">2</span>
<span class="line-number">3</span>
<span class="line-number">4</span>
<span class="line-number">5</span>
</pre></td><td class="code"><pre><code class="scala"><span class="line"><span class="k">val</span> <span class="n">clicks</span> <span class="k">=</span> <span class="n">df</span><span class="o">.</span><span class="n">filter</span><span class="o">(</span><span class="s">&quot;click = 1&quot;</span><span class="o">).</span><span class="n">count</span><span class="o">()</span>
</span><span class="line"><span class="k">val</span> <span class="n">ctr</span> <span class="k">=</span> <span class="n">clicks</span><span class="o">.</span><span class="n">toFloat</span> <span class="o">/</span> <span class="n">totalCount</span>
</span><span class="line">
</span><span class="line"><span class="n">clicks</span><span class="k">:</span> <span class="kt">Long</span> <span class="o">=</span> <span class="mi">6865066</span>
</span><span class="line"><span class="n">ctr</span><span class="k">:</span> <span class="kt">Float</span> <span class="o">=</span> <span class="mf">0.16980562</span>
</span></code></pre></td></tr></table></div></figure></notextile></div>
<p>The CTR is 0.169 (or 16.9%) which is quite a high number, the common value in the industry is about 0.2-0.3%. So a high value is probably because non-clicks and clicks are subsampled according to different strategies, as stated by Avazu.</p>

<p>Now, the question is which features should we use to create a predictive model? This is a difficult question that requires a deep knowledge of the problem domain. Let’s try to learn it from the dataset we have.</p>

<p>For example, let’s explore the <code>device_conn_type</code> feature. Our assumption might be that this is a categorical variable like Wi-Fi, 2G, 3G or LTE. This might be a relevant feature since clicking on an ad with a slow connection is not something common.</p>

<p>At first, we register the dataframe as a SQL table:</p>

<div class="bogus-wrapper"><notextile><figure class="code"><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
</pre></td><td class="code"><pre><code class="scala"><span class="line"><span class="n">df</span><span class="o">.</span><span class="n">registerTempTable</span><span class="o">(</span><span class="s">&quot;training&quot;</span><span class="o">)</span>
</span></code></pre></td></tr></table></div></figure></notextile></div>

<p>and run the SQL query:</p>

<div class="bogus-wrapper"><notextile><figure class="code"><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
<span class="line-number">2</span>
<span class="line-number">3</span>
<span class="line-number">4</span>
</pre></td><td class="code"><pre><code class="sql"><span class="line"><span class="o">%</span><span class="k">sql</span>
</span><span class="line"><span class="k">SELECT</span> <span class="n">device_conn_type</span><span class="p">,</span> <span class="k">SUM</span><span class="p">(</span><span class="n">click</span><span class="p">)</span> <span class="k">as</span> <span class="n">clicks_num</span><span class="p">,</span> <span class="k">COUNT</span><span class="p">(</span><span class="n">click</span><span class="p">)</span> <span class="k">as</span> <span class="n">impression</span><span class="p">,</span> <span class="k">SUM</span><span class="p">(</span><span class="n">click</span><span class="p">)</span><span class="o">/</span><span class="k">COUNT</span><span class="p">(</span><span class="n">click</span><span class="p">)</span> <span class="k">as</span> <span class="n">ctr</span>
</span><span class="line"><span class="k">FROM</span> <span class="n">training</span>
</span><span class="line"><span class="k">GROUP</span> <span class="k">BY</span> <span class="n">device_conn_type</span>
</span></code></pre></td></tr></table></div></figure></notextile></div>

<p><img src="http://dyagilev.org/images/ie-ml/6_device_conn_type.png" /></p>

<p><img src="http://dyagilev.org/images/ie-ml/7_device_conn_type_2.png" /></p>

<p>We see that there are four connection type categories. Two categories with CTR 18% and 13%, and the first one is almost 90% of the whole dataset. The other two categories have significantly lower CTR.</p>

<p>Another observation we may notice is that features C15 and C16 look like the ad size:</p>

<div class="bogus-wrapper"><notextile><figure class="code"><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
<span class="line-number">2</span>
<span class="line-number">3</span>
<span class="line-number">4</span>
<span class="line-number">5</span>
</pre></td><td class="code"><pre><code class="sql"><span class="line"><span class="o">%</span><span class="k">sql</span>
</span><span class="line"><span class="k">SELECT</span> <span class="n">C15</span><span class="p">,</span> <span class="n">C16</span><span class="p">,</span> <span class="k">COUNT</span><span class="p">(</span><span class="n">click</span><span class="p">)</span> <span class="k">as</span> <span class="n">impression</span><span class="p">,</span> <span class="k">SUM</span><span class="p">(</span><span class="n">click</span><span class="p">)</span><span class="o">/</span><span class="k">COUNT</span><span class="p">(</span><span class="n">click</span><span class="p">)</span> <span class="k">as</span> <span class="n">ctr</span>
</span><span class="line"><span class="k">FROM</span> <span class="n">training</span>
</span><span class="line"><span class="k">GROUP</span> <span class="k">BY</span> <span class="n">C15</span><span class="p">,</span> <span class="n">C16</span>
</span><span class="line"><span class="k">ORDER</span> <span class="k">BY</span> <span class="n">ctr</span> <span class="k">DESC</span>
</span></code></pre></td></tr></table></div></figure></notextile></div>

<p><img src="http://dyagilev.org/images/ie-ml/11_banner_dimension.png" /></p>

<p>We can notice some correlation between the ad size and its performance. The most common one appears to be 320x50px known as “mobile leaderboard” in <a href="https://support.google.com/adsense/answer/68727?hl=en">Google AdSense</a>.</p>

<p>What about other features? All of them represent categorical values, how many unique categories for each feature?</p>

<div class="bogus-wrapper"><notextile><figure class="code"><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
<span class="line-number">2</span>
<span class="line-number">3</span>
</pre></td><td class="code"><pre><code class="scala"><span class="line"><span class="n">df</span><span class="o">.</span><span class="n">columns</span><span class="o">.</span><span class="n">map</span><span class="o">(</span><span class="n">c</span> <span class="k">=&gt;</span> <span class="o">(</span><span class="n">c</span><span class="o">,</span> <span class="n">df</span><span class="o">.</span><span class="n">select</span><span class="o">(</span><span class="n">c</span><span class="o">).</span><span class="n">distinct</span><span class="o">().</span><span class="n">count</span><span class="o">()))</span>
</span><span class="line">
</span><span class="line"><span class="n">res14</span><span class="k">:</span> <span class="kt">Array</span><span class="o">[(</span><span class="kt">String</span>, <span class="kt">Long</span><span class="o">)]</span> <span class="k">=</span> <span class="nc">Array</span><span class="o">((</span><span class="n">id</span><span class="o">,</span><span class="mi">40428967</span><span class="o">),</span> <span class="o">(</span><span class="n">click</span><span class="o">,</span><span class="mi">2</span><span class="o">),</span> <span class="o">(</span><span class="n">hour</span><span class="o">,</span><span class="mi">240</span><span class="o">),</span> <span class="o">(</span><span class="n">C1</span><span class="o">,</span><span class="mi">7</span><span class="o">),</span> <span class="o">(</span><span class="n">banner_pos</span><span class="o">,</span><span class="mi">7</span><span class="o">),</span> <span class="o">(</span><span class="n">site_id</span><span class="o">,</span><span class="mi">4737</span><span class="o">),</span> <span class="o">(</span><span class="n">site_domain</span><span class="o">,</span><span class="mi">7745</span><span class="o">),</span> <span class="o">(</span><span class="n">site_category</span><span class="o">,</span><span class="mi">26</span><span class="o">),</span> <span class="o">(</span><span class="n">app_id</span><span class="o">,</span><span class="mi">8552</span><span class="o">),</span> <span class="o">(</span><span class="n">app_domain</span><span class="o">,</span><span class="mi">559</span><span class="o">),</span> <span class="o">(</span><span class="n">app_category</span><span class="o">,</span><span class="mi">36</span><span class="o">),</span> <span class="o">(</span><span class="n">device_id</span><span class="o">,</span><span class="mi">2686408</span><span class="o">),</span> <span class="o">(</span><span class="n">device_ip</span><span class="o">,</span><span class="mi">6729486</span><span class="o">),</span> <span class="o">(</span><span class="n">device_model</span><span class="o">,</span><span class="mi">8251</span><span class="o">),</span> <span class="o">(</span><span class="n">device_type</span><span class="o">,</span><span class="mi">5</span><span class="o">),</span> <span class="o">(</span><span class="n">device_conn_type</span><span class="o">,</span><span class="mi">4</span><span class="o">),</span> <span class="o">(</span><span class="n">C14</span><span class="o">,</span><span class="mi">2626</span><span class="o">),</span> <span class="o">(</span><span class="n">C15</span><span class="o">,</span><span class="mi">8</span><span class="o">),</span> <span class="o">(</span><span class="n">C16</span><span class="o">,</span><span class="mi">9</span><span class="o">),</span> <span class="o">(</span><span class="n">C17</span><span class="o">,</span><span class="mi">435</span><span class="o">),</span> <span class="o">(</span><span class="n">C18</span><span class="o">,</span><span class="mi">4</span><span class="o">),</span> <span class="o">(</span><span class="n">C19</span><span class="o">,</span><span class="mi">68</span><span class="o">),</span> <span class="o">(</span><span class="n">C20</span><span class="o">,</span><span class="mi">172</span><span class="o">),</span> <span class="o">(</span><span class="n">C21</span><span class="o">,</span><span class="mi">60</span><span class="o">))</span>
</span></code></pre></td></tr></table></div></figure></notextile></div>

<p>We see that there are some features with a lot of unique values, for example, <code>device_ip</code> has 6M+ different values.
Machine learning algorithms are typically defined in terms of numerical vectors rather than categorical values. Converting such categorical features will result in a high dimensional vector which might be very expensive.
We will need to deal with this later.</p>

<h2 id="processing-and-transforming-the-data">Processing and transforming the data</h2>

<p>Looking further at the dataset, we can see that the <code>hour</code> feature is in <code>YYMMDDHH</code> format.
To allow the predictive model to effectively learn from this feature it makes sense to transform it into three features: year, month and hour.
Let’s develop the function to transform the dataframe:</p>

<div class="bogus-wrapper"><notextile><figure class="code"><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
<span class="line-number">2</span>
<span class="line-number">3</span>
<span class="line-number">4</span>
<span class="line-number">5</span>
<span class="line-number">6</span>
<span class="line-number">7</span>
<span class="line-number">8</span>
<span class="line-number">9</span>
<span class="line-number">10</span>
<span class="line-number">11</span>
<span class="line-number">12</span>
<span class="line-number">13</span>
<span class="line-number">14</span>
<span class="line-number">15</span>
<span class="line-number">16</span>
<span class="line-number">17</span>
<span class="line-number">18</span>
<span class="line-number">19</span>
<span class="line-number">20</span>
<span class="line-number">21</span>
<span class="line-number">22</span>
<span class="line-number">23</span>
<span class="line-number">24</span>
<span class="line-number">25</span>
<span class="line-number">26</span>
<span class="line-number">27</span>
<span class="line-number">28</span>
<span class="line-number">29</span>
</pre></td><td class="code"><pre><code class="scala"><span class="line"><span class="k">import</span> <span class="nn">java.text.SimpleDateFormat</span>
</span><span class="line"><span class="k">import</span> <span class="nn">java.util.</span><span class="o">{</span><span class="nc">Calendar</span><span class="o">,</span> <span class="nc">Date</span><span class="o">}</span>
</span><span class="line"><span class="k">import</span> <span class="nn">org.apache.spark.sql.DataFrame</span>
</span><span class="line">
</span><span class="line"><span class="k">object</span> <span class="nc">DateUtils</span> <span class="o">{</span>
</span><span class="line">  <span class="k">val</span> <span class="n">dateFormat</span> <span class="k">=</span> <span class="k">new</span> <span class="nc">ThreadLocal</span><span class="o">[</span><span class="kt">SimpleDateFormat</span><span class="o">]()</span> <span class="o">{</span>
</span><span class="line">    <span class="k">override</span> <span class="k">def</span> <span class="n">initialValue</span><span class="o">()</span><span class="k">:</span> <span class="kt">SimpleDateFormat</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">SimpleDateFormat</span><span class="o">(</span><span class="s">&quot;yyMMddHH&quot;</span><span class="o">)</span>
</span><span class="line">  <span class="o">}</span>
</span><span class="line">
</span><span class="line">  <span class="k">def</span> <span class="n">parse</span><span class="o">(</span><span class="n">s</span><span class="k">:</span> <span class="kt">String</span><span class="o">,</span> <span class="n">field</span><span class="k">:</span> <span class="kt">Int</span><span class="o">)</span><span class="k">:</span> <span class="kt">Int</span> <span class="o">=</span> <span class="o">{</span>
</span><span class="line">    <span class="k">val</span> <span class="n">date</span> <span class="k">=</span> <span class="n">dateFormat</span><span class="o">.</span><span class="n">get</span><span class="o">().</span><span class="n">parse</span><span class="o">(</span><span class="n">s</span><span class="o">)</span>
</span><span class="line">    <span class="k">val</span> <span class="n">cal</span> <span class="k">=</span> <span class="nc">Calendar</span><span class="o">.</span><span class="n">getInstance</span><span class="o">()</span>
</span><span class="line">    <span class="n">cal</span><span class="o">.</span><span class="n">setTime</span><span class="o">(</span><span class="n">date</span><span class="o">)</span>
</span><span class="line">    <span class="n">cal</span><span class="o">.</span><span class="n">get</span><span class="o">(</span><span class="n">field</span><span class="o">)</span>
</span><span class="line">  <span class="o">}</span>
</span><span class="line"><span class="o">}</span>
</span><span class="line">
</span><span class="line"><span class="k">def</span> <span class="n">transformHour</span><span class="o">(</span><span class="n">df</span><span class="k">:</span> <span class="kt">DataFrame</span><span class="o">)</span><span class="k">:</span> <span class="kt">DataFrame</span> <span class="o">=</span> <span class="o">{</span>
</span><span class="line">  <span class="k">val</span> <span class="n">toYear</span> <span class="k">=</span> <span class="n">udf</span><span class="o">[</span><span class="kt">Int</span>, <span class="kt">String</span><span class="o">](</span><span class="n">s</span> <span class="k">=&gt;</span> <span class="nc">DateUtils</span><span class="o">.</span><span class="n">parse</span><span class="o">(</span><span class="n">s</span><span class="o">,</span> <span class="nc">Calendar</span><span class="o">.</span><span class="nc">YEAR</span><span class="o">))</span>
</span><span class="line">  <span class="k">val</span> <span class="n">toMonth</span> <span class="k">=</span> <span class="n">udf</span><span class="o">[</span><span class="kt">Int</span>, <span class="kt">String</span><span class="o">](</span><span class="n">s</span> <span class="k">=&gt;</span> <span class="nc">DateUtils</span><span class="o">.</span><span class="n">parse</span><span class="o">(</span><span class="n">s</span><span class="o">,</span> <span class="nc">Calendar</span><span class="o">.</span><span class="nc">MONTH</span><span class="o">))</span>
</span><span class="line">  <span class="k">val</span> <span class="n">toDay</span> <span class="k">=</span> <span class="n">udf</span><span class="o">[</span><span class="kt">Int</span>, <span class="kt">String</span><span class="o">](</span><span class="n">s</span> <span class="k">=&gt;</span> <span class="nc">DateUtils</span><span class="o">.</span><span class="n">parse</span><span class="o">(</span><span class="n">s</span><span class="o">,</span> <span class="nc">Calendar</span><span class="o">.</span><span class="nc">DAY_OF_MONTH</span><span class="o">))</span>
</span><span class="line">  <span class="k">val</span> <span class="n">toHour</span> <span class="k">=</span> <span class="n">udf</span><span class="o">[</span><span class="kt">Int</span>, <span class="kt">String</span><span class="o">](</span><span class="n">s</span> <span class="k">=&gt;</span> <span class="nc">DateUtils</span><span class="o">.</span><span class="n">parse</span><span class="o">(</span><span class="n">s</span><span class="o">,</span> <span class="nc">Calendar</span><span class="o">.</span><span class="nc">HOUR_OF_DAY</span><span class="o">))</span>
</span><span class="line">
</span><span class="line">  <span class="n">df</span><span class="o">.</span><span class="n">withColumn</span><span class="o">(</span><span class="s">&quot;time_year&quot;</span><span class="o">,</span> <span class="n">toYear</span><span class="o">(</span><span class="n">df</span><span class="o">(</span><span class="s">&quot;hour&quot;</span><span class="o">)))</span>
</span><span class="line">  <span class="o">.</span><span class="n">withColumn</span><span class="o">(</span><span class="s">&quot;time_month&quot;</span><span class="o">,</span> <span class="n">toMonth</span><span class="o">(</span><span class="n">df</span><span class="o">(</span><span class="s">&quot;hour&quot;</span><span class="o">)))</span>
</span><span class="line">  <span class="o">.</span><span class="n">withColumn</span><span class="o">(</span><span class="s">&quot;time_day&quot;</span><span class="o">,</span> <span class="n">toDay</span><span class="o">(</span><span class="n">df</span><span class="o">(</span><span class="s">&quot;hour&quot;</span><span class="o">)))</span>
</span><span class="line">  <span class="o">.</span><span class="n">withColumn</span><span class="o">(</span><span class="s">&quot;time_hour&quot;</span><span class="o">,</span> <span class="n">toHour</span><span class="o">(</span><span class="n">df</span><span class="o">(</span><span class="s">&quot;hour&quot;</span><span class="o">)))</span>
</span><span class="line">  <span class="o">.</span><span class="n">drop</span><span class="o">(</span><span class="s">&quot;hour&quot;</span><span class="o">)</span>
</span><span class="line"><span class="o">}</span>
</span></code></pre></td></tr></table></div></figure></notextile></div>

<p>We can now apply this transformation to our dataframe and see the result:</p>

<div class="bogus-wrapper"><notextile><figure class="code"><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
<span class="line-number">2</span>
<span class="line-number">3</span>
</pre></td><td class="code"><pre><code class="scala"><span class="line"><span class="k">val</span> <span class="n">hourDecoded</span> <span class="k">=</span> <span class="n">transformHour</span><span class="o">(</span><span class="n">df</span><span class="o">)</span>
</span><span class="line"><span class="n">hourDecoded</span><span class="o">.</span><span class="n">cache</span><span class="o">()</span>
</span><span class="line"><span class="n">hourDecoded</span><span class="o">.</span><span class="n">show</span><span class="o">(</span><span class="mi">10</span><span class="o">)</span>
</span></code></pre></td></tr></table></div></figure></notextile></div>

<div class="bogus-wrapper"><notextile><figure class="code"><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
<span class="line-number">2</span>
<span class="line-number">3</span>
<span class="line-number">4</span>
<span class="line-number">5</span>
<span class="line-number">6</span>
<span class="line-number">7</span>
<span class="line-number">8</span>
<span class="line-number">9</span>
<span class="line-number">10</span>
<span class="line-number">11</span>
<span class="line-number">12</span>
<span class="line-number">13</span>
<span class="line-number">14</span>
</pre></td><td class="code"><pre><code class="scala"><span class="line"><span class="o">+--------------------+-----+----+----------+--------+-----------+-------------+--------+----------+------------+---------+---------+------------+-----------+----------------+-----+---+---+----+---+---+------+---+---------+----------+--------+---------+</span>
</span><span class="line"><span class="o">|</span>                  <span class="n">id</span><span class="o">|</span><span class="n">click</span><span class="o">|</span>  <span class="n">C1</span><span class="o">|</span><span class="n">banner_pos</span><span class="o">|</span> <span class="n">site_id</span><span class="o">|</span><span class="n">site_domain</span><span class="o">|</span><span class="n">site_category</span><span class="o">|</span>  <span class="n">app_id</span><span class="o">|</span><span class="n">app_domain</span><span class="o">|</span><span class="n">app_category</span><span class="o">|</span><span class="n">device_id</span><span class="o">|</span><span class="n">device_ip</span><span class="o">|</span><span class="n">device_model</span><span class="o">|</span><span class="n">device_type</span><span class="o">|</span><span class="n">device_conn_type</span><span class="o">|</span>  <span class="n">C14</span><span class="o">|</span><span class="n">C15</span><span class="o">|</span><span class="n">C16</span><span class="o">|</span> <span class="n">C17</span><span class="o">|</span><span class="n">C18</span><span class="o">|</span><span class="n">C19</span><span class="o">|</span>   <span class="n">C20</span><span class="o">|</span><span class="n">C21</span><span class="o">|</span><span class="n">time_year</span><span class="o">|</span><span class="n">time_month</span><span class="o">|</span><span class="n">time_day</span><span class="o">|</span><span class="n">time_hour</span><span class="o">|</span>
</span><span class="line"><span class="o">+--------------------+-----+----+----------+--------+-----------+-------------+--------+----------+------------+---------+---------+------------+-----------+----------------+-----+---+---+----+---+---+------+---+---------+----------+--------+---------+</span>
</span><span class="line"><span class="o">|</span> <span class="mi">1000009418151094273</span><span class="o">|</span>    <span class="mi">0</span><span class="o">|</span><span class="mi">1005</span><span class="o">|</span>         <span class="mi">0</span><span class="o">|</span><span class="mi">1</span><span class="n">fbe01fe</span><span class="o">|</span>   <span class="n">f3845767</span><span class="o">|</span>     <span class="mi">28905</span><span class="n">ebd</span><span class="o">|</span><span class="n">ecad2386</span><span class="o">|</span>  <span class="mi">7801</span><span class="n">e8d9</span><span class="o">|</span>    <span class="mi">07</span><span class="n">d7df22</span><span class="o">|</span> <span class="n">a99f214a</span><span class="o">|</span> <span class="n">ddd2926e</span><span class="o">|</span>    <span class="mi">44956</span><span class="n">a24</span><span class="o">|</span>          <span class="mi">1</span><span class="o">|</span>               <span class="mi">2</span><span class="o">|</span><span class="mi">15706</span><span class="o">|</span><span class="mi">320</span><span class="o">|</span> <span class="mi">50</span><span class="o">|</span><span class="mi">1722</span><span class="o">|</span>  <span class="mi">0</span><span class="o">|</span> <span class="mi">35</span><span class="o">|</span>    <span class="o">-</span><span class="mi">1</span><span class="o">|</span> <span class="mi">79</span><span class="o">|</span>     <span class="mi">2014</span><span class="o">|</span>         <span class="mi">9</span><span class="o">|</span>      <span class="mi">21</span><span class="o">|</span>        <span class="mi">0</span><span class="o">|</span>
</span><span class="line"><span class="o">|</span><span class="mi">10000169349117863715</span><span class="o">|</span>    <span class="mi">0</span><span class="o">|</span><span class="mi">1005</span><span class="o">|</span>         <span class="mi">0</span><span class="o">|</span><span class="mi">1</span><span class="n">fbe01fe</span><span class="o">|</span>   <span class="n">f3845767</span><span class="o">|</span>     <span class="mi">28905</span><span class="n">ebd</span><span class="o">|</span><span class="n">ecad2386</span><span class="o">|</span>  <span class="mi">7801</span><span class="n">e8d9</span><span class="o">|</span>    <span class="mi">07</span><span class="n">d7df22</span><span class="o">|</span> <span class="n">a99f214a</span><span class="o">|</span> <span class="mi">96809</span><span class="n">ac8</span><span class="o">|</span>    <span class="mi">711</span><span class="n">ee120</span><span class="o">|</span>          <span class="mi">1</span><span class="o">|</span>               <span class="mi">0</span><span class="o">|</span><span class="mi">15704</span><span class="o">|</span><span class="mi">320</span><span class="o">|</span> <span class="mi">50</span><span class="o">|</span><span class="mi">1722</span><span class="o">|</span>  <span class="mi">0</span><span class="o">|</span> <span class="mi">35</span><span class="o">|</span><span class="mi">100084</span><span class="o">|</span> <span class="mi">79</span><span class="o">|</span>     <span class="mi">2014</span><span class="o">|</span>         <span class="mi">9</span><span class="o">|</span>      <span class="mi">21</span><span class="o">|</span>        <span class="mi">0</span><span class="o">|</span>
</span><span class="line"><span class="o">|</span><span class="mi">10000371904215119486</span><span class="o">|</span>    <span class="mi">0</span><span class="o">|</span><span class="mi">1005</span><span class="o">|</span>         <span class="mi">0</span><span class="o">|</span><span class="mi">1</span><span class="n">fbe01fe</span><span class="o">|</span>   <span class="n">f3845767</span><span class="o">|</span>     <span class="mi">28905</span><span class="n">ebd</span><span class="o">|</span><span class="n">ecad2386</span><span class="o">|</span>  <span class="mi">7801</span><span class="n">e8d9</span><span class="o">|</span>    <span class="mi">07</span><span class="n">d7df22</span><span class="o">|</span> <span class="n">a99f214a</span><span class="o">|</span> <span class="n">b3cf8def</span><span class="o">|</span>    <span class="mi">8</span><span class="n">a4875bd</span><span class="o">|</span>          <span class="mi">1</span><span class="o">|</span>               <span class="mi">0</span><span class="o">|</span><span class="mi">15704</span><span class="o">|</span><span class="mi">320</span><span class="o">|</span> <span class="mi">50</span><span class="o">|</span><span class="mi">1722</span><span class="o">|</span>  <span class="mi">0</span><span class="o">|</span> <span class="mi">35</span><span class="o">|</span><span class="mi">100084</span><span class="o">|</span> <span class="mi">79</span><span class="o">|</span>     <span class="mi">2014</span><span class="o">|</span>         <span class="mi">9</span><span class="o">|</span>      <span class="mi">21</span><span class="o">|</span>        <span class="mi">0</span><span class="o">|</span>
</span><span class="line"><span class="o">|</span><span class="mi">10000640724480838376</span><span class="o">|</span>    <span class="mi">0</span><span class="o">|</span><span class="mi">1005</span><span class="o">|</span>         <span class="mi">0</span><span class="o">|</span><span class="mi">1</span><span class="n">fbe01fe</span><span class="o">|</span>   <span class="n">f3845767</span><span class="o">|</span>     <span class="mi">28905</span><span class="n">ebd</span><span class="o">|</span><span class="n">ecad2386</span><span class="o">|</span>  <span class="mi">7801</span><span class="n">e8d9</span><span class="o">|</span>    <span class="mi">07</span><span class="n">d7df22</span><span class="o">|</span> <span class="n">a99f214a</span><span class="o">|</span> <span class="n">e8275b8f</span><span class="o">|</span>    <span class="mi">6332421</span><span class="n">a</span><span class="o">|</span>          <span class="mi">1</span><span class="o">|</span>               <span class="mi">0</span><span class="o">|</span><span class="mi">15706</span><span class="o">|</span><span class="mi">320</span><span class="o">|</span> <span class="mi">50</span><span class="o">|</span><span class="mi">1722</span><span class="o">|</span>  <span class="mi">0</span><span class="o">|</span> <span class="mi">35</span><span class="o">|</span><span class="mi">100084</span><span class="o">|</span> <span class="mi">79</span><span class="o">|</span>     <span class="mi">2014</span><span class="o">|</span>         <span class="mi">9</span><span class="o">|</span>      <span class="mi">21</span><span class="o">|</span>        <span class="mi">0</span><span class="o">|</span>
</span><span class="line"><span class="o">|</span><span class="mi">10000679056417042096</span><span class="o">|</span>    <span class="mi">0</span><span class="o">|</span><span class="mi">1005</span><span class="o">|</span>         <span class="mi">1</span><span class="o">|</span><span class="n">fe8cc448</span><span class="o">|</span>   <span class="mi">9166</span><span class="n">c161</span><span class="o">|</span>     <span class="mi">0569</span><span class="n">f928</span><span class="o">|</span><span class="n">ecad2386</span><span class="o">|</span>  <span class="mi">7801</span><span class="n">e8d9</span><span class="o">|</span>    <span class="mi">07</span><span class="n">d7df22</span><span class="o">|</span> <span class="n">a99f214a</span><span class="o">|</span> <span class="mi">9644</span><span class="n">d0bf</span><span class="o">|</span>    <span class="mi">779</span><span class="n">d90c2</span><span class="o">|</span>          <span class="mi">1</span><span class="o">|</span>               <span class="mi">0</span><span class="o">|</span><span class="mi">18993</span><span class="o">|</span><span class="mi">320</span><span class="o">|</span> <span class="mi">50</span><span class="o">|</span><span class="mi">2161</span><span class="o">|</span>  <span class="mi">0</span><span class="o">|</span> <span class="mi">35</span><span class="o">|</span>    <span class="o">-</span><span class="mi">1</span><span class="o">|</span><span class="mi">157</span><span class="o">|</span>     <span class="mi">2014</span><span class="o">|</span>         <span class="mi">9</span><span class="o">|</span>      <span class="mi">21</span><span class="o">|</span>        <span class="mi">0</span><span class="o">|</span>
</span><span class="line"><span class="o">|</span><span class="mi">10000720757801103869</span><span class="o">|</span>    <span class="mi">0</span><span class="o">|</span><span class="mi">1005</span><span class="o">|</span>         <span class="mi">0</span><span class="o">|</span><span class="n">d6137915</span><span class="o">|</span>   <span class="n">bb1ef334</span><span class="o">|</span>     <span class="n">f028772b</span><span class="o">|</span><span class="n">ecad2386</span><span class="o">|</span>  <span class="mi">7801</span><span class="n">e8d9</span><span class="o">|</span>    <span class="mi">07</span><span class="n">d7df22</span><span class="o">|</span> <span class="n">a99f214a</span><span class="o">|</span> <span class="mi">05241</span><span class="n">af0</span><span class="o">|</span>    <span class="mi">8</span><span class="n">a4875bd</span><span class="o">|</span>          <span class="mi">1</span><span class="o">|</span>               <span class="mi">0</span><span class="o">|</span><span class="mi">16920</span><span class="o">|</span><span class="mi">320</span><span class="o">|</span> <span class="mi">50</span><span class="o">|</span><span class="mi">1899</span><span class="o">|</span>  <span class="mi">0</span><span class="o">|</span><span class="mi">431</span><span class="o">|</span><span class="mi">100077</span><span class="o">|</span><span class="mi">117</span><span class="o">|</span>     <span class="mi">2014</span><span class="o">|</span>         <span class="mi">9</span><span class="o">|</span>      <span class="mi">21</span><span class="o">|</span>        <span class="mi">0</span><span class="o">|</span>
</span><span class="line"><span class="o">|</span><span class="mi">10000724729988544911</span><span class="o">|</span>    <span class="mi">0</span><span class="o">|</span><span class="mi">1005</span><span class="o">|</span>         <span class="mi">0</span><span class="o">|</span><span class="mi">8</span><span class="n">fda644b</span><span class="o">|</span>   <span class="mi">25</span><span class="n">d4cfcd</span><span class="o">|</span>     <span class="n">f028772b</span><span class="o">|</span><span class="n">ecad2386</span><span class="o">|</span>  <span class="mi">7801</span><span class="n">e8d9</span><span class="o">|</span>    <span class="mi">07</span><span class="n">d7df22</span><span class="o">|</span> <span class="n">a99f214a</span><span class="o">|</span> <span class="n">b264c159</span><span class="o">|</span>    <span class="n">be6db1d7</span><span class="o">|</span>          <span class="mi">1</span><span class="o">|</span>               <span class="mi">0</span><span class="o">|</span><span class="mi">20362</span><span class="o">|</span><span class="mi">320</span><span class="o">|</span> <span class="mi">50</span><span class="o">|</span><span class="mi">2333</span><span class="o">|</span>  <span class="mi">0</span><span class="o">|</span> <span class="mi">39</span><span class="o">|</span>    <span class="o">-</span><span class="mi">1</span><span class="o">|</span><span class="mi">157</span><span class="o">|</span>     <span class="mi">2014</span><span class="o">|</span>         <span class="mi">9</span><span class="o">|</span>      <span class="mi">21</span><span class="o">|</span>        <span class="mi">0</span><span class="o">|</span>
</span><span class="line"><span class="o">|</span><span class="mi">10000918755742328737</span><span class="o">|</span>    <span class="mi">0</span><span class="o">|</span><span class="mi">1005</span><span class="o">|</span>         <span class="mi">1</span><span class="o">|</span><span class="n">e151e245</span><span class="o">|</span>   <span class="mi">7</span><span class="n">e091613</span><span class="o">|</span>     <span class="n">f028772b</span><span class="o">|</span><span class="n">ecad2386</span><span class="o">|</span>  <span class="mi">7801</span><span class="n">e8d9</span><span class="o">|</span>    <span class="mi">07</span><span class="n">d7df22</span><span class="o">|</span> <span class="n">a99f214a</span><span class="o">|</span> <span class="n">e6f67278</span><span class="o">|</span>    <span class="n">be74e6fe</span><span class="o">|</span>          <span class="mi">1</span><span class="o">|</span>               <span class="mi">0</span><span class="o">|</span><span class="mi">20632</span><span class="o">|</span><span class="mi">320</span><span class="o">|</span> <span class="mi">50</span><span class="o">|</span><span class="mi">2374</span><span class="o">|</span>  <span class="mi">3</span><span class="o">|</span> <span class="mi">39</span><span class="o">|</span>    <span class="o">-</span><span class="mi">1</span><span class="o">|</span> <span class="mi">23</span><span class="o">|</span>     <span class="mi">2014</span><span class="o">|</span>         <span class="mi">9</span><span class="o">|</span>      <span class="mi">21</span><span class="o">|</span>        <span class="mi">0</span><span class="o">|</span>
</span><span class="line"><span class="o">|</span><span class="mi">10000949271186029916</span><span class="o">|</span>    <span class="mi">1</span><span class="o">|</span><span class="mi">1005</span><span class="o">|</span>         <span class="mi">0</span><span class="o">|</span><span class="mi">1</span><span class="n">fbe01fe</span><span class="o">|</span>   <span class="n">f3845767</span><span class="o">|</span>     <span class="mi">28905</span><span class="n">ebd</span><span class="o">|</span><span class="n">ecad2386</span><span class="o">|</span>  <span class="mi">7801</span><span class="n">e8d9</span><span class="o">|</span>    <span class="mi">07</span><span class="n">d7df22</span><span class="o">|</span> <span class="n">a99f214a</span><span class="o">|</span> <span class="mi">37</span><span class="n">e8da74</span><span class="o">|</span>    <span class="mi">5</span><span class="n">db079b5</span><span class="o">|</span>          <span class="mi">1</span><span class="o">|</span>               <span class="mi">2</span><span class="o">|</span><span class="mi">15707</span><span class="o">|</span><span class="mi">320</span><span class="o">|</span> <span class="mi">50</span><span class="o">|</span><span class="mi">1722</span><span class="o">|</span>  <span class="mi">0</span><span class="o">|</span> <span class="mi">35</span><span class="o">|</span>    <span class="o">-</span><span class="mi">1</span><span class="o">|</span> <span class="mi">79</span><span class="o">|</span>     <span class="mi">2014</span><span class="o">|</span>         <span class="mi">9</span><span class="o">|</span>      <span class="mi">21</span><span class="o">|</span>        <span class="mi">0</span><span class="o">|</span>
</span><span class="line"><span class="o">|</span><span class="mi">10001264480619467364</span><span class="o">|</span>    <span class="mi">0</span><span class="o">|</span><span class="mi">1002</span><span class="o">|</span>         <span class="mi">0</span><span class="o">|</span><span class="mi">84</span><span class="n">c7ba46</span><span class="o">|</span>   <span class="n">c4e18dd6</span><span class="o">|</span>     <span class="mi">50</span><span class="n">e219e0</span><span class="o">|</span><span class="n">ecad2386</span><span class="o">|</span>  <span class="mi">7801</span><span class="n">e8d9</span><span class="o">|</span>    <span class="mi">07</span><span class="n">d7df22</span><span class="o">|</span> <span class="n">c357dbff</span><span class="o">|</span> <span class="n">f1ac7184</span><span class="o">|</span>    <span class="mi">373</span><span class="n">ecbe6</span><span class="o">|</span>          <span class="mi">0</span><span class="o">|</span>               <span class="mi">0</span><span class="o">|</span><span class="mi">21689</span><span class="o">|</span><span class="mi">320</span><span class="o">|</span> <span class="mi">50</span><span class="o">|</span><span class="mi">2496</span><span class="o">|</span>  <span class="mi">3</span><span class="o">|</span><span class="mi">167</span><span class="o">|</span><span class="mi">100191</span><span class="o">|</span> <span class="mi">23</span><span class="o">|</span>     <span class="mi">2014</span><span class="o">|</span>         <span class="mi">9</span><span class="o">|</span>      <span class="mi">21</span><span class="o">|</span>        <span class="mi">0</span><span class="o">|</span>
</span><span class="line"><span class="o">+--------------------+-----+----+----------+--------+-----------+-------------+--------+----------+------------+---------+---------+------------+-----------+----------------+-----+---+---+----+---+---+------+---+---------+----------+--------+---------+</span>
</span></code></pre></td></tr></table></div></figure></notextile></div>

<p>It looks like the year and month have only one value, let’s verify it:</p>

<div class="bogus-wrapper"><notextile><figure class="code"><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
<span class="line-number">2</span>
<span class="line-number">3</span>
<span class="line-number">4</span>
<span class="line-number">5</span>
</pre></td><td class="code"><pre><code class="scala"><span class="line"><span class="n">hourDecoded</span><span class="o">.</span><span class="n">select</span><span class="o">(</span><span class="s">&quot;time_month&quot;</span><span class="o">).</span><span class="n">distinct</span><span class="o">.</span><span class="n">count</span><span class="o">()</span>
</span><span class="line"><span class="n">hourDecoded</span><span class="o">.</span><span class="n">select</span><span class="o">(</span><span class="s">&quot;time_year&quot;</span><span class="o">).</span><span class="n">distinct</span><span class="o">.</span><span class="n">count</span><span class="o">()</span>
</span><span class="line">
</span><span class="line"><span class="n">res20</span><span class="k">:</span> <span class="kt">Long</span> <span class="o">=</span> <span class="mi">1</span>
</span><span class="line"><span class="n">res21</span><span class="k">:</span> <span class="kt">Long</span> <span class="o">=</span> <span class="mi">1</span>
</span></code></pre></td></tr></table></div></figure></notextile></div>

<p>We can safely drop these columns as they don’t bring any knowledge to our model:</p>

<div class="bogus-wrapper"><notextile><figure class="code"><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
</pre></td><td class="code"><pre><code class="scala"><span class="line"><span class="k">val</span> <span class="n">hourDecoded2</span> <span class="k">=</span> <span class="n">hourDecoded</span><span class="o">.</span><span class="n">drop</span><span class="o">(</span><span class="s">&quot;time_month&quot;</span><span class="o">).</span><span class="n">drop</span><span class="o">(</span><span class="s">&quot;time_year&quot;</span><span class="o">)</span>
</span></code></pre></td></tr></table></div></figure></notextile></div>

<p>Let’s also convert <code>click</code> from String to Double type.</p>

<div class="bogus-wrapper"><notextile><figure class="code"><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
<span class="line-number">2</span>
<span class="line-number">3</span>
<span class="line-number">4</span>
<span class="line-number">5</span>
<span class="line-number">6</span>
</pre></td><td class="code"><pre><code class="scala"><span class="line"><span class="k">import</span> <span class="nn">org.apache.spark.sql.types.DoubleType</span>
</span><span class="line">
</span><span class="line"><span class="k">val</span> <span class="n">prepared</span> <span class="k">=</span> <span class="n">hourDecoded2</span>
</span><span class="line">    <span class="o">.</span><span class="n">withColumn</span><span class="o">(</span><span class="s">&quot;clickTmp&quot;</span><span class="o">,</span> <span class="n">hourDecoded2</span><span class="o">(</span><span class="s">&quot;click&quot;</span><span class="o">).</span><span class="n">cast</span><span class="o">(</span><span class="nc">DoubleType</span><span class="o">))</span>
</span><span class="line">    <span class="o">.</span><span class="n">drop</span><span class="o">(</span><span class="s">&quot;click&quot;</span><span class="o">)</span>
</span><span class="line">    <span class="o">.</span><span class="n">withColumnRenamed</span><span class="o">(</span><span class="s">&quot;clickTmp&quot;</span><span class="o">,</span> <span class="s">&quot;click&quot;</span><span class="o">)</span>
</span></code></pre></td></tr></table></div></figure></notextile></div>

<h2 id="saving-preprocessed-data-to-the-data-grid">Saving preprocessed data to the data grid</h2>

<p>The entire training dataset contains 40M+ rows, it takes quite a long time to experiment with different algorithms and approaches even in a clustered environment.
We want to sample the dataset and checkpoint it to the in-memory data grid that is running collocated with Spark.
This way we can:
* quickly iterate through different approaches
* restart the Zeppelin session or launch other Spark applications and pick up the dataset more quickly from memory</p>

<p>Since the training dataset contains the data for the 10 days, we can pick any day and sample it:</p>

<div class="bogus-wrapper"><notextile><figure class="code"><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
<span class="line-number">2</span>
<span class="line-number">3</span>
</pre></td><td class="code"><pre><code class="scala"><span class="line"><span class="n">prepared</span><span class="o">.</span><span class="n">filter</span><span class="o">(</span><span class="s">&quot;time_day = 21&quot;</span><span class="o">).</span><span class="n">count</span><span class="o">()</span>
</span><span class="line">
</span><span class="line"><span class="n">res51</span><span class="k">:</span> <span class="kt">Long</span> <span class="o">=</span> <span class="mi">4122995</span>
</span></code></pre></td></tr></table></div></figure></notextile></div>

<p>There are 4M+ rows for this day, which is about 10% of the entire dataset.</p>

<p>Now let’s save it to the data grid. This can be done with two lines of code:</p>

<div class="bogus-wrapper"><notextile><figure class="code"><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
<span class="line-number">2</span>
</pre></td><td class="code"><pre><code class="scala"><span class="line"><span class="k">import</span> <span class="nn">org.apache.spark.sql.insightedge._</span>
</span><span class="line"><span class="n">prepared</span><span class="o">.</span><span class="n">filter</span><span class="o">(</span><span class="s">&quot;time_day = 21&quot;</span><span class="o">).</span><span class="n">write</span><span class="o">.</span><span class="n">grid</span><span class="o">.</span><span class="n">save</span><span class="o">(</span><span class="s">&quot;day_21&quot;</span><span class="o">)</span>
</span></code></pre></td></tr></table></div></figure></notextile></div>

<p>Any time later in another Spark context we can bring the collection to the Spark memory with:</p>
<div class="bogus-wrapper"><notextile><figure class="code"><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
</pre></td><td class="code"><pre><code class="scala"><span class="line"><span class="k">val</span> <span class="n">df</span> <span class="k">=</span> <span class="n">sqlContext</span><span class="o">.</span><span class="n">read</span><span class="o">.</span><span class="n">grid</span><span class="o">.</span><span class="n">load</span><span class="o">(</span><span class="s">&quot;day_21&quot;</span><span class="o">)</span>
</span></code></pre></td></tr></table></div></figure></notextile></div>

<p>Also, we want to transform the <code>test</code> dataset that we will use for prediction in a similar way.</p>

<div class="bogus-wrapper"><notextile><figure class="code"><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
<span class="line-number">2</span>
<span class="line-number">3</span>
<span class="line-number">4</span>
<span class="line-number">5</span>
<span class="line-number">6</span>
<span class="line-number">7</span>
<span class="line-number">8</span>
<span class="line-number">9</span>
<span class="line-number">10</span>
</pre></td><td class="code"><pre><code class="scala"><span class="line"><span class="k">val</span> <span class="n">testDf</span> <span class="k">=</span> <span class="n">sqlContext</span><span class="o">.</span><span class="n">read</span>
</span><span class="line">      <span class="o">.</span><span class="n">format</span><span class="o">(</span><span class="s">&quot;com.databricks.spark.csv&quot;</span><span class="o">)</span>
</span><span class="line">      <span class="o">.</span><span class="n">option</span><span class="o">(</span><span class="s">&quot;header&quot;</span><span class="o">,</span> <span class="s">&quot;true&quot;</span><span class="o">)</span>
</span><span class="line">      <span class="o">.</span><span class="n">option</span><span class="o">(</span><span class="s">&quot;inferSchema&quot;</span><span class="o">,</span> <span class="s">&quot;false&quot;</span><span class="o">)</span>
</span><span class="line">      <span class="o">.</span><span class="n">load</span><span class="o">(</span><span class="s">&quot;hdfs://10.8.1.116/data/avazu_ctr/test&quot;</span><span class="o">)</span>
</span><span class="line">
</span><span class="line"><span class="n">transformHour</span><span class="o">(</span><span class="n">testDf</span><span class="o">)</span>
</span><span class="line">    <span class="o">.</span><span class="n">drop</span><span class="o">(</span><span class="s">&quot;time_month&quot;</span><span class="o">)</span>
</span><span class="line">    <span class="o">.</span><span class="n">drop</span><span class="o">(</span><span class="s">&quot;time_year&quot;</span><span class="o">)</span>
</span><span class="line">    <span class="o">.</span><span class="n">write</span><span class="o">.</span><span class="n">grid</span><span class="o">.</span><span class="n">save</span><span class="o">(</span><span class="s">&quot;test&quot;</span><span class="o">)</span>
</span></code></pre></td></tr></table></div></figure></notextile></div>

<p>The complete listing of notebook can be found on <a href="https://github.com/InsightEdge/insightedge-ctr-demo/blob/master/zeppelin/CTR%20demo.json">github</a>. You can import it to Zeppelin and play with it on your own.</p>

<h2 id="a-simple-algorithm">A simple algorithm</h2>

<p>Now that we have training and test datasets sampled, initially preprocessed and available in the data grid, we can close Web Notebook and start experimenting with
different techniques and algorithms by submitting Spark applications.</p>

<p>For our first baseline approach let’s take a single feature <code>device_conn_type</code> and <a href="https://en.wikipedia.org/wiki/Logistic_regression">logistic regression</a> algorithm:</p>

<div class="bogus-wrapper"><notextile><figure class="code"><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
<span class="line-number">2</span>
<span class="line-number">3</span>
<span class="line-number">4</span>
<span class="line-number">5</span>
<span class="line-number">6</span>
<span class="line-number">7</span>
<span class="line-number">8</span>
<span class="line-number">9</span>
<span class="line-number">10</span>
<span class="line-number">11</span>
<span class="line-number">12</span>
<span class="line-number">13</span>
<span class="line-number">14</span>
<span class="line-number">15</span>
<span class="line-number">16</span>
<span class="line-number">17</span>
<span class="line-number">18</span>
<span class="line-number">19</span>
<span class="line-number">20</span>
<span class="line-number">21</span>
<span class="line-number">22</span>
<span class="line-number">23</span>
<span class="line-number">24</span>
<span class="line-number">25</span>
<span class="line-number">26</span>
<span class="line-number">27</span>
<span class="line-number">28</span>
<span class="line-number">29</span>
<span class="line-number">30</span>
<span class="line-number">31</span>
<span class="line-number">32</span>
<span class="line-number">33</span>
<span class="line-number">34</span>
<span class="line-number">35</span>
<span class="line-number">36</span>
<span class="line-number">37</span>
<span class="line-number">38</span>
<span class="line-number">39</span>
<span class="line-number">40</span>
<span class="line-number">41</span>
<span class="line-number">42</span>
<span class="line-number">43</span>
<span class="line-number">44</span>
<span class="line-number">45</span>
<span class="line-number">46</span>
<span class="line-number">47</span>
<span class="line-number">48</span>
<span class="line-number">49</span>
<span class="line-number">50</span>
<span class="line-number">51</span>
<span class="line-number">52</span>
<span class="line-number">53</span>
<span class="line-number">54</span>
<span class="line-number">55</span>
<span class="line-number">56</span>
<span class="line-number">57</span>
<span class="line-number">58</span>
<span class="line-number">59</span>
<span class="line-number">60</span>
<span class="line-number">61</span>
<span class="line-number">62</span>
<span class="line-number">63</span>
<span class="line-number">64</span>
<span class="line-number">65</span>
<span class="line-number">66</span>
<span class="line-number">67</span>
<span class="line-number">68</span>
<span class="line-number">69</span>
<span class="line-number">70</span>
<span class="line-number">71</span>
<span class="line-number">72</span>
<span class="line-number">73</span>
<span class="line-number">74</span>
<span class="line-number">75</span>
</pre></td><td class="code"><pre><code class="scala"><span class="line"><span class="k">import</span> <span class="nn">com.gigaspaces.spark.context.GigaSpacesConfig</span>
</span><span class="line"><span class="k">import</span> <span class="nn">com.gigaspaces.spark.implicits._</span>
</span><span class="line"><span class="k">import</span> <span class="nn">org.apache.spark.ml.feature.</span><span class="o">{</span><span class="nc">OneHotEncoder</span><span class="o">,</span> <span class="nc">StringIndexer</span><span class="o">}</span>
</span><span class="line"><span class="k">import</span> <span class="nn">org.apache.spark.mllib.classification.LogisticRegressionWithLBFGS</span>
</span><span class="line"><span class="k">import</span> <span class="nn">org.apache.spark.mllib.evaluation.BinaryClassificationMetrics</span>
</span><span class="line"><span class="k">import</span> <span class="nn">org.apache.spark.mllib.linalg.Vector</span>
</span><span class="line"><span class="k">import</span> <span class="nn">org.apache.spark.mllib.regression.LabeledPoint</span>
</span><span class="line"><span class="k">import</span> <span class="nn">org.apache.spark.sql.SQLContext</span>
</span><span class="line"><span class="k">import</span> <span class="nn">org.apache.spark.sql.insightedge._</span>
</span><span class="line"><span class="k">import</span> <span class="nn">org.apache.spark.</span><span class="o">{</span><span class="nc">SparkConf</span><span class="o">,</span> <span class="nc">SparkContext</span><span class="o">}</span>
</span><span class="line">
</span><span class="line"><span class="k">object</span> <span class="nc">CtrDemo1</span> <span class="o">{</span>
</span><span class="line">
</span><span class="line">  <span class="k">def</span> <span class="n">main</span><span class="o">(</span><span class="n">args</span><span class="k">:</span> <span class="kt">Array</span><span class="o">[</span><span class="kt">String</span><span class="o">])</span><span class="k">:</span> <span class="kt">Unit</span> <span class="o">=</span> <span class="o">{</span>
</span><span class="line">    <span class="k">if</span> <span class="o">(</span><span class="n">args</span><span class="o">.</span><span class="n">length</span> <span class="o">&lt;</span> <span class="mi">3</span><span class="o">)</span> <span class="o">{</span>
</span><span class="line">      <span class="nc">System</span><span class="o">.</span><span class="n">err</span><span class="o">.</span><span class="n">println</span><span class="o">(</span><span class="s">&quot;Usage: CtrDemo1 &lt;spark master url&gt; &lt;grid locator&gt; &lt;train collection&gt;&quot;</span><span class="o">)</span>
</span><span class="line">      <span class="nc">System</span><span class="o">.</span><span class="n">exit</span><span class="o">(</span><span class="mi">1</span><span class="o">)</span>
</span><span class="line">    <span class="o">}</span>
</span><span class="line">
</span><span class="line">    <span class="k">val</span> <span class="nc">Array</span><span class="o">(</span><span class="n">master</span><span class="o">,</span> <span class="n">gridLocator</span><span class="o">,</span> <span class="n">trainCollection</span><span class="o">)</span> <span class="k">=</span> <span class="n">args</span>
</span><span class="line">
</span><span class="line">    <span class="c1">// Configure InsightEdge settings</span>
</span><span class="line">    <span class="k">val</span> <span class="n">gsConfig</span> <span class="k">=</span> <span class="nc">GigaSpacesConfig</span><span class="o">(</span><span class="s">&quot;insightedge-space&quot;</span><span class="o">,</span> <span class="nc">None</span><span class="o">,</span> <span class="nc">Some</span><span class="o">(</span><span class="n">gridLocator</span><span class="o">))</span>
</span><span class="line">    <span class="k">val</span> <span class="n">sc</span> <span class="k">=</span> <span class="k">new</span> <span class="nc">SparkContext</span><span class="o">(</span><span class="k">new</span> <span class="nc">SparkConf</span><span class="o">().</span><span class="n">setAppName</span><span class="o">(</span><span class="s">&quot;CtrDemo1&quot;</span><span class="o">).</span><span class="n">setMaster</span><span class="o">(</span><span class="n">master</span><span class="o">).</span><span class="n">setGigaSpaceConfig</span><span class="o">(</span><span class="n">gsConfig</span><span class="o">))</span>
</span><span class="line">    <span class="k">val</span> <span class="n">sqlContext</span> <span class="k">=</span> <span class="k">new</span> <span class="nc">SQLContext</span><span class="o">(</span><span class="n">sc</span><span class="o">)</span>
</span><span class="line">
</span><span class="line">    <span class="c1">// load training collection from data grid</span>
</span><span class="line">    <span class="k">val</span> <span class="n">trainDf</span> <span class="k">=</span> <span class="n">sqlContext</span><span class="o">.</span><span class="n">read</span><span class="o">.</span><span class="n">grid</span><span class="o">.</span><span class="n">load</span><span class="o">(</span><span class="n">trainCollection</span><span class="o">)</span>
</span><span class="line">    <span class="n">trainDf</span><span class="o">.</span><span class="n">cache</span><span class="o">()</span>
</span><span class="line">
</span><span class="line">    <span class="c1">// use one-hot-encoder to convert &#39;device_conn_type&#39; categorical feature into a vector</span>
</span><span class="line">    <span class="k">val</span> <span class="n">indexed</span> <span class="k">=</span> <span class="k">new</span> <span class="nc">StringIndexer</span><span class="o">()</span>
</span><span class="line">      <span class="o">.</span><span class="n">setInputCol</span><span class="o">(</span><span class="s">&quot;device_conn_type&quot;</span><span class="o">)</span>
</span><span class="line">      <span class="o">.</span><span class="n">setOutputCol</span><span class="o">(</span><span class="s">&quot;device_conn_type_index&quot;</span><span class="o">)</span>
</span><span class="line">      <span class="o">.</span><span class="n">fit</span><span class="o">(</span><span class="n">trainDf</span><span class="o">)</span>
</span><span class="line">      <span class="o">.</span><span class="n">transform</span><span class="o">(</span><span class="n">trainDf</span><span class="o">)</span>
</span><span class="line">
</span><span class="line">    <span class="k">val</span> <span class="n">encodedDf</span> <span class="k">=</span> <span class="k">new</span> <span class="nc">OneHotEncoder</span><span class="o">()</span>
</span><span class="line">      <span class="o">.</span><span class="n">setDropLast</span><span class="o">(</span><span class="kc">false</span><span class="o">)</span>
</span><span class="line">      <span class="o">.</span><span class="n">setInputCol</span><span class="o">(</span><span class="s">&quot;device_conn_type_index&quot;</span><span class="o">)</span>
</span><span class="line">      <span class="o">.</span><span class="n">setOutputCol</span><span class="o">(</span><span class="s">&quot;device_conn_type_vector&quot;</span><span class="o">)</span>
</span><span class="line">      <span class="o">.</span><span class="n">transform</span><span class="o">(</span><span class="n">indexed</span><span class="o">)</span>
</span><span class="line">
</span><span class="line">    <span class="c1">// convert dataframe to a label points RDD</span>
</span><span class="line">    <span class="k">val</span> <span class="n">encodedRdd</span> <span class="k">=</span> <span class="n">encodedDf</span><span class="o">.</span><span class="n">map</span> <span class="o">{</span> <span class="n">row</span> <span class="k">=&gt;</span>
</span><span class="line">      <span class="k">val</span> <span class="n">label</span> <span class="k">=</span> <span class="n">row</span><span class="o">.</span><span class="n">getAs</span><span class="o">[</span><span class="kt">Double</span><span class="o">](</span><span class="s">&quot;click&quot;</span><span class="o">)</span>
</span><span class="line">      <span class="k">val</span> <span class="n">features</span> <span class="k">=</span> <span class="n">row</span><span class="o">.</span><span class="n">getAs</span><span class="o">[</span><span class="kt">Vector</span><span class="o">](</span><span class="s">&quot;device_conn_type_vector&quot;</span><span class="o">)</span>
</span><span class="line">      <span class="nc">LabeledPoint</span><span class="o">(</span><span class="n">label</span><span class="o">,</span> <span class="n">features</span><span class="o">)</span>
</span><span class="line">    <span class="o">}</span>
</span><span class="line">
</span><span class="line">    <span class="c1">// Split data into training (60%) and test (40%)</span>
</span><span class="line">    <span class="k">val</span> <span class="nc">Array</span><span class="o">(</span><span class="n">trainingRdd</span><span class="o">,</span> <span class="n">testRdd</span><span class="o">)</span> <span class="k">=</span> <span class="n">encodedRdd</span><span class="o">.</span><span class="n">randomSplit</span><span class="o">(</span><span class="nc">Array</span><span class="o">(</span><span class="mf">0.6</span><span class="o">,</span> <span class="mf">0.4</span><span class="o">),</span> <span class="n">seed</span> <span class="k">=</span> <span class="mi">11L</span><span class="o">)</span>
</span><span class="line">    <span class="n">trainingRdd</span><span class="o">.</span><span class="n">cache</span><span class="o">()</span>
</span><span class="line">
</span><span class="line">    <span class="c1">// Run training algorithm to build the model</span>
</span><span class="line">    <span class="k">val</span> <span class="n">model</span> <span class="k">=</span> <span class="k">new</span> <span class="nc">LogisticRegressionWithLBFGS</span><span class="o">()</span>
</span><span class="line">      <span class="o">.</span><span class="n">setNumClasses</span><span class="o">(</span><span class="mi">2</span><span class="o">)</span>
</span><span class="line">      <span class="o">.</span><span class="n">run</span><span class="o">(</span><span class="n">trainingRdd</span><span class="o">)</span>
</span><span class="line">
</span><span class="line">    <span class="c1">// Clear the prediction threshold so the model will return probabilities</span>
</span><span class="line">    <span class="n">model</span><span class="o">.</span><span class="n">clearThreshold</span>
</span><span class="line">
</span><span class="line">    <span class="c1">// Compute raw scores on the test set</span>
</span><span class="line">    <span class="k">val</span> <span class="n">predictionAndLabels</span> <span class="k">=</span> <span class="n">testRdd</span><span class="o">.</span><span class="n">map</span> <span class="o">{</span> <span class="k">case</span> <span class="nc">LabeledPoint</span><span class="o">(</span><span class="n">label</span><span class="o">,</span> <span class="n">features</span><span class="o">)</span> <span class="k">=&gt;</span>
</span><span class="line">      <span class="k">val</span> <span class="n">prediction</span> <span class="k">=</span> <span class="n">model</span><span class="o">.</span><span class="n">predict</span><span class="o">(</span><span class="n">features</span><span class="o">)</span>
</span><span class="line">      <span class="o">(</span><span class="n">prediction</span><span class="o">,</span> <span class="n">label</span><span class="o">)</span>
</span><span class="line">    <span class="o">}</span>
</span><span class="line">
</span><span class="line">    <span class="c1">// Instantiate metrics object</span>
</span><span class="line">    <span class="k">val</span> <span class="n">metrics</span> <span class="k">=</span> <span class="k">new</span> <span class="nc">BinaryClassificationMetrics</span><span class="o">(</span><span class="n">predictionAndLabels</span><span class="o">)</span>
</span><span class="line">
</span><span class="line">    <span class="k">val</span> <span class="n">auROC</span> <span class="k">=</span> <span class="n">metrics</span><span class="o">.</span><span class="n">areaUnderROC</span>
</span><span class="line">    <span class="n">println</span><span class="o">(</span><span class="s">&quot;Area under ROC = &quot;</span> <span class="o">+</span> <span class="n">auROC</span><span class="o">)</span>
</span><span class="line">  <span class="o">}</span>
</span><span class="line"><span class="o">}</span>
</span></code></pre></td></tr></table></div></figure></notextile></div>

<p>We will explain a little bit more what happens here.</p>

<p>At first, we load the training dataset from the data grid, which we prepared and saved earlier with Web Notebook.</p>

<p>Then we use <a href="https://spark.apache.org/docs/1.6.0/api/java/org/apache/spark/ml/feature/StringIndexer.html">StringIndexer</a> and <a href="https://spark.apache.org/docs/1.6.0/api/java/org/apache/spark/ml/feature/OneHotEncoder.html">OneHotEncoder</a> to map  a column of categories to a column of binary vectors. For example, with 4 categories of <code>device_conn_type</code>, an input value
of the second category would map to an output vector of <code>[0.0, 1.0, 0.0, 0.0, 0.0]</code>.</p>

<p>Then we convert a dataframe to an <code>RDD[LabeledPoint]</code> since the <a href="https://spark.apache.org/docs/1.6.0/api/java/org/apache/spark/mllib/classification/LogisticRegressionWithLBFGS.html">LogisticRegressionWithLBFGS</a> expects RDD as a training parameter.
We train the logistic regression and use it to predict the click for the test dataset. Finally we compute the metrics of our classifier comparing the predicted labels with actual ones.</p>

<p>To build this application and submit it to the InsightEdge cluster:</p>
<div class="bogus-wrapper"><notextile><figure class="code"><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
<span class="line-number">2</span>
</pre></td><td class="code"><pre><code class="bash"><span class="line">sbt clean assembly
</span><span class="line">./bin/insightedge-submit --class io.insightedge.demo.ctr.CtrDemo1 --master spark://10.8.1.115:7077 --executor-memory 16G  ~/avazu_ctr/insightedge-ctr-demo-assembly-1.0.0.jar spark://10.8.1.115:7077 10.8.1.115:4174 day_21
</span></code></pre></td></tr></table></div></figure></notextile></div>

<p>It takes about 2 minutes for the application to complete and output the following:</p>
<div class="bogus-wrapper"><notextile><figure class="code"><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
</pre></td><td class="code"><pre><code class="bash"><span class="line">Area under <span class="nv">ROC</span> <span class="o">=</span> 0.5177127622153417
</span></code></pre></td></tr></table></div></figure></notextile></div>

<p>We get <a href="https://en.wikipedia.org/wiki/Receiver_operating_characteristic#Area_under_the_curve">AUROC</a> slightly better than a random guess (AUROC = 0.5), which is not so bad for our first approach, but we can definitely do better.</p>

<h2 id="experimenting-with-more-features">Experimenting with more features</h2>

<p>Let’s try to select more features and see how it affects our metrics.</p>

<p>For this we created a new version of our app <a href="https://github.com/InsightEdge/insightedge-ctr-demo/blob/master/src/main/scala/io/insightedge/demo/ctr/CtrDemo2.scala">CtrDemo2</a> where we
can easily select features we want to include. We use <a href="https://spark.apache.org/docs/1.6.0/api/java/org/apache/spark/ml/feature/VectorAssembler.html">VectorAssembler</a> to assemble multiple feature vectors into a single <code>features</code> one:</p>

<div class="bogus-wrapper"><notextile><figure class="code"><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
<span class="line-number">2</span>
<span class="line-number">3</span>
<span class="line-number">4</span>
</pre></td><td class="code"><pre><code class="scala"><span class="line"><span class="k">val</span> <span class="n">assembledDf</span> <span class="k">=</span> <span class="k">new</span> <span class="nc">VectorAssembler</span><span class="o">()</span>
</span><span class="line">  <span class="o">.</span><span class="n">setInputCols</span><span class="o">(</span><span class="n">categoricalColumnsVectors</span><span class="o">.</span><span class="n">toArray</span><span class="o">)</span>
</span><span class="line">  <span class="o">.</span><span class="n">setOutputCol</span><span class="o">(</span><span class="s">&quot;features&quot;</span><span class="o">)</span>
</span><span class="line">  <span class="o">.</span><span class="n">transform</span><span class="o">(</span><span class="n">encodedDf</span><span class="o">)</span>
</span></code></pre></td></tr></table></div></figure></notextile></div>

<p>The results are the following:</p>

<ul>
  <li>with additionally included <code>device_type</code>: AUROC = 0.531015564807053</li>
  <li><code>+</code> <code>time_day</code> and <code>time_hour</code>: AUROC = 0.5555488992624483</li>
  <li><code>+</code> <code>C15</code>, <code>C16</code>, <code>C17</code>, <code>C18</code>, <code>C19</code>, <code>C20</code>, <code>C21</code>: AUROC = 0.7000630113145946</li>
</ul>

<p>You can notice how the AUROC is being improved as we add more and more features. This comes with the cost of the training time:</p>

<p><img src="http://dyagilev.org/images/ie-ml/13_application_time.png" /></p>

<p>We didn’t include high-cardinality features such as <code>device_ip</code> and <code>device_id</code> as they will blow up the feature vector size. One may consider applying techniques such as feature hashing
to reduce the dimension. We will leave it out of this blog post’s scope.</p>

<h2 id="tuning-algorithm-parameters">Tuning algorithm parameters</h2>

<p>Tuning algorithm parameters is a search problem. We will use <a href="http://spark.apache.org/docs/latest/ml-guide.html">Spark Pipeline API</a> with a <a href="https://en.wikipedia.org/wiki/Hyperparameter_optimization">Grid Search</a> technique.
Grid search evaluates a model for each combination of algorithm parameters specified in a grid (do not confuse with data grid).</p>

<p>Pipeline API supports model selection using <a href="https://en.wikipedia.org/wiki/Cross-validation_(statistics)">cross-validation</a> technique. For each set of parameters it trains the given <code>Estimator</code> and evaluates it using the given <code>Evaluator</code>.
We will use <code>BinaryClassificationEvaluator</code> that has AUROC as a metric by default.</p>

<div class="bogus-wrapper"><notextile><figure class="code"><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
<span class="line-number">2</span>
<span class="line-number">3</span>
<span class="line-number">4</span>
<span class="line-number">5</span>
<span class="line-number">6</span>
<span class="line-number">7</span>
<span class="line-number">8</span>
<span class="line-number">9</span>
<span class="line-number">10</span>
<span class="line-number">11</span>
<span class="line-number">12</span>
<span class="line-number">13</span>
<span class="line-number">14</span>
<span class="line-number">15</span>
<span class="line-number">16</span>
</pre></td><td class="code"><pre><code class="scala"><span class="line"><span class="k">val</span> <span class="n">lr</span> <span class="k">=</span> <span class="k">new</span> <span class="nc">LogisticRegression</span><span class="o">().</span><span class="n">setLabelCol</span><span class="o">(</span><span class="s">&quot;click&quot;</span><span class="o">)</span>
</span><span class="line"><span class="k">val</span> <span class="n">pipeline</span> <span class="k">=</span> <span class="k">new</span> <span class="nc">Pipeline</span><span class="o">().</span><span class="n">setStages</span><span class="o">(</span><span class="nc">Array</span><span class="o">(</span><span class="n">assembler</span><span class="o">,</span> <span class="n">lr</span><span class="o">))</span>
</span><span class="line">
</span><span class="line"><span class="k">val</span> <span class="n">paramGrid</span> <span class="k">=</span> <span class="k">new</span> <span class="nc">ParamGridBuilder</span><span class="o">()</span>
</span><span class="line">  <span class="o">.</span><span class="n">addGrid</span><span class="o">(</span><span class="n">lr</span><span class="o">.</span><span class="n">regParam</span><span class="o">,</span> <span class="nc">Array</span><span class="o">(</span><span class="mf">0.01</span> <span class="o">,</span> <span class="mf">0.1</span> <span class="cm">/*, 1.0 */</span><span class="o">))</span>
</span><span class="line">  <span class="o">.</span><span class="n">addGrid</span><span class="o">(</span><span class="n">lr</span><span class="o">.</span><span class="n">elasticNetParam</span><span class="o">,</span> <span class="nc">Array</span><span class="o">(</span><span class="mf">0.0</span> <span class="cm">/*, 0.5 , 1.0 */</span><span class="o">))</span>
</span><span class="line">  <span class="o">.</span><span class="n">addGrid</span><span class="o">(</span><span class="n">lr</span><span class="o">.</span><span class="n">fitIntercept</span><span class="o">,</span> <span class="nc">Array</span><span class="o">(</span><span class="kc">false</span> <span class="cm">/*, true */</span><span class="o">))</span>
</span><span class="line">  <span class="o">.</span><span class="n">build</span><span class="o">()</span>
</span><span class="line">
</span><span class="line"><span class="k">val</span> <span class="n">cv</span> <span class="k">=</span> <span class="k">new</span> <span class="nc">CrossValidator</span><span class="o">()</span>
</span><span class="line">  <span class="o">.</span><span class="n">setEstimator</span><span class="o">(</span><span class="n">pipeline</span><span class="o">)</span>
</span><span class="line">  <span class="o">.</span><span class="n">setEvaluator</span><span class="o">(</span><span class="k">new</span> <span class="nc">BinaryClassificationEvaluator</span><span class="o">().</span><span class="n">setLabelCol</span><span class="o">(</span><span class="s">&quot;click&quot;</span><span class="o">))</span>
</span><span class="line">  <span class="o">.</span><span class="n">setEstimatorParamMaps</span><span class="o">(</span><span class="n">paramGrid</span><span class="o">)</span>
</span><span class="line">  <span class="o">.</span><span class="n">setNumFolds</span><span class="o">(</span><span class="mi">3</span><span class="o">)</span>
</span><span class="line">
</span><span class="line"><span class="k">val</span> <span class="n">cvModel</span> <span class="k">=</span> <span class="n">cv</span><span class="o">.</span><span class="n">fit</span><span class="o">(</span><span class="n">encodedTrainDf</span><span class="o">)</span>
</span></code></pre></td></tr></table></div></figure></notextile></div>

<p>We included two regularization parameters 0.01 and 0.1 in our search grid for now, others are commented out for now.</p>

<p>Output the best set of parameters:</p>

<div class="bogus-wrapper"><notextile><figure class="code"><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
<span class="line-number">2</span>
<span class="line-number">3</span>
<span class="line-number">4</span>
<span class="line-number">5</span>
<span class="line-number">6</span>
<span class="line-number">7</span>
</pre></td><td class="code"><pre><code class="scala"><span class="line"><span class="n">println</span><span class="o">(</span><span class="s">&quot;Grid search results:&quot;</span><span class="o">)</span>
</span><span class="line"><span class="n">cvModel</span><span class="o">.</span><span class="n">getEstimatorParamMaps</span><span class="o">.</span><span class="n">zip</span><span class="o">(</span><span class="n">cvModel</span><span class="o">.</span><span class="n">avgMetrics</span><span class="o">).</span><span class="n">foreach</span><span class="o">(</span><span class="n">println</span><span class="o">)</span>
</span><span class="line">
</span><span class="line"><span class="n">println</span><span class="o">(</span><span class="s">&quot;Best set of parameters found:&quot;</span> <span class="o">+</span> <span class="n">cvModel</span><span class="o">.</span><span class="n">getEstimatorParamMaps</span>
</span><span class="line">  <span class="o">.</span><span class="n">zip</span><span class="o">(</span><span class="n">cvModel</span><span class="o">.</span><span class="n">avgMetrics</span><span class="o">)</span>
</span><span class="line">  <span class="o">.</span><span class="n">maxBy</span><span class="o">(</span><span class="k">_</span><span class="o">.</span><span class="n">_2</span><span class="o">)</span>
</span><span class="line">  <span class="o">.</span><span class="n">_1</span><span class="o">)</span>
</span></code></pre></td></tr></table></div></figure></notextile></div>

<p>Use the best model to predict <code>test</code> dataset loaded from the data grid:</p>
<div class="bogus-wrapper"><notextile><figure class="code"><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
<span class="line-number">2</span>
<span class="line-number">3</span>
</pre></td><td class="code"><pre><code class="scala"><span class="line"><span class="k">val</span> <span class="n">predictionDf</span> <span class="k">=</span> <span class="n">cvModel</span><span class="o">.</span><span class="n">transform</span><span class="o">(</span><span class="n">encodedTestDf</span><span class="o">).</span><span class="n">select</span><span class="o">(</span><span class="s">&quot;id&quot;</span><span class="o">,</span> <span class="s">&quot;probability&quot;</span><span class="o">).</span><span class="n">map</span> <span class="o">{</span>
</span><span class="line">  <span class="k">case</span> <span class="nc">Row</span><span class="o">(</span><span class="n">id</span><span class="k">:</span> <span class="kt">String</span><span class="o">,</span> <span class="n">probability</span><span class="k">:</span> <span class="kt">Vector</span><span class="o">)</span> <span class="k">=&gt;</span> <span class="o">(</span><span class="n">id</span><span class="o">,</span> <span class="n">probability</span><span class="o">(</span><span class="mi">1</span><span class="o">))</span>
</span><span class="line"><span class="o">}.</span><span class="n">toDF</span><span class="o">(</span><span class="s">&quot;id&quot;</span><span class="o">,</span> <span class="s">&quot;click&quot;</span><span class="o">)</span>
</span></code></pre></td></tr></table></div></figure></notextile></div>

<p>Then the results are saved back to csv on hdfs, so we can submit them to Kaggle, see the complete listing in <a href="https://github.com/InsightEdge/insightedge-ctr-demo/blob/master/src/main/scala/io/insightedge/demo/ctr/CtrDemo3.scala">CtrDemo3</a>.</p>

<p>It takes about 27 mins to train and compare models for two regularization parameters 0.01 and 0.1. The results are:</p>
<div class="bogus-wrapper"><notextile><figure class="code"><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
<span class="line-number">2</span>
<span class="line-number">3</span>
<span class="line-number">4</span>
<span class="line-number">5</span>
<span class="line-number">6</span>
<span class="line-number">7</span>
<span class="line-number">8</span>
<span class="line-number">9</span>
<span class="line-number">10</span>
<span class="line-number">11</span>
<span class="line-number">12</span>
<span class="line-number">13</span>
<span class="line-number">14</span>
<span class="line-number">15</span>
<span class="line-number">16</span>
<span class="line-number">17</span>
</pre></td><td class="code"><pre><code class="scala"><span class="line"><span class="nc">Grid</span> <span class="n">search</span> <span class="n">results</span><span class="k">:</span>
</span><span class="line"><span class="o">({</span>
</span><span class="line">	<span class="kt">logreg_2b70e3edf1f0-elasticNetParam:</span> <span class="err">0</span><span class="kt">.</span><span class="err">0</span><span class="o">,</span>
</span><span class="line">	<span class="kt">logreg_2b70e3edf1f0-fitIntercept:</span> <span class="kt">false</span><span class="o">,</span>
</span><span class="line">	<span class="kt">logreg_2b70e3edf1f0-regParam:</span> <span class="err">0</span><span class="kt">.</span><span class="err">01</span>
</span><span class="line"><span class="o">},</span><span class="mf">0.7000655195682837</span><span class="o">)</span>
</span><span class="line"><span class="o">({</span>
</span><span class="line">	<span class="n">logreg_2b70e3edf1f0</span><span class="o">-</span><span class="n">elasticNetParam</span><span class="k">:</span> <span class="err">0</span><span class="kt">.</span><span class="err">0</span><span class="o">,</span>
</span><span class="line">	<span class="n">logreg_2b70e3edf1f0</span><span class="o">-</span><span class="n">fitIntercept</span><span class="k">:</span> <span class="kt">false</span><span class="o">,</span>
</span><span class="line">	<span class="n">logreg_2b70e3edf1f0</span><span class="o">-</span><span class="n">regParam</span><span class="k">:</span> <span class="err">0</span><span class="kt">.</span><span class="err">1</span>
</span><span class="line"><span class="o">},</span><span class="mf">0.697680175815199</span><span class="o">)</span>
</span><span class="line">
</span><span class="line"><span class="nc">Best</span> <span class="n">params</span> <span class="n">found</span><span class="k">:</span><span class="o">{</span>
</span><span class="line">	<span class="kt">logreg_2b70e3edf1f0-elasticNetParam:</span> <span class="err">0</span><span class="kt">.</span><span class="err">0</span><span class="o">,</span>
</span><span class="line">	<span class="kt">logreg_2b70e3edf1f0-fitIntercept:</span> <span class="kt">false</span><span class="o">,</span>
</span><span class="line">	<span class="n">logreg_2b70e3edf1f0</span><span class="o">-</span><span class="n">regParam</span><span class="k">:</span> <span class="err">0</span><span class="kt">.</span><span class="err">01</span>
</span><span class="line"><span class="o">}</span>
</span></code></pre></td></tr></table></div></figure></notextile></div>

<p>This simple logistic regression model has a rank of 1109 out of 1603 competitors in Kaggle.</p>

<p>The future improvements are only limited by data science skills and creativity. One may consider:</p>

<ul>
  <li>implement <a href="https://www.kaggle.com/wiki/LogarithmicLoss">Logarithmic Loss</a> function as an Evaluator since it’s used by Kaggle to calculate the model score. In our example we used AUROC</li>
  <li>include other features that we didn’t select</li>
  <li>generate additional features such click history of a user</li>
  <li>use a hashing trick to reduce the features vector dimension</li>
  <li>try other machine learning algorithms, the winner of competition used  <a href="http://www.csie.ntu.edu.tw/~r01922136/slides/ffm.pdf">Field-aware Factorization Machines</a></li>
</ul>

<h2 id="architecture">Architecture</h2>

<p>The following diagram demonstrates the design of machine learning application with InsightEdge.</p>

<p><img src="http://dyagilev.org/images/ie-ml/arch.png" /></p>

<p>The key design advantages are:</p>

<ul>
  <li>the single platform <strong>converges</strong> analytical processing (machine learning) powered by Spark with transactional processing powered by custom real-time applications;</li>
  <li>real-time applications can execute any OLTP query (read, insert, update, delete) on training data that is <strong>immediately available</strong> for Spark analytical queries or machine learning routines. There is no need to build a complex ETL pipeline that extracts training data from OLTP database with Kafka/Flume/HDFS. Besides the complexity, an ETL pipeline introduces unwanted latency that can be a stopper for reactive machine learning apps.
With InsightEdge, Spark applications can view the <strong>live</strong> data;</li>
  <li>the training data lives in the memory of data grid, which acts as an extension of Spark memory. This way we can load the data <strong>quicker</strong>;</li>
  <li>An in-memory data grid is a <strong>general-purpose</strong> highly available and fault tolerant storage. With support of ACID transactions and SQL queries it becomes the primary storage for the application;</li>
  <li>InsightEdge stack is <strong>scalable</strong> in both computation (Spark) and storage (data grid) tiers. This makes it attractive for large-scale machine learning.</li>
</ul>

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

<p>In this blog post we demonstrated how to use machine learning algorithms with InsightEdge. We went through typical stages:</p>

<ul>
  <li>interactive data exploration with Zeppelin</li>
  <li>feature selection and transformation</li>
  <li>training predictive models</li>
  <li>calculating model metrics</li>
  <li>tuning parameters</li>
</ul>

<p>We didn’t have a goal to build a perfect predictive model, so there is great room for improvement.</p>

<p>In the architecture section we discussed how the typical design may look like, what are the benefits of using InsightEdge for machine learning.</p>

<p>The Zeppelin notebook can be found <a href="https://github.com/InsightEdge/insightedge-ctr-demo/blob/master/zeppelin/CTR%20demo.json">here</a> and submittable spark apps <a href="https://github.com/InsightEdge/insightedge-ctr-demo/tree/master/src/main/scala/io/insightedge/demo/ctr">here</a></p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Java enums in distributed systems]]></title>
    <link href="http://dyagilev.org/blog/2016/01/20/java-enums-in-distributed-systems/"/>
    <updated>2016-01-20T23:01:20+02:00</updated>
    <id>http://dyagilev.org/blog/2016/01/20/java-enums-in-distributed-systems</id>
    <content type="html"><![CDATA[<p>Did you ever think about how <code>hashCode()</code> of <code>java.lang.Enum</code> implemented?</p>

<!-- more -->

<p>Surprisingly or not it’s</p>

<div class="bogus-wrapper"><notextile><figure class="code"><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
<span class="line-number">2</span>
<span class="line-number">3</span>
</pre></td><td class="code"><pre><code class="java"><span class="line"><span class="kd">public</span> <span class="kd">final</span> <span class="kt">int</span> <span class="nf">hashCode</span><span class="o">()</span> <span class="o">{</span>
</span><span class="line">  <span class="k">return</span> <span class="kd">super</span><span class="o">.</span><span class="na">hashCode</span><span class="o">();</span>
</span><span class="line"><span class="o">}</span>
</span></code></pre></td></tr></table></div></figure></notextile></div>

<p>it returns the <code>Object</code>’s <code>hashCode</code> which is an internal address of the object to a certain extend. From the first glance it totally makes sense since <code>Enum</code> values are singletons.</p>

<p>Now imagine you are building distributed system. Distributed systems use <code>hashCode</code> to</p>

<ul>
  <li>determine which worker in a cluster should handle part of a huge job</li>
  <li>determine which node in a cluster should store given item of dataset (e.g. in distributed cache)</li>
</ul>

<p>The same <code>Enum</code> instance would give you a different <code>hashCode</code> value in different JVMs/hosts, screwing up your Hadoop job or put/lookup in distributed storage. Just something I faced recently.</p>

]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Slides for my JavaDay 2015 talk]]></title>
    <link href="http://dyagilev.org/blog/2015/10/03/slides-for-my-javaday-2015-talk/"/>
    <updated>2015-10-03T20:03:35+03:00</updated>
    <id>http://dyagilev.org/blog/2015/10/03/slides-for-my-javaday-2015-talk</id>
    <content type="html"><![CDATA[<p>Slides for my <a href="http://javaday.org.ua/">JavaDay 2015</a> talk …</p>

<!-- more -->

<script async="" class="speakerdeck-embed" data-id="03546cf257194217a070c456c429cb07" data-ratio="1.77777777777778" src="//speakerdeck.com/assets/embed.js"></script>

]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Resilient distributed systems with Netflix Hystrix]]></title>
    <link href="http://dyagilev.org/blog/2015/08/10/resilient-distributed-systems-with-netflix-hystrix/"/>
    <updated>2015-08-10T20:20:31+03:00</updated>
    <id>http://dyagilev.org/blog/2015/08/10/resilient-distributed-systems-with-netflix-hystrix</id>
    <content type="html"><![CDATA[<p>Slides of my talk on building resilient systems with Hystrix …</p>

<!-- more -->

<script async="" class="speakerdeck-embed" data-id="8cbfb811446c44c18779faf7e3a237af" data-ratio="1.77777777777778" src="//speakerdeck.com/assets/embed.js"></script>

]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Understanding Parser Combinators in Scala]]></title>
    <link href="http://dyagilev.org/blog/2015/07/15/understanding-parser-combinators-in-scala/"/>
    <updated>2015-07-15T21:50:16+03:00</updated>
    <id>http://dyagilev.org/blog/2015/07/15/understanding-parser-combinators-in-scala</id>
    <content type="html"><![CDATA[<p>Slides of my talk on Parser Combinators in internal Scala School …</p>

<!-- more -->

<p>We will gradually build an embedded domain-specific language (DSL) for specifying grammars in a EBNF-like notation in Scala.
We will use monadic parser combinators approach. As a result we should be able to parse JSON document using our library.</p>

<script async="" class="speakerdeck-embed" data-id="d9a3949cc82940eca6bee69a71791563" data-ratio="1.77777777777778" src="//speakerdeck.com/assets/embed.js"></script>

<p>Sources used in the slides can be found <a href="https://github.com/fe2s/parser-combinators-talk">on github</a></p>

]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Distributed Configuration for SaaS application]]></title>
    <link href="http://dyagilev.org/blog/2015/06/01/distributed-configuration-for-saas-application/"/>
    <updated>2015-06-01T21:36:36+03:00</updated>
    <id>http://dyagilev.org/blog/2015/06/01/distributed-configuration-for-saas-application</id>
    <content type="html"><![CDATA[<p>Recently I was involved into discussion of SaaS application design. One of the questions was how to manage configuration in large scale SaaS. The rise of clouds, microservices and container virtualization influence approaches used in configuration management.  In this article we will look what we can learn from these concepts and how we can apply these lessons in SaaS and other generic distributed systems. In the second part of the article, we will build a PoC of elastic tenant aware application leveraging <a href="http://zookeeper.apache.org/">Zookeeper</a> with 200 lines in Scala.</p>

<!-- more -->

<p>Before considering any architecture, let’s refresh what requirements are essential for typical SaaS application:</p>

<ul>
  <li><strong>elasticity</strong>, ability to add and remove tenants in runtime with zero system downtime</li>
  <li><strong>scalability</strong>, ability to handle growing amount of users</li>
  <li><strong>availability</strong>, the service should stay functional and usable to fulfil business requirements</li>
</ul>

<p>(we skip other fundamental requirements such as security as they are not primary focus of the article)</p>

<p>Okay, how do we usually deal with <strong>scalability concerns</strong>?</p>

<p>When it comes to application services(or application servers), we prefer  making them stateless, so we can simply run multiple copies and distribute load between them achieving scaling out (horizontal) capabilities. And what about availability? Pretty the same, run redundant copies of your service. So far so good.</p>

<p>Okay, but having hundreds of customers(tenants) multiplied on number of application servers per customer requires <strong>unreasonable overhead</strong> on memory, CPU and other hardware resources. Usually customers are different in size, usage patterns and timezones. Under these conditions generated load is spread unequally and lead to suboptimal resources utilization. Further cost overhead comes from licenses of underlying software(databases, application servers, operating systems, etc).</p>

<p><strong>Multitenancy</strong> comes to the rescue. Multitenancy implies the ability to serve multiple tenants with a single application instance thus spreading the load more equally and amortizing infrastructure overhead. Though multitenancy is not free, the downside is the increased engineering complexity that requires additional development effort. On the other hand some of these issues can be partially addressed with <strong>virtualization</strong>, it looks attractive since doesn’t require any significant architecture redesign.</p>

<p>Having scalable application server layer is only part of the problem, with growing amount of customers the data storage has to be scalable as well. Designing multitenant data storage is another huge topic, we will not dig into consideration details. While NoSQL solutions are able to scale out of the box, with relational databases we have to scale them manually allocating database instance per one or several customers, thus application server instance may talk to multiple databases.</p>

<p>Remember, in dynamic scalable SaaS environment application servers, databases and load balancer instances come and go while relying on each other combining a distributed cluster. The load balancer should know about application server instances, and application server instance should talk to databases. So when the database for new customer is provisioned, some or all application servers have to be notified about database layer changes and load balancer has to be notified of all changes in a farm of application servers. In short, we need an ability to link the pieces of multi-tier application together in realtime.</p>

<p>Configuration management tools like <a href="https://www.chef.io/chef/">Chef</a>, <a href="http://puppetlabs.com/">Puppet</a>, etc are able to configure a node based on centralized configuration, though they are not designed to be responsive and propagate configuration changes quickly. Additionally they are not designed to detect failures or tolerate network partitions.</p>

<p>If you think about multi-tier configuration in a more abstract way, you will easily recognize <strong>service registry</strong> and <strong>service discovery</strong> patterns that people use many years building distributed systems.</p>

<p>Even more, with the rise of container based virtualization and <a href="http://www.docker.com/">Docker</a> in particularly, service discovery becomes very <a href="https://docs.docker.com/swarm/discovery/">important part</a>. Containers need an ability to discovery each other adopting to the current environment.</p>

<p>Let’s see how SaaS configuration will look from service discovery perspective.</p>

<p><img src="http://dyagilev.org/images/saas-configuration/service-discovery.jpg" width="75%" height="75%" /></p>

<p>Okay, but can we just use simple database with inserts and selects to register and discovery? Well, there are a few concerns with that:</p>

<ul>
  <li><strong>availability</strong>, configuration store should be distributed and tolerate nodes failure, otherwise it will be a single point of failure.</li>
  <li>service <strong>failure detection</strong>, we need some sort of services monitoring, if service goes down, dependent services should react correspondingly. The are several approaches for failure detection design. Client can keep TCP connection indicating it’s alive, service can periodically send messages or configuration store itself can monitor service endpoint. This design decision is a subject for debates, one should consider what is reasonable in the context. There a few available service discovery tools following these patterns.</li>
</ul>

<p>Now that we have discussed the high level design and requirements of configuration store, we can briefly mention open source tools: <a href="http://zookeeper.apache.org">Zookeeper</a>, <a href="http://www.consul.io/">Consul</a>, <a href="http://coreos.com/etcd/">etcd</a>, <a href="http://github.com/Netflix/eureka">Eureka</a>, <a href="http://nerds.airbnb.com/smartstack-service-discovery-cloud/">SmartStack</a>, <a href="http://github.com/ha/doozerd">Doozer</a>, <a href="http://www.serfdom.io/">Serf</a>, etc.</p>

<p>I would highlight three of them definitely worth checking:</p>

<ul>
  <li><a href="http://zookeeper.apache.org">Apache Zookeeper</a>. Written in Java, choses consistency over availability, old, mature, battle-tested, very popular in middleware(Hadoop, Kafka, Storm, etc). Hard to use properly because of low level API. Consider using existing recipes or <a href="http://curator.apache.org">Curator</a> library.</li>
  <li><a href="http://www.consul.io">HashiCorp Consul</a>. Written in Go, flexible between consistency and availability, new and promising, high level features out of the box, multi datacenter support.</li>
  <li><a href="http://nerds.airbnb.com/smartstack-service-discovery-cloud">Airbnb SmartStack</a>. Written in Ruby on top of Zookeeper and <a href="http://www.haproxy.org/">HAProxy</a>, unique design where application service talks to HAProxy on localhost, adds Zookeeper caching to favour availability over consistency.</li>
</ul>

<h2 id="part-2-building-a-poc">Part 2. Building a PoC</h2>

<p>Let’s build a simple demo application to proof the concept described above. We will try to simplify things as much as possible sacrificing correctness and errors handling sometimes, but still suitable for illustration purpose. Note, one should leverage existing <a href="http://curator.apache.org/curator-x-discovery/">Curator service discovery recipes</a> when building production quality applications. The source code is available on <a href="https://github.com/fe2s/zk-tenant">github</a></p>

<p>For this PoC we choose tenant aware model rather than multitenancy to demonstrate how to incorporate custom logic with service discovery. In this model client (tenant) is routed to a configurable number of application services while they use common database.</p>

<p>Beware that this model has its drawbacks such as weaker scale-in (reducing the quantity of servers) and cost saving capabilities since the load is not equally spread comparing to true multitenancy. On the other hand this can be compensated with container virtualization in some sort. Also this model implies support of multiple release versions of application and has better support of application level caching. Again, the tenancy model itself is not a subject here rather than a centralized configuration.</p>

<p><img src="http://dyagilev.org/images/saas-configuration/poc-tenancy.jpg" width="75%" height="75%" /></p>

<p>Our stack is:</p>

<ul>
  <li>Scala</li>
  <li>Zookeeper for configuration store</li>
  <li><a href="http://curator.apache.org">Curator</a> library as a client for Zookeeper</li>
  <li><a href="http://spray.io/">Spray</a>(runs on top of Akka) for lightweight http services</li>
  <li>HAProxy load balancer</li>
</ul>

<h3 id="zookeeper-data-model">Zookeeper data model</h3>

<p>We define Zookeeper data model as a following hierarchical structure.</p>

<pre><code>/app
  /client-{id}
    /db
    /app-server-slots
</code></pre>

<p>Znode <code>/db</code> contains database details such as connection url. Znode <code>/app-server-slots</code> defines the maximum number of application server instances we want to run for given client.</p>

<p>Here is an example with 3 clients, the value of znode follows <code>=</code> sign.</p>

<pre><code>/app
  /client-1
    /db = jdbc://db-client-1:5555
    /app-server-slots = 2
  /client-2
    /db = jdbc://db-client-2:5555
    /app-server-slots = 2
  /client-3
    /db = jdbc://db-client-3:5555
    /app-server-slots = 1
</code></pre>

<p>Service registration is implemented using so called ephemeral znodes. Unlike standard znodes they exist as long as the session that created the znode is active.</p>

<p>When application server starts it registers itself creating ephemeral znode under corresponding client. Respectively when application server is brought down or it failures for some reason, the ephemeral znode is automatically deleted. The value of znode contains http service location.</p>

<pre><code>/app
/client-1
  /db = jdbc://db-client-1:5555
  /app-server-slots = 2
  /app-server#0000000001 = host1:56003
</code></pre>

<h3 id="load-balancer-configuration">Load Balancer configuration</h3>

<p>We use HAProxy as a load balancer. To reconfigure HAProxy in runtime we created a simple agent that runs alongside HAProxy process and watches for any configuration changes. Once it detects any changes in Zookeeper, it rewrites HAProxy config and send a command to reload it.</p>

<h3 id="how-it-works-in-action">How it works in action</h3>

<p>At first we start Zookeeper <code>zkServer.sh start</code></p>

<p>Then we create some data model to play with by running <code>ZkSchemaBuilder.scala</code>. You can browse zookeeper data with <code>zkCli.sh</code> tool.</p>

<p>Start HAProxy <code>/haproxy/start.sh</code> and HAProxy agent running <code>HAProxyAgent.scala</code></p>

<pre><code>Starting HAProxy agent Thread[main,5,main]
/client-1
/client-2
/client-3
Rewriting HAProxy config /Users/fe2s/Projects/zk-tenant/haproxy/haproxy.conf
Reloading HAProxy config
</code></pre>

<p>At this point we should be able to hit <code>http://localhost:8080/</code> though it will return 503 since there are no actual backend services running. Let’s fix it.</p>

<p>Run <code>Boot.scala</code> to start application server with http service.</p>

<pre><code>[INFO] [06/01/2015 20:22:16.084] [on-spray-can-akka.actor.default-dispatcher-2] [akka://on-spray-can/user/IO-HTTP/listener-0] Bound to Oleksiis-Mac-mini.local/192.168.0.100:57581
registering service available at Oleksiis-Mac-mini.local:57581
looking for a client with free slots
client client-1 slots: 2 occupied: 0
Found client client-1 with available slot(s) ... registering
Configuring HttpService with dbUrl jdbc://db-client-1:5555
</code></pre>

<p>We see that http service was brought up for client-1 that had 2 available slots.</p>

<p>Now run <code>Boot.scala</code> several more times to start more servers and check <code>haproxy/haproxy.conf</code></p>

<pre><code>defaults
 mode http
 timeout connect 5000ms
 timeout client 50000ms
 timeout server 50000ms

frontend http-in
 bind *:8080
 acl client-1-path path_beg /client-1
 acl client-2-path path_beg /client-2
 acl client-3-path path_beg /client-3
 use_backend client-1-backend if client-1-path
 use_backend client-2-backend if client-2-path
 use_backend client-3-backend if client-3-path

backend client-1-backend
 balance roundrobin
 server app Oleksiis-Mac-mini.local:57581
 server app Oleksiis-Mac-mini.local:57588

backend client-2-backend
 balance roundrobin
 server app Oleksiis-Mac-mini.local:57591
 server app Oleksiis-Mac-mini.local:57595

backend client-3-backend
 balance roundrobin
 server app Oleksiis-Mac-mini.local:57598
</code></pre>

<p>As we see HAProxy agent observed and propagated all configuration changes to haproxy.conf. We use HAProxy acl feature to route http requests by url prefix, i.e. requests starting with <code>/client-1</code> will be routed to a farm of application servers serving for client-1.</p>

<p>Now if we hit <a href="http://localhost:8080/client-1/test">http://localhost:8080/client-1/test</a> we should get a response “OK. Http service configured with db url: jdbc://db-client-1:5555”. Voila!</p>

<p>You should also notice that killing an application server process will result in immediate reconfiguration of HAProxy.</p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Spring Data and type-safe API for GigaSpaces XAP]]></title>
    <link href="http://dyagilev.org/blog/2015/04/12/spring-data-and-type-safe-api-for-gigaspaces-xap/"/>
    <updated>2015-04-12T21:21:13+03:00</updated>
    <id>http://dyagilev.org/blog/2015/04/12/spring-data-and-type-safe-api-for-gigaspaces-xap</id>
    <content type="html"><![CDATA[<p>We have developed Spring Data API for GigaSpaces XAP with a number of fancy extensions. Check it out on <a href="https://github.com/Gigaspaces/xap-spring-data">github</a></p>

<h3 id="motivation">Motivation:</h3>

<ul>
  <li>make it easy to use in-memory datagrid for those who already have experience with Spring Data APIs such as Spring Data MongoDB, JPA, Redis, etc.</li>
  <li>significantly reduce the amount of boilerplate code required to implement data access layer</li>
  <li>reduce the amount of effort in switching from any Spring Data implementation to XAP</li>
  <li>catch API errors at compile time (type-safe API using QueryDSL)</li>
</ul>

<!-- more -->

<h2 id="features">Features</h2>

<ul>
  <li>Spring configuration support using Java based <code>@Configuration</code> classes or XML namespace, filters support</li>
  <li>CRUD and Paging repositories extended with XAP specific features such as projections, change API, lease, take, etc</li>
  <li>repository for XAP Documents</li>
  <li>selectively exposing CRUD methods</li>
  <li>query methods (e.g. <code>findByNameAndAge</code>)</li>
  <li>custom methods</li>
  <li>common query lookup strategies</li>
  <li>property expressions</li>
  <li>special parameters handling including <code>Sort</code> and <code>Pageable</code></li>
  <li>native XAP API support</li>
  <li>seamless integration with all native XAP features - persistence, transcations, event processing, security, indexes, lease, etc</li>
  <li>ability to work with multiple spaces</li>
  <li>QueryDSL integration to support type-safe API(queries, projection, change API)</li>
</ul>

<h2 id="getting-started">Getting Started</h2>
<ul>
  <li>documentation including 5-mins guide <a href="http://docs.gigaspaces.com/sbp/spring-data.html">http://docs.gigaspaces.com/sbp/spring-data.html</a></li>
  <li><a href="https://github.com/Gigaspaces/xap-spring-data/tree/master/examples">examples</a> and <a href="https://github.com/Gigaspaces/xap-spring-data">sources</a></li>
</ul>

]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Keep up with Spark Streaming at in-memory speed using GigaSpaces XAP]]></title>
    <link href="http://dyagilev.org/blog/2015/03/07/Keep-up-with-Spark-Streaming-at-in-memory-speed-using-GigaSpaces-XAP/"/>
    <updated>2015-03-07T21:33:06+02:00</updated>
    <id>http://dyagilev.org/blog/2015/03/07/Keep-up-with-Spark-Streaming-at-in-memory-speed-using-GigaSpaces-XAP</id>
    <content type="html"><![CDATA[<p>Spark Streaming is a popular engine for stream processing and its ability to compute data in memory makes it very attractive. However Spark Streaming is not self-sufficient, it relies on external data source and storage to output computation results. Therefore, in many cases the overall performance is limited by slow external components that are not able to keep up with Spark’s throughput and/or introduce unacceptable latency.</p>

<p>In this article we describe how we use GigaSpaces XAP in-memory datagrid to address this challenge. Code sources are available on <a href="https://github.com/fe2s/xap-spark">github</a></p>

<!-- more -->

<ul id="markdown-toc">
  <li><a href="#introduction" id="markdown-toc-introduction">Introduction</a></li>
  <li><a href="#challenge" id="markdown-toc-challenge">Challenge</a></li>
  <li><a href="#solution" id="markdown-toc-solution">Solution</a>    <ul>
      <li><a href="#xap-stream" id="markdown-toc-xap-stream">XAP Stream</a></li>
      <li><a href="#spark-input-dstream" id="markdown-toc-spark-input-dstream">Spark Input DStream</a></li>
      <li><a href="#output-spark-computation-results-to-xap" id="markdown-toc-output-spark-computation-results-to-xap">Output Spark computation results to XAP</a></li>
    </ul>
  </li>
  <li><a href="#word-counter-demo" id="markdown-toc-word-counter-demo">Word Counter Demo</a>    <ul>
      <li><a href="#high-level-design" id="markdown-toc-high-level-design">High-level design</a></li>
      <li><a href="#installing-and-building-the-demo-application" id="markdown-toc-installing-and-building-the-demo-application">Installing and building the Demo application</a></li>
      <li><a href="#deploying-xap-space-and-web-pu" id="markdown-toc-deploying-xap-space-and-web-pu">Deploying XAP Space and Web PU</a></li>
      <li><a href="#launch-spark-application" id="markdown-toc-launch-spark-application">Launch Spark Application</a>        <ul>
          <li><a href="#option-a-run-embedded-spark-cluster" id="markdown-toc-option-a-run-embedded-spark-cluster">Option A. Run embedded Spark cluster</a></li>
          <li><a href="#option-b-run-spark-standalone-mode-cluster" id="markdown-toc-option-b-run-spark-standalone-mode-cluster">Option B. Run Spark standalone mode cluster</a>            <ul>
              <li><a href="#run-spark" id="markdown-toc-run-spark">Run Spark</a></li>
              <li><a href="#submit-application" id="markdown-toc-submit-application">Submit application</a></li>
            </ul>
          </li>
        </ul>
      </li>
      <li><a href="#launch-feeder-application" id="markdown-toc-launch-feeder-application">Launch Feeder application</a></li>
    </ul>
  </li>
</ul>

<h1 id="introduction">Introduction</h1>

<p>Real-time processing is becoming more and more popular. <a href="https://spark.apache.org/streaming/">Spark Streaming</a> is an extension of the core Spark API that allows scalable, high-throughput, fault-tolerant stream processing of live data streams.</p>

<p>Spark Streaming has many use cases: user activity analytics on web, recommendation systems, censor data analytics, fraud detection, sentiment analytics and more.</p>

<p>Data can be ingested to Spark cluster from many sources like HDS, Kafka, Flume, etc and can be processed using complex algorithms expressed with high-level functions like <code>map</code>, <code>reduce</code>, <code>join</code> and <code>window</code>. Finally, processed data can be pushed out to filesystems or databases.</p>

<p><img src="http://dyagilev.org/images/xap-spark/spark-streaming.jpg" /></p>

<h1 id="challenge">Challenge</h1>

<p>Spark cluster keeps intermediate chunks of data (RDD) in memory and, if required, rarely touches HDFS to checkpoint stateful computation, therefore it is able to process huge volumes of data at in-memory speed. However, in many cases the overall performance is limited by slow input and output data sources that are not able to stream and store data with in-memory speed.</p>

<h1 id="solution">Solution</h1>

<p>In this pattern we address performance challenge by integrating Spark Streaming with XAP. XAP is used as a stream data source and a scalable, fast, reliable persistent storage.</p>

<p><img src="http://dyagilev.org/images/xap-spark/high-level.jpg" /></p>

<ol>
  <li>Producer writes the data to XAP stream</li>
  <li>Spark worker reads the data from XAP stream and propagates it further for computation</li>
  <li>Spark saves computation results to XAP datagrid where they can be queried to display on UI</li>
</ol>

<p>Let’s discuss this in more details.</p>

<h2 id="xap-stream">XAP Stream</h2>

<p>On XAP side we introduce the concept of stream. Please find <code>XAPStream</code> – an implementation that supports writing data in single and batch modes and reading in batch mode. <code>XAPStream</code> leverages XAP’s <code>FIFO</code> (First In, First Out) capabilities.</p>

<p>Here is an example how one can write data to <code>XAPStream</code>. Let’s consider we are building a Word Counter application and would like to write sentences of text to the stream.</p>

<p>At first we create a data model that represents a sentence. Note, that the space class should be annotated with <code>FIFO</code> support.</p>

<div class="bogus-wrapper"><notextile><figure class="code"><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
<span class="line-number">2</span>
<span class="line-number">3</span>
<span class="line-number">4</span>
<span class="line-number">5</span>
<span class="line-number">6</span>
</pre></td><td class="code"><pre><code class="java"><span class="line"><span class="nd">@SpaceClass</span><span class="o">(</span><span class="n">fifoSupport</span> <span class="o">=</span> <span class="n">FifoSupport</span><span class="o">.</span><span class="na">OPERATION</span><span class="o">)</span>
</span><span class="line"><span class="kd">public</span> <span class="kd">class</span> <span class="nc">Sentence</span> <span class="kd">implements</span> <span class="n">Serializable</span> <span class="o">{</span>
</span><span class="line">    <span class="kd">private</span> <span class="n">String</span> <span class="n">id</span><span class="o">;</span>
</span><span class="line">    <span class="kd">private</span> <span class="n">String</span> <span class="n">text</span><span class="o">;</span>
</span><span class="line">    <span class="o">[</span><span class="n">getters</span> <span class="n">setters</span> <span class="n">omitted</span> <span class="k">for</span> <span class="n">brevity</span><span class="o">]</span>
</span><span class="line"><span class="o">}</span>
</span></code></pre></td></tr></table></div></figure></notextile></div>
<blockquote>
  <p>Complete sources of Sentence.java can be found <a href="https://github.com/fe2s/xap-spark/blob/master/word-counter-demo/space-model/src/main/java/com/gigaspaces/spark/streaming/wordcounter/Sentence.java">here</a></p>
</blockquote>

<h2 id="spark-input-dstream">Spark Input DStream</h2>

<p>In order to ingest data from XAP to Spark, we implemented a custom <code>ReceiverInputDStream</code> that starts the <code>XAPReceiver</code> on Spark worker nodes to receive the data.</p>

<p><code>XAPReceiver</code> is a stream consumer that reads batches of data in multiple threads in parallel to achieve the maximum throughput.</p>

<p><code>XAPInputDStream</code> can be created using the following function in <code>XAPUtils</code> object.</p>

<div class="bogus-wrapper"><notextile><figure class="code"><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
<span class="line-number">2</span>
<span class="line-number">3</span>
<span class="line-number">4</span>
<span class="line-number">5</span>
<span class="line-number">6</span>
<span class="line-number">7</span>
<span class="line-number">8</span>
<span class="line-number">9</span>
<span class="line-number">10</span>
<span class="line-number">11</span>
<span class="line-number">12</span>
<span class="line-number">13</span>
</pre></td><td class="code"><pre><code class="scala"><span class="line"><span class="cm">/**</span>
</span><span class="line"><span class="cm">   * Creates InputDStream with GigaSpaces XAP used as an external data store</span>
</span><span class="line"><span class="cm">   *</span>
</span><span class="line"><span class="cm">   * @param ssc streaming context</span>
</span><span class="line"><span class="cm">   * @param storageLevel RDD persistence level</span>
</span><span class="line"><span class="cm">   * @param template template used to match items when reading from XAP stream</span>
</span><span class="line"><span class="cm">   * @param batchSize number of items to read from</span>
</span><span class="line"><span class="cm">   * @param readRetryInterval time to wait till the next read attempt if nothing consumed</span>
</span><span class="line"><span class="cm">   * @param parallelReaders number of parallel readers</span>
</span><span class="line"><span class="cm">   * @tparam T Class type of the object of this stream</span>
</span><span class="line"><span class="cm">   * @return Input DStream</span>
</span><span class="line"><span class="cm">   */</span>
</span><span class="line">  <span class="k">def</span> <span class="n">createStream</span><span class="o">[</span><span class="kt">T</span> <span class="k">&lt;:</span> <span class="kt">java.io.Serializable</span> <span class="kt">:</span> <span class="kt">ClassTag</span><span class="o">](</span><span class="n">ssc</span><span class="k">:</span> <span class="kt">StreamingContext</span><span class="o">,</span> <span class="n">template</span><span class="k">:</span> <span class="kt">T</span><span class="o">,</span> <span class="n">batchSize</span><span class="k">:</span><span class="kt">Int</span><span class="o">,</span> <span class="n">readRetryInterval</span><span class="k">:</span> <span class="kt">Duration</span> <span class="o">=</span> <span class="nc">Milliseconds</span><span class="o">(</span><span class="mi">100</span><span class="o">),</span> <span class="n">parallelReaders</span><span class="k">:</span> <span class="kt">Int</span><span class="o">,</span> <span class="n">storageLevel</span><span class="k">:</span> <span class="kt">StorageLevel</span> <span class="o">=</span> <span class="nc">MEMORY_AND_DISK_SER</span><span class="o">){</span><span class="err">…</span><span class="o">}</span>
</span></code></pre></td></tr></table></div></figure></notextile></div>

<p>Here is an example of creating XAP Input stream. At first we set XAP space url in Spark config:</p>

<div class="bogus-wrapper"><notextile><figure class="code"><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
<span class="line-number">2</span>
<span class="line-number">3</span>
<span class="line-number">4</span>
<span class="line-number">5</span>
</pre></td><td class="code"><pre><code class="scala"><span class="line">  <span class="k">val</span> <span class="n">sparkConf</span> <span class="k">=</span> <span class="k">new</span> <span class="nc">SparkConf</span><span class="o">()</span>
</span><span class="line">      <span class="o">.</span><span class="n">setAppName</span><span class="o">(</span><span class="s">&quot;XAPWordCount&quot;</span><span class="o">)</span>
</span><span class="line">      <span class="o">.</span><span class="n">setMaster</span><span class="o">(</span><span class="n">config</span><span class="o">.</span><span class="n">sparkMaster</span><span class="o">)</span>
</span><span class="line">      <span class="o">.</span><span class="n">setJars</span><span class="o">(</span><span class="nc">Seq</span><span class="o">(</span><span class="n">config</span><span class="o">.</span><span class="n">jarLocation</span><span class="o">))</span>
</span><span class="line">      <span class="o">.</span><span class="n">set</span><span class="o">(</span><span class="nc">XAPUtils</span><span class="o">.</span><span class="nc">SPACE_URL_CONF_KEY</span><span class="o">,</span> <span class="s">&quot;jini://*/*/space&quot;</span><span class="o">)</span>
</span></code></pre></td></tr></table></div></figure></notextile></div>

<p>And then we create a stream by merging two parallel sub-streams:</p>

<div class="bogus-wrapper"><notextile><figure class="code"><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
<span class="line-number">2</span>
<span class="line-number">3</span>
</pre></td><td class="code"><pre><code class="scala"><span class="line"><span class="k">val</span> <span class="n">numStreams</span> <span class="k">=</span> <span class="mi">2</span>
</span><span class="line"><span class="k">val</span> <span class="n">streams</span> <span class="k">=</span> <span class="o">(</span><span class="mi">1</span> <span class="n">to</span> <span class="n">numStreams</span><span class="o">).</span><span class="n">map</span><span class="o">(</span><span class="k">_</span> <span class="k">=&gt;</span> <span class="nc">XAPUtils</span><span class="o">.</span><span class="n">createStream</span><span class="o">(</span><span class="n">context</span><span class="o">,</span> <span class="k">new</span> <span class="nc">Sentence</span><span class="o">(),</span> <span class="mi">50</span><span class="o">,</span> <span class="nc">Milliseconds</span><span class="o">(</span><span class="mi">100</span><span class="o">),</span> <span class="mi">4</span><span class="o">))</span>
</span><span class="line"><span class="k">val</span> <span class="n">stream</span> <span class="k">=</span> <span class="n">context</span><span class="o">.</span><span class="n">union</span><span class="o">(</span><span class="n">streams</span><span class="o">)</span>
</span></code></pre></td></tr></table></div></figure></notextile></div>

<p>Once the stream is created, we can apply any Spark functions like <code>map</code>, <code>filter</code>, <code>reduce</code>, <code>transform</code>, etc.</p>

<p>For instance, to compute a word counter of five-letter words over a sliding window, one can do the following:</p>

<div class="bogus-wrapper"><notextile><figure class="code"><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
<span class="line-number">2</span>
<span class="line-number">3</span>
<span class="line-number">4</span>
<span class="line-number">5</span>
<span class="line-number">6</span>
</pre></td><td class="code"><pre><code class="scala"><span class="line"><span class="k">val</span> <span class="n">words</span> <span class="k">=</span> <span class="n">stream</span><span class="o">.</span><span class="n">flatMap</span><span class="o">(</span><span class="k">_</span><span class="o">.</span><span class="n">getText</span><span class="o">.</span><span class="n">split</span><span class="o">(</span><span class="s">&quot; &quot;</span><span class="o">)).</span><span class="n">filter</span><span class="o">(</span><span class="k">_</span><span class="o">.</span><span class="n">size</span> <span class="o">==</span> <span class="mi">5</span><span class="o">)</span>
</span><span class="line"><span class="k">val</span> <span class="n">wordCountWindow</span> <span class="k">=</span> <span class="n">words</span>
</span><span class="line">      <span class="o">.</span><span class="n">map</span><span class="o">((</span><span class="k">_</span><span class="o">,</span> <span class="mi">1</span><span class="o">))</span>
</span><span class="line">      <span class="o">.</span><span class="n">reduceByKeyAndWindow</span><span class="o">(</span><span class="k">_</span> <span class="o">+</span> <span class="k">_</span><span class="o">,</span> <span class="nc">Seconds</span><span class="o">(</span><span class="mi">5</span><span class="o">))</span>
</span><span class="line">      <span class="o">.</span><span class="n">map</span> <span class="o">{</span> <span class="k">case</span> <span class="o">(</span><span class="n">word</span><span class="o">,</span> <span class="n">count</span><span class="o">)</span> <span class="k">=&gt;</span> <span class="o">(</span><span class="n">count</span><span class="o">,</span> <span class="n">word</span><span class="o">)}</span>
</span><span class="line">      <span class="o">.</span><span class="n">transform</span><span class="o">(</span><span class="k">_</span><span class="o">.</span><span class="n">sortByKey</span><span class="o">(</span><span class="n">ascending</span> <span class="k">=</span> <span class="kc">false</span><span class="o">))</span>
</span></code></pre></td></tr></table></div></figure></notextile></div>

<h2 id="output-spark-computation-results-to-xap">Output Spark computation results to XAP</h2>

<p>Output operations allow the <code>DStream</code>’s data to be pushed out to external systems. Please refer to <a href="https://spark.apache.org/docs/1.1.0/streaming-programming-guide.html#output-operations-on-dstreams">Spark documentation</a> for the details.</p>

<p>To minimize the cost of creating XAP connection for each <code>RDD</code>, we created a connection pool named <code>GigaSpaceFactory</code>. Here is an example how to output <code>RDD</code> to XAP:</p>

<div class="bogus-wrapper"><notextile><figure class="code"><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
<span class="line-number">2</span>
<span class="line-number">3</span>
<span class="line-number">4</span>
<span class="line-number">5</span>
</pre></td><td class="code"><pre><code class="scala"><span class="line"><span class="n">wordCountWindow</span><span class="o">.</span><span class="n">foreachRDD</span><span class="o">(</span><span class="n">rdd</span> <span class="k">=&gt;</span> <span class="o">{</span>
</span><span class="line">      <span class="k">val</span> <span class="n">gigaSpace</span> <span class="k">=</span> <span class="nc">GigaSpaceFactory</span><span class="o">.</span><span class="n">getOrCreate</span><span class="o">(</span><span class="n">config</span><span class="o">.</span><span class="n">spaceUrl</span><span class="o">)</span>
</span><span class="line">      <span class="k">val</span> <span class="n">topList</span> <span class="k">=</span> <span class="n">rdd</span><span class="o">.</span><span class="n">take</span><span class="o">(</span><span class="mi">10</span><span class="o">).</span><span class="n">map</span> <span class="o">{</span> <span class="k">case</span> <span class="o">(</span><span class="n">count</span><span class="o">,</span> <span class="n">word</span><span class="o">)</span> <span class="k">=&gt;</span> <span class="k">new</span> <span class="nc">WordCount</span><span class="o">(</span><span class="n">word</span><span class="o">,</span> <span class="n">count</span><span class="o">)}</span>
</span><span class="line">      <span class="n">gigaSpace</span><span class="o">.</span><span class="n">write</span><span class="o">(</span><span class="k">new</span> <span class="nc">TopWordCounts</span><span class="o">(</span><span class="n">topList</span><span class="o">))</span>
</span><span class="line"><span class="o">})</span>
</span></code></pre></td></tr></table></div></figure></notextile></div>

<blockquote>
  <p>Please, note that in this example a XAP connection is created and data is written from Spark driver. In some cases, one may want to write data from the Spark worker. Please, refer to Spark documentation - it explains different design patterns using <code>foreachRDD</code>.</p>
</blockquote>

<h1 id="word-counter-demo">Word Counter Demo</h1>

<p>As a part of this integration pattern, we demonstrate how to build an application that consumes live stream of text and displays top 10 five-letter words over a sliding window in real-time. The user interface consists of a simple single page web application displaying a table of top 10 words and a word cloud. The data on UI is updated every second.</p>

<p><img src="http://dyagilev.org/images/xap-spark/spark-word-counter.jpg" /></p>

<h2 id="high-level-design">High-level design</h2>

<p>The high-level design diagram of the Word Counter Demo is below:</p>

<p><img src="http://dyagilev.org/images/xap-spark/example.jpg" /></p>

<ol>
  <li>Feeder is a standalone scala application that reads book from text file in a cycle and writes lines to XAP Stream.</li>
  <li>Stream is consumed by the Spark cluster which performs all necessary computing.</li>
  <li>Computation results are stored in the XAP space.</li>
  <li>End user is browsing the web page hosted in a Web PU that continuously updates dashboard with AJAX requests backed by the rest service.</li>
</ol>

<h2 id="installing-and-building-the-demo-application">Installing and building the Demo application</h2>

<ol>
  <li>Download <a href="http://www.gigaspaces.com/LatestProductVersion">XAP</a></li>
  <li><a href="http://docs.gigaspaces.com/xap100/installation.html">Install XAP</a></li>
  <li>Install Maven and the <a href="http://docs.gigaspaces.com/xap100/maven-plugin.html">XAP Maven plug-in</a></li>
  <li>Download the application <a href="https://github.com/fe2s/xap-spark">source code</a></li>
  <li>Build a project by running <code>mvn clean install</code></li>
</ol>

<h2 id="deploying-xap-space-and-web-pu">Deploying XAP Space and Web PU</h2>

<ol>
  <li>Set the XAP lookup group to <code>spark</code> by adding <code>export LOOKUPGROUPS=spark</code> line to <code>&lt;XAP_HOME&gt;/bin/setenv.sh/bat</code></li>
  <li>Start a Grid Service Agent by running the <code>gs-agent.sh/bat</code> script</li>
  <li>Deploy a space by running <code>mvn os:deploy -Dgroups=spark</code> from <code>&lt;project_root&gt;/word-counter-demo</code> directory</li>
</ol>

<h2 id="launch-spark-application">Launch Spark Application</h2>

<h3 id="option-a-run-embedded-spark-cluster">Option A. Run embedded Spark cluster</h3>

<p>This is the simplest option that doesn’t require downloading and installing Spark distributive, which is useful for the development purposes. Spark runs in the embedded mode with as many worker threads as logical cores on your machine.</p>

<ol>
  <li>Navigate to the <code>&lt;project_root&gt;/word-counter-demo/spark/target</code> directory</li>
  <li>Run the following command <code>java -jar spark-wordcounter.jar -s jini://*/*/space?groups=spark -m local[*]</code></li>
</ol>

<h3 id="option-b-run-spark-standalone-mode-cluster">Option B. Run Spark standalone mode cluster</h3>

<p>In this option Spark runs a cluster in the standalone mode (as an alternative to running on a Mesos or YARN cluster managers).</p>

<h4 id="run-spark">Run Spark</h4>

<ol>
  <li>Download Spark (tested with Spark 1.2.1 pre-built with Hadoop 2.4)</li>
  <li>Follow <a href="http://spark.apache.org/docs/1.2.0/spark-standalone.html">instructions</a> to run a master and 2 workers. Here is an example of commands with hostname <code>fe2s</code> (remember to substitute it with yours)</li>
</ol>
<div class="bogus-wrapper"><notextile><figure class="code"><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
<span class="line-number">2</span>
<span class="line-number">3</span>
</pre></td><td class="code"><pre><code class="scala"><span class="line"><span class="o">./</span><span class="n">sbin</span><span class="o">/</span><span class="n">start</span><span class="o">-</span><span class="n">master</span><span class="o">.</span><span class="n">sh</span>
</span><span class="line"><span class="o">./</span><span class="n">bin</span><span class="o">/</span><span class="n">spark</span><span class="o">-</span><span class="k">class</span> <span class="nc">org</span><span class="o">.</span><span class="n">apache</span><span class="o">.</span><span class="n">spark</span><span class="o">.</span><span class="n">deploy</span><span class="o">.</span><span class="n">worker</span><span class="o">.</span><span class="nc">Worker</span> <span class="n">spark</span><span class="o">://</span><span class="n">fe2s</span><span class="k">:</span><span class="err">7077</span>
</span><span class="line"><span class="kt">./bin/spark-class</span> <span class="kt">org.apache.spark.deploy.worker.Worker</span> <span class="kt">spark://fe2s:</span><span class="err">7077</span>
</span></code></pre></td></tr></table></div></figure></notextile></div>

<h4 id="submit-application">Submit application</h4>

<ol>
  <li>Submit an application to Spark (in this example driver runs locally)</li>
  <li>Navigate to the <code>&lt;project_root&gt;/word-counter-demo/spark/target</code> directory</li>
  <li>Run <code>java -jar spark-wordcounter.jar -s jini://*/*/space?groups=spark -m spark://fe2s:7077 -j ./spark-wordcounter.jar</code></li>
  <li>Spark web console should be available at <a href="http://fe2s:8080">http://fe2s:8080</a></li>
</ol>

<h2 id="launch-feeder-application">Launch Feeder application</h2>

<ol>
  <li>Navigate to <code>&lt;project_root&gt;/word-counter-demo/feeder/target</code></li>
  <li>Run <code>java -jar feeder.jar -g spark -n 500</code></li>
</ol>

<p>At this point all components should be up and running. The application is available at <a href="http://localhost:8090/web/">http://localhost:8090/web/</a></p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Slides for my talk on Storm, Kafka and GigaSpaces]]></title>
    <link href="http://dyagilev.org/blog/2014/09/23/slides-for-my-talk-on-storm-kafka-gigaspaces/"/>
    <updated>2014-09-23T18:39:26+03:00</updated>
    <id>http://dyagilev.org/blog/2014/09/23/slides-for-my-talk-on-storm-kafka-gigaspaces</id>
    <content type="html"><![CDATA[<p>Can be found <a href="http://slides.com/oleksiydyagilev/storm">here</a></p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[GigaSpaces and Storm integration]]></title>
    <link href="http://dyagilev.org/blog/2014/08/21/gigaspaces-storm/"/>
    <updated>2014-08-21T17:27:50+03:00</updated>
    <id>http://dyagilev.org/blog/2014/08/21/gigaspaces-storm</id>
    <content type="html"><![CDATA[<p>Real-time processing is becoming very popular, and Storm is a popular open source framework and runtime used by Twitter for processing real-time data streams. Storm addresses the complexity of running real time streams through a compute cluster by providing an elegant set of abstractions that make it easier to reason about your problem domain by letting you focus on data flows rather than on implementation details.</p>

<p>Storm has many use cases: realtime analytics, online machine learning, continuous computation, distributed RPC, ETL, and more. Storm is fast: a benchmark clocked it at over a million tuples processed per second per node. It is scalable, fault-tolerant, guarantees your data will be processed, and is easy to set up and operate.</p>

<p>This pattern integrates XAP with Storm. XAP is used as stream data source and fast reliable persistent storage, whereas Storm is in charge of data processing. We support both pure Storm and Trident framework.</p>

<p>As part of this integration we provide classic <strong>Word Counter</strong> and <strong>Twitter Reach</strong> implementations on top of XAP and Trident.</p>

<p>Also, we demonstrate how to build highly available, scalable equivalent of <strong>Realtime Google Analytics</strong> application with XAP and Storm. Application can be deployed to cloud with one click using Cloudify.</p>

<p>Sources are available on <a href="https://github.com/fe2s/xap-storm">github</a></p>

<!-- more -->

<ul id="markdown-toc">
  <li><a href="#storm-in-a-nutshell" id="markdown-toc-storm-in-a-nutshell">Storm in a Nutshell</a></li>
  <li><a href="#spouts" id="markdown-toc-spouts">Spouts</a>    <ul>
      <li><a href="#storm-spout" id="markdown-toc-storm-spout">Storm Spout</a></li>
      <li><a href="#trident-spout" id="markdown-toc-trident-spout">Trident Spout</a></li>
    </ul>
  </li>
  <li><a href="#trident-state" id="markdown-toc-trident-state">Trident State</a>    <ul>
      <li><a href="#trident-read-only-state" id="markdown-toc-trident-read-only-state">Trident Read-Only state</a></li>
    </ul>
  </li>
  <li><a href="#storm-bolts" id="markdown-toc-storm-bolts">Storm bolts</a></li>
  <li><a href="#illustrative-example-real-time-google-analytics" id="markdown-toc-illustrative-example-real-time-google-analytics">Illustrative example: Real-time Google Analytics</a>    <ul>
      <li><a href="#high-level-architecture-diagram" id="markdown-toc-high-level-architecture-diagram">High-level architecture diagram</a></li>
      <li><a href="#google-analytics-topology-high-level-overview" id="markdown-toc-google-analytics-topology-high-level-overview">Google Analytics Topology. High level overview.</a></li>
      <li><a href="#top-urls-topology-branch" id="markdown-toc-top-urls-topology-branch">Top urls topology branch</a></li>
      <li><a href="#active-users-topology-branch" id="markdown-toc-active-users-topology-branch">Active users topology branch</a></li>
      <li><a href="#page-view-time-series-topology-branch" id="markdown-toc-page-view-time-series-topology-branch">Page view time series topology branch</a></li>
      <li><a href="#geo-topology-branch" id="markdown-toc-geo-topology-branch">Geo topology branch</a></li>
      <li><a href="#building-the-application" id="markdown-toc-building-the-application">Building the Application</a></li>
      <li><a href="#deploying-in-development-environment" id="markdown-toc-deploying-in-development-environment">Deploying in development environment</a></li>
      <li><a href="#deploying-in-development-environment-with-embedded-storm" id="markdown-toc-deploying-in-development-environment-with-embedded-storm">Deploying in development environment with embedded Storm</a></li>
      <li><a href="#deploying-to-cloud" id="markdown-toc-deploying-to-cloud">Deploying to cloud</a></li>
    </ul>
  </li>
</ul>

<h1 id="storm-in-a-nutshell">Storm in a Nutshell</h1>

<p>Storm is a real time, open source data streaming framework that functions entirely in memory.  It constructs a processing graph that feeds data from an input source through processing nodes.  The processing graph is called a “topology”.  The input data sources are called “spouts”, and the processing nodes are called “bolts”.  The data model consists of tuples.  Tuples flow from Spouts to the bolts, which execute user code. Besides simply being locations where data is transformed or accumulated, bolts can also join streams and branch streams.</p>

<p>Storm is designed to be run on several machines to provide parallelism.  Storm topologies are deployed in a manner somewhat similar to a webapp or a XAP processing unit; a jar file is presented to a deployer which distributes it around the cluster where it is loaded and executed.  A topology runs until it is killed.</p>

<p><img src="http://dyagilev.org/images/xap-storm/storm-nutshell.png" /></p>

<p>Beside Storm, there is a <strong>Trident</strong> – a high-level abstraction for doing realtime computing on top of Storm. Trident adds primitives like groupBy, filter, merge, aggregation to simplify common computation routines. Trident has consistent, exactly-once semantics, so it is easy to reason about Trident topologies.</p>

<p>Capability to guarantee exactly-once semantics comes with additional cost. To guarantee that, incremental processing should be done on top of persistence data source. Trident has to ensure that all updates are idempotent. Usually that leads to lower throughput and higher latency than similar topology with pure Storm.</p>

<h1 id="spouts">Spouts</h1>

<p>Basically, Spouts provide the source of tuples for Storm processing.  For spouts to be maximally performant and reliable, they need to provide tuples in batches, and be able to replay failed batches when necessary.  Of course, in order to have batches, you need storage, and to be able to replay batches, you need reliable storage.  XAP is about the highest performing, reliable source of data out there, so a spout that serves tuples from XAP is a natural combination.</p>

<p><img src="http://dyagilev.org/images/xap-storm/xap-general-spout.jpg" /></p>

<p>Depending on domain model and level of guarantees you want to provide, you choose either pure Storm or Trident. We provide Spout implementations for both – <code>XAPSimpleSpout</code> and <code>XAPTranscationalTridentSpout</code> respectively.</p>

<h2 id="storm-spout">Storm Spout</h2>

<p><code>XAPSimpleSpout</code> is a spout implementation for pure Storm that reads data in batches from XAP. On XAP side we introduce conception of stream. Please find <code>SimpleStream</code> – a stream implementation that supports writing data in single and batch modes and reading in batch mode. <code>SimpleStream</code> leverages XAP’s FIFO(First In, First Out) capabilities.</p>

<p><img src="http://dyagilev.org/images/xap-storm/simple-spout.jpg" /></p>

<p><code>SimpleStream</code> works with arbitrary space class that has <code>FifoSupport.OPERATION</code> annotation and implements <code>Serializable</code>.</p>

<p>Here is an example how one may write data to <code>SimpleStream</code> and process it in Storm topology. Let’s consider we would like to build an application to analyze the stream of page views (user clicks) on website. At first, we create a data model that represents a page view</p>

<div class="bogus-wrapper"><notextile><figure class="code"><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
<span class="line-number">2</span>
<span class="line-number">3</span>
<span class="line-number">4</span>
<span class="line-number">5</span>
<span class="line-number">6</span>
<span class="line-number">7</span>
</pre></td><td class="code"><pre><code class="java"><span class="line"><span class="nd">@SpaceClass</span><span class="o">(</span><span class="n">fifoSupport</span> <span class="o">=</span> <span class="n">FifoSupport</span><span class="o">.</span><span class="na">OPERATION</span><span class="o">)</span>
</span><span class="line"><span class="kd">public</span> <span class="kd">class</span> <span class="nc">PageView</span> <span class="kd">implements</span> <span class="n">Serializable</span> <span class="o">{</span>
</span><span class="line">    <span class="kd">private</span> <span class="n">String</span> <span class="n">id</span><span class="o">;</span>
</span><span class="line">    <span class="kd">private</span> <span class="n">String</span> <span class="n">page</span><span class="o">;</span>
</span><span class="line">   <span class="kd">private</span> <span class="n">String</span> <span class="n">sessionId</span><span class="o">;</span>
</span><span class="line">   <span class="o">[</span><span class="n">getters</span> <span class="n">setters</span> <span class="n">omitted</span> <span class="k">for</span> <span class="n">brevity</span><span class="o">]</span>
</span><span class="line"><span class="o">}</span>
</span></code></pre></td></tr></table></div></figure></notextile></div>

<p>Now we would like to create a reference to stream instance and write some data.</p>

<div class="bogus-wrapper"><notextile><figure class="code"><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
<span class="line-number">2</span>
</pre></td><td class="code"><pre><code class="java"><span class="line"><span class="n">SimpleStream</span><span class="o">&lt;</span><span class="n">PageView</span><span class="o">&gt;</span> <span class="n">stream</span> <span class="o">=</span> <span class="k">new</span> <span class="n">SimpleStream</span><span class="o">&lt;&gt;(</span><span class="n">space</span><span class="o">,</span> <span class="k">new</span> <span class="nf">PageView</span><span class="o">());</span>
</span><span class="line"><span class="n">stream</span><span class="o">.</span><span class="na">writeBatch</span><span class="o">(</span><span class="n">pageViews</span><span class="o">);</span>
</span></code></pre></td></tr></table></div></figure></notextile></div>

<p>The second argument of <code>SimpleStream</code> is a template used to match objects during reading. 
If you want to have several streams with the same type, template objects should differentiate your streams.</p>

<p>Now let’s create a spout for <code>PageView</code> stream.</p>

<div class="bogus-wrapper"><notextile><figure class="code"><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
<span class="line-number">2</span>
<span class="line-number">3</span>
<span class="line-number">4</span>
<span class="line-number">5</span>
</pre></td><td class="code"><pre><code class="java"><span class="line"><span class="kd">public</span> <span class="kd">class</span> <span class="nc">PageViewSpout</span> <span class="kd">extends</span> <span class="n">XAPSimpleSpout</span><span class="o">&lt;</span><span class="n">PageView</span><span class="o">&gt;</span> <span class="o">{</span>
</span><span class="line">    <span class="kd">public</span> <span class="nf">PageViewSpout</span><span class="o">()</span> <span class="o">{</span>
</span><span class="line">        <span class="kd">super</span><span class="o">(</span><span class="k">new</span> <span class="nf">PageViewTupleConverter</span><span class="o">(),</span> <span class="k">new</span> <span class="nf">PageView</span><span class="o">());</span>
</span><span class="line">    <span class="o">}</span>
</span><span class="line"><span class="o">}</span>
</span></code></pre></td></tr></table></div></figure></notextile></div>

<p>To create a spout, we have to specify how we want our space class be converted to Storm tuple. That is exactly what <code>TupleConverter</code> knows about.</p>

<div class="bogus-wrapper"><notextile><figure class="code"><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
<span class="line-number">2</span>
<span class="line-number">3</span>
<span class="line-number">4</span>
<span class="line-number">5</span>
<span class="line-number">6</span>
<span class="line-number">7</span>
<span class="line-number">8</span>
<span class="line-number">9</span>
<span class="line-number">10</span>
<span class="line-number">11</span>
</pre></td><td class="code"><pre><code class="java"><span class="line"><span class="kd">class</span> <span class="nc">PageViewTupleConverter</span> <span class="kd">implements</span> <span class="n">TupleConverter</span><span class="o">&lt;</span><span class="n">PageView</span><span class="o">&gt;</span> <span class="o">{</span>
</span><span class="line">    <span class="nd">@Override</span>
</span><span class="line">    <span class="kd">public</span> <span class="n">Fields</span> <span class="nf">tupleFields</span><span class="o">()</span> <span class="o">{</span>
</span><span class="line">        <span class="k">return</span> <span class="k">new</span> <span class="nf">Fields</span><span class="o">(</span><span class="s">&quot;page&quot;</span><span class="o">,</span> <span class="s">&quot;session&quot;</span><span class="o">);</span>
</span><span class="line">    <span class="o">}</span>
</span><span class="line">
</span><span class="line">    <span class="nd">@Override</span>
</span><span class="line">    <span class="kd">public</span> <span class="n">List</span><span class="o">&lt;</span><span class="n">Object</span><span class="o">&gt;</span> <span class="nf">spaceObjectToTuple</span><span class="o">(</span><span class="n">PageView</span> <span class="n">pageView</span><span class="o">)</span> <span class="o">{</span>
</span><span class="line">        <span class="k">return</span> <span class="n">Arrays</span><span class="o">.&lt;</span><span class="n">Object</span><span class="o">&gt;</span><span class="n">asList</span><span class="o">(</span><span class="n">pageView</span><span class="o">.</span><span class="na">getPage</span><span class="o">(),</span> <span class="n">pageView</span><span class="o">.</span><span class="na">getSessionId</span><span class="o">());</span>
</span><span class="line">    <span class="o">}</span>
</span><span class="line"><span class="o">}</span>
</span></code></pre></td></tr></table></div></figure></notextile></div>

<p>At this point we have everything ready to build Storm topology with <code>PageViewSpout</code>.</p>

<div class="bogus-wrapper"><notextile><figure class="code"><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
<span class="line-number">2</span>
<span class="line-number">3</span>
<span class="line-number">4</span>
<span class="line-number">5</span>
</pre></td><td class="code"><pre><code class="java"><span class="line"><span class="n">Config</span> <span class="n">conf</span> <span class="o">=</span> <span class="k">new</span> <span class="nf">Config</span><span class="o">();</span>
</span><span class="line"><span class="n">conf</span><span class="o">.</span><span class="na">put</span><span class="o">(</span><span class="n">ConfigConstants</span><span class="o">.</span><span class="na">XAP_SPACE_URL_KEY</span><span class="o">,</span> <span class="s">&quot;jini://*/*/space&quot;</span><span class="o">);</span>
</span><span class="line"><span class="n">conf</span><span class="o">.</span><span class="na">put</span><span class="o">(</span><span class="n">ConfigConstants</span><span class="o">.</span> <span class="n">XAP_STREAM_BATCH_SIZE</span><span class="o">,</span> <span class="mi">300</span><span class="o">);</span>
</span><span class="line"><span class="n">TopologyBuilder</span> <span class="n">builder</span> <span class="o">=</span> <span class="k">new</span> <span class="nf">TopologyBuilder</span><span class="o">();</span>
</span><span class="line"><span class="n">builder</span><span class="o">.</span><span class="na">setSpout</span><span class="o">(</span><span class="s">&quot;pageViewSpout&quot;</span><span class="o">,</span> <span class="k">new</span> <span class="nf">PageViewSpout</span><span class="o">());</span>
</span></code></pre></td></tr></table></div></figure></notextile></div>

<p><code>ConfigConstants.XAP_SPACE_URL_KEY</code> is a space URL</p>

<p><code>ConfigConstants. XAP_STREAM_BATCH_SIZE</code> is a maximum number of items that spout reads from XAP with one hit.</p>

<h2 id="trident-spout">Trident Spout</h2>

<p><code>XAPTranscationalTridentSpout</code> is a scalable, fault-tolerant, transactional spout for Trident, supports pipelining. Let’s discuss all its properties in details.</p>

<p>For spout to be maximally performant, we want an ability to scale the number of instances to control the parallelism of reader threads.</p>

<p>There are several spout APIs available that we could potentially use for our XAPTranscationalTridentSpout implementation:</p>

<ul>
  <li><code>IPartitionedTridentSpout</code>: A transactional spout that reads from a partitioned data source. The problem with this API is that it doesn’t acknowledge when batch is successfully processed which is critical for in memory solutions since we want to remove items from the grid as soon as they have been processed. Another option would be to use XAP’s lease capability to remove items by time out. This might be unsafe, if we keep items too long, we might consume all available memory.</li>
  <li><code>ITridentSpout</code>: The most general API. Setting parallelism hint for this spout to N will create N spout instances, single coordinator and N emitters. When coordinator issues new transaction id, it passes this id to all emitters. Emitter reads its portion of transaction by given transaction id. Merged data from all emitters forms transaction.</li>
</ul>

<p>For our implementation we choose <code>ITridentSpout</code> API.</p>

<p><img src="http://dyagilev.org/images/xap-storm/trident-spout.jpg" /></p>

<p>There is one to one mapping between XAP partitions and emitters.</p>

<p>Storm framework guarantees that topology is high available, if some component fails, it restarts it. That means our spout implementation should be stateless or able to recover its state after failure.</p>

<p>When emitter is created, it calls remote service <code>ConsumerRegistryService</code> to register itself. <code>ConsumerRegistryService</code> knows the number of XAP partitions and keeps track of the last allocated partition.  This information is reliably stored in the space, see <code>ConsumerRegistry.java</code>.</p>

<p><img src="http://dyagilev.org/images/xap-storm/consumer-registry.jpg" /></p>

<p>Remember that parallelism hint for <code>XAPTranscationalTridentSpout</code> should equal to the number of XAP partitions.</p>

<p>The property of being transactional is defined in Trident as following:
- batches for a given txid are always the same. Replays of batches for a txid will exact same set of tuples as the first time that batch was emitted for that txid.
- there’s no overlap between batches of tuples (tuples are in one batch or another, never multiple).
- every tuple is in a batch (no tuples are skipped)</p>

<p><code>XAPTranscationalTridentSpout</code> works with <code>PartitionedStream</code> that wraps stream elements into Item class and keeps items ordered by ‘offset’ property. There is one <code>PartitionStream</code> instance per XAP partition.</p>

<p><img src="http://dyagilev.org/images/xap-storm/partitioned-stream.jpg" /></p>

<p>Stream’s <code>WriterHead</code> holds the last offset in the stream.  Any time batch of elements (or single element) written to stream, <code>WriterHead</code> incremented by the number of elements. Allocated numbers used to populate offset property of Items. <code>WriterHead</code> object is kept in heap, there is no need to keep it in space. If primary partition fails, <code>WriterHead</code> is reinitialized to be the max offset value for given stream.</p>

<p><code>ReaderHead</code> points to the last read item. We have to keep this value in the space, otherwise if partition fails we won’t be able to infer this value.</p>

<p>When spout request new batch, we take <code>ReaderHead</code>, read data from that point and update <code>ReaderHead</code>. New <code>BatchMetadata</code> object is placed to the space, it keeps start offset and number of items in the batch. In case Storm requests transaction replaying, we are able to reread exactly the same items by given batchId. Finally, once Storm acknowledges that batch successfully processed, we delete <code>BatchMetadata</code> and corresponding items from the space.</p>

<p>By default, Trident processes a single batch at a time, waiting for the batch to succeed or fail before trying another batch. We can get significantly higher throughput  and lower latency of processing of each batch – by pipelining the batches. You configure the maximum amount of batches to be processed simultaneously with the “topology.max.spout.pending” property.</p>

<p>Operations with <code>PartitionedStream</code> are encapsulated in remote service – <code>PartitionedStreamService</code>.</p>

<p>Here is an example how to use <code>XAPTransactionalTridentSpout</code>:</p>

<div class="bogus-wrapper"><notextile><figure class="code"><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
<span class="line-number">2</span>
<span class="line-number">3</span>
<span class="line-number">4</span>
<span class="line-number">5</span>
<span class="line-number">6</span>
<span class="line-number">7</span>
<span class="line-number">8</span>
<span class="line-number">9</span>
</pre></td><td class="code"><pre><code class="java"><span class="line"><span class="n">Config</span> <span class="n">conf</span> <span class="o">=</span> <span class="k">new</span> <span class="nf">Config</span><span class="o">();</span>
</span><span class="line"><span class="n">conf</span><span class="o">.</span><span class="na">setMaxSpoutPending</span><span class="o">(</span><span class="mi">20</span><span class="o">);</span>
</span><span class="line"><span class="n">conf</span><span class="o">.</span><span class="na">put</span><span class="o">(</span><span class="n">ConfigConstants</span><span class="o">.</span><span class="na">XAP_SPACE_URL_KEY</span><span class="o">,</span> <span class="s">&quot;jini://*/*/space?groups=XAPTransactionalTridentSpoutTest-test&quot;</span><span class="o">);</span>
</span><span class="line"><span class="n">TridentTopology</span> <span class="n">topology</span> <span class="o">=</span> <span class="k">new</span> <span class="nf">TridentTopology</span><span class="o">();</span>
</span><span class="line"><span class="n">TridentState</span> <span class="n">wordCounts</span> <span class="o">=</span> <span class="n">topology</span>
</span><span class="line">                <span class="o">.</span><span class="na">newStream</span><span class="o">(</span><span class="s">&quot;spout1&quot;</span><span class="o">,</span> <span class="n">spout</span><span class="o">)</span>
</span><span class="line">                <span class="o">.</span><span class="na">each</span><span class="o">(</span><span class="k">new</span> <span class="nf">Fields</span><span class="o">(</span><span class="s">&quot;sentence&quot;</span><span class="o">),</span> <span class="k">new</span> <span class="nf">Split</span><span class="o">(),</span> <span class="k">new</span> <span class="nf">Fields</span><span class="o">(</span><span class="s">&quot;word&quot;</span><span class="o">))</span>
</span><span class="line">                <span class="o">.</span><span class="na">groupBy</span><span class="o">(</span><span class="k">new</span> <span class="nf">Fields</span><span class="o">(</span><span class="s">&quot;word&quot;</span><span class="o">))</span>
</span><span class="line">                <span class="o">.</span><span class="na">persistentAggregate</span><span class="o">(</span><span class="n">stateFactory</span><span class="o">,</span> <span class="k">new</span> <span class="nf">Count</span><span class="o">(),</span> <span class="k">new</span> <span class="nf">Fields</span><span class="o">(</span><span class="s">&quot;count&quot;</span><span class="o">)).</span><span class="na">parallelismHint</span><span class="o">(</span><span class="mi">16</span><span class="o">);</span>
</span></code></pre></td></tr></table></div></figure></notextile></div>

<p>The full example that demonstrates usage of <code>XAPTransactionalTridentSpout</code> to address classic <em>Word Counter</em> problem can be found in <code>XAPTransactionalTridentSpoutTest</code>.</p>

<h1 id="trident-state">Trident State</h1>

<p>Trident has first-class abstractions for reading from and writing to stateful sources. Details are available on the <a href="https://storm.incubator.apache.org/documentation/Trident-state">Storm wiki site</a>.</p>

<p>In Trident topology that is persisting state via this mechanism, the overall throughput is almost certainly constrained by the performance of the state persistence.  This is a good place where XAP can step in and provide extremely high performance persistence for stream processing state.</p>

<p><img src="http://dyagilev.org/images/xap-storm/trident-state.jpg" /></p>

<p>XAP Trident state implementation supports all state types – non-transactional, transactional and opaque.  All you need to create a Trident state is configure space url and choose appropriate factory method of <code>XAPStateFactory</code> class:</p>

<div class="bogus-wrapper"><notextile><figure class="code"><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
<span class="line-number">2</span>
<span class="line-number">3</span>
<span class="line-number">4</span>
<span class="line-number">5</span>
<span class="line-number">6</span>
<span class="line-number">7</span>
<span class="line-number">8</span>
<span class="line-number">9</span>
</pre></td><td class="code"><pre><code class="java"><span class="line"><span class="n">Config</span> <span class="n">conf</span> <span class="o">=</span> <span class="k">new</span> <span class="nf">Config</span><span class="o">();</span>
</span><span class="line"><span class="n">conf</span><span class="o">.</span><span class="na">setMaxSpoutPending</span><span class="o">(</span><span class="mi">20</span><span class="o">);</span>
</span><span class="line"><span class="n">conf</span><span class="o">.</span><span class="na">put</span><span class="o">(</span><span class="n">ConfigConstants</span><span class="o">.</span><span class="na">XAP_SPACE_URL_KEY</span><span class="o">,</span> <span class="s">&quot;jini://*/*/space &quot;</span><span class="o">);</span>
</span><span class="line"><span class="n">StateFactory</span> <span class="n">stateFactory</span> <span class="o">=</span> <span class="n">XAPStateFactory</span><span class="o">.</span><span class="na">transactional</span><span class="o">();</span>
</span><span class="line"><span class="n">TridentState</span> <span class="n">wordCounts</span> <span class="o">=</span> <span class="n">topology</span>
</span><span class="line">                <span class="o">.</span><span class="na">newStream</span><span class="o">(</span><span class="s">&quot;spout1&quot;</span><span class="o">,</span> <span class="n">spout</span><span class="o">).</span><span class="na">parallelismHint</span><span class="o">(</span><span class="mi">16</span><span class="o">)</span>
</span><span class="line">                <span class="o">.</span><span class="na">each</span><span class="o">(</span><span class="k">new</span> <span class="nf">Fields</span><span class="o">(</span><span class="s">&quot;sentence&quot;</span><span class="o">),</span> <span class="k">new</span> <span class="nf">Split</span><span class="o">(),</span> <span class="k">new</span> <span class="nf">Fields</span><span class="o">(</span><span class="s">&quot;word&quot;</span><span class="o">))</span>
</span><span class="line">                <span class="o">.</span><span class="na">groupBy</span><span class="o">(</span><span class="k">new</span> <span class="nf">Fields</span><span class="o">(</span><span class="s">&quot;word&quot;</span><span class="o">))</span>
</span><span class="line">                <span class="o">.</span><span class="na">persistentAggregate</span><span class="o">(</span><span class="n">stateFactory</span><span class="o">,</span> <span class="k">new</span> <span class="nf">Count</span><span class="o">(),</span> <span class="k">new</span> <span class="nf">Fields</span><span class="o">(</span><span class="s">&quot;count&quot;</span><span class="o">)).</span><span class="na">parallelismHint</span><span class="o">(</span><span class="mi">16</span><span class="o">);</span>
</span></code></pre></td></tr></table></div></figure></notextile></div>

<p>The full example can be found in <code>TridentWordCountTest</code>.</p>

<h2 id="trident-read-only-state">Trident Read-Only state</h2>

<p>Trident Read-Only state allows to lookup persistent data during the computation.</p>

<p>Consider <em>Twitter Reach</em> example. Reach is the number of unique people exposed to a URL on Twitter. To compute reach, you need to fetch all the people who ever tweeted a URL, fetch all the followers of all those people, unique that set of followers, and that count that uniqued set.</p>

<p>XAP is a good candidate to store reference data such as tweeted url and followers. You can easily create XAP read-only state with <code>XAPReadOnlyStateFactory</code>. The following example demonstrates how to create a read-only state for <code>TweeterUrl</code> and <code>Followers</code> classes. The input arguments that Trident pass to <code>stateQuery()</code> are used as space ids.</p>

<p>The full example can be found in <code>TridentReachTest</code>.</p>

<div class="bogus-wrapper"><notextile><figure class="code"><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
<span class="line-number">2</span>
<span class="line-number">3</span>
<span class="line-number">4</span>
<span class="line-number">5</span>
<span class="line-number">6</span>
<span class="line-number">7</span>
<span class="line-number">8</span>
<span class="line-number">9</span>
<span class="line-number">10</span>
<span class="line-number">11</span>
</pre></td><td class="code"><pre><code class="java"><span class="line"><span class="n">TridentState</span> <span class="n">tweetedUrls</span> <span class="o">=</span> <span class="n">topology</span><span class="o">.</span><span class="na">newStaticState</span><span class="o">(</span><span class="n">XAPReadOnlyStateFactory</span><span class="o">.</span><span class="na">byIds</span><span class="o">(</span><span class="n">TweetedUrl</span><span class="o">.</span><span class="na">class</span><span class="o">));</span>
</span><span class="line"><span class="n">TridentState</span> <span class="n">followers</span> <span class="o">=</span> <span class="n">topology</span><span class="o">.</span><span class="na">newStaticState</span><span class="o">(</span><span class="n">XAPReadOnlyStateFactory</span><span class="o">.</span><span class="na">byIds</span><span class="o">(</span><span class="n">Followers</span><span class="o">.</span><span class="na">class</span><span class="o">));</span>
</span><span class="line">        <span class="n">topology</span><span class="o">.</span><span class="na">newDRPCStream</span><span class="o">(</span><span class="s">&quot;reach&quot;</span><span class="o">,</span> <span class="n">drpc</span><span class="o">)</span>
</span><span class="line">                <span class="o">.</span><span class="na">stateQuery</span><span class="o">(</span><span class="n">tweetedUrls</span><span class="o">,</span> <span class="k">new</span> <span class="nf">Fields</span><span class="o">(</span><span class="s">&quot;args&quot;</span><span class="o">),</span> <span class="k">new</span> <span class="nf">MapGet</span><span class="o">(),</span> <span class="k">new</span> <span class="nf">Fields</span><span class="o">(</span><span class="s">&quot;tweetedUrls&quot;</span><span class="o">))</span>
</span><span class="line">                <span class="o">.</span><span class="na">each</span><span class="o">(</span><span class="k">new</span> <span class="nf">Fields</span><span class="o">(</span><span class="s">&quot;tweetedUrls&quot;</span><span class="o">),</span> <span class="k">new</span> <span class="nf">ExpandTweetersList</span><span class="o">(),</span> <span class="k">new</span> <span class="nf">Fields</span><span class="o">(</span><span class="s">&quot;tweeter&quot;</span><span class="o">))</span>
</span><span class="line">                <span class="o">.</span><span class="na">shuffle</span><span class="o">()</span>
</span><span class="line">                <span class="o">.</span><span class="na">stateQuery</span><span class="o">(</span><span class="n">followers</span><span class="o">,</span> <span class="k">new</span> <span class="nf">Fields</span><span class="o">(</span><span class="s">&quot;tweeter&quot;</span><span class="o">),</span> <span class="k">new</span> <span class="nf">MapGet</span><span class="o">(),</span> <span class="k">new</span> <span class="nf">Fields</span><span class="o">(</span><span class="s">&quot;followers&quot;</span><span class="o">))</span>
</span><span class="line">                <span class="o">.</span><span class="na">each</span><span class="o">(</span><span class="k">new</span> <span class="nf">Fields</span><span class="o">(</span><span class="s">&quot;followers&quot;</span><span class="o">),</span> <span class="k">new</span> <span class="nf">ExpandFollowersList</span><span class="o">(),</span> <span class="k">new</span> <span class="nf">Fields</span><span class="o">(</span><span class="s">&quot;follower&quot;</span><span class="o">))</span>
</span><span class="line">                <span class="o">.</span><span class="na">groupBy</span><span class="o">(</span><span class="k">new</span> <span class="nf">Fields</span><span class="o">(</span><span class="s">&quot;follower&quot;</span><span class="o">))</span>
</span><span class="line">                <span class="o">.</span><span class="na">aggregate</span><span class="o">(</span><span class="k">new</span> <span class="nf">One</span><span class="o">(),</span> <span class="k">new</span> <span class="nf">Fields</span><span class="o">(</span><span class="s">&quot;one&quot;</span><span class="o">))</span>
</span><span class="line">                <span class="o">.</span><span class="na">aggregate</span><span class="o">(</span><span class="k">new</span> <span class="nf">Fields</span><span class="o">(</span><span class="s">&quot;one&quot;</span><span class="o">),</span> <span class="k">new</span> <span class="nf">Sum</span><span class="o">(),</span> <span class="k">new</span> <span class="nf">Fields</span><span class="o">(</span><span class="s">&quot;reach&quot;</span><span class="o">));</span>
</span></code></pre></td></tr></table></div></figure></notextile></div>

<p>Another option to create XAP read-only state is to use SQL query. In this case <code>stateQuery’s</code> input arguments are used as SQL parameters:</p>

<div class="bogus-wrapper"><notextile><figure class="code"><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
<span class="line-number">2</span>
</pre></td><td class="code"><pre><code class="java"><span class="line"><span class="n">SQLQuery</span><span class="o">&lt;</span><span class="n">Person</span><span class="o">&gt;</span> <span class="n">sqlQuery</span> <span class="o">=</span> <span class="k">new</span> <span class="n">SQLQuery</span><span class="o">&lt;&gt;(</span><span class="n">Person</span><span class="o">.</span><span class="na">class</span><span class="o">,</span> <span class="s">&quot;name = ? AND age &gt; 30&quot;</span><span class="o">).</span><span class="na">setProjections</span><span class="o">(</span><span class="s">&quot;age&quot;</span><span class="o">);</span>
</span><span class="line"><span class="n">TridentState</span> <span class="n">state</span> <span class="o">=</span> <span class="n">topology</span><span class="o">.</span><span class="na">newStaticState</span><span class="o">(</span><span class="n">XAPReadOnlyStateFactory</span><span class="o">.</span><span class="na">bySqlQuery</span><span class="o">(</span><span class="n">sqlQuery</span><span class="o">));</span>
</span></code></pre></td></tr></table></div></figure></notextile></div>

<p>The full example can be found in <code>SqlQueryReadOnlyStateTest</code>.</p>

<h1 id="storm-bolts">Storm bolts</h1>

<p>If pure Storm suits better your needs, most likely you will want to read/write data from bolts to persistent storage. For instance, imagine you are processing stream of data and would like to present computation result on UI. So the final bolt in your topology pipeline should write result to XAP which can then be accessed from anywhere. For this purpose we created <code>XAPAwareRichBolt</code> and <code>XAPAwareBasicBolt</code> that have a reference to space proxy. All you need is to configure space url and extend XAP aware bolt.</p>

<p>Example:</p>

<div class="bogus-wrapper"><notextile><figure class="code"><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
<span class="line-number">2</span>
<span class="line-number">3</span>
<span class="line-number">4</span>
<span class="line-number">5</span>
<span class="line-number">6</span>
<span class="line-number">7</span>
</pre></td><td class="code"><pre><code class="java"><span class="line"><span class="kd">public</span> <span class="kd">class</span> <span class="nc">TotalActiveUsersBolt</span> <span class="kd">extends</span> <span class="n">XAPAwareBasicBolt</span> <span class="o">{</span>
</span><span class="line">   <span class="kd">public</span> <span class="kt">void</span> <span class="nf">execute</span><span class="o">(</span><span class="n">Tuple</span> <span class="n">tuple</span><span class="o">,</span> <span class="n">BasicOutputCollector</span> <span class="n">collector</span><span class="o">)</span> <span class="o">{</span>
</span><span class="line">        <span class="n">ActiveUsersReport</span> <span class="n">report</span> <span class="o">=</span> <span class="err">…</span> <span class="o">;</span> <span class="c1">// compute     </span>
</span><span class="line">        <span class="n">space</span><span class="o">.</span><span class="na">write</span><span class="o">(</span><span class="n">report</span><span class="o">);</span>
</span><span class="line">   <span class="o">}</span>
</span><span class="line">  <span class="err">…</span><span class="o">.</span>
</span><span class="line"><span class="o">}</span>
</span></code></pre></td></tr></table></div></figure></notextile></div>

<h1 id="illustrative-example-real-time-google-analytics">Illustrative example: Real-time Google Analytics</h1>

<p>In this section we demonstrate how to build highly available, scalable equivalent of <em>Real-time Google Analytics</em> application and deploy it to cloud with one click using <em>Cloudify</em>.</p>

<p>Real-Time Google Analytics allows you to monitor activity as it happens on your site. The reports are updated continuously and each page view is reported seconds after it occurs on your site. For example, you can see:</p>

<ul>
  <li>how many people are on your site right now</li>
  <li>dynamic of page views during last minute</li>
  <li>users geographic locations</li>
  <li>traffic sources that referred them</li>
  <li>which pages or events they’re interacting with</li>
</ul>

<p><img src="http://dyagilev.org/images/xap-storm/storm-screen-with-browser.jpg" /></p>

<h2 id="high-level-architecture-diagram">High-level architecture diagram</h2>

<p><img src="http://dyagilev.org/images/xap-storm/google-analytics-high-level.jpg" /></p>

<p><em>PageView feeder</em> is a standalone java application that simulates users on the site. It continuously sends <code>PageView</code> json to rest service endpoints deployed in XAP web PU. PageView looks like this</p>

<div class="bogus-wrapper"><notextile><figure class="code"><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
<span class="line-number">2</span>
<span class="line-number">3</span>
<span class="line-number">4</span>
<span class="line-number">5</span>
<span class="line-number">6</span>
</pre></td><td class="code"><pre><code class="json"><span class="line"><span class="p">{</span>
</span><span class="line"> <span class="err">“sessionId”:</span>  <span class="err">“sessionid581239234”,</span>
</span><span class="line"><span class="err">“referral”:</span> <span class="err">“https://www.google.com/#q=gigaspace”,</span>
</span><span class="line"><span class="err">“page”:</span> <span class="err">“http://www.gigaspaces.com/about”,</span>
</span><span class="line"><span class="err">“ip”:</span> <span class="err">“89.162.139.2”</span>
</span><span class="line"><span class="p">}</span>
</span></code></pre></td></tr></table></div></figure></notextile></div>

<p>Rest service converts JSON documents to space object and writes them to the stream. Stream is consumed by Storm topology which performs all necessary processing in memory and stores results in XAP space. End user is browsing web page hosted in Web PU that continuously updates reports with AJAX requests backed by another rest service. Rest service reads report from XAP space.</p>

<p>We use pure Storm to build topology. There are several reasons why we don’t use Trident for this application. We are tolerant to page views loss if some Storm node fails. We don’t need exactly-once processing semantic. Instead, we want to maximize throughput and minimize latency.</p>

<h2 id="google-analytics-topology-high-level-overview">Google Analytics Topology. High level overview.</h2>

<p><img src="http://dyagilev.org/images/xap-storm/google-analytics-topology.jpg" /></p>

<p>PageView spout forks five branches, each branch calculates its report and can be scaled independently. The final bolt in the branch writes data to XAP space.  In the next sections we take a closer look at branches design.</p>

<h2 id="top-urls-topology-branch">Top urls topology branch</h2>

<p>Top urls report displays top 10 visited urls for the last ten seconds. Topology implements distributed rolling count algorithm. The report is updated every second.</p>

<p><img src="http://dyagilev.org/images/xap-storm/top-urls.jpg" /></p>

<p>Tuples flow from spout to <code>UrlRollingCountBolt</code> grouped by ‘url’. <code>UrlRollingCountBolt</code> calculates rolling count with sliding windows of 10 seconds for every url. Sliding windows is basically a cyclic buffer with a head pointing to current slot. When bolt receives new tuple, it finds a sliding window for this tuple and increments the number in current slot. Every two seconds <code>UrlRollingCountBolt</code> emits the sum of sliding window for every url, then sliding windows advance and head points to the next slot.</p>

<p>The url and its rolling count flow to <code>IntermediateRankingsBolt</code> which maintains pair of (url, count) in sorted by count order and emits its top 10 urls to the final stage. <code>TotalUrlRankingBolt</code> calculates the global top 10 urls and writes report object to XAP space. The primitives to implement rolling count algorithm can be found in <a href="https://github.com/apache/incubator-storm/tree/master/examples/storm-starter">storm-starter</a> project.</p>

<p>Top referrals topology branch is identical to top urls one. The only difference in is that we calculate ‘referral’ rather than ‘url’ tuple field.</p>

<h2 id="active-users-topology-branch">Active users topology branch</h2>

<p>Active users report displays how many people on the site right now. We assume that if user hasn’t opened any page for the last N seconds, then user has left the site. Users are uniquely identified by ‘sessionId’ tuple field. For demo purpose N is configured to 5 seconds, though it should be much longer in real life application.</p>

<p><img src="http://dyagilev.org/images/xap-storm/active-users.jpg" /></p>

<p>Tuples flow from spout to <code>PartitionedActiveUsersBolt</code> grouped by ‘sessionId’. For every sessionId  <code>PartitionedActiveUsersBolt</code>  keeps track of the last seen time. Every second it removes sessions seen last time earlier than N seconds before and then emits the number of remaining ones.</p>

<p><code>TotalActiveUsersBolt</code> maintains a map of [source_task, count] and emits the total count for all sources. Report is written to XAP.</p>

<h2 id="page-view-time-series-topology-branch">Page view time series topology branch</h2>

<p>Page view time series report displays the dynamic of visited pages for last minute. The chart is updated every second.</p>

<p><img src="http://dyagilev.org/images/xap-storm/page-views.jpg" /></p>

<p><code>PageViewCountBolt</code> calculates the number of page views and passes local count to <code>PageViewTimeSeriesBolt</code> every second. <code>PageViewTimeSeriesBolt</code> maintains a sliding window counter and writes report to XAP space.</p>

<h2 id="geo-topology-branch">Geo topology branch</h2>

<p>Geo report displays a map of users’ geographical location. Depending on the volume of traffic from particular country, country is filled with different colors on the map.</p>

<p><img src="http://dyagilev.org/images/xap-storm/geo.jpg" /></p>

<p>IP address converted to country using <a href="http://dev.maxmind.com/">MaxMind GeoIP database</a>. The database is a binary file loaded into <code>GeoIPBolt’s</code> heap. <code>GeoIpLookupService</code> ensures that it’s loaded only once per JVM.</p>

<h2 id="building-the-application">Building the Application</h2>

<ol>
  <li><a href="http://www.gigaspaces.com/LatestProductVersion">Download</a> and <a href="http://wiki.gigaspaces.com/wiki/display/XAP95/Installing+GigaSpaces">install</a> XAP</li>
  <li>Install Maven and the GigaSpaces Maven plug-in</li>
  <li>The application source can be found on <a href="https://github.com/fe2s/xap-storm">github</a></li>
  <li>Build the project by running <code>mvn clean install</code></li>
</ol>

<h2 id="deploying-in-development-environment">Deploying in development environment</h2>

<ol>
  <li>Follow this <a href="https://storm.incubator.apache.org/documentation/Setting-up-a-Storm-cluster.html">documentation</a> to install and run Zookeeper, Nimbus, Supervisor and optionally Storm UI.</li>
  <li>Start a <a href="http://wiki.gigaspaces.com/wiki/display/XAP95/The+Grid+Service+Agent">Grid Service Agent</a> by running the <code>gs-agent.sh/bat</code> script</li>
  <li>Deploy space and Web PU by running the following from project root folder:</li>
</ol>
<div class="bogus-wrapper"><notextile><figure class="code"><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
<span class="line-number">2</span>
</pre></td><td class="code"><pre><code class="json"><span class="line"><span class="err">cd</span> <span class="err">google-analytics</span>
</span><span class="line"><span class="err">mvn</span> <span class="err">os:deploy</span>
</span></code></pre></td></tr></table></div></figure></notextile></div>
<ol>
  <li>Add <code>apache-storm-0.9.2-incubating/bin</code> to your <code>$PATH</code></li>
  <li>Run the following to deploy topology to Storm cluster
<code>storm jar ./storm-topology/target/storm-topology-1.0-SNAPSHOT.jar com.gigaspaces.storm.googleanalytics.topology.GoogleAnalyticsTopology google-analytics 127.0.0.1</code></li>
  <li>Run feeder
<code>java -jar ./feeder/target/feeder-1.0-SNAPSHOT.jar 127.0.0.1</code></li>
  <li>Open browser <a href="http://localhost:8090/web/">http://localhost:8090/web/</a> to view Google Analytics UI</li>
  <li>To undeploy topology run <code>storm kill google-analytics</code></li>
</ol>

<h2 id="deploying-in-development-environment-with-embedded-storm">Deploying in development environment with embedded Storm</h2>

<ol>
  <li>To run topology in embedded Storm you don’t need to install Zookeeper and Storm. Follow all steps from previous section except deployment to Strom.</li>
  <li>Open <code>google-analytics/storm-topology/pom.xml</code> and change scope of storm-core artifact from ‘provided’ to ‘compile’.</li>
  <li>Rebuild the project</li>
  <li>Run storm topology <code>java -jar ./storm-topology/target/storm-topology-1.0-SNAPSHOT.jar</code>. Alternatively you can <code>GoogleAnalyticsTopology</code> from your IDE.</li>
</ol>

<h2 id="deploying-to-cloud">Deploying to cloud</h2>

<p><em>Please note, recipes tested with Centos 6 only</em></p>

<ol>
  <li>Install <a href="http://getcloudify.org/">Cloudify 2.7</a></li>
  <li>Make sure that <code>&lt;project_root&gt;/cloudify/apps/storm-demo/deployer/files</code> contains up-to-date version of <code>space-1.0.-SNAPSHOT.jar</code>, <code>web.war</code> and <code>feeder-1.0-SNAPSHOT.jar</code>. As well as <code>&lt;project_root&gt;/cloudify/apps/storm-demo/storm-nimbus/commands</code> contains <code>storm-topology-1.0-SNAPSHOT.jar</code> (you can copy them from maven’s target directories using <code>&lt;project_root&gt;/dev-scripts/copy-artifacts-to-cloudify.sh</code> script)</li>
  <li>Copy <code>&lt;project_root&gt;/cloudify</code> recipes to <code>&lt;cloudify_install&gt;/recipes</code> directory</li>
  <li>Run cloudify <code>&lt;cloudify_install&gt;/bin/cloudify.sh</code></li>
  <li>Bootsrap cloud (to bootsrap local cloud, run the following in Cloudify Shell <code>bootstrap-localcloud</code>)</li>
  <li>Start installation <code>install-application storm-demo</code></li>
  <li>Once installation completed, open Cloudify Management Console and check the ip address of <code>xap-management</code> service. Google Analytics UI should be available at <code>http://&lt;xap_management_service_ip&gt;:8090/web</code></li>
</ol>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[GigaSpaces with Kafka]]></title>
    <link href="http://dyagilev.org/blog/2014/05/14/xap-kafka/"/>
    <updated>2014-05-14T16:46:56+03:00</updated>
    <id>http://dyagilev.org/blog/2014/05/14/xap-kafka</id>
    <content type="html"><![CDATA[<p><a href="http://kafka.apache.org/">Apache Kafka</a> is a distributed publish-subscribe messaging system. It is designed to support persistent messaging with a O(1) disk structures that provides constant time performance even with many TB of stored messages. Apache Kafka provides High-throughput even with very modest hardware, Kafka can support hundreds of thousands of messages per second. Apache Kafka supports partitioning the messages over Kafka servers and distributing consumption over a cluster of consumer machines while maintaining per-partition ordering semantics. Many times Apache Kafka is used to perform parallel data load into Hadoop.</p>

<p>This pattern integrates <a href="http://www.gigaspaces.com/">GigaSpaces</a> with Apache Kafka. GigaSpaces’ write-behind IMDG operations to Kafka making it available for the subscribers. Such could be Hadoop or other data warehousing systems using the data for reporting and processing. Sources are available on <a href="https://github.com/fe2s/xap-kafka">github</a></p>

<!-- more -->

<h2 id="xap-kafka-integration-architecture">XAP Kafka Integration Architecture</h2>

<p>The XAP Kafka integration is done via the <code>SpaceSynchronizationEndpoint</code> interface deployed as a Mirror service PU. It consumes a batch of IMDG operations, converts them to custom Kafka messages and sends these to the Kafka server using the Kafka Producer API.</p>

<p><img src="http://dyagilev.org/images/xap-kafka/xap-kafka.jpg" /></p>

<p>GigaSpace-Kafka protocol is simple and represents the data and its IMDG operation. The message consists of the IMDG operation type (Write, Update , remove, etc.) and the actual data object. The Data object itself could be represented either as a single object or as a Space Document with key/values pairs (<code>SpaceDocument</code>).
Since a Kafka message is sent over the wire, it should be serialized to bytes in some way.
The default encoder utilizes Java serialization mechanism which implies Space classes (domain model) to be <code>Serializable</code>.</p>

<p>By default Kafka messages are uniformly distributed across Kafka partitions. Please note, even though IMDG operations appear ordered in <code>SpaceSynchronizationEndpoint</code>, it doesn’t imply correct ordering of data processing in Kafka consumers. See below diagram:</p>

<p><img src="http://dyagilev.org/images/xap-kafka/xap-kafka-ordering.jpg" /></p>

<h2 id="getting-started">Getting started</h2>

<h3 id="download-the-kafka-example">Download the Kafka Example</h3>

<p>You can download the example code from <a href="https://github.com/fe2s/xap-kafka">github</a>.
The example located under <code>&lt;project_root&gt;/example</code>. It demonstrates how to configure Kafka persistence and implements a simple Kafka consumer pulling data from Kafka and store in HsqlDB.</p>

<h3 id="running-the-example">Running the Example</h3>
<p>In order to run an example, please follow the instruction below:</p>

<p>Step 1: Install Kafka<br /></p>

<p>Step 2:	Start Zookeeper and Kafka server</p>
<div class="bogus-wrapper"><notextile><figure class="code"><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
<span class="line-number">2</span>
</pre></td><td class="code"><pre><code class="sh"><span class="line">bin/zookeeper-server-start.sh config/zookeeper.properties
</span><span class="line">bin/kafka-list-topic.sh --zookeeper localhost:2181
</span></code></pre></td></tr></table></div></figure></notextile></div>

<p>Step 3:	Build project</p>
<div class="bogus-wrapper"><notextile><figure class="code"><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
<span class="line-number">2</span>
</pre></td><td class="code"><pre><code class="sh"><span class="line"><span class="nb">cd</span> &lt;project_root&gt;
</span><span class="line">mvn clean install
</span></code></pre></td></tr></table></div></figure></notextile></div>

<p>Step 4:	Deploy example to GigaSpaces</p>
<div class="bogus-wrapper"><notextile><figure class="code"><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
<span class="line-number">2</span>
</pre></td><td class="code"><pre><code class="sh"><span class="line"><span class="nb">cd </span>example
</span><span class="line">mvn os:deploy
</span></code></pre></td></tr></table></div></figure></notextile></div>

<p>Step 5:	Check GigaSpaces log files, there should be messages from the Feeder and Consumer.</p>

<h2 id="configuration">Configuration</h2>

<h3 id="library-dependency">Library Dependency</h3>

<p>The following maven dependency needs to be included in your project in order to use Kafka persistence. This artifact is built from <code>&lt;project_rood&gt;/kafka-persistence</code> source directory.</p>

<div class="bogus-wrapper"><notextile><figure class="code"><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
<span class="line-number">2</span>
<span class="line-number">3</span>
<span class="line-number">4</span>
<span class="line-number">5</span>
</pre></td><td class="code"><pre><code class="xml"><span class="line"><span class="nt">&lt;dependency&gt;</span>
</span><span class="line">	<span class="nt">&lt;groupId&gt;</span>com.epam<span class="nt">&lt;/groupId&gt;</span>
</span><span class="line">	<span class="nt">&lt;artifactId&gt;</span>kafka-persistence<span class="nt">&lt;/artifactId&gt;</span>
</span><span class="line">	<span class="nt">&lt;version&gt;</span>1.0-SNAPSHOT<span class="nt">&lt;/version&gt;</span>
</span><span class="line"><span class="nt">&lt;/dependency&gt;</span>
</span></code></pre></td></tr></table></div></figure></notextile></div>

<h2 id="mirror-service">Mirror service</h2>

<p>Here is an example of the Kafka Space Synchronization Endpoint configuration:</p>

<div class="bogus-wrapper"><notextile><figure class="code"><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
<span class="line-number">2</span>
<span class="line-number">3</span>
<span class="line-number">4</span>
<span class="line-number">5</span>
<span class="line-number">6</span>
<span class="line-number">7</span>
<span class="line-number">8</span>
<span class="line-number">9</span>
<span class="line-number">10</span>
<span class="line-number">11</span>
<span class="line-number">12</span>
<span class="line-number">13</span>
<span class="line-number">14</span>
<span class="line-number">15</span>
<span class="line-number">16</span>
<span class="line-number">17</span>
</pre></td><td class="code"><pre><code class="xml"><span class="line"><span class="nt">&lt;bean</span> <span class="na">id=</span><span class="s">&quot;kafkaSpaceSynchronizationEndpoint&quot;</span> <span class="na">class=</span><span class="s">&quot;com.epam.openspaces.persistency.kafka.KafkaSpaceSynchronizationEndpointFactoryBean&quot;</span><span class="nt">&gt;</span>
</span><span class="line">	<span class="nt">&lt;property</span> <span class="na">name=</span><span class="s">&quot;producerProperties&quot;</span><span class="nt">&gt;</span>
</span><span class="line">		<span class="nt">&lt;props&gt;</span>
</span><span class="line">			<span class="nt">&lt;prop</span> <span class="na">key=</span><span class="s">&quot;metadata.broker.list&quot;</span><span class="nt">&gt;</span> localhost:9092<span class="nt">&lt;/prop&gt;</span>
</span><span class="line">			<span class="nt">&lt;prop</span> <span class="na">key=</span><span class="s">&quot;request.required.acks&quot;</span><span class="nt">&gt;</span>1<span class="nt">&lt;/prop&gt;</span>
</span><span class="line">		<span class="nt">&lt;/props&gt;</span>
</span><span class="line">	<span class="nt">&lt;/property&gt;</span>
</span><span class="line"><span class="nt">&lt;/bean&gt;</span>
</span><span class="line">
</span><span class="line">
</span><span class="line"><span class="c">&lt;!--</span>
</span><span class="line"><span class="c">	The mirror space. Uses the Kafka external data source. Persists changes done on the Space that</span>
</span><span class="line"><span class="c">	connects to this mirror space into the Kafka.</span>
</span><span class="line"><span class="c">--&gt;</span>
</span><span class="line"><span class="nt">&lt;os-core:mirror</span> <span class="na">id=</span><span class="s">&quot;mirror&quot;</span> <span class="na">url=</span><span class="s">&quot;/./mirror-service&quot;</span> <span class="na">space-sync-endpoint=</span><span class="s">&quot;kafkaSpaceSynchronizationEndpoint&quot;</span> <span class="na">operation-grouping=</span><span class="s">&quot;group-by-replication-bulk&quot;</span><span class="nt">&gt;</span>
</span><span class="line">	<span class="nt">&lt;os-core:source-space</span> <span class="na">name=</span><span class="s">&quot;space&quot;</span> <span class="na">partitions=</span><span class="s">&quot;2&quot;</span> <span class="na">backups=</span><span class="s">&quot;1&quot;</span><span class="nt">/&gt;</span>
</span><span class="line"><span class="nt">&lt;/os-core:mirror&gt;</span>
</span></code></pre></td></tr></table></div></figure></notextile></div>

<p>Please consult Kafka documentation for the full list of available producer properties.
You can override the default properties if there is a need to customize GigaSpace-Kafka protocol. See Customization section below for details.</p>

<h3 id="space-class">Space class</h3>

<p>In order to associate a Kafka topic with the domain model class, the class needs to be annotated with the <code>@KafkaTopic</code> annotation and declared as <code>Serializable</code>. Here is an example</p>

<div class="bogus-wrapper"><notextile><figure class="code"><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
<span class="line-number">2</span>
<span class="line-number">3</span>
<span class="line-number">4</span>
<span class="line-number">5</span>
</pre></td><td class="code"><pre><code class="java"><span class="line"><span class="nd">@KafkaTopic</span><span class="o">(</span><span class="s">&quot;user_activity&quot;</span><span class="o">)</span>
</span><span class="line"><span class="nd">@SpaceClass</span>
</span><span class="line"><span class="kd">public</span> <span class="kd">class</span> <span class="nc">UserActivity</span> <span class="kd">implements</span> <span class="n">Serializable</span> <span class="o">{</span>
</span><span class="line">    <span class="o">...</span>
</span><span class="line"><span class="o">}</span>
</span></code></pre></td></tr></table></div></figure></notextile></div>

<h3 id="space-documents">Space Documents</h3>

<p>To configure a Kafka topic for a SpaceDocuments or Extended SpaceDocument, the property <code>KafkaPersistenceConstants.SPACE_DOCUMENT_KAFKA_TOPIC_PROPERTY_NAME</code> should be added to document. Here is an example</p>

<div class="bogus-wrapper"><notextile><figure class="code"><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
<span class="line-number">2</span>
<span class="line-number">3</span>
<span class="line-number">4</span>
<span class="line-number">5</span>
<span class="line-number">6</span>
</pre></td><td class="code"><pre><code class="java"><span class="line"><span class="kd">public</span> <span class="kd">class</span> <span class="nc">Product</span> <span class="kd">extends</span> <span class="n">SpaceDocument</span> <span class="o">{</span>
</span><span class="line">
</span><span class="line"><span class="kd">public</span> <span class="nf">Product</span><span class="o">()</span> <span class="o">{</span>
</span><span class="line">	<span class="kd">super</span><span class="o">(</span><span class="s">&quot;Product&quot;</span><span class="o">);</span>
</span><span class="line">	<span class="kd">super</span><span class="o">.</span><span class="na">setProperty</span><span class="o">(</span><span class="n">SPACE_DOCUMENT_KAFKA_TOPIC_PROPERTY_NAME</span><span class="o">,</span> <span class="s">&quot;product&quot;</span><span class="o">);</span>
</span><span class="line"><span class="o">}</span>
</span></code></pre></td></tr></table></div></figure></notextile></div>

<p>It’s also possible to configure the name of the property which defines the Kafka topic for SpaceDocuments. Set <code>spaceDocumentKafkaTopicName</code> to the desired value as shown below.</p>

<div class="bogus-wrapper"><notextile><figure class="code"><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
<span class="line-number">2</span>
<span class="line-number">3</span>
<span class="line-number">4</span>
</pre></td><td class="code"><pre><code class="xml"><span class="line"><span class="nt">&lt;bean</span> <span class="na">id=</span><span class="s">&quot;kafkaSpaceSynchronizationEndpoint&quot;</span> <span class="na">class=</span><span class="s">&quot;com.epam.openspaces.persistency.kafka.KafkaSpaceSynchrspaceDocumentKafkaTopicNameonizationEndpointFactoryBean&quot;</span><span class="nt">&gt;</span>
</span><span class="line">	...
</span><span class="line">	<span class="nt">&lt;property</span> <span class="na">name=</span><span class="s">&quot;spaceDocumentKafkaTopicName&quot;</span> <span class="na">value=</span><span class="s">&quot;topic_name&quot;</span> <span class="nt">/&gt;</span>
</span><span class="line"><span class="nt">&lt;/bean&gt;</span>
</span></code></pre></td></tr></table></div></figure></notextile></div>

<h2 id="kafka-consumers">Kafka consumers</h2>

<p>The Kafka persistence library provides a wrapper around the native Kafka Consumer API for the GigaSpace-Kafka protocol serialization. Please see <code>com.epam.openspaces.persistency.kafka.consumer.KafkaConsumer</code>, example of how to use it under <code>&lt;project_root&gt;/example module</code>.</p>

<h2 id="customization">Customization</h2>

<ul>
  <li>Kafka persistence was designed to be extensible and customizable.</li>
  <li>If you need to create a custom protocol between GigaSpace and Kafka, provide an implementation of <code>AbstractKafkaMessage</code>, <code>AbstractKafkaMessageKey</code>, <code>AbstractKafkaMessageFactory</code>.</li>
  <li>If you would like to customize how data grid operations are sent to Kafka or how the Kafka topic is chosen for a given entity, provide an implementation of ‘AbstractKafkaSpaceSynchronizationEndpoint’.</li>
  <li>If you want to create a custom serializer, look at <code>KafkaMessageDecoder</code> and <code>KafkaMessageKeyDecoder</code>.</li>
  <li>Kafka Producer client (which is used under the hood) can be configured with a number of settings, see Kafka documentation.</li>
</ul>

]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Optimization Trick]]></title>
    <link href="http://dyagilev.org/blog/2014/05/13/optimization-trick/"/>
    <updated>2014-05-13T21:48:28+03:00</updated>
    <id>http://dyagilev.org/blog/2014/05/13/optimization-trick</id>
    <content type="html"><![CDATA[<p>An interesting optimization trick from <a href="http://storm.incubator.apache.org/">Storm</a> internals which reminded me some algorithmic problem(see in the end).</p>

<p>For those who haven’t heard about Storm, in short it’s a distributed realtime computation system. Scalable, fault tolerant and guarantees that every message is fully processed.</p>

<!-- more -->

<p><img src="http://dyagilev.org/images/storm/storm.png" /></p>

<p>The incoming tuple(message in Storm terminology) is processed by the bolt(processing node). Bolt can spawn more tuples which in their turn are further processed by succeeding bolt(s). So you end up with a tuple tree (directed acyclic graph actually).</p>

<p>The question is how to guarantee that every tuple is fully processed, i.e if some bolt fails, the tuple is replayed. We have to acknowledge when intermediate tuple created and when it’s processed. With huge tuple trees, tracking the entire tree state is memory expensive.</p>

<p>Strom designers decided to use an elegant trick which allows to know when tuple is fully processed with O(1) memory.</p>

<p>Tuple is associated with a random 64bit id. The tree state is also 64bit value called ‘ack val’. Whenever tuple is created or acknowledged, just <code>XOR</code> tuple id with ack val. When ack val becomes 0, the tree is fully processed. Yes, there is a a small probability of mistake, at 10K acks per second, it will take 50,000,000 years until a mistake is made. And even then, it will only cause data loss if that tuple happens to fail.</p>

<p>Now the algorithmic problem. Given an array of integer numbers. Each number except one appears exactly twice. The remaining number appears only once in the array, find this number with one iteration and O(1) memory. Easy, yay!</p>

]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[How To Decrypt Jetty's Https Tcp Dump]]></title>
    <link href="http://dyagilev.org/blog/2013/03/26/how-to-decrypt-jettys-https-tcp-dump/"/>
    <updated>2013-03-26T16:22:03+03:00</updated>
    <id>http://dyagilev.org/blog/2013/03/26/how-to-decrypt-jettys-https-tcp-dump</id>
    <content type="html"><![CDATA[<p>If you want to capture jetty’s tcp dump of https and analyze encrypted packets later - here is an instruction. Applies for Jetty 7, not sure if the same works for other versions.</p>

<!-- more -->

<p><strong>Step 1.</strong> Find obfuscated password in jetty.xml, it should start with OBF: prefix. Run it through the following deobfuscating function which I found in jetty sources.</p>

<div class="bogus-wrapper"><notextile><figure class="code"><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
<span class="line-number">2</span>
<span class="line-number">3</span>
<span class="line-number">4</span>
<span class="line-number">5</span>
<span class="line-number">6</span>
<span class="line-number">7</span>
<span class="line-number">8</span>
<span class="line-number">9</span>
<span class="line-number">10</span>
<span class="line-number">11</span>
<span class="line-number">12</span>
<span class="line-number">13</span>
<span class="line-number">14</span>
<span class="line-number">15</span>
<span class="line-number">16</span>
<span class="line-number">17</span>
<span class="line-number">18</span>
<span class="line-number">19</span>
</pre></td><td class="code"><pre><code class="java"><span class="line"><span class="kd">public</span> <span class="kd">class</span> <span class="nc">Password</span> <span class="o">{</span>
</span><span class="line">    <span class="kd">public</span> <span class="kd">static</span> <span class="kd">final</span> <span class="n">String</span> <span class="n">__OBFUSCATE</span> <span class="o">=</span> <span class="s">&quot;OBF:&quot;</span><span class="o">;</span>
</span><span class="line">
</span><span class="line">    <span class="kd">public</span> <span class="kd">static</span> <span class="n">String</span> <span class="nf">deobfuscate</span><span class="o">(</span><span class="n">String</span> <span class="n">s</span><span class="o">)</span> <span class="o">{</span>
</span><span class="line">	<span class="k">if</span> <span class="o">(</span><span class="n">s</span><span class="o">.</span><span class="na">startsWith</span><span class="o">(</span><span class="n">__OBFUSCATE</span><span class="o">))</span> <span class="n">s</span> <span class="o">=</span> <span class="n">s</span><span class="o">.</span><span class="na">substring</span><span class="o">(</span><span class="mi">4</span><span class="o">);</span>
</span><span class="line">
</span><span class="line">	<span class="kt">byte</span><span class="o">[]</span> <span class="n">b</span> <span class="o">=</span> <span class="k">new</span> <span class="kt">byte</span><span class="o">[</span><span class="n">s</span><span class="o">.</span><span class="na">length</span><span class="o">()</span> <span class="o">/</span> <span class="mi">2</span><span class="o">];</span>
</span><span class="line">	<span class="kt">int</span> <span class="n">l</span> <span class="o">=</span> <span class="mi">0</span><span class="o">;</span>
</span><span class="line">	<span class="k">for</span> <span class="o">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="o">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">s</span><span class="o">.</span><span class="na">length</span><span class="o">();</span> <span class="n">i</span> <span class="o">+=</span> <span class="mi">4</span><span class="o">)</span> <span class="o">{</span>
</span><span class="line">	    <span class="n">String</span> <span class="n">x</span> <span class="o">=</span> <span class="n">s</span><span class="o">.</span><span class="na">substring</span><span class="o">(</span><span class="n">i</span><span class="o">,</span> <span class="n">i</span> <span class="o">+</span> <span class="mi">4</span><span class="o">);</span>
</span><span class="line">	    <span class="kt">int</span> <span class="n">i0</span> <span class="o">=</span> <span class="n">Integer</span><span class="o">.</span><span class="na">parseInt</span><span class="o">(</span><span class="n">x</span><span class="o">,</span> <span class="mi">36</span><span class="o">);</span>
</span><span class="line">	    <span class="kt">int</span> <span class="n">i1</span> <span class="o">=</span> <span class="o">(</span><span class="n">i0</span> <span class="o">/</span> <span class="mi">256</span><span class="o">);</span>
</span><span class="line">	    <span class="kt">int</span> <span class="n">i2</span> <span class="o">=</span> <span class="o">(</span><span class="n">i0</span> <span class="o">%</span> <span class="mi">256</span><span class="o">);</span>
</span><span class="line">	    <span class="n">b</span><span class="o">[</span><span class="n">l</span><span class="o">++]</span> <span class="o">=</span> <span class="o">(</span><span class="kt">byte</span><span class="o">)</span> <span class="o">((</span><span class="n">i1</span> <span class="o">+</span> <span class="n">i2</span> <span class="o">-</span> <span class="mi">254</span><span class="o">)</span> <span class="o">/</span> <span class="mi">2</span><span class="o">);</span>
</span><span class="line">	<span class="o">}</span>
</span><span class="line">
</span><span class="line">	<span class="k">return</span> <span class="k">new</span> <span class="nf">String</span><span class="o">(</span><span class="n">b</span><span class="o">,</span> <span class="mi">0</span><span class="o">,</span> <span class="n">l</span><span class="o">);</span>
</span><span class="line">    <span class="o">}</span>
</span><span class="line"><span class="o">}</span>
</span></code></pre></td></tr></table></div></figure></notextile></div>

<p><strong>Step 2.</strong> Now you should have the password for keystore. The location of keystore should be listed in jetty.xml. Import keys to intermediate PKCS12 format</p>

<pre><code>$ /usr/java/jdk1.6.0_13/bin/keytool -importkeystore -srckeystore $YOUR_PATH_HERE/keystore -destkeystore intermediate.p12 -deststoretype PKCS12
</code></pre>

<p><strong>Step 3</strong>. Extract RSA key from PKCS12</p>

<pre><code>$ openssl pkcs12 -in intermediate.p12  -nocerts -nodes -passin pass:$YOUR_PASS_HERE | openssl rsa -out privateRSAKey.pem
</code></pre>

<p><strong>Step 4.</strong> Now you are good to feed wireshark or other preferred tool with RSA key.</p>

]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Fractal Tree Indexes]]></title>
    <link href="http://dyagilev.org/blog/2012/03/29/fractal-tree-indexes/"/>
    <updated>2012-03-29T15:58:13+03:00</updated>
    <id>http://dyagilev.org/blog/2012/03/29/fractal-tree-indexes</id>
    <content type="html"><![CDATA[<p>Парни из <a href="http://www.tokutek.com/">Tokutek</a> реализовали engine <a href="http://en.wikipedia.org/wiki/TokuDB">TokuDB</a> для MySQL как замену InnoDB, улучшив производительность операций вставки, запросов и компрессии. В основе индекса лежит  так называемое Fractal Tree.</p>

<p>Посмотрим за счет чего достигается скорость вставки и поиска по сравнению с классическим B-tree.</p>

<!-- more -->

<h2 id="section">Асимптотическая оценка</h2>

<p>Необходимо оговориться, что мы рассматриваем случай когда индекс хранится на диске, а не в памяти. Поэтому нас интересует оценка количества операций IO, а не операций CPU. Операции CPU занимают ничтожно малое время по сравнению с IO.</p>

<p><strong>Вставка:</strong></p>

<p>B-tree <code>O((log N)/(log B))</code></p>

<p>Fractal-tree <code>O((log N)/(B^(1-k)))</code></p>

<p><strong>Поиск:</strong></p>

<p>B-tree <code>O((log N)/(log B))</code></p>

<p>Fractal-tree <code>O((log N)/(log (k*B^(1-k)))</code></p>

<p>где B - размер IO блока</p>

<p>Итак, упрощенный вариант Fractal Tree:</p>

<ul>
  <li><code>log N</code> массивов размером <code>2^i</code></li>
  <li>каждый массив или полностью пустой или полностью заполнен</li>
  <li>каждый массив отсортирован</li>
</ul>

<p><img src="http://dyagilev.org/images/fractal-tree/fractal_tree1.png" width="50%" height="50%" /></p>

<h3 id="section-1">Вставка</h3>
<p>Начинаем сверху. Смотрим на верхний массив размером 1. Если он пустой - кладем туда элемент. Иначе вынимаем элемент и сортируем с данным во временом массиве. Спускаемся вниз к массиву размером 2. Если он пустой - кладем туда нашу пару. Если нет - мержим их с временным массивом. Так как оба массива отсортированы, то делаем ето за <code>O(X)</code>, где <code>X</code> ето длина массива. По сути ето операция merge из merge-сортировки. 
Амортизированное время вставки занимает <code>O((log N)/B)</code>, хотя в худшем случае вставка одного элемента может повлечь за собой перезапись огромного количества данных по цепочке. Чтобы избежать пиков с длительным ответом TokuDB порождает отдельный поток для вставки, ответ для клиента возвращается немедленно.</p>

<h3 id="section-2">Поиск</h3>
<p>проходимся по массивам, в кажом массиве используем бинарный поиск. Итого - <code>O((log N)^2)</code>. Это медленнее чем B-tree. Как это можно ускорить?</p>

<p>Идея состоит в том, чтобы во время поиска в очередном массиве использовать некоторую информацию из поиска предыдущего массива. И так по цепочке. А информация следующая - каждый элемент массива хранит ссылку на его ‘виртуальное’ место в следующем массиве. Называется ето fractional cascading. 
Итого, <code>log N</code> массивов, константное время на каждом массиве дает <code>O(log N)</code>.</p>

<p><img src="http://dyagilev.org/images/fractal-tree/fractal_tree2.png" width="50%" height="50%" /></p>

<p>В целом товарищи из Tokutek считают что в будущем все перейдут на фрактальные деревья как замена Б-деревьям. Детали алгоритма запатентованы.</p>

<h3 id="section-3">Ссылки</h3>

<ul>
  <li><a href="http://video.mit.edu/watch/lecture-19-how-tokudb-fractal-tree-indexes-work-1361/">презентация в MIT</a></li>
  <li><a href="http://www.mysqlperformanceblog.com/2009/04/28/detailed-review-of-tokutek-storage-engine/">бенчмаркинг InnoDB vs TokuDB</a></li>
</ul>

]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Consistent Hashing]]></title>
    <link href="http://dyagilev.org/blog/2012/03/23/consistent-hashing/"/>
    <updated>2012-03-23T15:34:54+03:00</updated>
    <id>http://dyagilev.org/blog/2012/03/23/consistent-hashing</id>
    <content type="html"><![CDATA[<p>Техника консистентного хеширования <a href="http://en.wikipedia.org/wiki/Consistent_hashing">consistent hashing</a> довольно популярна при создании распределенных систем, тем не менее я не смог найти описания алгоритма на русском языке. Попробую изложить, возможно кому-то пригодится.</p>

<!-- more -->

<h2 id="section">Проблема</h2>
<p>Предположим вы разрабатываете приложение и решили кешировать данные для улучшения производительности. Так же вы решили использовать горизонтальное масштабирование и разнести данные на N серверов.</p>

<p>Итого, есть N серверов и необходимо реализовать две функции:</p>

<div class="bogus-wrapper"><notextile><figure class="code"><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
<span class="line-number">2</span>
</pre></td><td class="code"><pre><code class="java"><span class="line"><span class="kt">void</span> <span class="nf">put</span><span class="o">(</span><span class="n">Key</span> <span class="n">k</span><span class="o">,</span> <span class="n">Item</span> <span class="n">i</span><span class="o">)</span> <span class="c1">// положить элемент i с ключом k в кеш</span>
</span><span class="line"><span class="n">Item</span> <span class="nf">get</span><span class="o">(</span><span class="n">Key</span> <span class="n">k</span><span class="o">);</span> <span class="c1">// вытащить элемент по ключу k</span>
</span></code></pre></td></tr></table></div></figure></notextile></div>

<p>Имея ключ k, как узнать на каком сервере лежит соответствующий ему элемент?</p>

<h2 id="section-1">Решение</h2>
<p>Первое что приходит в голову - использовать обычную хеш-таблицу. Берем ключ k, применяем к нему хеш-функцию и считаем остаток от деления на количество серверов N - <code>hash(k) mod N</code>. Да, это будет работать, но что произойдет когда мы захотим добавить ещё один сервер ? Нам необходимо будет перехешировать все данные, большую часть которых нужно будет загрузить на новые сервера. Это дорогая операция. Также не понятно что делать в случае падения существующего сервера.</p>

<p>Здесь появляется консистентное кеширование. Идея простая, возьмем окружность и будем рассматривать ее как интервал на котором определена хеш-функция функция. Применив хеш-функцию к набору ключей (синие точки) и серверов (зеленые точки) сможем разместить их на окружности.</p>

<p><img src="http://dyagilev.org/images/consistent-hashing/consistent_hashing11.png" width="50%" height="50%" /></p>

<p>Для того чтобы определить на каком сервере размещен ключ, найдем ключ на окружности и будем двигаться по часовой стрелке до ближайшего сервера.</p>

<p>Теперь в случае падения (недоступности) сервера, загрузить на новые сервер необходимо только недоступные данные. Все остальные хеши не меняют свое местоположение, то есть консистенты.</p>

<p><img src="http://dyagilev.org/images/consistent-hashing/consistent_hashing2.png" width="50%" height="50%" /></p>

<p>При добавлении нового сервера соседний разделяет с ним часть своих данных.</p>

<p><img src="http://dyagilev.org/images/consistent-hashing/consistent_hashing3.png" width="50%" height="50%" /></p>

<p>В целом ето все. На практике также применяют следующий трюк. Сервер можно пометить на окружности не одной точкой, а несколькими.</p>

<p><img src="http://dyagilev.org/images/consistent-hashing/consistent_hashing4.png" width="50%" height="50%" /></p>

<p>Что ето дает ?
- более равномерное распределение данных по серверам
- при падении сервера данные распределяются не на один соседний, а на несколько, распределяя тем самым нагрузку
- при добавлении нового сервера, точки можно делать ‘активными’ постепенно одна за другой, предотвращая шквальную нагрузку на сервер
- если конфигурация серверов отличается, например размером диска, количество данных можно контролировать числом его точек. Больше точек - большая длина окружности принадлежит етому серверу и соответственно больше данных.</p>

<h2 id="section-2">Реализация</h2>

<p>Храним хеши серверов в виде какого-либо дерева, например Red-Black. Операция поиска сервера по ключу будет занимать <code>O(log n)</code>.</p>

]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Code Refactoring Detection]]></title>
    <link href="http://dyagilev.org/blog/2012/03/21/code-refactoring-detection/"/>
    <updated>2012-03-21T15:13:35+03:00</updated>
    <id>http://dyagilev.org/blog/2012/03/21/code-refactoring-detection</id>
    <content type="html"><![CDATA[<p>An idea of feature I would love to see in code review and diff/merge tools.</p>

<p>Consider you are reviewing code changes or making 3-way merge where among various things a name of some method has changed. Personally I don’t want to go through tens of files and check that all usages of method changed accordingly.</p>

<p>I would like to see it in more declarative way. One phrase saying ‘method <code>Foo.bar()</code> has changed to <code>Foo.bar2()</code>’ would be enough. Imagine you could accept this particular change and now tool ignores it making the whole picture clearer.</p>

<p>Say for  static object-oriented languages I can imagine a number of refactoring types  where this could be useful - method extract, all kind of renames, replacing inheritance with delegation, replacing constructor with factory methods and so on.</p>

<p>How difficult would it be to implement semantic aware diff on top of Intellij IDEA ?</p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Hello Blog]]></title>
    <link href="http://dyagilev.org/blog/2012/03/21/hello/"/>
    <updated>2012-03-21T14:11:52+03:00</updated>
    <id>http://dyagilev.org/blog/2012/03/21/hello</id>
    <content type="html"><![CDATA[<p>This blog is my shady nook to write some random thoughts on computer science and related topics.</p>

]]></content>
  </entry>
  
</feed>
