<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:cc="http://cyber.law.harvard.edu/rss/creativeCommonsRssModule.html">
    <channel>
        <title><![CDATA[Stories by Kartik Khare on Medium]]></title>
        <description><![CDATA[Stories by Kartik Khare on Medium]]></description>
        <link>https://medium.com/@kharekartik?source=rss-2c9d8b2edb6e------2</link>
        <image>
            <url>https://cdn-images-1.medium.com/fit/c/150/150/2*tqC_KqXkvgD_Rq1A0JwWoA.jpeg</url>
            <title>Stories by Kartik Khare on Medium</title>
            <link>https://medium.com/@kharekartik?source=rss-2c9d8b2edb6e------2</link>
        </image>
        <generator>Medium</generator>
        <lastBuildDate>Sun, 15 Nov 2026 14:17:33 GMT</lastBuildDate>
        <atom:link href="https://medium.com/@kharekartik/feed" rel="self" type="application/rss+xml"/>
        <webMaster><![CDATA[yourfriends@medium.com]]></webMaster>
        <atom:link href="http://medium.superfeedr.com/" rel="hub"/>
        <item>
            <title><![CDATA[Apache Helix: The Distributed System’s Orchestra Conductor]]></title>
            <link>https://medium.com/better-programming/apache-helix-the-distributed-systems-orchestra-conductor-532abaebb99a?source=rss-2c9d8b2edb6e------2</link>
            <guid isPermaLink="false">https://medium.com/p/532abaebb99a</guid>
            <category><![CDATA[big-data]]></category>
            <category><![CDATA[software-engineering]]></category>
            <category><![CDATA[distributed-systems]]></category>
            <category><![CDATA[programming]]></category>
            <category><![CDATA[apache-pinot]]></category>
            <dc:creator><![CDATA[Kartik Khare]]></dc:creator>
            <pubDate>Tue, 28 Feb 2023 19:34:23 GMT</pubDate>
            <atom:updated>2023-02-28T19:34:23.695Z</atom:updated>
            <content:encoded><![CDATA[<h4>Achieve harmony in complex clusters using finite-state machines</h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*ZVyw0fnJZF41MxnW1C43tQ.png" /><figcaption>Image by DALLE-2 (credit to human artists)</figcaption></figure><p>Distributed systems are mainstream in software workflows these days. These are the systems where servers communicate with the client and each other (usually through gossip and consensus protocols). Distributed systems help solve a major problem where you must spread out a common task across multiple servers for performance and availability.</p><p>You also need to ensure consistency and fault tolerance in such systems. Otherwise, either task will return an output that results from a data snapshot at two different points in time or an incomplete result because some node failed during processing.</p><h3>Writing a Distributed System Is Not Easy</h3><p>If only it were as easy as implementing API calls between servers. Running and maintaining a distributed system is not a simple task in itself.<br>As you can imagine, writing a new one is much more challenging.</p><p>Take <a href="https://cassandra.apache.org/_/index.html">CassandraDB</a> as an example. The basic function of the DB is to process a CQL query, gather data from multiple nodes and return the aggregated response.</p><p>However, in the background, you have to communicate properly between all the nodes using <a href="https://docs.datastax.com/en/cassandra-oss/3.0/cassandra/architecture/archGossipAbout.html">Gossip</a>, reach a leadership consensus via <a href="https://www.freecodecamp.org/news/how-to-implement-paxos-algorithm-in-pure-functions/">Paxos</a>, and ensure it is fault-tolerant by distributing SSTables correctly. All of this has to be done while supporting the addition and removing existing nodes while providing the same query performance.</p><p>What if you allowed a distributed system to separate its supervision logic from its business logic? That way, the developers of a distributed DB similar to Cassandra can focus only on the query execution part while the fault tolerance, replication, and token assignment are taken care of by this new entity.</p><h3>Enter Apache Helix</h3><p><a href="https://helix.apache.org/">Apache Helix</a> allows developers to express your complex distributed system as a simple finite-state machine. All the management operations are then represented as transitions in this state machine.</p><p>This is great because the general view is that a cluster’s operations differ so much from each system to the next that creating a generic framework is impossible. By using Helix, developers can focus on the core functionalities of their systems and let Helix take care of cluster management.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*2fs-VeaE5v2tpQ6WdVhj5w.png" /><figcaption>image by author</figcaption></figure><h3>Wait, a Distributed System Is a State Machine?</h3><p>If you don’t know about <a href="https://en.wikipedia.org/wiki/Finite-state_machine">state machines</a>, it’s a model that can represent a system in a fixed number of states. It also models what transitions are valid from one state to another.</p><p>Let’s take the example of the popular real-time event-processing framework <a href="https://kafka.apache.org/">Apache Kafka</a>. A Kafka cluster consists of multiple topics. Each topic consists of multiple partitions.</p><p>For a topic partition, Apache Kafka needs to ensure the following:</p><ul><li>There is always one leader. The leader is the only one that can accept the writes.</li><li>There are N replicas of the partition where the user can decide N.</li><li>The replicas should be distributed evenly among M server nodes for a fault-tolerant setup.</li><li>Only replicas in sync with the leader can be promoted to a leader if it crashes.</li></ul><p>Helix can take care of all this with the following state machine:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*MCv2X6ZVvAXGsW3a2eOH6g.png" /><figcaption>image by author</figcaption></figure><p>This allows Kafka-like systems to focus only on reading/writing data from the partition logs and other admin operations such as adding/deleting topics, managing offsets, and so on.</p><p>Let’s take another example of <a href="https://pinot.apache.org/">Apache Pinot</a>.</p><p>Pinot has multiple tables, each composed of small segment files. The analogy would be a single Cassandra Table comprised of small SSTable files.</p><p>For a particular segment, Pinot has the following constraints:</p><ul><li>There should be N replicas of the segment where the user can configure N</li><li>There can only be one consuming segment for each table. All replicas of that segment should be in a consuming state as well.</li><li>The rest of the segment should be in an online state, i.e., ready-to-serve queries.</li><li>In case M new nodes are added, the new segments should also be distributed evenly across these nodes.</li><li>A segment and replicas should be dropped in case users reach or manually delete a retention duration.</li></ul><p>This can be represented with the following state machine:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*FyaZ8O9TbOqOiB9yXZ4qhw.png" /><figcaption>image by author</figcaption></figure><h3>So, Where Does Helix Come Into the Picture?</h3><p>The user will upload these state machines (in a form of config) to Helix. Helix then keeps track of all the nodes in the cluster through inbuilt monitoring.</p><p>It also keeps track of the state of each partition in each node. Helix then creates a map of what the ideal state of each partition should be. If the partition&#39;s actual and ideal state does not match, Helix will queue up State transitions.</p><p>The attractiveness of Helix though is due to the following:</p><ul><li>It can do multiple state transitions in parallel.</li><li>Take care of coordination between multiple servers. For example, if a serverB is made leader instead of serverA, remove the serverB from the leadership before adding serverA.</li><li>It can decide the order of state transitions even when done in parallel to not violate the constraints. For example, upgrading a new server with no data to the leader OR assigning some replica partition to a node that has the leader as well.</li><li>Decide the priority of the transitions (which can be modified using a config). For example, if a leader fails at the same time some new nodes are added to the cluster, the transition from replica to leader status should take higher priority than the transition from idle to replica state.</li><li>Modify the ideal state based on cluster monitoring. For example, where to move the partitions residing on the failed node if a node goes down?</li></ul><p>Here’s how the state machine for Kafka can be implemented via Helix:</p><pre>    StateModelDefinition.Builder builder = new StateModelDefinition.Builder(&quot;your-state-model&quot;);<br>    builder.initialState(&quot;IDLE&quot;);<br><br>    builder.addState(&quot;IDLE&quot;);<br>    builder.addState(&quot;REPLICA&quot;);<br>    builder.addState(&quot;LEADER&quot;);<br>    // Set the initial state when the node starts<br><br>    // Add transitions between the states.<br>    builder.addTransition(&quot;IDLE&quot;, &quot;REPLICA&quot;);<br>    builder.addTransition(&quot;REPLICA&quot;, &quot;LEADER&quot;);<br>    builder.addTransition(&quot;REPLICA&quot;, &quot;IDLE&quot;);<br>    builder.addTransition(&quot;LEADER&quot;, &quot;REPLICA&quot;);<br>    <br>    // Set constraint that there can only be 1 leader at a time<br>    builder.dynamicUpperBound(&quot;LEADER&quot;, &quot;1&quot;);<br><br>    // Set transition priority<br>    // the first inserted gets the top most priority.<br>    List&lt;String&gt; stateTransitionPriorityList = new ArrayList&lt;String&gt;();<br><br>    stateTransitionPriorityList.add(&quot;REPLICA-LEADER&quot;);<br>    stateTransitionPriorityList.add(&quot;IDLE-REPLICA&quot;);<br>    stateTransitionPriorityList.add(&quot;REPLICA-IDLE&quot;);<br>    stateTransitionPriorityList.add(&quot;LEADER-REPLICA&quot;);<br><br>    record.setListField(StateModelDefinitionProperty.STATE_TRANSITION_PRIORITYLIST.toString(),<br>        stateTransitionPriorityList);<br>    </pre><h3>Assigning State to Each Partition</h3><p>Internally, Helix uses a <a href="https://github.com/apache/helix/blob/b7c62b5346d50e20f0b40ef463ed0663aad9e1ac/helix-core/src/main/java/org/apache/helix/controller/rebalancer/strategy/crushMapping/CRUSHPlacementAlgorithm.java">modified version of the RUSH algorithm</a> to decide the final state and then uses a greedy approach to figure out valid transactions and schedule them. By greedy approach, I mean the transactions are appended to a list of pending transactions until we get two transactions that cannot be done in parallel.</p><p>Helix also allows users to plugin any other placement strategy, e.g., ConsistentHashing depending on the use case.</p><h3>How Is Helix Agnostic of Core System Logic?</h3><p>It should know how to deal with data movement across servers, how to read data in each partition, and so on, right?</p><p>Well, I never said Helix performs the state transition. Helix issues the request with the transition that needs to be performed to the server that needs to perform it. Currently, it is done using <a href="https://zookeeper.apache.org/">Apache Zookeeper</a> as a message queue.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*IVCkYLZ4qX0O77L3OPp7cA.png" /><figcaption>Using Zookeeper as MQ to communicate transition</figcaption></figure><pre>// Helix leader, running on a controller node<br>state.setPartitionState(partitionName, serverName, &quot;ONLINE&quot;);<br><br>// Helix Listener, running inside each server<br>@Transition(from = &quot;OFFLINE&quot;, to = &quot;ONLINE&quot;)<br>public void onBecomeOnlineFromOffline(Message message, NotificationContext context) {<br>  String partitionName = message.getPartitionName();<br>  // Logic to add partition<br>  // Create partition files<br>  // add partition to queryable list<br>}</pre><p>Each server has a listener which keeps track of new messages in the queue. The listener has the business logic on what to do when asked to move from State A to State B. For example, if it needs to copy data from S3 for bootstrapping or start accepting writes since it has been promoted to leader.</p><p>The server doesn’t need to care about what other servers are doing, e.g., who is the leader, if some other server already has the same data, and so on. It simply performs what it’s asked to do and then updates the actual state of the partition. If it cannot complete the task, it reverts to an error, and Helix can take appropriate follow-up action.</p><h3>Conclusion</h3><p>Although Apache Helix can help you simplify cluster management to an amazing degree, it still lacks some features:</p><ul><li>Helix can’t take heterogeneous servers into account while calculating the final state. For example, you should be able to schedule twice as many partitions on a node that is 2X larger than the rest of the nodes.</li><li>Helix can’t properly account for replication in a cluster distributed across multiple AZs. In such cases, you must ensure the replicas are present in different AZs.</li></ul><p>You can use the following references to learn more about Helix:</p><ul><li>Helix repository — <a href="https://github.com/apache/helix/tree/master">https://github.com/apache/helix/tree/master</a></li><li>Helix whitepaper — <a href="https://dl.acm.org/doi/10.1145/2391229.2391248">https://dl.acm.org/doi/10.1145/2391229.2391248</a></li><li>Helix in Apache Pinot — <a href="https://docs.pinot.apache.org/basics/architecture">https://docs.pinot.apache.org/basics/architecture</a></li></ul><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=532abaebb99a" width="1" height="1" alt=""><hr><p><a href="https://medium.com/better-programming/apache-helix-the-distributed-systems-orchestra-conductor-532abaebb99a">Apache Helix: The Distributed System’s Orchestra Conductor</a> was originally published in <a href="https://betterprogramming.pub/">Better Programming</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Navigating the Minefield of RocksDB Configuration Options]]></title>
            <link>https://medium.com/better-programming/navigating-the-minefield-of-rocksdb-configuration-options-246af1e1d3f9?source=rss-2c9d8b2edb6e------2</link>
            <guid isPermaLink="false">https://medium.com/p/246af1e1d3f9</guid>
            <category><![CDATA[database]]></category>
            <category><![CDATA[programming]]></category>
            <category><![CDATA[configuration]]></category>
            <category><![CDATA[distributed-systems]]></category>
            <category><![CDATA[software]]></category>
            <dc:creator><![CDATA[Kartik Khare]]></dc:creator>
            <pubDate>Tue, 03 Jan 2023 14:37:30 GMT</pubDate>
            <atom:updated>2023-01-05T20:15:00.968Z</atom:updated>
            <content:encoded><![CDATA[<h4>Unleashing the full potential of your RocksDB with the right configuration</h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*F45PzjiV3rkuRKnL" /><figcaption>Photo by <a href="https://unsplash.com/@hermez777?utm_source=medium&amp;utm_medium=referral">Hermes Rivera</a> on <a href="https://unsplash.com/?utm_source=medium&amp;utm_medium=referral">Unsplash</a></figcaption></figure><p><a href="https://medium.com/walmartglobaltech/https-medium-com-kharekartik-rocksdb-and-embedded-databases-1a0f8e6ea74f">Embedded DBs</a> have become commonplace in a lot of new distributed systems. They offer a way to store a lot of data on a local disk, thereby providing good performance compared to a database located on a different server. They are also lightweight, so they don’t put a lot of pressure on the main application running on the same server.</p><p>Facebook (now Meta) created RocksDB, which is the crown jewel among the embedded DBs. It is used in popular libs such as <a href="https://flink.apache.org/2021/01/18/rocksdb.html">Apache Flink</a>, <a href="http://myrocks.io/">MyRocks SQL engine</a>, and more.</p><p><a href="http://rocksdb.org/">RocksDB</a> has a lot of information on its website, but it is hard to find all the configuration options. As a new user, it was difficult for me to understand which configurations were important to set, what they meant, and where to change them.</p><p>This is what I plan to tackle in this article. All the options discussed here are applicable for Block-based table formatand may not apply to the Plain or Blob table format. It is also not an exhaustive list of options but instead the most popular and useful selection.</p><h3>Use Your Fuel Wisely!</h3><p>Memory optimization is crucial for any database, and RocksDB is no exception. By maximizing the efficiency of your memory usage, you can improve the speed of both reads and writes in your database. Memory is used for two key structures in RocksDB: the cache and the memtables.</p><h4><strong>Block cache</strong></h4><p>RocksDB comes with various types and levels of cache for improving read speeds. The most common one is the Block cache. It is enabled by default with a max size of 8MB. As the name indicates, the block cache stores the SSTable blocks. They can be data blocks, index blocks, filter blocks, or something else. Most of the time, you will want to increase this cache size to improve speed.</p><p>RocksDB has two cache options: LRUCache, which is the default, and ClockCache. ClockCache is faster because it doesn&#39;t need locks, but it doesn&#39;t provide the same guarantees as LRUCache. It was also marked as experimental until November 2022.</p><h4><strong>Row cache</strong></h4><p>This cache is used for storing actual data for a key. The cache’s documentation is almost non-existent, but it provides fast data access in case you do a lot of point lookups. RocksDB first looks in the row cache and then in the block cache for the data. Like block cache, you can use either LRUCache or ClockCache as the actual implementation for row cache.</p><p><strong>Cache filters</strong> — Other than the data blocks, you can also ask RocksDB to cache index and filter blocks. These blocks need to be accessed almost every time to need to look up a key in the data block. Caching these blocks improves the read latencies while trading off memory. The following options can be used:</p><ul><li>cache_index_and_filter_blocks — Set it to true to enable caching these blocks.</li><li>cache_index_and_filter_blocks_with_high_priority — Set it to true so that these blocks are cached in the high-priority region of the LRU cache. These blocks are evicted much later than the low-priority region in case the cache is full.</li><li>pin_l0_filter_and_index_blocks_in_cache — L0 level requires reading all the files since they have overlapping key ranges. Enabling this always keeps the L0 filter and index blocks in the cache</li></ul><h4><strong>Memtable</strong></h4><p>RocksDB writes go first to Memtables, a data structure that resides in the memory. These tables are then flushed to the L0 level in the DB. Since memory is a precious resource, you need to control the size of the memtables in the RocksDB and the frequency of flushes.</p><p>This is possible via the following configs:</p><ul><li>write_buffer_size — this is the max size of each memtable that RocksDB can create. Beyond this size, a new memtable is created, and the existing one is queued up for flushing to the disk. The default value is 64MB.</li><li>max_write_buffer_number — these are the max number of memtables that can exist in the memory before a flush is triggered. The default is two so that when the first memtable is flushed, the writes can continue on the second memtable.</li><li>min_write_buffer_number_to_merge — minimum number of memtables that will be merged before a flush is triggered. The merge operation happens in memory. This can help you reduce disk io for flushes, especially if memtables contain a lot of duplicate keys or deletes. The default is one, so no memtables are merged.</li><li>db_write_buffer_size — this is the max size of all memtables combined across column families. The default is -1, which means there is no global limit on memtables. If you set it to a value &gt; 0, RocksDB will initialize a WriteBufferManager that keeps track of all memtables sizes across column families.</li></ul><p>You can grep for flush_reason stats in the RocksDB Log file to check the size of the column family before getting flushes as well as the reason it flushed. For example, you will see values like Write Buffer Fullwhich means it was triggered because of max_write_buffer_number or you will see a value like Write Buffer Manager which is because of db_write_buffer_size.</p><h4>Memtable formats</h4><p>There are four supported memtable formats in RocksDB:</p><ul><li>SkipList</li><li>HashSkipList</li><li>HashLinkList</li><li>Vector</li></ul><p>You can go through the <a href="https://github.com/facebook/rocksdb/wiki/MemTable">official wiki</a> for more detail on these formats. The default is SkipListFactory which performs well for almost every use case. It is the only one that can support concurrent writes. The flushes are also faster for this memtable format.</p><p>The second format that you can try is the Vector memtable. It provides good performance if you do a lot of random writes. Some popular frameworks, such as Kafka Streams, <a href="https://www.confluent.io/blog/how-to-tune-rocksdb-kafka-streams-state-stores-performance/">use this table format</a>.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*kJCJfsf7l7_EPN-aaOFp0A.png" /><figcaption>all remaining images by author</figcaption></figure><h3>Use the Expressway, Not Single-Lane Roads!</h3><p>IncreaseParallelism — The number of threads RocksDB will use for all background operations. Even if you increase the max_background_jobs but keep parallelism as 1, it won’t lead to a lot of performance increase. Ideally, this option should be kept equal to the number of cores in your machine.</p><p>Max_Background_Jobs — These are the number of flushes and compactions that can be scheduled in parallel on your threads. Flushes take higher priority, while compactions take lower priority by default. This is so that a compaction operation doesn’t block any writes to the databases.</p><p>CPU usage is also affected by compaction. We will take a look at the compaction options in the next section since they are more I/O intensive than CPU intensive.</p><h3>Avoid Walls and Slow Corners!</h3><p>Since RocksDB is a database, it is expected that you’ll be hitting I/O bottlenecks sooner or later. The two most I/O-intensive operations are flush and compaction. Flushes are mostly tuned by adjusting the memtable sizes and numbers. We have already gone through those in the Memtables section.</p><h4><strong>Compaction</strong></h4><p>RocksDB performs compactions in the background where multiple SSTables files are merged. It also removes deleted keys from the file. A new SSTable file is created and then put into the same or different LSM level. The following options can be used to tune the whole process:</p><p>level0_file_num_compaction_trigger — L0 is the first level in RocksDB. It is where all Memtables are flushed. You can control the number of files that should be present in the L0 level before a flush is triggered. Increasing this config means compactions occur much less frequently but need to process a lot of data.</p><p>level0_slowdown_writes_trigger — Compactions occur on low-priority threads by default in RocksDB. Compactions may get delayed due to no available threads. In such cases, you may want to slow down writes so compactions don’t have to process extreme amounts of data when they get scheduled. This config is used for that. By default, the limit is 20 files at the L0 level.</p><p>level0_stop_writes_trigger — Similar to the previous, but once the number of files at the L0 level reaches this limit, we stop processing writes altogether and may schedule compaction. By default, the limit is 36 files.</p><p>target_file_size_base — RocksDB needs an estimate of how large files it should create on each level starting from L1. By default, this file size is 64MB for L1, and then it gets multiplied by target_file_size_multiplier for the next level. By default, the multiplier is 1, so all levels will have similar-sized files.</p><p>max_bytes_for_level_base — Max amount of data that can be present in L1-Level. By default, this is 256 MB. For levels greater than L1, this base size is multiplied by max_bytes_for_level_multiplier. So L2 can contain 256 * 10 = 2GB of data, L3 can contain 256 * 10 ** 2 = 20GB of data, and so on.</p><p>subcompactions — Compactions that occur for L0 → Ln files can’t run in parallel. The only way to speed them up is to partition the data into appropriate key ranges and then run sub-compactions on each of those partitions. By default, it is 1, which means the sub-compactions are disabled. You can refer to the <a href="https://github.com/facebook/rocksdb/wiki/Sub-Compaction">official wiki</a> for more scenarios in which sub-compactions can help.</p><p>disable_auto_compactions — Disables the compactions altogether. You should use it for Bulk loads when a lot of data is written to RocksDB. Post the bulk load, and you can trigger manual compaction, so the number of files gets reduced.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*juYIgmN__VzTvQbrYa-bFQ.png" /></figure><h3>Use the Fastest Route!</h3><p>RocksDB uses an LSM tree for managing data. LSM trees are quite good for write-heavy workloads but struggle under read-heavy workloads. The DB has to go through a lot of files to get a particular key. Besides enabling Cache, RocksDB offers multiple ways to improve read speeds by reducing lookups. Here’s those methods:</p><p>Bloom filters — RocksDB allows SSTable level bloom filters so you can skip reading indexes if a key is not in a particular file. You can set the number of bits to control the accuracy of a bloom filter.</p><p>whole_key_filtering — If you do a lot of point lookups, generally, using this setting can give you better results. All the filters, such as the bloom filter, will use all the bytes of the key rather than only a few prefix bytes. This option only affects the SSTables.</p><p>Hash index — RocksDB uses a binary search index by default to look up blocks and then search for keys inside a block. You can, however, switch to the hash index. This allows for faster lookups but uses more space. You can use the following configs to enable and tune the hash index.</p><ul><li>data_block_index_type — kDataBlockBinaryAndHash. This adds a hash index at the end of the data block. Without it, only binary search will be used to find a key inside a data block.</li><li>data_block_hash_table_util_ratio — This is the load_factor for this hashtable, i.e., (number_of_entries / number_of_buckets). Setting it to less than 1 means there are more buckets than the number of entries reducing the number of reads for point lookups. Setting it to &gt; 1 means each bucket can contain multiple elements, but the space used by the hash index will be lower.</li><li>kHashSearch — This modifies the Index block to use hash search instead of binary search to lookup data blocks. This differs from the above config as it is used to find data blocks in a file. The kDataBlockBinaryAndHash is used to find keys/restart intervals inside a data block.</li></ul><p>memtable_prefix_bloom_size_ratio — By default, the bloom filters are created at file levels. If you enable this option, a bloom filter is also created in the memtable for queries that go beyond the memtable to disk. The config determines the ratio of the memtable (i.e., write_buffer_size) that should be used for this bloom filter. Max value allowed is 0.25, i.e., 25%.</p><p>memtable_whole_key_filtering — If set to true, the bloom filter created using the previous config will contain complete key bytes instead of just prefixes. This is useful for point lookups. This option is different from whole_key_filtering, which affects only the SSTable files.</p><p>prefix_extractor — You can also tell RocksDB to use only the first few bytes of your keys in the indices instead of using the whole key. This helps in reducing space used for indices and filters. It also optimizes range queries. The most commonly used one is the FixedLen prefix extractor, where you can specify the number of bits it should use from the key.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*vWFHO1HYAqmGxRt_81BWTg.png" /></figure><h3>Conclusion</h3><p>The config options mentioned here are not exhaustive. You can scourge through the official wiki OR lookup comments in one of the options C++ file linked at the bottom. RocksDB offers certain helper methods, which you should use to set good enough defaults before you tune the options yourself. Examples of such methods include the following:</p><ul><li>options.OptimizeForPointLookup — To improve read latencies</li><li>options.PrepareForBulkLoad — To reduce compaction and rate-limiting while loading a lot of data in the DB</li><li>options.OptimizeLevelStyleCompaction — To improve compaction speeds and minimize the total number of compactions</li></ul><p>Also, don’t forget to set your table format version to the latest. <strong>By default, it is 4 for backward compatibility, but the latest version is 5</strong>. By making this change, you’ll be able to take advantage of all the SSTable improvements in the latest RocksDB releases.</p><p>You can use the following references to learn more about RocksDB:</p><ul><li><a href="https://github.com/facebook/rocksdb/blob/main/options/options.cc">Options C++ Code</a> which contains most of the preset options</li><li><a href="https://github.com/facebook/rocksdb/wiki/MemTable">Memtable Wiki</a></li><li><a href="https://github.com/facebook/rocksdb/wiki/Block-Cache">Block Cache Wiki</a></li></ul><pre><strong>Want to Connect?</strong><br><br>You can find me on LinkedIn, Twitter, or send an email to <br><a href="/service/mailto:kharekartik@gmail.com">kharekartik@gmail.com</a> to share feedback.</pre><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=246af1e1d3f9" width="1" height="1" alt=""><hr><p><a href="https://medium.com/better-programming/navigating-the-minefield-of-rocksdb-configuration-options-246af1e1d3f9">Navigating the Minefield of RocksDB Configuration Options</a> was originally published in <a href="https://betterprogramming.pub/">Better Programming</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[How to Package Java Projects in Python Tar files]]></title>
            <link>https://codeburst.io/how-to-package-java-projects-in-python-tar-files-b9b3ff7a0627?source=rss-2c9d8b2edb6e------2</link>
            <guid isPermaLink="false">https://medium.com/p/b9b3ff7a0627</guid>
            <category><![CDATA[java]]></category>
            <category><![CDATA[apache]]></category>
            <category><![CDATA[programming]]></category>
            <category><![CDATA[python]]></category>
            <category><![CDATA[software-development]]></category>
            <dc:creator><![CDATA[Kartik Khare]]></dc:creator>
            <pubDate>Tue, 02 Mar 2021 00:40:12 GMT</pubDate>
            <atom:updated>2021-03-02T06:42:31.054Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*3Sor6HO5ug2kIbvq" /><figcaption>Photo by <a href="https://unsplash.com/@chuttersnap?utm_source=medium&amp;utm_medium=referral">CHUTTERSNAP</a> on <a href="https://unsplash.com/?utm_source=medium&amp;utm_medium=referral">Unsplash</a></figcaption></figure><p>Before diving into this article, I should state that — as a developer — any situation requiring placing a language A project into a language B package should occur very rarely. Most of the time it’s preferable to consider re-designing the interaction between various language components in these situations. But what if this situation is unavoidable? Open source projects such as <a href="https://flink.apache.org/">Apache Flink</a> and <a href="https://spark.apache.org/">Apache Spark</a> serve as examples. These projects have been written completely in Java but also have python modules available for those who don’t want to use the Java API.</p><p>However, Flink and Spark depend on <a href="https://hadoop.apache.org/">Apache Hadoop</a> so they don’t have a choice to write their code in a language other than Java. Also, the architecture is such that java code can’t be deployed in the cloud as an API and the python module can simply call the API as is the case with most database drivers.</p><h3>Structure of the project</h3><p>We must first modify the structure of the project so that the python module can easily discover the java modules. To do this we will keep the project structure as follows</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/902/1*R7LMJgkm5W2lTpc3PmGB0g.png" /></figure><p>In the python project, we’ll create two files</p><ul><li><strong>MANIFEST.in</strong></li><li><strong>setup.py</strong></li></ul><p>The <strong>MANIFEST.in</strong> file contains the directories which need to be packaged in the python tar file.</p><p>The setup.py file contains the code to create the package and discover the java modules at runtime.</p><h3>Create a test code</h3><p>First, let’s create a java class Main.classthat just prints the class name in the stdout. We will package this class in the jar file dummy-java-module-1.0-SNAPSHOT.jar</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/b17670331d95ca2cfd4d7418202c4fc8/href">https://medium.com/media/b17670331d95ca2cfd4d7418202c4fc8/href</a></iframe><p>Next, we’ll implement the file dummy.py which just executes this jar using the standard java command java -cp /path/to/dummy-java-module-1.0-SNAPSHOT.jar org.example.Main</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/da87cbac24cd19638ae7d97074772e1d/href">https://medium.com/media/da87cbac24cd19638ae7d97074772e1d/href</a></iframe><p>We will put the dummy.py file in a directory called java_integration_lib . Now let’s take a look at how to discover the path of the jar file at runtime. We’ll be implementing thefind_modules code used in this step.</p><h3>Discover Java Jar path</h3><p>This is the most critical step of this process. As a developer, you can’t hardcode the path of the Jar in the dummy.py file. The path needs to be discovered at runtime.</p><p>If you are running the jar from the source directory, you can simply use the path of the dummy-java-module for running the jar.</p><p>But if the module is being run after being imported, then you need to use the path of the packaged jar. In the package, we will put the jar in /jars the directory. The code to do this is provided in the next section.</p><p>We will create a new file find_modules.py . In this file, we will implement the following code</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/f67d76b714e9480446bff6719452a323/href">https://medium.com/media/f67d76b714e9480446bff6719452a323/href</a></iframe><p>The code does two things -</p><ol><li>Check if the find_modules.py is in the source directory. If yes, simply use the path of the jar in thetarget folder.</li><li>If the find_modules.py is not in the source directory, it is being imported as a module. In that case, we get the jar from /jars the directory inside the module’s installation directory (which is generally /usr/lib/python3.7/site-packages/ )</li></ol><p>We also check if the jar is actually present in these directories. If it is not, then the code throws an exception.</p><h3>Package the code</h3><p>Now the final step is to package the code in the tar.gz file which can be installed using pip . We first compile the java module into a jar file using the command</p><p>mvn clean package</p><p>Once done, change the directory to the dummy-python-module . We write the following code in the setup.py file.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/15c95099ca2caef40aad9b75c4b1545f/href">https://medium.com/media/15c95099ca2caef40aad9b75c4b1545f/href</a></iframe><p>The code can be divided into the following parts -</p><ul><li>Create a jars directory and copy the jar file in this directory</li><li>Declare the packages and the corresponding files in those packages<br>We declare a python package java_integration_lib.jars .</li><li>Since this package doesn’t exist by default, we tell the setup module to create a directory jars for this package. Note that, this jars directory is different from the one created in the previous step.</li><li>Then, we tell the setup to load only the *.jar files in this directory. The paths are interpreted as relative to the directory containing the package.</li><li>Run python setup function.</li></ul><p>For more details on how to package a simple module, you can look at <a href="https://packaging.python.org/">the official documentation</a>.</p><p>Now, in the MANIFEST.in the file you need to tell python that the jars the directory needs to be packaged in the python package.</p><p>You can do that by simply mentioning the command -</p><p>graft jars</p><p>Now simply run the following command</p><p>python setup.py sdist</p><p>This should create a directory dist inside the current directory and then add the python packagejava-integration-lib-1.0.0.tar.gz .</p><p>You can now install this python package using the command</p><p>pip install dist/java-integration-lib-1.0.0.tar.gz</p><h3>Test the hybrid package</h3><p>You can use the following code to test the package</p><pre>from java_integration_lib import dummy<br><br>dummy.foo_bar()</pre><p>If correctly installed you should get the output -</p><p>b&#39;Java class org.example.Main executed!!\n&#39;</p><p>The complete working demo code can be found in my repository — Thanks for reading!</p><p><a href="https://github.com/KKcorps/python-java-hybrid-example">KKcorps/python-java-hybrid-example</a></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=b9b3ff7a0627" width="1" height="1" alt=""><hr><p><a href="https://codeburst.io/how-to-package-java-projects-in-python-tar-files-b9b3ff7a0627">How to Package Java Projects in Python Tar files</a> was originally published in <a href="https://codeburst.io/">codeburst</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Utilize UDFs to Supercharge Queries in Apache Pinot]]></title>
            <link>https://medium.com/apache-pinot-developer-blog/utilize-udfs-to-supercharge-queries-in-apache-pinot-e488a0f164f1?source=rss-2c9d8b2edb6e------2</link>
            <guid isPermaLink="false">https://medium.com/p/e488a0f164f1</guid>
            <category><![CDATA[analytics]]></category>
            <category><![CDATA[software-development]]></category>
            <category><![CDATA[sql]]></category>
            <category><![CDATA[real-time-analytics]]></category>
            <category><![CDATA[programming]]></category>
            <dc:creator><![CDATA[Kartik Khare]]></dc:creator>
            <pubDate>Tue, 29 Sep 2020 15:30:04 GMT</pubDate>
            <atom:updated>2023-01-03T07:47:30.669Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*VtswFI-HcaXyyjhK" /><figcaption>Photo by <a href="https://unsplash.com/@hishahadat?utm_source=medium&amp;utm_medium=referral">Shahadat Rahman</a> on <a href="https://unsplash.com/?utm_source=medium&amp;utm_medium=referral">Unsplash</a></figcaption></figure><p>Apache Pinot is a realtime distributed OLAP datastore that can answer hundreds of thousands of queries with millisecond latencies. You can head over to <a href="https://pinot.apache.org/">https://pinot.apache.org/</a> to get started with Apache Pinot.</p><p>While using any database, we can come across a scenario where a function required for the query is not supported out of the box. In such time, we have to resort to raising a pull request for a new function or finding a tedious workaround.</p><p>Pinot aims to solve this particular pain-point by giving users the power to add their functions with almost zero lines of code. The 0.5.0 release comes bundled with two such features —</p><ul><li>Support for Scalar Functions that allow users to write and add their functions as a plugin.</li><li>Support for inline <a href="https://groovy-lang.org/">Apache Groovy</a> scripts in SQL queries.</li></ul><p>In this article, we’ll be focusing on Scalar functions.</p><h3>What are Scalar Functions?</h3><p>Scalar functions in Pinot are stateless functions which transform input A into output A. e.g. upper , lower , length etc. are scalar functions. However, sum , count are non-scalar functions.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/f8baf0b4217a1c8fa06303a707af948c/href">https://medium.com/media/f8baf0b4217a1c8fa06303a707af948c/href</a></iframe><p>You can take a look at the scalar functions supported out of the box in <a href="https://github.com/apache/incubator-pinot/tree/master/pinot-common/src/main/java/org/apache/pinot/common/function/scalar">official repository</a>.</p><h3>Implement a scalar function</h3><p>Let’s create a maven java project. We’ll call this project scalar-function-example.</p><p>Scalar functions require @ScalarFunction annotation. This annotation is present in pinot-spi package. You can add the package dependency in maven as follows —</p><pre>&lt;dependency&gt;<br>  &lt;groupId&gt;org.apache.pinot&lt;/groupId&gt;<br>  &lt;artifactId&gt;pinot-spi&lt;/artifactId&gt;<br>  &lt;version&gt;0.11.0&lt;/version&gt;<br>  &lt;scope&gt;provided&lt;/scope&gt;<br>&lt;/dependency&gt;</pre><p>Let’s create a class CustomScalarFunctions that will contain all of our functions. Let’s write a java method that converts latitude and longitude to <a href="https://www.movable-type.co.uk/scripts/geohash.html">geohash encoding</a>. Generally, Geohash encoders are not available out of the box in most of the database.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/a485475fbb32282326c40e673bf71105/href">https://medium.com/media/a485475fbb32282326c40e673bf71105/href</a></iframe><p>Now, you need to annotate this function with @ScalarFunction. The annotation supports the following arguments —</p><ul><li><strong>name </strong>— The name of the function to be used while querying. e.g., the Java method name can be calculateLength , but the query name can be calculate_length_str. The default is the same as the method name.</li><li><strong>enable </strong>— Boolean value indicating whether this function should be registered or not for the usage. This can be used in particular scenarios where you may require to disable a function because of some error or duplicate methods.</li></ul><p>Finally, the class should be present in the package name, which follows the following pattern — org.apache.pinot.*.function.*</p><p>This is because we find the custom methods using reflection, and currently, only the packages with the mentioned pattern are considered for the search. The final code should look as follows</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/83e698483985079ba619c7755cfe601e/href">https://medium.com/media/83e698483985079ba619c7755cfe601e/href</a></iframe><h3>Register the Scalar Function</h3><p>Once you have written the code, just compile the code into a JAR file.</p><p>Next, if you haven’t downloaded Pinot, you can do that from <a href="https://pinot.apache.org/download/">our website</a>.</p><p>Note that scalar functions are supported only in releases 0.5.0 and above.</p><p>Now, copy the JAR in theplugins or lib directory of the Pinot binary distribution and restart the Pinot cluster.</p><h3>Use the Scalar Function</h3><p>Now you can use the registered function as follows</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/b4128b9a97cb1919d335692950f13362/href">https://medium.com/media/b4128b9a97cb1919d335692950f13362/href</a></iframe><p>All of the arguments can either be literal constants or column names. An exception will be thrown if the column’s data type doesn’t match the input data type.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*xjGd7yem6IUsMaq4T8KzkQ.png" /><figcaption>custom function demo</figcaption></figure><h3>RoadMap</h3><p>We saw how easy it is to add your methods to Pinot for various use cases. The current implementation has few limitations —</p><ul><li>Only the following data types are supported in Input and output — Integer, Long, Float, Double, String, and Bytes. Objects such as Lists, Map are not supported currently.</li><li>You can return only a single value.</li><li>No support for multi-valued columns.</li><li>Only Java methods are supported as of the moment.</li></ul><p>We are working on removing most of these limitations in future releases.</p><h4>Groovy Functions</h4><p>Pinot also supports Apache Groovy script in SQL which you can use to write custom functions. It requires no additional code from the user. You can directly use the scripts in the query as follows —</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/d860f71824e9e7aa0997779ded695ee3/href">https://medium.com/media/d860f71824e9e7aa0997779ded695ee3/href</a></iframe><p>Read <a href="https://docs.pinot.apache.org/users/user-guide-query/scalar-functions">our official documentation</a> for more info on both Groovy and Scalar functions.</p><p>You can find the complete demo code in <a href="https://github.com/KKcorps/pinot-examples">the pinot-examples repository.</a></p><p>If you liked this article, head over to <a href="https://pinot.apache.org/">https://pinot.apache.org</a> and try it out today.</p><p>You can head over to <a href="https://communityinviter.com/apps/apache-pinot/apache-pinot">our slack workspace </a>for any queries or discussions.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=e488a0f164f1" width="1" height="1" alt=""><hr><p><a href="https://medium.com/apache-pinot-developer-blog/utilize-udfs-to-supercharge-queries-in-apache-pinot-e488a0f164f1">Utilize UDFs to Supercharge Queries in Apache Pinot</a> was originally published in <a href="https://medium.com/apache-pinot-developer-blog">Apache Pinot Developer Blog</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Leverage Plugins to Ingest Parquet Files from S3 In pinot]]></title>
            <link>https://medium.com/apache-pinot-developer-blog/leverage-plugins-to-ingest-parquet-files-from-s3-in-pinot-decb12e4d09d?source=rss-2c9d8b2edb6e------2</link>
            <guid isPermaLink="false">https://medium.com/p/decb12e4d09d</guid>
            <category><![CDATA[distributed-systems]]></category>
            <category><![CDATA[pinot]]></category>
            <category><![CDATA[s3]]></category>
            <category><![CDATA[spark]]></category>
            <category><![CDATA[big-data]]></category>
            <dc:creator><![CDATA[Kartik Khare]]></dc:creator>
            <pubDate>Tue, 18 Aug 2020 14:05:20 GMT</pubDate>
            <atom:updated>2020-10-07T13:13:06.182Z</atom:updated>
            <content:encoded><![CDATA[<h3>Leverage Plugins to Ingest Parquet Files from S3 in Pinot</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*afbs7azGt-GpSVeP" /><figcaption>Photo by <a href="https://unsplash.com/@feelfarbig?utm_source=medium&amp;utm_medium=referral">Feelfarbig Magazine</a> on <a href="https://unsplash.com/?utm_source=medium&amp;utm_medium=referral">Unsplash</a></figcaption></figure><p>One of the primary advantages of using Pinot is its <a href="https://docs.pinot.apache.org/developers/plugin-architecture">pluggable architecture</a>. The plugins make it easy to add support for any third-party system which can be an execution framework, a filesystem, or input format.</p><p>In this tutorial, we will use three such plugins to easily ingest data and push it to our Pinot cluster. The plugins we will be using are -</p><ul><li>pinot-batch-ingestion-spark</li><li>pinot-s3</li><li>pinot-parquet</li></ul><p>You can check out <a href="https://docs.pinot.apache.org/basics/data-import/batch-ingestion">Batch Ingestion</a>, <a href="https://docs.pinot.apache.org/basics/data-import/pinot-file-system">File systems</a>, and <a href="https://docs.pinot.apache.org/basics/data-import/pinot-input-formats">Input formats</a> for all the available plugins.</p><h3>Setup</h3><p>We are using the following tools and frameworks for this tutorial -</p><ul><li><a href="https://spark.apache.org/">Apache Spark 2.2.3</a> (Although any spark 2.X should work)</li><li><a href="https://parquet.apache.org/">Apache Parquet 1.8.2</a></li><li><a href="https://aws.amazon.com/s3/">Amazon S3</a></li><li><a href="https://pinot.apache.org/">Apache Pinot 0.4.0</a></li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*cqBLpHon_ahLTYyiY8vEOQ.png" /><figcaption>Pinot Ingestion and Query flow</figcaption></figure><h3>Input Data</h3><p>We need to get input data to ingest first. For our demo, we’ll just create some small Parquet files and upload them to our S3 bucket. The easiest way is to create CSV files and then convert them to Parquet. CSV makes it human-readable and thus easier to modify the input in case of some failure in our demo. We will call this file students.csv</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/e256508414f18acbc73a490fd115d8f1/href">https://medium.com/media/e256508414f18acbc73a490fd115d8f1/href</a></iframe><p>Now, we’ll create Parquet files from the above CSV file using Spark. Since this is a small program, we will be using the Spark shell instead of writing a full-fledged Spark code.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/a6befbea3dac0936aea4acd638d0eaa7/href">https://medium.com/media/a6befbea3dac0936aea4acd638d0eaa7/href</a></iframe><p>The .parquet files can now be found in /path/to/batch_input directory. You can now upload this directory to S3 either using their UI or running the following command</p><pre>aws s3 cp /path/to/batch_input s3://my-bucket/batch-input/ --recursive</pre><h3>Create Schema and Table</h3><p>We need to create a table to query the data that will be ingested. All tables in Pinot are associated with a schema. You can check out <a href="https://docs.pinot.apache.org/configuration-reference/table">Table configuration</a> and <a href="https://docs.pinot.apache.org/configuration-reference/schema">Schema configuration</a> for more details on creating configurations.</p><p>For our demo, we will have the following schema and table configs</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/6ed4882ad7cefba1bb131f58aa7fa522/href">https://medium.com/media/6ed4882ad7cefba1bb131f58aa7fa522/href</a></iframe><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/c30625884adfa8fcd3534b986d09a057/href">https://medium.com/media/c30625884adfa8fcd3534b986d09a057/href</a></iframe><p>We can now upload these configurations to Pinot and create an empty table. We will be using pinot-admin.sh CLI for this purpose.</p><pre>pinot-admin.sh AddTable -tableConfigFile /path/to/student_table.json -schemaFile /path/to/student_schema.json -controllerHost localhost -controllerPort 9000 -exec</pre><p>You can check out <a href="https://docs.pinot.apache.org/operators/cli">Command-Line Interface (CLI)</a> for all the available commands.</p><p>Our table will now be available in the <a href="https://docs.pinot.apache.org/basics/components/exploring-pinot">Pinot data explorer</a></p><h3>Ingest Data</h3><p>Now that our data is available in S3 as well as we have the Tables in Pinot, we can start the process of ingesting the data. Data ingestion in Pinot involves the following steps -</p><ul><li>Read data and generate compressed segment files from input</li><li>Upload the compressed segment files to the output location</li><li>Push the location of the segment files to the controller</li></ul><p>Once the location is available to the controller, it can notify the servers to download the segment files and populate the tables.</p><p>The above steps can be performed using any distributed executor of your choice such as Hadoop, Spark, Flink, etc. For this demo, we will be using Apache Spark to execute the steps.</p><p>Pinot provides runners for Spark out of the box. So as a user, you don’t need to write a single line of code. You can write runners for any other executor using our provided interfaces.</p><p>First, we will create a job spec configuration file for our data ingestion process.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/53e10cd29a50237011da34bf56e7abd2/href">https://medium.com/media/53e10cd29a50237011da34bf56e7abd2/href</a></iframe><p>In the job spec, we have kept the execution framework as spark and configured the appropriate runners for each of our steps. We also need a temporary stagingDir for our spark job. This directory is cleaned up after our job has executed.</p><p>We also provide the S3 Filesystem and Parquet reader implementation in the config to use. You can refer <a href="https://docs.pinot.apache.org/configuration-reference/job-specification">Ingestion Job Spec </a>for a complete list of configurations.</p><p>We can now run our Spark job to execute all the steps and populate data in Pinot.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/f337f2cc2118c6284abd33bfb3deec5e/href">https://medium.com/media/f337f2cc2118c6284abd33bfb3deec5e/href</a></iframe><p>In the command, we have included the JARs of all the required plugins in the Spark’s driver classpath. In practice, you only need to do this if you get a ClassNotFoundException.</p><p>Voila! Now our data is successfully ingested. Let’s try to query it from Pinot’s broker.</p><pre>bin/pinot-admin.sh PostQuery -brokerHost localhost -brokerPort 8000 -queryType sql -query &quot;SELECT * FROM students LIMIT 10&quot;</pre><p>If everything went right, you should receive the following output</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/023e9cd6c05f1d210dd1022e56ec3e69/href">https://medium.com/media/023e9cd6c05f1d210dd1022e56ec3e69/href</a></iframe><p>You can also view the results in the Data explorer UI.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*h4mL3HcILKHjmTRdo2grHg.png" /></figure><p>Pinot’s powerful pluggable architecture allowed us to successfully ingest parquet records from S3 with just a few configurations. The process described in this article is highly-scalable and can be used to ingest billions of records with minimal latency.</p><p>You can check out Pinot on <a href="https://pinot.apache.org/">the official website</a>. Refer to <a href="https://docs.pinot.apache.org/">our documentation</a> to get started with the setup and in the case of any issues, the community is there to help on the <a href="http://apache-pinot.slack.com/">official slack channel</a>.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=decb12e4d09d" width="1" height="1" alt=""><hr><p><a href="https://medium.com/apache-pinot-developer-blog/leverage-plugins-to-ingest-parquet-files-from-s3-in-pinot-decb12e4d09d">Leverage Plugins to Ingest Parquet Files from S3 In pinot</a> was originally published in <a href="https://medium.com/apache-pinot-developer-blog">Apache Pinot Developer Blog</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Learning Multi-dimensional indices: The next big thing in OLAP DBs]]></title>
            <link>https://medium.com/data-science/learning-multi-dimensional-indices-a7aaa2044d8e?source=rss-2c9d8b2edb6e------2</link>
            <guid isPermaLink="false">https://medium.com/p/a7aaa2044d8e</guid>
            <category><![CDATA[big-data]]></category>
            <category><![CDATA[database]]></category>
            <category><![CDATA[software-development]]></category>
            <category><![CDATA[machine-learning]]></category>
            <category><![CDATA[programming]]></category>
            <dc:creator><![CDATA[Kartik Khare]]></dc:creator>
            <pubDate>Thu, 09 Apr 2020 16:25:39 GMT</pubDate>
            <atom:updated>2020-04-09T16:25:39.497Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*yVYLjTnCafgm7o7M" /><figcaption>Photo by <a href="https://unsplash.com/@franki?utm_source=medium&amp;utm_medium=referral">Franki Chamaki</a> on <a href="https://unsplash.com/?utm_source=medium&amp;utm_medium=referral">Unsplash</a></figcaption></figure><p>The overflow of data in the world opened up a multitude of opportunities to learn and analyze the behavior of people all around the world. Most analyses require at least a few days of data if not more which results in a need of a fast queryable storage engine. OLAP databases exist to serve this purpose only i.e. to make huge amounts of data easily queryable with minimal latencies.</p><p>To minimize the latency, all databases have indices created on data. The index is generally a tree-based structure such as B-tree, R-Tree, etc. which based on some fixed key will directly provide you the row or the block containing the data rather than having you scan all the rows.</p><p>The unique issue with OLAP databases is that the queries can be done on multiple columns at once e.g. a query to get a total number of orders by users, date, and city. Technically, you can create indices on multiple dimensions as well but you will have to make assumptions on what query patterns the user will follow. You also have to assume the amount of data each column will hold to make an effective indexing decision.</p><p>What if the database itself adapted its index according to the data inserted? Learned Multi-dimensional Indexes are an effort to answer this particular problem. In this blog, we are going to take a look at one of those algorithms.</p><h4>Flood</h4><p>Flood algorithm has been designed for in-memory indexes. It can be modified to use in the OLTP databases as well. There are two key ideas behind Flood:</p><ol><li>Use a sample query filter workload to determine how often certain dimensions are used, which ones are used together and which ones are more selective than others. Based on this info, customize the entire layout to optimize performance.</li><li>Use empirical CDF models to flatten multi-dimensional skewed data into a uniform space.</li></ol><p>Let us assume an index needs to be created on d-dimensional data. In such data, there is no natural sort order. So, the algorithm first chooses a dimension that will be used for sorting. The algorithm then creates a d-1 dimensional grid where each dimension is divided into equally spaced columns. Each cell in such a grid contains multiple data points.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/832/1*K0QKG5C3l1Z25CGTBuHwHg.png" /></figure><p>To ensure that data points are distributed uniformly, they are normalized using the min and max value of the particular dimension. The last dimension is used for sorting the data within each cell.</p><h4>Query</h4><p>A query generally consists of k dimensions where k &lt; d. Since the query already contains the range of values for each dimension that need to be queried, we can simply select all the cells which lie within that range along with cells that have partial overlap. This step is known as Projection.</p><p>For the cells which have partial overlap, we can use the fact that the data in them is sorted and select the relevant data using binary search. This is only possible when you have the sort dimension as a part of the query. This step is known as Refinement.</p><p>Once we have all the data, we can refine it further to check if any out of range data is there or not and then return the result to the user. This step is known as Scan.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*EBjT-_ImbRcOT-oBfuVIVw.png" /></figure><h4>Layout optimization</h4><p>The primary strength of FLOOD lies in its ability to optimize the data layout to minimize the query latency. To minimize the latency, you first need a proxy to determine the query performance. The algorithm uses a custom cost function to serve the purpose. The cost function is composed of three parts:</p><ul><li><em>wpNc</em>, where wp is the average time to perform refinement on a cell and Nc, is the total number of cells in the grid.</li><li><em>wrNc</em> where wr is the average time to perform refinement on a cell and Nc is the total number of cells in the grid.</li><li><em>wsNs</em>, where ws is the average time to perform each scan and Ns, is the total number of scanned data points.</li></ul><p>The model for query time then can be calculated as -</p><p>wpNc + wrNc + wsNs</p><p>The next step is to calculate the weights wp, wr, and ws. For this Flood uses a simple model that takes features such as the total number of cells, the mean, median and tail quantile of the sizes of filterable cells, the number of dimensions, etc. Flood only trains the weight model once and re-uses it for multiple data layouts.</p><p>The final step is to optimize the layout which consists of fiddling with the dimensions for the sort dimension and the number of columns in each dimension.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*pQS0OuCGe-0JFCoeNTGdng.png" /></figure><p>In each iteration, the algorithm chooses one of the d dimensions as the sort dimension. Rest all the dimensions are used to create the d-1 dimensional grid. Then, it runs a gradient descent algorithm to determine the number of columns that minimizes the query time.</p><p>Flood re-tunes the data layout for each new workload. Since it is not possible by DB administrator to generate most probable queries to provide as an input to the model, Flood itself generates queries for training by randomizing some dimensions for grouping, others for filtering and leaving the rest. The group by aggregate functions are also randomized.</p><h4>Conclusion</h4><p>Flood algorithm marks a significant milestone on a path of self-learning indices. It is highly practical and can be used in a real-world database as demonstrated in the paper. Flood however still suffers from drawbacks that need to be fixed to make it a completely generic algorithm. Some of those drawbacks are -</p><ul><li>It doesn’t support the insertion of new data. The whole dataset needs to be fitted again in case a new data arrives. The current version is meant to be used only for read-only workloads.</li><li>It is single-threaded and can’t support concurrency in its current implementation.</li><li>Although Flood re-tunes itself to a new workload, it still struggles to determine when has the workload changed enough for a retune operation to be triggered.</li></ul><p>In the end, I am hopeful that these issues will be fixed in the future and OLAP databases will end up becoming at least an order of magnitude faster as demonstrated in the paper.</p><p>You can use to following references to learn more about Learned data structures and indices:</p><ul><li><a href="https://arxiv.org/abs/1912.01668">Learning Multi-dimensional Indexes by Vikram Nathan, Jialin Ding, Mohammad Alizadeh, Tim Kraska</a></li><li><a href="https://arxiv.org/abs/1712.01208">The Case for Learned Index Structures by Tim Kraska, Alex Beutel, Ed H. Chi, Jeffrey Dean, Neoklis Polyzotis</a></li></ul><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=a7aaa2044d8e" width="1" height="1" alt=""><hr><p><a href="https://medium.com/data-science/learning-multi-dimensional-indices-a7aaa2044d8e">Learning Multi-dimensional indices: The next big thing in OLAP DBs</a> was originally published in <a href="https://medium.com/data-science">TDS Archive</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[A Glimpse into my “WFH in Quarantine” Life]]></title>
            <link>https://medium.com/@kharekartik/a-glimpse-into-my-wfh-in-quarantine-life-3957c885500?source=rss-2c9d8b2edb6e------2</link>
            <guid isPermaLink="false">https://medium.com/p/3957c885500</guid>
            <category><![CDATA[work-life-balance]]></category>
            <category><![CDATA[personal]]></category>
            <category><![CDATA[work-from-home]]></category>
            <category><![CDATA[lifestyle]]></category>
            <category><![CDATA[software-development]]></category>
            <dc:creator><![CDATA[Kartik Khare]]></dc:creator>
            <pubDate>Wed, 01 Apr 2020 19:28:27 GMT</pubDate>
            <atom:updated>2020-04-01T19:44:32.834Z</atom:updated>
            <content:encoded><![CDATA[<p>COVID-19 has taken over the world and has forced people to stay at home. I am no exception and have been working from home for the past 3 weeks.</p><p>The state of the world is gloomy and I wish it gets better as soon as possible. However, even in these tough times, there are a lot of things I am liking about WFH. I am fortunate enough in life to be able to do work from home in these tough times and this article in no way is meant to disrespect those who can’t.</p><h4>The Lab</h4><p>I currently work in a retail giant as a Software Engineer. My company already had all the remote working facilities such as Zoom, Slack, remote conferencing rooms, VPN, etc. I used to do WFH once a week or fortnight and the same is true for my team.</p><p>Due to the reasons mentioned above, I already had a minimal WFH setup. It consists of the following:</p><ul><li><a href="https://www.amazon.in/gp/product/B01BGI6GZC">LG 23 inch Full HD LCD monitor</a></li><li>MacBook Pro 15 inch 2018 model</li><li><a href="https://www.furlenco.com/package-groups/vitello-study-table-chair-special-price-264">A basic wooden table and Chair</a></li><li><a href="https://www.amazon.in/Sony-WH-1000XM3-Wireless-Cancellation-Headphones/dp/B07HZ8JWCL">Sony WH-1000MX3 Noise-canceling headphones</a> (The noise-canceling element is pretty useless these days)</li></ul><figure><img alt="WFH setup" src="https://cdn-images-1.medium.com/proxy/1*cWUg3bW3LiZ7oy5waIrH7g.jpeg" /><figcaption>WFH setup</figcaption></figure><p>All of this is set up in a separate room which is well sunlit. I used to work sitting in bed but that more often than not resulted in small unnecessary naps.</p><h4>The Experiment</h4><p>My sleep cycle has been completely disrupted these days. I am a latent night owl and the only reason I woke up early was to go to the office on time. Now, I usually sleep at around 2–3 AM and wake up at 10 AM because I make no compromise on 8 hours of sleep (except when deadlines are pretty strict or I am binge-watching a TV series). Strangely on weekend, I get up early around 7 because I need to buy the bread, eggs, and milk before they go out of stock.</p><p>After I am completely awake and done with shenanigans, I prepare a cup of coffee. I am a chai addict but I have switched to coffee because cleaning the tea leaves out of pan and sieve is not fun at all. I drink only half of what I have prepared so that I can have something to drink 2 hours later when I will again have the caffeine cravings.<br>I then have a small breakfast such as omelets or sandwiches. I scroll through <a href="https://news.ycombinator.com/">Hacker News</a> and <a href="https://techcrunch.com/">Techcrunch</a> for some latest news while sipping on my coffee.<br>Then, I proceed to clean my utensils which I left uncleaned last night due to lethargy.</p><p>By the time I have a bath, its around 11:30AM and time for my daily standup which lasts for about 30 min. Then, I sit, put on my headphones, start my <em>Liked songs</em> playlist on Spotify and proceed to work uninterrupted till 1:30PM.</p><figure><img alt="Liked Songs playlist (as you can see I have an eclectic taste in music)" src="https://cdn-images-1.medium.com/proxy/1*X2WAwV6CdvdfpMu-Ts0lzg.png" /><figcaption>Liked Songs playlist (as you can see I have an eclectic taste in music)</figcaption></figure><p>I then proceed to prepare my lunch. I have only started learning cooking so I cook only basic stuff like Lentils, Rice, Chapati/Paratha, Rajma and 2–3 potato-based curries. The general life hack these days is to prepare enough so that I can eat it in dinner as well (not applicable on weekends though).</p><p>I am generally done with the lunch break in 1.5 hours so around 3–3:30 PM, I start working again till 5:30PM. If I have a bazillion JIRA tickets remaining, I prepare a cup of coffee and work till some of them are done. Otherwise, I generally proceed to watch some random videos on YouTube because it thinks I should watch those and shows them in my feed.</p><p>Then its time for some light 15–30 min. of exercise. I use one Android app called Fat to Fit (which likely won’t happen :P). It has a fixed routine for a month so I don’t have to decide for myself.</p><p>After I am done, with all the exercise, I generally go to my balcony to unwind for 15–20 min. and then switch on my PS4 for a couple of sessions of FIFA 2019 (need to buy 2020) since its fun and not at all exhausting.</p><p>At around 7PM, when I have finally rested its time to either do some research for my blogs or read some books. These days I am reading <a href="https://www.goodreads.com/book/show/43723901-lifespan">Lifespan by David A. Sinclair</a> and <a href="https://www.goodreads.com/book/show/38743564-billion-dollar-whale">Billion Dollar Whale by Bradley Hope and Tom Wright</a>. I exclusively read non-fiction but balance heavy deep stuff with light and fun frequently as I have done in this case.</p><p>I then proceed to eat my dinner while watching news updates or lighthearted comedy TV series. Currently, I am watching Arrested Development on Hotstar. Since there is already so much doom and gloom around, I am avoiding dark movies completely although I certainly enjoy them more.</p><p>After dinner, if I am not exhausted, I either do some easy or medium questions on Leetcode or listen to a podcast and continue reading. I then proceed to shape up my drafts, adding one or two patches at a time or start watching some movie or anime depending on my mood. Finally, its time to sleep (in case something has not broken down in production).</p><h4>The Observations</h4><p>So as with all the activities in my life, there are both sides to the coin to the WFH situation. Let’s begin with what I like:</p><ul><li>I have become more efficient at getting my work done. This is primarily because I am not being interrupted for casual conversations or chai breaks in between.</li><li>My time management skills have upgraded a notch due to the fact I have to cook and clean myself along with a plethora of stuff I was already doing. I use <a href="https://www.notion.so/">Notion</a> to manage my daily life. Here’s what my dashboard looks like</li></ul><figure><img alt="Personal Notion Workspace" src="https://cdn-images-1.medium.com/proxy/1*tzZWCINmpFF-NVRGH4RpAg.png" /><figcaption>Personal Notion Workspace</figcaption></figure><ul><li>Not getting stuck in daily traffic and sucking all the smoke in the air while standing idle is definitely a big win.</li></ul><p>Now, what do I dislike and miss? Well,</p><ul><li>Cleaning floors for sure.</li><li>Struggle to buy groceries when all the delivery apps are assigning slots of 1 week and later.</li><li>Connecting with teammates for random chitchat over tea</li><li>Beer and Pizza</li></ul><p>Another unanticipated outcome of the WFH situation is that I have been connecting with my friends a lot more than usual. I have been lucky enough in life to have amazing friends. These days we frequently hold zoom calls now that most of us are trapped within 4 walls for weeks.</p><p>As Ralph Waldo Emerson once said</p><blockquote>It is one of the blessings of old friends that you can afford to be stupid with them</blockquote><p>On that note, I hope the lockdown situation gets over soon and we can catch up over a glass of beer and a night of intensive board games soon. Till then the only objective is to grow my knowledge and to not grow my body.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=3957c885500" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[How Does Zookeeper Servers Remain In sync?]]></title>
            <link>https://codeburst.io/how-does-zookeeper-servers-remain-in-sync-21e9b085e639?source=rss-2c9d8b2edb6e------2</link>
            <guid isPermaLink="false">https://medium.com/p/21e9b085e639</guid>
            <category><![CDATA[distributed-systems]]></category>
            <category><![CDATA[technology]]></category>
            <category><![CDATA[software-development]]></category>
            <category><![CDATA[big-data]]></category>
            <category><![CDATA[programming]]></category>
            <dc:creator><![CDATA[Kartik Khare]]></dc:creator>
            <pubDate>Mon, 30 Mar 2020 15:13:04 GMT</pubDate>
            <atom:updated>2020-04-01T21:30:55.392Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*n9XPXWHyUmGFYgBM" /><figcaption>Photo by <a href="https://unsplash.com/@gcsgpp?utm_source=medium&amp;utm_medium=referral">Gabriel Gusmao</a> on <a href="https://unsplash.com/?utm_source=medium&amp;utm_medium=referral">Unsplash</a></figcaption></figure><p><a href="https://zookeeper.apache.org/">Apache Zookeeper</a> is probably one of the most amusing and intricate distributed frameworks. It is generally used as an intermediary to keep distributed servers in sync. Implementing sync ourselves can lead to multiple race conditions. So, Developers use Zookeeper in various systems without even thinking twice about any alternative. The wide adoption serves as a testament to its robustness and performance.</p><p>A question then pops up: How does Zookeeper manage to keep its servers in sync. You can’t use a parent zookeeper to manage a child zookeeper, thus creating inception like scenario without the limbo.</p><p>Hopefully, this article will try to answer the above query.</p><h4>Leader and Followers</h4><p>Zookeeper servers always have one leader which manages the complete data. All writes first happen on the leader and then are synced to followers. The leader helps in deciding authority over the correct state at a time and provides a single point of coordination.</p><p>Reads can happen from any of the servers, including the leader. It is entirely possible that some follower is not in sync with the leader, and you might end up reading an older state.</p><p>To keep Followers and Leaders in sync, Zookeeper uses the ZAB protocol.</p><h3>ZAB</h3><p>Zookeeper leader keeps all the followers in sync using its custom protocol called <strong>ZAB (Zookeeper Atomic Broadcast Protocol)</strong>. It is a custom protocol designed for Zookeeper to solve challenges such as maintaining causal order among the data shared. All the state changes (known as transactions) in ZAB are idempotent and incremental. So applying a state change multiple times produces the same result. However, the order of changes needs to be maintained.<br>A transaction in ZAB is identified by a unique identifier called zxid which is a 64-bit integer composed of two 32-bit slices -</p><ul><li><strong>Epoch</strong> - An integer that is incremented every time a leader election happens.</li><li><strong>Counter</strong> - An integer counter that is incremented after every valid transaction.</li></ul><p>ZAB Protocol consists of three phases —</p><p>1. <strong>Discovery</strong> — determine who is the leader and how much data is missing</p><p>2. <strong>Synchronization</strong> — sync all the servers so that the missing data is updated</p><p>3. <strong>Broadcast</strong> — start transmitting transaction occurring in real-time</p><p>Before discussing each of these phases in detail, we need to understand what are the necessary guarantees that the protocol provides that make each of the above phases effective.</p><p>ZAB provides 3 necessary guarantees which are:</p><ul><li><strong>Integrity</strong> — If a process receives a transaction with zxid <strong>Z</strong> than some other process has broadcast a transaction with zxid <strong>Z</strong></li><li><strong>Total Order</strong> — If a process delivers transaction with zxid <strong>Z</strong> before one with zxid <strong>Z&#39;</strong> , than any other process which delivers <strong>Z&#39;</strong> must also deliver z, and it should deliver <strong>Z</strong> before <strong>Z&#39;</strong></li><li><strong>Agreement </strong>— If a process delivers z and another one delivers <strong>Z’</strong> , then either the first process should deliver <strong>Z’</strong> or the second process should deliver <strong>Z</strong>. This guarantees that the state of the two processes does not diverge.</li></ul><p>These three safety properties guarantee that the final state of all the servers is consistent once all the transactions have been delivered to each other.</p><p>All the phases occurring in ZAB are quite similar to two-phased commits. First, a proposal is sent by the leader, the follower on the receiving end sends an ACK, and then a commit is sent by the leader to complete the transaction.</p><h4>Discovery</h4><p>In this phase, the leader and follower decide which server contains the true history of the transactions which have occurred until now. <br>A prospective leader is chosen first using a simple leader election algorithm.<br>The process contains the following exchanges:</p><ol><li>Follower sends the last proposed epoch to the prospective leader.</li><li>The leader gets last accepted epoch from a quorum of followers and sends a new epoch, which is greater than all the epochs it has received.</li><li>If the new epoch is greater than last proposed epoch, followers update their proposed epoch and send their last acknowledged epoch along with their last zxid to leaders</li><li>The leader selects the history of the follower with the highest zxid and epoch as the truth.</li></ol><figure><img alt="" src="https://cdn-images-1.medium.com/max/942/1*wNKacZZfWdHtyUKOEXIhpA.png" /></figure><p>Zookeeper performs an optimization where it selects the server with the highest epoch and zxid as the prospective leader so that it already has all the data which needs to be synced. The following piece of code in <strong><em>FastLeaderElection.java</em></strong> in Zookeeper checks the following-</p><pre>/**<br>     * Check if a pair (server id, zxid) succeeds our<br>     * current vote.<br>     *<br>     * @param id    Server identifier<br>     * @param zxid  Last zxid observed by the issuer of this vote<br>     */<br>    protected boolean totalOrderPredicate(long newId, long newZxid, long newEpoch, long curId, long curZxid, long curEpoch) {<br>        LOG.debug(&quot;id: &quot; + newId + &quot;, proposed id: &quot; + curId + &quot;, zxid: 0x&quot; +<br>                Long.toHexString(newZxid) + &quot;, proposed zxid: 0x&quot; + Long.toHexString(curZxid));<br>        if(self.getQuorumVerifier().getWeight(newId) == 0){<br>            return false;<br>        }<br><br>        /*<br>         * We return true if one of the following three cases hold:<br>         * 1- New epoch is higher<br>         * 2- New epoch is the same as current epoch, but new zxid is higher<br>         * 3- New epoch is the same as current epoch, new zxid is the same<br>         *  as current zxid, but server id is higher.<br>         */<br><br>        return ((newEpoch &gt; curEpoch) ||<br>                ((newEpoch == curEpoch) &amp;&amp;<br>                ((newZxid &gt; curZxid) || ((newZxid == curZxid) &amp;&amp; (newId &gt; curId)))));<br>    }</pre><h4>Synchronisation</h4><p>In this phase, the history of transactions is synced across all the followers.</p><ol><li>The prospective leader proposes itself as the new leader since it has the highest zxid and epoch.</li><li>If the followers’ last accepted proposal has the same epoch as the new leader, it sets its current epoch as the same, sends ACK to the leader, and starts accepting all the missing transactions through a DIFF call.</li><li>Upon receiving the ACK from a quorum of followers, the leader sends a commit message and delivers all the missing transactions to the followers.</li></ol><figure><img alt="" src="https://cdn-images-1.medium.com/max/942/1*3GzIaOvafpDstkk8wEHaSg.jpeg" /></figure><p>This synchronisation phase also prevents causal conflicts. It guarantees that all processes in the quorum deliver transactions of prior epochs before transactions of the new epoch e are proposed.</p><h4>Broadcast</h4><p>This is the phase that occurs after a quorum of servers has decided a leader, appended the missing data, and is ready to accept new transactions.</p><ol><li>Leader proposes a transaction with zxid higher than all previous ids</li><li>Followers accept the proposed transaction from the leader and append it to their history. An ACK messaged is sent once the transaction is written to durable storage.</li><li>If the leader received ACK from a quorum of followers for the transaction, then it sends a commit message.</li><li>Followers on receiving a commit message broadcast the transactions among each other.</li></ol><figure><img alt="" src="https://cdn-images-1.medium.com/max/942/1*eSoaoRe1FtJidNgw9ZSXNQ.jpeg" /></figure><p>Each server in zookeeper executes one iteration of this protocol at a time. In case of an exception, such as epoch not matching with the leader, the servers can start a new iteration beginning from the first phase.</p><p>You can refer the following links to learn more about Zookeeper’s internals:</p><ol><li><a href="https://marcoserafini.github.io/papers/zab.pdf">Zab: High-performance broadcast for primary-backup systems</a></li><li><a href="https://zookeeper.apache.org/doc/r3.6.0/zookeeperInternals.html">ZooKeeper Internals</a></li><li><a href="http://shop.oreilly.com/product/0636920028901.do">ZooKeeper: Distributed Process Coordination</a></li></ol><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=21e9b085e639" width="1" height="1" alt=""><hr><p><a href="https://codeburst.io/how-does-zookeeper-servers-remain-in-sync-21e9b085e639">How Does Zookeeper Servers Remain In sync?</a> was originally published in <a href="https://codeburst.io/">codeburst</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Why Apache Airflow Is a Great Choice for Managing Data Pipelines]]></title>
            <link>https://medium.com/data-science/why-apache-airflow-is-a-great-choice-for-managing-data-pipelines-48effcce3e41?source=rss-2c9d8b2edb6e------2</link>
            <guid isPermaLink="false">https://medium.com/p/48effcce3e41</guid>
            <category><![CDATA[data]]></category>
            <category><![CDATA[big-data]]></category>
            <category><![CDATA[data-engineering]]></category>
            <category><![CDATA[programming]]></category>
            <category><![CDATA[software-engineering]]></category>
            <dc:creator><![CDATA[Kartik Khare]]></dc:creator>
            <pubDate>Mon, 20 Jan 2020 07:46:32 GMT</pubDate>
            <atom:updated>2020-01-20T11:00:41.466Z</atom:updated>
            <content:encoded><![CDATA[<h4>A glimpse at capabilities which makes Airflow better than its predecessors</h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*OcvSZKbjlPSW2mZN" /><figcaption>Photo by <a href="https://unsplash.com/@seiseisei?utm_source=medium&amp;utm_medium=referral">Seika I</a> on <a href="https://unsplash.com/?utm_source=medium&amp;utm_medium=referral">Unsplash</a></figcaption></figure><p><a href="https://airflow.apache.org/">Apache Airflow</a> is an open-source scheduler to manage your regular jobs. It is an excellent tool to organize, execute, and monitor your workflows so that they work seamlessly.</p><p>Apache Airflow solved a lot of problems that the predecessors faced. Let’s first understand the architecture, and then we’ll take a look at what makes Airflow better.</p><h4>DAGs</h4><p>DAGs (Directed Acyclic Graphs) represent a workflow in Airflow. Each node in a DAG represents a task that needs to be run. The user mentions the frequency at which a particular DAG needs to be run. The user can also specify the trigger rule for each task in a DAG. e.g., You may want to trigger an alert task right after one of the previous tasks fails.</p><p>Let us try to understand the various components of Airflow.</p><h4>Core components</h4><p>Airflow primary consists of the following components -</p><ol><li>Scheduler</li><li>Webserver</li><li>Executor</li><li>Backend</li></ol><h4>Scheduler</h4><p>It is responsible for scheduling your tasks according to the frequency mentioned. It looks for all the eligible DAGs and then puts them in the queue. If a DAG failed and retry is enabled, the scheduler will automatically put that DAG up for retry. The number of retries can be limited on a DAG level.</p><h4>Webserver</h4><p>The webserver is the frontend for Airflow. Users can enable/disable, retry, and see logs for a DAG all from the UI. Users can also drill down in a DAG to see which tasks have failed, what caused the failure, how long did the task run for, and when was the task last retried.</p><p>This UI makes Airflow superior to its competitors. e.g., In Apache Oozie, seeing logs for non-MR (map-reduce) jobs is a pain.</p><h4>Executor</h4><p>It is responsible for actually running a task. Executor controls on which worker to run a task, how many tasks to run in parallel, and update the status of the task as it progress.</p><p>You can run your task on multiple workers managed by Celery or Dask or Kubernetes.</p><p>The tasks are pulled from a queue, which can be either Redis or RabbitMQ.</p><p>By default, Airflow uses SerialExecutor, which only runs one task at a time on a local machine. This is not advised to be done in production.</p><h4>Backend</h4><p>Airflow uses MySQL or PostgreSQL to store the configuration as well as the state of all the DAG and task runs. By default, Airflow uses SQLite as a backend by default, so no external setup is required. The SQLite backend is not recommended for production since data loss is probable.</p><figure><img alt="Airflow components" src="https://cdn-images-1.medium.com/proxy/1*CEojZqU4FWcbOwOTgwttDw.jpeg" /><figcaption>Airflow components</figcaption></figure><h3>So what makes Airflow the right scheduler for Data pipelines?</h3><h4>Monitoring</h4><p>Airflow provides various methods of monitoring. You can see the status of the tasks from the UI. It sends an mail in case a DAG fails. You can also send the email if a task breaches the defined SLA. The logs for a task can also be viewed from the Airflow UI itself.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*E67lOsw-SvOyd8OEwXvMww.png" /><figcaption>Airflow DAG UI</figcaption></figure><h4>Lineage</h4><p>This feature came pretty recently in Airflow v1.10. Lineage allows you to track the origins of data, what happens to it, and where it moves over time, such as Hive table or S3/HDFS partition.</p><p>It comes pretty handily when you multiple data tasks reading and writing into storage. The user needs to define the input and output data sources for each task, and a graph is created in Apache Atlas, depicting the relationship between various data sources.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*T5xjyWfodXLpQD4gQUqCVg.png" /><figcaption>Example Apache Atlas instance graph (from <a href="https://atlas.apache.org/#/QuickStart)">https://atlas.apache.org/#/QuickStart)</a></figcaption></figure><h4>Sensors</h4><p>Sensors allow a user to trigger a task based on a certain pre-condition. The user needs to specify the type of sensor and the frequency at which they need to check for the condition. e.g., You can use the HDFS Partition sensor to trigger a task when a particular partition such as date is available.</p><h4>Customization</h4><p>Airflow also allows users to create their operators and sensors in case an already rich ecosystem of existing ones doesn’t satisfy your requirements. I wrote a SparkOperator because the official one didn’t allow me to tweak all the parameters. All the code is written in Python, which makes it easy for any developer to integrate.</p><p>Apart from all the benefits mentioned above, Airflow also has seamless integration with all the services in big data ecosystems such as Hadoop, Spark, etc. Since all the code is written in Python, getting started with Airflow will only take a couple of minutes. You can take a look at the <a href="https://airflow.apache.org/docs/stable/start.html">official quickstart guide</a>.</p><p>You can also explore <a href="https://databand.ai/">https://databand.ai/</a> for a much more powerful setup to monitor your data pipelines powered by Apache Airflow.</p><p><a href="https://databand.ai/">Databand - Data Pipeline Observability | Observability for DAGs and ML</a></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=48effcce3e41" width="1" height="1" alt=""><hr><p><a href="https://medium.com/data-science/why-apache-airflow-is-a-great-choice-for-managing-data-pipelines-48effcce3e41">Why Apache Airflow Is a Great Choice for Managing Data Pipelines</a> was originally published in <a href="https://medium.com/data-science">TDS Archive</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Deploying ML Models in Distributed Real-time Data Streaming Applications]]></title>
            <link>https://medium.com/data-science/deploying-ml-models-in-distributed-real-time-data-streaming-applications-217954a0b423?source=rss-2c9d8b2edb6e------2</link>
            <guid isPermaLink="false">https://medium.com/p/217954a0b423</guid>
            <category><![CDATA[programming]]></category>
            <category><![CDATA[machine-learning]]></category>
            <category><![CDATA[software-engineering]]></category>
            <category><![CDATA[big-data]]></category>
            <category><![CDATA[data-science]]></category>
            <dc:creator><![CDATA[Kartik Khare]]></dc:creator>
            <pubDate>Sat, 11 Jan 2020 22:18:50 GMT</pubDate>
            <atom:updated>2020-01-11T22:18:50.362Z</atom:updated>
            <content:encoded><![CDATA[<h4>Explore the various strategies to deploy ML models in Apache Flink/Spark or other realtime data streaming applications.</h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*Lw62ttKzZ87B3ckn" /><figcaption>Photo by <a href="https://unsplash.com/@franckinjapan?utm_source=medium&amp;utm_medium=referral">Franck V.</a> on <a href="https://unsplash.com/?utm_source=medium&amp;utm_medium=referral">Unsplash</a></figcaption></figure><p>Machine Learning has gone from zero to one in the past decade. The rise of ML can be seen as one of the most defining moments in the tech industry. Today ML models are ubiquitous in almost all the services.</p><p>One of the challenges which remain to date is the training and inference of models using real-time data. Let’s take a look at the various strategies which you can use in data streaming production jobs to make predictions.</p><h3>Model alongside the pipeline</h3><p>The natural approach for predictions on real-time data is to run your ML model in the pipeline processing the data.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*91pmmz85GYMstCMxIPKC_A.jpeg" /><figcaption>Deploying model in pipeline executors</figcaption></figure><p>This approach has two major complications -</p><ol><li>Integration of the pipeline’s code and the model’s code.</li><li>Optimizing the integrated pipeline to make efficient use of the underlying resources.</li></ol><h4>Integration</h4><p>Most of the real-time data pipelines are written using either Java or Python. Both <a href="https://spark.apache.org/">Apache Spark</a> and <a href="https://flink.apache.org/">Apache Flink</a> provide Python API. This allows for easy integration of models written using <a href="https://scikit-learn.org/stable/">Scikit-Learn</a> or <a href="https://www.tensorflow.org/">Tensorflow</a>.</p><p>You can also use <a href="https://spark.apache.org/mllib/">Spark MLlib</a> or <a href="https://github.com/FlinkML">Flink ML</a> to create models. These models are convenient to integrate, and you don’t have to worry about scaling and fault-tolerance.</p><p>But what if you have a pre-existing data pipeline which is written in Java or Scala? In that case, it makes much more sense to use Tensorflow Java API or third-party libraries such as <a href="https://github.com/combust/mleap">MLeap</a> or <a href="https://github.com/jpmml/jpmml-evaluator">JPMML</a> to export your Scikit-learn models and use them inside your code. JPMML supports a lot of models but MLeap is faster.</p><h4>Optimization</h4><p>The choice between Python and Java/Scala represents a tradeoff between versatility and performance. It would be best if you made the decision based on the use case, the amount of expected data, and the latency expected. I prefer Scala for most of the applications since the expected input records were in the order of millions per second.</p><p>Another optimization is the number of parallel executors that you should allocate to your model. If it’s a lightweight model such as Logistic Regression or a small Random forest, you can even run a single instance of the model and re-partition data to go to the single executor (this is never a good idea in production). For heavy models such as large random forests or deep neural nets, finding the right number of executors is mostly an exercise in trial &amp; error.</p><p>You might also need to optimize your ML models so that they can fit in the memory. There are several tools available for this purpose.</p><p><a href="https://www.tensorflow.org/lite">TensorFlow Lite | ML for Mobile and Edge Devices</a></p><p>Another complication with this approach is updating the model to a newer version. A fresh deployment is generally required for the update. This also makes A/B testing quite non-trivial.</p><h3>Model as a REST service</h3><p>This is one of the most popular approaches for inference. Run your python code inside a docker container and provide a REST interface to get the results. Tensorflow already provides the REST model serving out of the box.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*QZuaJQQbhaMErv1B1_-x7Q.jpeg" /><figcaption>Deploying ML Model as a service</figcaption></figure><p>For Java, you can use MLeap or DeepLearning4J. You can also dynamically increase/decrease the number of servers according to the throughput in this approach.</p><p>If your model calls are async, this approach fails to trigger back pressure in case there is a burst of data such as during restarts. This can lead to OOM failures in the model servers. Extra precautions must be taken to prevent such scenarios.</p><p>Latencies are also high since you need a network call to fetch the results. The latencies can slightly be reduced by using <a href="https://grpc.io/">gRPC</a> instead of REST.</p><p><a href="https://codeburst.io/heres-how-you-can-go-beyond-http-1-1-59e73f68bf75">Here’s How You Can Go Beyond Http 1.1</a></p><h3>Database as a model store</h3><p>If you have a fixed model architecture e.g., Linear Regression, Random Forest, or a small neural net, the weights can be stored in a distributed database such as Cassandra. You can create the model at the runtime using the weights and make the predictions on the new model.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*6dQ664fVvw38WQvf1bQX6g.jpeg" /><figcaption>Storing models in database</figcaption></figure><p>This approach is a hybrid of the first and second approaches. It allows you to update the model at runtime without requiring a new deployment while also proving back pressure capabilities. It comes at the cost of versatility since you are limiting the number of potential options for the models.</p><h4>So which approach should you choose?</h4><p>Well, if you want to do a simple POC or your model is pretty lightweight, go with the REST model server. Ease of integration and very few code changes required to run your model makes it an attractive choice. A/B testing can also be done quickly.</p><p>If you require predictions to happen in tens of milliseconds, the pipelined approach is the one to prefer.</p><p>Lastly, the model store approach should only be used in the case when you have several models e.g., an ML model per city data, and they are lightweight too.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=217954a0b423" width="1" height="1" alt=""><hr><p><a href="https://medium.com/data-science/deploying-ml-models-in-distributed-real-time-data-streaming-applications-217954a0b423">Deploying ML Models in Distributed Real-time Data Streaming Applications</a> was originally published in <a href="https://medium.com/data-science">TDS Archive</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
    </channel>
</rss>