<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Sunny Srinidhi</title>
    <description>The latest articles on DEV Community by Sunny Srinidhi (@contactsunny).</description>
    <link>https://dev.to/contactsunny</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F28411%2F72a1b312-19ce-4038-9ebe-600deff7d4a2.jpg</url>
      <title>DEV Community: Sunny Srinidhi</title>
      <link>https://dev.to/contactsunny</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/contactsunny"/>
    <language>en</language>
    <item>
      <title>Streamline Data Transfer with AWS DataSync: A Comprehensive Guide</title>
      <dc:creator>Sunny Srinidhi</dc:creator>
      <pubDate>Sat, 09 Mar 2024 04:20:41 +0000</pubDate>
      <link>https://dev.to/contactsunny/streamline-data-transfer-with-aws-datasync-a-comprehensive-guide-4p88</link>
      <guid>https://dev.to/contactsunny/streamline-data-transfer-with-aws-datasync-a-comprehensive-guide-4p88</guid>
      <description>&lt;p&gt;In today’s data-driven world, the need for efficient and reliable data transfer solutions has never been greater. Enterprises across various industries are constantly seeking ways to streamline their data workflows, ensuring that critical information is seamlessly synchronized between on-premises and cloud environments. AWS DataSync emerges as a powerful tool in this landscape, offering robust capabilities to facilitate fast, secure, and automated data transfers.&lt;/p&gt;




&lt;h1&gt;
  
  
  What is AWS DataSync?
&lt;/h1&gt;

&lt;p&gt;AWS DataSync is a fully managed service designed to simplify and accelerate data transfer between on-premises storage systems and Amazon S3, Amazon EFS (Elastic File System), or Amazon FSx (File System for Windows and Lustre). With DataSync, organisations can effortlessly migrate large volumes of data, synchronise files between different storage systems, or replicate data for backup and disaster recovery purposes.&lt;/p&gt;




&lt;h1&gt;
  
  
  How Does AWS DataSync Work?
&lt;/h1&gt;

&lt;p&gt;At its core, AWS DataSync operates on a simple yet effective mechanism. It leverages a combination of agents deployed on the source and destination endpoints, along with a centralized control plane managed by AWS. These agents facilitate high-speed data transfer over optimised network connections, ensuring minimal latency and maximum throughput.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7163gwlj1ww81gzkfd2f.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7163gwlj1ww81gzkfd2f.png" alt="Image description" width="800" height="472"&gt;&lt;/a&gt;&lt;br&gt;
Figure 1: AWS DataSync Architecture&lt;/p&gt;

&lt;p&gt;DataSync employs incremental data transfer techniques, only transferring the changes made to files since the last synchronization. This ensures efficiency by minimizing the amount of data transmitted during subsequent sync operations. Additionally, the service offers built-in data integrity checks and encryption, safeguarding data during transit and at rest.&lt;/p&gt;




&lt;h1&gt;
  
  
  Use Cases and Examples
&lt;/h1&gt;

&lt;h3&gt;
  
  
  1. Data Migration to the Cloud
&lt;/h3&gt;

&lt;p&gt;Imagine a multinational corporation seeking to migrate terabytes of data from its on-premises data center to Amazon S3 for better scalability and cost-effectiveness. By utilizing AWS DataSync, the organization can orchestrate the migration process seamlessly. DataSync enables parallel transfers, allowing multiple files to be transferred concurrently, thereby minimizing migration time.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Hybrid Cloud Workflows
&lt;/h3&gt;

&lt;p&gt;In a hybrid cloud environment, where organizations maintain a combination of on-premises infrastructure and cloud resources, DataSync plays a pivotal role in synchronizing data between these disparate environments. For instance, a media production company may use on-premises storage for editing and processing large video files, while leveraging Amazon S3 for archival and distribution. DataSync ensures that files are synchronized between these environments in near real-time, enabling seamless collaboration and resource optimization.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Disaster Recovery and Backup
&lt;/h3&gt;

&lt;p&gt;Business continuity and disaster recovery are critical concerns for enterprises of all sizes. AWS DataSync provides an efficient solution for replicating data to AWS storage services, ensuring that organizations have up-to-date backups in case of unforeseen events. By configuring DataSync to regularly synchronise data between on-premises storage and Amazon S3, businesses can minimise data loss and expedite recovery processes during emergencies.&lt;/p&gt;




&lt;h1&gt;
  
  
  Getting Started with AWS DataSync
&lt;/h1&gt;

&lt;p&gt;Setting up AWS DataSync is straightforward and can be accomplished in a few simple steps:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Create a DataSync Agent: Begin by deploying DataSync agents on the source and destination endpoints. These agents act as intermediaries responsible for facilitating data transfer.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Define a Task: Configure a DataSync task to specify the source location, destination location, and any additional parameters such as transfer options and scheduling preferences.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Monitor and Manage Tasks: Once the task is activated, monitor its progress using the AWS Management Console or command-line interface. DataSync provides detailed metrics and logs to track transfer performance and identify any issues.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Optimize Performance: Fine-tune DataSync settings to optimize performance based on your specific requirements. Adjust parameters such as concurrency, bandwidth throttling, and data validation options to achieve optimal transfer speeds and reliability.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;h1&gt;
  
  
  Conclusion
&lt;/h1&gt;

&lt;p&gt;AWS DataSync offers a robust solution for simplifying and accelerating data transfer workflows in hybrid cloud environments. Whether it’s migrating data to the cloud, synchronizing files across distributed storage systems, or ensuring data resilience through backups, DataSync provides the tools and capabilities to meet diverse business needs. By leveraging its intuitive interface, scalable architecture, and seamless integration with AWS services, organizations can streamline their data operations and unlock new opportunities for innovation and growth.&lt;/p&gt;

&lt;p&gt;In a data-centric world where agility and efficiency are paramount, AWS DataSync empowers enterprises to stay ahead of the curve and harness the full potential of their data assets.&lt;/p&gt;




&lt;p&gt;And if you like what you see here, or on my personal blog, Medium blog and Dev.To blog, and would like to see more of such helpful technical posts in the future, consider supporting me on Patreon and Github.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Understanding the Battle of Database Storage: Row-Oriented vs. Columnar</title>
      <dc:creator>Sunny Srinidhi</dc:creator>
      <pubDate>Fri, 08 Mar 2024 15:00:02 +0000</pubDate>
      <link>https://dev.to/contactsunny/understanding-the-battle-of-database-storage-row-oriented-vs-columnar-ggp</link>
      <guid>https://dev.to/contactsunny/understanding-the-battle-of-database-storage-row-oriented-vs-columnar-ggp</guid>
      <description>&lt;p&gt;In the vast landscape of databases, where data reigns supreme, lies a critical decision point for architects and developers alike: row-oriented storage versus columnar storage. This choice can significantly impact performance, storage efficiency, and query speed, making it imperative to grasp the nuances of each approach. In this exploration, we delve into the depths of row-oriented and columnar storage, unraveling their intricacies and shedding light on which might be the best fit for your data-driven endeavours.&lt;/p&gt;




&lt;h1&gt;
  
  
  Row-Oriented Storage: The Traditional Approach
&lt;/h1&gt;

&lt;p&gt;Row-oriented storage is the conventional method of storing data in databases. In this model, data is organised and stored row by row, with each row containing all the fields (attributes or columns) for a particular record. This approach aligns well with transactional databases where operations primarily involve fetching entire rows.&lt;/p&gt;

&lt;p&gt;One of the primary advantages of row-oriented storage is its suitability for transactional processing. Since transactions typically involve working with complete records, retrieving entire rows becomes efficient. Additionally, row-oriented storage simplifies data updates and inserts, as the entire record is stored contiguously.&lt;/p&gt;

&lt;p&gt;However, row-oriented storage has its drawbacks, particularly in analytics and reporting scenarios. When querying for specific columns across multiple rows, row-oriented storage may exhibit performance bottlenecks. This is because retrieving data by column necessitates scanning through each row, resulting in increased I/O overhead and reduced query speed, especially for large datasets.&lt;/p&gt;




&lt;h1&gt;
  
  
  Columnar Storage: A Paradigm Shift in Data Warehousing
&lt;/h1&gt;

&lt;p&gt;Columnar storage, on the other hand, flips the traditional row-oriented approach on its head by storing data vertically, column by column. In this model, each column is stored separately, with all values for a particular column grouped together. This design is well-suited for analytical workloads where queries typically involve aggregations, filtering, and analysing specific attributes across multiple records.&lt;/p&gt;

&lt;p&gt;The key advantage of columnar storage lies in its efficiency for analytics and reporting. Since columns are stored separately, queries that involve selecting specific attributes can be executed much faster compared to row-oriented storage. Columnar databases excel at handling complex analytical queries, such as those commonly found in data warehousing environments.&lt;/p&gt;

&lt;p&gt;Moreover, columnar storage often boasts superior compression capabilities. Since columns tend to contain repeated values, compression algorithms can exploit this redundancy to achieve significant space savings. This not only reduces storage costs but also improves query performance by minimising disk I/O.&lt;/p&gt;

&lt;p&gt;However, columnar storage may not be as efficient for transactional workloads. Due to its design, inserting or updating individual records entails modifying multiple columnar structures, which can introduce overhead, particularly in write-heavy environments. As a result, columnar databases are often used in conjunction with row-oriented databases, with each optimised for their respective workload types.&lt;/p&gt;




&lt;h1&gt;
  
  
  Choosing the Right Storage Model
&lt;/h1&gt;

&lt;p&gt;Selecting the appropriate storage model depends on various factors, including the nature of your data, the types of queries you intend to run, and the performance requirements of your application. Here are some considerations to help guide your decision:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Workload Type: Determine whether your application primarily handles transactional processing or analytical queries. For transactional workloads, row-oriented storage may be more suitable, whereas columnar storage shines in analytical scenarios.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Query Patterns: Analyse the types of queries your application frequently executes. If your queries involve selecting specific columns across a large number of records, columnar storage is likely the better choice. Conversely, if queries primarily retrieve entire records, row-oriented storage may suffice.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Performance Requirements: Consider the performance characteristics required by your application. If query speed and scalability are paramount, columnar storage may offer superior performance for analytical workloads. However, if transactional throughput is critical, row-oriented storage might be more appropriate.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Data Compression and Storage Efficiency: Evaluate the potential for data compression and storage savings offered by each storage model. Columnar storage often excels in this regard, particularly for datasets with high redundancy in column values.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Hybrid Approaches: Explore hybrid approaches that leverage both row-oriented and columnar storage based on workload requirements. This allows you to optimise performance and efficiency for different types of queries within the same database system.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;h1&gt;
  
  
  Examples of Row-Oriented and Columnar Storage in Action
&lt;/h1&gt;

&lt;p&gt;Let's delve deeper into real-world scenarios where row-oriented and columnar storage shine, illustrating the practical implications of each approach.&lt;/p&gt;

&lt;h2&gt;
  
  
  Example 1: Online Retail Database
&lt;/h2&gt;

&lt;p&gt;Consider an online retail database that stores information about customer orders, including order details such as customer name, product ID, quantity, and purchase date.&lt;/p&gt;

&lt;p&gt;Row-Oriented Storage: In a row-oriented storage model, each order is stored as a single row, with all order details contained within that row. This layout is well-suited for transactional processing, as retrieving an entire order—such as when processing a purchase or updating customer information—can be done efficiently.&lt;/p&gt;

&lt;p&gt;Columnar Storage: In a columnar storage model, each attribute (e.g., customer name, product ID, quantity) is stored separately. This design excels when running analytical queries, such as calculating total sales by product or analysing customer purchasing patterns. By storing similar data types together, columnar storage enables faster query execution and better compression ratios, leading to improved performance and reduced storage costs for analytical workloads.&lt;/p&gt;

&lt;h2&gt;
  
  
  Example 2: Financial Data Analysis
&lt;/h2&gt;

&lt;p&gt;Imagine a financial institution analysing vast amounts of market data to identify trends, assess risk, and make informed investment decisions.&lt;/p&gt;

&lt;p&gt;Row-Oriented Storage: In a row-oriented storage model, each financial instrument's data (e.g., stock prices, trading volume, market indicators) is stored as a single row. This structure facilitates transactional processing, enabling quick updates to individual records and efficient retrieval of complete datasets for real-time trading.&lt;/p&gt;

&lt;p&gt;Columnar Storage: In a columnar storage model, financial data attributes are stored separately, with each column containing data for a specific metric (e.g., closing price, volume traded, price-to-earnings ratio). Analysing historical stock prices or performing complex calculations across multiple securities becomes more efficient with columnar storage, as it allows for parallel processing of columnar data and optimised query execution for analytical queries.&lt;/p&gt;

&lt;h2&gt;
  
  
  Example 3: Healthcare Records Management
&lt;/h2&gt;

&lt;p&gt;Consider a healthcare database managing patient records, including demographic information, medical history, and treatment details.&lt;/p&gt;

&lt;p&gt;Row-Oriented Storage: In a row-oriented storage model, each patient's data is stored as a single row, encompassing all relevant information. This format facilitates patient-centric operations, such as updating medical records or retrieving comprehensive patient profiles for clinical assessments.&lt;/p&gt;

&lt;p&gt;Columnar Storage: In a columnar storage model, healthcare data attributes (e.g., patient age, diagnosis code, treatment duration) are stored separately by column. Analysing population health trends, conducting epidemiological studies, or querying specific medical conditions across a large patient cohort becomes more efficient with columnar storage. The ability to selectively retrieve and process relevant columns enhances query performance and supports data-driven decision-making in healthcare.&lt;/p&gt;

&lt;h2&gt;
  
  
  Applying Storage Models to Real-World Scenarios
&lt;/h2&gt;

&lt;p&gt;In each of these examples, the choice between row-oriented and columnar storage depends on the specific requirements and use cases of the application. By understanding the strengths and trade-offs of each storage model, organisations can make informed decisions when designing database systems to meet their performance, scalability, and analytical needs. Whether optimising for transactional processing, analytical querying, or storage efficiency, selecting the right storage model is paramount in harnessing the full potential of data-driven applications.&lt;/p&gt;




&lt;h1&gt;
  
  
  Visual Representation of Data in Each Storage Type
&lt;/h1&gt;

&lt;p&gt;Let's illustrate row-oriented and columnar storage using the same dataset representing sales transactions in an online retail store. This dataset contains information about customer orders, including order ID, customer name, product ID, quantity, and purchase date.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5mrylk5csc1hu8uk36p7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5mrylk5csc1hu8uk36p7.png" alt="Image description" width="800" height="256"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Row-Oriented Storage
&lt;/h2&gt;

&lt;p&gt;In row-oriented storage, each order is stored as a single row, encompassing all order details.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5mrylk5csc1hu8uk36p7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5mrylk5csc1hu8uk36p7.png" alt="Image description" width="800" height="256"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Columnar Storage
&lt;/h2&gt;

&lt;p&gt;In columnar storage, each attribute (e.g., Order ID, Customer Name, Product ID) is stored separately.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0chvjbi786lb43o2idbc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0chvjbi786lb43o2idbc.png" alt="Image description" width="800" height="214"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h1&gt;
  
  
  Comparison
&lt;/h1&gt;

&lt;h2&gt;
  
  
  Row-Oriented Storage
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Advantages
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Efficient for transactional operations.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Simplicity in updating and retrieving entire records.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Disadvantages
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Querying specific attributes across multiple records may result in performance bottlenecks, especially for large datasets.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Columnar Storage
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Advantages
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Superior performance for analytical queries.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Efficient compression and storage savings due to redundancy in column values.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Disadvantages
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Not as efficient for transactional processing, particularly in write-heavy environments.&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  Conclusion
&lt;/h1&gt;

&lt;p&gt;In the perpetual tug-of-war between row-oriented and columnar storage, there is no one-size-fits-all solution. Each storage model comes with its own set of advantages and trade-offs, making it essential to carefully assess your requirements and choose accordingly. Whether you prioritise transactional throughput, analytical performance, or storage efficiency, understanding the nuances of row-oriented and columnar storage is crucial for building robust and scalable database systems. By leveraging the strengths of each approach, you can unlock the full potential of your data and propel your applications to new heights of efficiency and performance.&lt;/p&gt;

</description>
      <category>database</category>
      <category>bigdata</category>
      <category>storage</category>
      <category>datascience</category>
    </item>
    <item>
      <title>Cleaning And Normalizing Data Using AWS Glue DataBrew</title>
      <dc:creator>Sunny Srinidhi</dc:creator>
      <pubDate>Tue, 18 Jan 2022 04:04:46 +0000</pubDate>
      <link>https://dev.to/aws-builders/cleaning-and-normalizing-data-using-aws-glue-databrew-1e10</link>
      <guid>https://dev.to/aws-builders/cleaning-and-normalizing-data-using-aws-glue-databrew-1e10</guid>
      <description>&lt;p&gt;A major part of any data pipeline is the cleaning of data. Depending on the project, cleaning data could mean a lot of things. But in most cases, it means normalizing data and bringing data into a format that is accepted within the project. For example, it could be extracting date and time components from a timestamp column into multiple columns, converting the case of a string column, or &lt;a href="https://blog.contactsunny.com/data-science/label-encoder-vs-one-hot-encoder-in-machine-learning" rel="noopener noreferrer"&gt;label encoding&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;We write code for this, and then design a pipeline for automating that using tools such as &lt;a href="https://blog.contactsunny.com/data-science/getting-started-with-apache-airflow" rel="noopener noreferrer"&gt;Apache Airflow&lt;/a&gt;. We have been doing this for years now. But as we move more and more of our processing and pipelines to the cloud, we can achieve these results of data cleaning by using tools provided by our cloud infrastructure providers. And usually, such tools don’t require any coding. Being a developer myself, I can see how this can seem threatening to my job. But I don’t want to get into that argument, because I think that’s moot.&lt;/p&gt;

&lt;p&gt;Anyway, in this post, we’ll take a look at AWS Glue DataBrew and how we can clean our datasets using this. For this &lt;a href="https://blog.contactsunny.com/proof-of-concepts-pocs" rel="noopener noreferrer"&gt;POC&lt;/a&gt;, I’m using S3 to both store the input and the processed output (mostly because that’s the easiest option I see) and using an NYC OpenData dataset. To be more specific, I’m using the &lt;a href="https://data.cityofnewyork.us/Public-Safety/Motor-Vehicle-Collisions-Crashes/h9gi-nx95" rel="noopener noreferrer"&gt;Motor Vehicle Collisions – Crashes&lt;/a&gt; dataset.&lt;/p&gt;




&lt;h2&gt;
  
  
  What Is AWS Glue DataBrew?
&lt;/h2&gt;

&lt;p&gt;We already know that AWS Glue is a tool for designing the extract, transform, and load (ETL) pipelines. It provides a lot of features for creating and running ETL jobs. DataBrew takes it one step ahead by providing features to also clean and transform the data to ready it for further processing or feeding to machine learning models.&lt;/p&gt;

&lt;p&gt;DataBrew provides over 250 transformations to get started with. These include filtering data, converting formats or converting data into standard formats, fixing data issues, extracting data from columns using regex, and much more. The best part is, these transformations are already available in DataBrew, which means we don’t need to code any of these transformations.&lt;/p&gt;

&lt;p&gt;The advantage of that is data analysts can design the transformation pipelines themselves without having to wait for developers to code the transformations. You can apply these transformations at any stage of the pipeline, especially if you already have a Glue ETL job.&lt;/p&gt;

&lt;p&gt;Another advantage here is that all of this is serverless. This means you only pay for what you use, and you don’t pay for anything when the pipeline isn’t running. Also, it can scale automatically depending on the amount of data coming in.&lt;/p&gt;

&lt;p&gt;So, let’s get started with DataBrew now. We’re first going to create a new project and a recipe. Then we’ll download the sample dataset and upload that to DataBrew. Then, we’ll apply a few transformations and create a job to run the transformations on the entire dataset. Finally, we’ll see how the transformed data looks like.&lt;/p&gt;




&lt;h2&gt;
  
  
  Getting Started With AWS Glue DataBrew
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Creating a project
&lt;/h3&gt;

&lt;p&gt;First, head over to the &lt;a href="https://console.aws.amazon.com/databrew/home?region=us-east-1#create-project" rel="noopener noreferrer"&gt;AWS Glue DataBrew console&lt;/a&gt; and create a new project. Next, provide a project name and a recipe name, as you can see from the screenshot below.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6vofvkuatywshgplihw3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6vofvkuatywshgplihw3.png" alt="create project" width="800" height="478"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For this POC, we can leave all the configurations to the defaults. After providing a name for the project and the recipe, scroll down to the Select a dataset section to upload the sample CSV dataset. If you haven’t yet downloaded the sample dataset, you can get it from the NYC OpenData website &lt;a href="https://data.cityofnewyork.us/Public-Safety/Motor-Vehicle-Collisions-Crashes/h9gi-nx95" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;In the Select a dataset section, select the New dataset option to the right of the screen. Provide a name to the dataset. Then select the File upload option in the Connect to a new dataset option. You can see this configuration in the screenshot below.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F70dcyrxlais4cc8pyhea.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F70dcyrxlais4cc8pyhea.png" alt="create dataset" width="800" height="395"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In the Enter S3 destination option, select an S3 bucket for hosting the source data and the transformed data. If you don’t have a bucket created for this, you can do so from here itself.&lt;/p&gt;

&lt;p&gt;Finally, scroll down to the bottom of the page and click the Create project button to create the project. This will take some time because the file has to be uploaded to the S3 bucket first, then a bunch of resources has to be provisioned, and also the file has to be previewed. During this time, you’ll see a progress indicator something like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbbb0e7ysaa86qupuzgu2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbbb0e7ysaa86qupuzgu2.png" alt="preparing data" width="800" height="432"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Applying transformations
&lt;/h3&gt;

&lt;p&gt;Once the data is prepared, you will see the data presented in a tabular form with a bunch of options at the top of the table. Here, we can select individual rows, select transformations, preview the results, and then apply the transformations. Also, we can manipulate the columns, such as duplicating columns, splitting columns, etc. These do count as transformations, but I just wanted to explicitly call them out as these are very commonly applied transformations.&lt;/p&gt;

&lt;p&gt;We’ll start applying transformations with the first column, which is the CRASH DATE column. In most analytics use cases, we would want to aggregate something over years. We usually have timestamp columns from which we extract the year component in queries. But the queries can be much easier and performant if we have a dedicated column for the year value. So we’ll now extract the year from the date column and save it as a new column called CRASH YEAR.&lt;/p&gt;

&lt;p&gt;For this, select the column, click the three dots at the top-right corner of the column, select the Extract menu, and then select the Date-time values option. This will open up a new configuration pane to the right side of the page, which is as shown in the screenshot below:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpjs87mh634oqfvwbboh2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpjs87mh634oqfvwbboh2.png" alt="crash year" width="385" height="640"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here, the Create column options, the Source column, and the Extract option sections should be already filled. In the Date-time unit to extract section, select Year from the dropdown menu, and enter CRASH YEAR as the Destination column field value. This will create a new column with the name CRASH YEAR with the year component extracted from the date column. Click the Preview button at the bottom to preview the changes. This should add a new column to the dataset, as shown in the screenshot below.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqbgw81e14bb1v5iqawe9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqbgw81e14bb1v5iqawe9.png" alt="year preview" width="605" height="616"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Click the Apply button to apply the transformation.&lt;/p&gt;

&lt;p&gt;Next, we’ll transform the CRASH TIME column to format it in the HH:MM format. It is already in a standard format. But for the sake of experimentation, we’ll do this anyway. So let’s repeat the same process again. Click on the three dots at the top right corner of the column, select the Format menu and then click the Date-time formats menu. Then scroll down to the end of the sub-menu and select More options. This will open up a similar configuration pane that we saw while extracting the year component earlier. Use the following screenshot as a reference to configure this transformation.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpkq1pt59n9dp9hc9guhh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpkq1pt59n9dp9hc9guhh.png" alt="time config" width="387" height="622"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Click the Preview changes options to see the difference after applying the transformation. It should be similar to the screenshot below. And finally, click the Apply button to apply the transformation. It is important to note that these changes will not be applied immediately after hitting the Apply button. The apply button will only save the transformation so that we can go back and change it anytime we want. The transformations will change the data only after we run the job.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2rapzzbyxkiv1zsvz5qr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2rapzzbyxkiv1zsvz5qr.png" alt="time preview" width="608" height="572"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now, we’ll apply the final transformation to another column. For machine learning use-cases, we need to eliminate as many text columns as we can. And in cases where we’re working with categorical data, we can map them to numbers to make sure the machine learning models don’t develop any biases. In our dataset, we have one such column – CONTRIBUTING FACTOR VEHICLE 1. This process of converting textual, categorical data into numbers is called Categorical Mapping. There’s one other way of doing this, called &lt;a href="https://blog.contactsunny.com/tech/label-encoder-vs-one-hot-encoder-in-machine-learning" rel="noopener noreferrer"&gt;One Hot Encoding&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Glue DataBrew provides both options. Click on the three dots at the top right corner of the column to open the context menu and scroll to the end, you’ll see both Categorical mapping and One-hot encode column options. For this example, we’ll go with categorical mapping. So select the menu to open the configuration panel. Match the configuration as shown in the screenshot below.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fd6rvuba2vqdrky8j1m2s.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fd6rvuba2vqdrky8j1m2s.png" alt="data mapping config" width="381" height="624"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Click the Preview changes button to preview the changes, which should look something like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmrct73w9as5yfcnirraf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmrct73w9as5yfcnirraf.png" alt="data mapping preview" width="602" height="568"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Click the Apply button again to apply this transformation. We should see a new column added with the mapped values. Now, for an actual project, this transformation isn’t going to help much for machine learning as the numerical values will make the model think this is some sort of ranking instead of categorical data. We need to use One-hot encoding for that. But we’ll stick with this for this example.&lt;/p&gt;

&lt;p&gt;After this, click the Recipe button at the top right corner of the table. You should now see all the transformations that we applied to the dataset. It should look something similar to the screenshot below.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyo6y5ivtx0p4mpn0642r.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyo6y5ivtx0p4mpn0642r.png" alt="recipes" width="784" height="824"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;From here, we should be able to easily edit any of these recipes or remove them. We can even add a new transformation from this pane. This comes in very handy when we want to check all the transformations we’re applying to a dataset in one place.&lt;/p&gt;

&lt;h3&gt;
  
  
  Running the job
&lt;/h3&gt;

&lt;p&gt;We are now finally ready to run the job to apply all these transformations to the entire dataset. You should see the Run job button at the top right corner of the page. Click that button and take a break now. This will take a few minutes to complete.&lt;/p&gt;

&lt;p&gt;In the meantime, we can look at the data lineage to visualize and understand the complete pipeline. Click the LINEAGE button that’s to the right of the Run job button. Here, you’ll see the flow of data from the S3 bucket, through the transformation recipes, and back to the S3 bucket. It should look similar to the one shown in the screenshot below.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8d8x15wastqqlikgocyc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8d8x15wastqqlikgocyc.png" alt="data lineage" width="800" height="433"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can see that we have one dataset created from source files in the S3 bucket. From that dataset, we have one project that contains six recipes. These six recipes are currently being applied to the dataset using a job, and finally, the transformed data is saved back to the S3 bucket.&lt;/p&gt;




&lt;h2&gt;
  
  
  That’s it
&lt;/h2&gt;

&lt;p&gt;And that’s pretty much it. This is, and I repeat, not a typical production pipeline. Usually, the source of data is streaming into the system, using tools such as Kinesis DataStream. The transformations are applied on the fly with micro-batching and stored in a data warehouse. But if the use case is for batch processing, S3 could be the source.&lt;/p&gt;

&lt;p&gt;Nonetheless, this exercise should help you get started with DataBrew and explore the tool’s capabilities for advanced transformations. And if the tool doesn’t provide a transformation that you need, or if you need to add a custom transformation, you can always use &lt;a href="https://blog.contactsunny.com/?s=lambda" rel="noopener noreferrer"&gt;Lambda functions&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;AWS has great tools for creating data pipelines with transformations, data lakes and warehouses, machine learning models, and more. You can check &lt;a href="https://blog.contactsunny.com/?s=aws" rel="noopener noreferrer"&gt;here&lt;/a&gt; for more resources, getting-started guides, and POCs on AWS services. And if you want me to explore a particular tool or service for you, do let me know in the comments.&lt;/p&gt;

&lt;p&gt;And if you like what you see here, or on my &lt;a href="https://medium.com/@contactsunny" rel="noopener noreferrer"&gt;Medium blog&lt;/a&gt; and &lt;a href="https://blog.contactsunny.com" rel="noopener noreferrer"&gt;personal blog&lt;/a&gt;, and would like to see more of such helpful technical posts in the future, consider supporting me on &lt;a href="https://www.patreon.com/contactsunny" rel="noopener noreferrer"&gt;Patreon&lt;/a&gt; and &lt;a href="https://github.com/sponsors/contactsunny" rel="noopener noreferrer"&gt;Github&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>aws</category>
      <category>datascience</category>
      <category>bigdata</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>The Dunning-Kruger Effect In Tech</title>
      <dc:creator>Sunny Srinidhi</dc:creator>
      <pubDate>Sun, 28 Nov 2021 15:04:49 +0000</pubDate>
      <link>https://dev.to/contactsunny/the-dunning-kruger-effect-in-tech-5acp</link>
      <guid>https://dev.to/contactsunny/the-dunning-kruger-effect-in-tech-5acp</guid>
      <description>&lt;p&gt;This is not the kind of post I usually write on my blog. This is more of a psychology lecture than a how-to tech tutorial. But it’s not completely irrelevant as well, because I’m going to talk about my experience with the Dunning-Kruger effect in tech that I’ve seen over the last decade.&lt;/p&gt;

&lt;p&gt;I’ve always been interested in learning more about psychology and how the brain works. Because the field is so bloody interesting, and also, it helps me understand people, make out patterns, and make better decisions. But I never write about it, because decidedly, I’m not expert in the field, and the Dunning-Kruger effect is about understanding that very fact. But first, let’s understand what it is.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Dunning-Kruger Effect
&lt;/h2&gt;

&lt;p&gt;To put it simple, the Dunning-Kruger effect is one of the many types of cognitive bias in which people tend to believe they more smarter and capable of things than they really are. That’s probably the most smiply the effect can be explained. And if you’ve spent any time in the tech industry, you don’t want me to tell how common this is.&lt;/p&gt;

&lt;p&gt;In his book The Dissent of Man, Charles Darwin wrote that “Ignorance more frequently begets confidence than does knowledge.” The Dunning-Kruger effect is more common than you might expect, and I’m very sure you have experienced this yourself. You might not want to believe that you are one of “those people,” but you most likely are. That’s because the Dunning-Kruger effect spares nobody.&lt;/p&gt;

&lt;p&gt;There have been many studies done in the social psychology space around this. But the most prominent and the most discussed is the Dunning-Kruger effect, named after the researchers David Dunning and Justin Kruger. The study that they conducted by itself wasn’t really that big or wide. They performed four experiments and around 100 participants (or less) in each of them.&lt;/p&gt;

&lt;p&gt;If you follow the Jimmy Kimmel show, you’d be familiar with the Lie Witness News segment, in which his crew go to popular locations and ask random people questions which are mostly made up, and see how they react. In one such episode, people were asked if the movie Godzilla is inventive to the people who survived the giant lizard attack in Tokyo in 1954. You’d be surprised with how people reacted.&lt;/p&gt;

&lt;p&gt;The reason most people go with the flow is that they don’t want to appear dumb or clueless, especially in front of a camera. But in lying about it, they make themselves look dumber, especially in front of a camera. Do you see the irony? It’s natural to feel dumb when you admit that you don’t know something. But the reality is that when you admit you don’t know something, you’re opening up an opportunity for yourself to learn something new. But when you lie about it, and continue to do so, you’re not only making yourself appear dumb and ignorant, but you start developing the false confidence that you know stuff and can hold a conversation about it.&lt;/p&gt;

&lt;p&gt;It’s not only depriving you of knowledge, but it’s also putting you on the path to dumbness, ignorance, and arrogance. You might have heard this from a lot of people before, that knowing the boundaries of your knowledge is the most important education you can have. If you are educated about what you know and what you don’t, and if are you are able to differentiate between the two, consider yourself smarter than most people and smarter than what you think you are.&lt;/p&gt;




&lt;h2&gt;
  
  
  How Is This Related To Tech?
&lt;/h2&gt;

&lt;p&gt;Enough of the psychology lecture, you say? I got the hint that you’re not really interested in that. So let’s see how this is related to our tech industry. You might have heard and read that you need to “fake it till you make it.” We all do that, and it’s necessary at times. I did it when I was fresh out of college and new in the tech industry. I was surrounded by some really smart people, entrepreneurs kinds. And these people hold patents to their names, and I was this kid who thought he knew what he was doing.&lt;/p&gt;

&lt;p&gt;So I did fake it, till I made it. But the problem is, some people don’t stop the faking even after making it. I see the Dunning-Kruger effect a lot in such people. It might not be right of me to talk about others, so let me tell you my story.&lt;/p&gt;

&lt;p&gt;Early in my career, actually when I was still in my college, I learnt who to build websites using PHP, Java, and MySQL. This was a very common and popular stack (the LAMP stack) back in the days. And because I was able to write APIs for CRUD operations and design websites to perform a few UI tricks, I thought I had mastered software engineers. So in my CV, I started rating myself 7 or 8 out of 10 in the said technologies. Remember, I was fresh out of college without any industry experience.&lt;/p&gt;

&lt;p&gt;Now, 8 years later, if you ask me to rate myself on those same technologies, I would not rate myself more than 4 or 5. It doesn’t mean that I’m a worse software engineer (or developer) or that I don’t have the same confidence in myself anymore. But it means that now I know what I know and what I don’t know.&lt;/p&gt;

&lt;p&gt;I went through the same phase 5-6 years back when I switched from software engineering to data engineering (don’t get me started on those designations). I started with Apache Spark for a project, I was able to do a lot of stuff with RDDs in Java for that project, and thought again that it’s done, I’m an expert in big data. But as I started reading up on stuff and interviewing for data-related jobs, I again realised that I just don’t know what I don’t know.&lt;/p&gt;

&lt;p&gt;That’s something I now say to everybody I know, that knowing what you know and realising that there’s a lot more that you don’t know is very important. If don’t know realise that, you are just making a fool of yourself, trust me.&lt;/p&gt;

&lt;p&gt;Once I started realising this, I started reading up on this, and that’s when I first came across studies done on this. I thought, wait, I can do my own study on this. I don’t know if this is ethical or not, but whenever I interviewed people, I started asking one or two questions in the interview that I made up on the spot. This is of course related to the technology that the candidate is interviewing for. But I made sure that the answers to these questions never had any effect on the outcome of the interview. And I’m sure that I’ve been put through such questions myself in the interviews that I’ve given over the years.&lt;/p&gt;

&lt;p&gt;But in those interviews, I realised that the Dunning-Kruger effect is actually real. I was taken aback by the answers the interviewees would give to such questions. There were maybe just a handful of people who admitted that they didn’t know what I was talking about or that there’s nothing of the sort that I was describing.&lt;/p&gt;

&lt;p&gt;I have nothing against the fact “fake it till you make it.” It is in fact necessary sometimes. But you need to be candid about it to yourself before the fake you consumes the real you. It can become destructive. And I’m not against new engineers who rate themselves high on their CVs, because they are just inexperienced and don’t know what they don’t know.&lt;/p&gt;

&lt;p&gt;But once you gain the experience and the knowledge, make sure you are cognizant of the fact that there’s a lot more to learn. Nobody, in my opinion, can be an expert in everything. You might be an expert in your field, but you can’t deny the fact that you don’t know it all.&lt;/p&gt;

&lt;p&gt;So, the Dunning-Kruger effect is very much real in the tech industry. And if this news to you, maybe it’s time to evaluate yourself, just to make sure you’re not oblivious to the fact that you don’t know it all.&lt;/p&gt;




&lt;p&gt;And if you like what you see here, or on my Medium blog and personal blog, and would like to see more of such helpful technical posts in the future, consider supporting me on Patreon and Github.&lt;/p&gt;

</description>
      <category>thedunningkrugereffect</category>
      <category>technology</category>
      <category>psychology</category>
      <category>mindfullness</category>
    </item>
    <item>
      <title>Understanding Apache Hive LLAP</title>
      <dc:creator>Sunny Srinidhi</dc:creator>
      <pubDate>Fri, 19 Nov 2021 04:50:55 +0000</pubDate>
      <link>https://dev.to/contactsunny/understanding-apache-hive-llap-4l60</link>
      <guid>https://dev.to/contactsunny/understanding-apache-hive-llap-4l60</guid>
      <description>&lt;p&gt;Apache Hive is a complex system when you look at it, but once you go looking for more info, it’s more interesting than complex. There are multiple query engines available for Hive, and then there’s LLAP on top of the query engines to make real-time, interactive queries more workable. Live Long And Process, or LLAP as it’s more popularly known as, is an awesome concept and execution when you learn more about it.&lt;/p&gt;

&lt;p&gt;And that’s exactly what I did, I read a few documents and other posts about LLAP and got a better understanding of it. I understood the Tez query engine too, and expect a post about it soon. In this, I’ll unpack whatever I learnt about LLAP and how it’s awesome if you use it.&lt;/p&gt;




&lt;h2&gt;
  
  
  Hive Query Engines
&lt;/h2&gt;

&lt;p&gt;There are three query engines for Hive:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;MapReduce (MR)&lt;/li&gt;
&lt;li&gt;Tez&lt;/li&gt;
&lt;li&gt;Spark&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  MapReduce (MR)
&lt;/h3&gt;

&lt;p&gt;MapReduce is the first query engine that shipped with Hive, and this is also the slowest of the bunch. When you submit a Hive query with MR as the query engine, every query gets converted into MapReduce jobs and gets submitted to YARN containers. YARN, or Yet Another Resource Negotiator, is common between MR and Tez query engines. But the problem with the MR query engine is that all queries need to be converted to MR jobs. The conversion itself takes time. So you can imagine how this query engine becomes slow with a lot of latency.&lt;/p&gt;

&lt;h3&gt;
  
  
  Apache Tez
&lt;/h3&gt;

&lt;p&gt;To overcome this latency issue, Tez was introduced as a query engine with later versions of Hive. Tez uses Directed Acyclic Graphs (DAGs) to process queries instead of MapReduce jobs. This greatly reduces latency and improves the query response times. WIth the latest version of Hive, even though MR query engine is deprecated, it is the default engine. But you get the deprecation warning whenever you enter the Hive shell and a recommendation to switch to either Tez or Spark as the query engine. And it is universally suggested to switch to Tez. We’ll see why in my next post.&lt;/p&gt;

&lt;h3&gt;
  
  
  Apache Spark
&lt;/h3&gt;

&lt;p&gt;Finally, we have Apache Spark as the third query engine option. And Spark, by far, is the fastest of them all. There are claims that Spark can improve the performance of Hive by as much as 100x, that’s a very bold claim. Tez doesn’t offer such high boost in performance, the much accepted boost is 10x. So you might say, well, Spark is the clear winner, right? Well, that depends! Tez and Spark both use DAGs to optimise the query performance of MR. So there can be a lot of parallel or concurrent execution of tasks in both.&lt;/p&gt;

&lt;p&gt;The choice between Tez and Spark for a query engine totally comes down to your application and needs. But no matter what you choose, LLAP and sit on top of that query engine and better the performance even more. For example, we know that Spark can cache (persist) data in memory or on disk if we need that data again. But this caching is available within the same Spark job, another Spark job can’t access that data. What if I tell you that’s possible with LLAP? I know, that’s crazy. So, let’s see what else LLAP can do.&lt;/p&gt;




&lt;h2&gt;
  
  
  Live Long And Process (LLAP)
&lt;/h2&gt;

&lt;p&gt;We’re finally going to talk about LLAP. The first you need to know, as I already made it clear, is that LLAP is not a query engine. It sits on top of the query engine to make query and data processing that much faster. If you image the various Hive components to be arranged as a stack, you have HDFS at the bottom, YARN on top of it, and then Hive itself at the top. Now imagine a layer of cache and in-memory processing on top of HDFS. This means a lot of requests don’t go to HDFS at all. That’s what, at a very high level, LLAP does.&lt;/p&gt;

&lt;p&gt;I’m definitely over-simplifying stuff when I say LLAP is a caching and in-memory processing layer. There’s definitely a better way to put it. Let me elaborate.&lt;/p&gt;

&lt;p&gt;You can think of LLAP as just another YARN application running on all data nodes in a Hadoop cluster. The only difference is that LLAP is a long-lived process (hence the name). But this doesn’t mean that it eats up all your resources. It can be configured to a very tiny process to process simple queries, or it can be configured to dynamically scale out and down whenever required. This brings in a very big difference compared to Spark. And because LLAP still work on YARN, it brings in all the advantages of YARN such as distributed nature and fault tolerance.&lt;/p&gt;

&lt;p&gt;LLAP runs daemon processes on data nodes, and these daemons are not tied to a user who is issuing Hive queries. And this is a very important distinction, because this allows LLAP to reuse cached data across users. So you and I both fire similar queries on the same table, LLAP will be able to use the cache that’s already available for both our queries, and that’ll boost the performance for both of us. Without LLAP, both the queries would have to perform the same operations individually. And you can imagine how that’s not very optimal.&lt;/p&gt;

&lt;p&gt;LLAP is not a query engine, and it’s optional&lt;br&gt;
You should’ve realised by now, LLAP is completely optional. It’s not mandatory to use LLAP for Hive queries. You only use LLAP if you want to improve the responsiveness of Hive queries both in interactive and batch modes. Even when you use LLAP, it’s not like every and all parts of a query are executed within LLAP. LLAP is not meant for that, that’s what query engines are for. LLAP takes parts of the queries that can benefit by using the cache or the long lived processes.&lt;/p&gt;

&lt;p&gt;LLAP doesn’t promise anywhere that it’ll execute the entire query itself. In fact, it’s actually the query engines that orchestrate what can go into LLAP and what can’t. And for now query engine Tez and other frameworks such as Pig can use LLAP in their stack. Unfortunately, support for MapReduce is not yet planned, and don’t hold your breath on this.&lt;/p&gt;

&lt;p&gt;And because LLAP is still built to work with YARN, the resource allocation is completely in control of YARN. This is also the reason LLAP daemon nodes are able to talk to each other and share data across nodes. Another advantage here is that the daemon processes themselves don’t require a lot of resources to function. YARN allocates the minimum required for the processes themselves and will increase the resource allocation as and when required depending on the workload. And to avoid heap issues or JVM memory issues, cached data is always kept off-heap and in large buffers. So processing of aggregations such as group by and joins will be much faster in LLAP compered to query engines.&lt;/p&gt;

&lt;h3&gt;
  
  
  Query fragments
&lt;/h3&gt;

&lt;p&gt;I already mentioned that LLAP usually executes parts of queries and not usually the complete query. These parts of queries are known as query fragments. These fragments include filters, data transformations, partial aggregations, projections, sorting, bucketing, joins, semi-joins, hash joins, etc. And it is to be noted that only certain “blessed” UDFs and Hive code are accepted into LLAP.&lt;/p&gt;

&lt;p&gt;For stability of the process and security of the data, LLAP doesn’t localise any code, and executes on the fly. And because the daemon is not tied to any particular user (as already mentioned), an LLAP node can allow parallel execution of various query fragments, across queries and sessions. This is one of the primary reasons for improved performance. Another good news, for developers specifically, is that LLAP APIs are directly available via client SDKs. You can directly specify relational transformations using these APIs.&lt;/p&gt;

&lt;h3&gt;
  
  
  Input and Output
&lt;/h3&gt;

&lt;p&gt;As I already mentioned, the daemons themselves have a very small footprint, and that’s because everything else is mostly done by offloading the work to multiple threads. Input and output, for example, are offloaded to threads. And transformations are done in separate threads. So as soon as an I/O thread makes the data ready, the data is passed on to separate threads for processing. This makes the I/O threads available for new I/O operations.&lt;/p&gt;

&lt;p&gt;The data is passed further in the process to other threads in run-length encoded (RLE) columnar format. This reduces copying data across threads and processes to a great extent. And by extension, caching is also in the same RLE format. You can start seeing the benefits here.&lt;/p&gt;

&lt;p&gt;I/O and caching depend heavily on the knowledge of the file format that the data is stored in. This is necessary if I/O and caching have to be performant. So LLAP has externalized this knowledge with the help of plugins. And to start with ORC is the first file format supported by LLAP. This is one of the reasons why there is an increase in the adoption of ORC as the preferred file format for external Hive tables.&lt;/p&gt;

&lt;p&gt;When it comes to caching, both metadata and the data itself are cached. As I mentioned in previous sections, data is cached off-heap to avoid other potential issues. But metadata, on the other hand, is stored in-process as Java objects. This makes sure that even if the data itself is evicted from the cache, the metadata is still in memory to avoid some overhead.&lt;/p&gt;

&lt;p&gt;I touched upon data eviction in cache. And as you might expect, there are various policies for data eviction. By default, LRFU policy is used. But you can plug in any other policy at any time.&lt;/p&gt;

&lt;h3&gt;
  
  
  Transactions
&lt;/h3&gt;

&lt;p&gt;This is one big area of debate in Hive, to transact or not to transact. But that’s a topic for a separate blog post. When it comes to LLAP, it understands transactions. And it is smart enough to perform transformations (such as merging or delta files) before the data is cached. If there are various transactions performed on the same tables, which is usually the case, LLAP can store multiple versions of data for each such variation. And the correct version will be fetched from the cache on the fly depending on the query. This will make sure that the same set of transformations are not being performed on the same data over and over again, thereby reducing a lot of processing time.&lt;/p&gt;




&lt;p&gt;Understanding LLAP doesn’t stop here. There’s a lot more to it, and the more you try to understand it, the more interesting it becomes. I’m planning to write more about it as and when I explore more. But for now, this is all I have. Understanding how LLAP works will make it a lot easier to write queries, and also to write queries in a way that can make use of these optimisations to reduce latency. I hope this helped you a slight bit with your Hadoop or Hive journey.&lt;/p&gt;




&lt;p&gt;And if you like what you see here, or on my &lt;a href="https://medium.com/@contactsunny"&gt;Medium blog&lt;/a&gt; and &lt;a href="https://blog.contactsunny.com"&gt;personal blog&lt;/a&gt;, and would like to see more of such helpful technical posts in the future, consider supporting me on &lt;a href="https://www.patreon.com/contactsunny"&gt;Patreon&lt;/a&gt; and &lt;a href="https://github.com/sponsors/contactsunny"&gt;Github&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>bigdata</category>
      <category>datascience</category>
      <category>database</category>
      <category>apachehive</category>
    </item>
    <item>
      <title>Installing Zsh and Oh-my-zsh on Windows 11 with WSL2</title>
      <dc:creator>Sunny Srinidhi</dc:creator>
      <pubDate>Sat, 06 Nov 2021 03:10:34 +0000</pubDate>
      <link>https://dev.to/contactsunny/installing-zsh-and-oh-my-zsh-on-windows-11-with-wsl2-1p5i</link>
      <guid>https://dev.to/contactsunny/installing-zsh-and-oh-my-zsh-on-windows-11-with-wsl2-1p5i</guid>
      <description>&lt;p&gt;&lt;em&gt;Before we begin, you might ask, why am I writing on something this trivial? I sold off my old MacBook Pro because I’m super excited about the new M1 Pro MacBook Pros. I have pre-ordered one of those and am waiting for it to come. Till then, I’m left with my gaming PC which is an Asus Zephyrus. So I thought I’ll make the best of it till I get my Mac. This will be series of posts in which I show to setup a Windows 11 PC to work with big data tools and technologies, mostly Hadoop. So let’s get into it.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Now that Windows 11 is officially available to the public, and given that Windows is the most popular operating system used by developers (according to stackoverflow, refer screenshot below), I thought it would be fun to install a new WSL system on my Windows 11 PC and try to set it up for big data tools. But before I could do that, I had to install Zsh and oh-my-zsh on it. I switched to using Zsh over half a decade ago and haven’t used Bash or any other shell ever. I just love how zsh look, the ability to theme it, and the community surrounding it. So, I started with zsh, and here is how you can install it too.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqhbwfl8hdk0pwg2ss7ub.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqhbwfl8hdk0pwg2ss7ub.png" alt="SlackOverFlow Dev Survey"&gt;&lt;/a&gt;&lt;br&gt;
Source: stackoverflow&lt;/p&gt;




&lt;h2&gt;
  
  
  Installing WSL2 on Windows 11
&lt;/h2&gt;

&lt;p&gt;The process of installing WSL on Windows 11 is not different than that on Windows 10. And if you are upgrading from a Windows 10 PC which already had WSL installed, you don’t even have to do anything extra. I already had mine setup on Windows 10 with Ubuntu 20.04 LTS. So that was readily available for me. But I wanted to setup everything fresh just to see if anything has changed (spoiler, it hasn’t). So I installed Debian this time.&lt;/p&gt;

&lt;p&gt;To install any distro of Linux on Windows 11, just open up the Microsoft Store and search for your favorite Linux distro. In the screenshot below, you can see that if I search for Ubuntu, I get multiple versions of Ubuntu listed in the store.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2kimct3tztqfcrqbn2ev.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2kimct3tztqfcrqbn2ev.png" alt="Ubuntu in Microsoft Store"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Just select the version that you want to the install and click the Get button. After the installation is complete, the shell should open up automatically to complete the installation.&lt;/p&gt;

&lt;p&gt;Soon after the installation is complete, you’ll see the bash shell something like the screenshot below.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5gqxmz1lz0yv5v5q3lhw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5gqxmz1lz0yv5v5q3lhw.png" alt="with bash"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As you can see, the prompt is pretty ugly, at least to me. I know I can customize it, but not to the extent of what’s already built into Zsh. Now that we have Debian installed, let’s start updating the packages and installing Zsh and oh-my-zsh.&lt;/p&gt;




&lt;h2&gt;
  
  
  Installing Zsh and Oh-my-zsh
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Updating package list and upgrade packages
&lt;/h3&gt;

&lt;p&gt;Before we start installing any packages, we first to have to update the package list so that we can then upgrade all the packages that we have already installed, and also fetch the references to the latest version of all the dependencies. This is pretty simple and will take just a few seconds. Run the following command in the terminal to update the references:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

&lt;span class="nb"&gt;sudo &lt;/span&gt;apt update


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Once you update the package list, if you find any updates available, most probably you will, you can run the following command to upgrade all packages already installed. But let me also mention that this step is optional.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

&lt;span class="nb"&gt;sudo &lt;/span&gt;apt upgrade


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h3&gt;
  
  
  Installing dependencies
&lt;/h3&gt;

&lt;p&gt;Now that we have updated the package list, we have to install the dependencies that we’ll need to install Zsh and oh-my-zsh. The dependency list isn’t really exhaustive, it’s just two packages. If I’m not mistaken, these two packages must come pre-installed if you install a full fat version of Linux, but because this is a WSL version, I think it’s pretty stripped down.&lt;/p&gt;

&lt;p&gt;Anyway, the two packages that we need to install are wget and git. And just to be clear, these are not required for installing Zsh, but for oh-my-zsh. And to be completely honest, you can technically install oh-my-zsh without these packages as well, but there are benefits if you do install this way. For instance, oh-my-zsh will automatically check for updates if you install it using git.&lt;/p&gt;

&lt;p&gt;To install these two packages, run the following two commands one after another in your terminal:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

&lt;span class="nb"&gt;sudo &lt;/span&gt;apt &lt;span class="nb"&gt;install &lt;/span&gt;wget
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt &lt;span class="nb"&gt;install &lt;/span&gt;git


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Together, it shouldn’t take more than a minute to install. These are pretty small packages. Once you’re done with these two, we can finally move on to installing Zsh.&lt;/p&gt;

&lt;h3&gt;
  
  
  Installing Zsh
&lt;/h3&gt;

&lt;p&gt;In some cases, Zsh should already be installed even in the WSL version of Ubuntu or Debian. But I’m not 100% sure because I already had Ubuntu installed on WSL, as I mentioned. But if it’s not installed, it’s just one little command:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

&lt;span class="nb"&gt;sudo &lt;/span&gt;apt &lt;span class="nb"&gt;install &lt;/span&gt;zsh


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;And that’s it, you have Zsh installed on your Windows 11 PC using WSL. This excites me very much for some reason. But we’re not yet done. Let’s install oh-my-zsh.&lt;/p&gt;

&lt;h3&gt;
  
  
  Installing oh-my-zsh
&lt;/h3&gt;

&lt;p&gt;If you don’t know what oh-my-zsh is, you can read all about it here. Installing this is another simple command. But it’s not using the apt package manager, but we’ll use wget and git to basically download the install script from the Git repo and run that on our machine. To install oh-my-zsh, run the following command in your terminal:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

sh &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;wget https://raw.github.com/robbyrussell/oh-my-zsh/master/tools/install.sh &lt;span class="nt"&gt;-O&lt;/span&gt; -&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Because it’s an open source package, you can just to the link in that command and look at what the script is doing yourself. The whole process should look similar to what you see in the screenshot below.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5qpu02quwb9iwo6gguxg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5qpu02quwb9iwo6gguxg.png" alt="oh-my-zsh windows 11"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Soon after the install, you can see the prompt change in your terminal. The default theme is applied. You can explore on how to get more themes and change it or customize it to your heart’s content.&lt;/p&gt;




&lt;h2&gt;
  
  
  Adding Useful Aliases
&lt;/h2&gt;

&lt;p&gt;If you’re a developer and use Git a lot for your projects, there are some commands that you’ll be typing everyday. This shouldn’t come as a surprise, but most developers have handy aliases for these commands to make life a bit easier in the terminal. To add aliases, you’ll have to edit the .zshrc file in your home directory. For this, run the following command to open up the file:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

vi ~/.zshrc


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;This will open the file in the vi editor. Scroll to the end of the file and the following lines:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

&lt;span class="nb"&gt;alias &lt;/span&gt;&lt;span class="nv"&gt;ll&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"ls -ltra"&lt;/span&gt;
&lt;span class="nb"&gt;alias &lt;/span&gt;&lt;span class="nv"&gt;gd&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"git diff"&lt;/span&gt;
&lt;span class="nb"&gt;alias &lt;/span&gt;&lt;span class="nv"&gt;gcmsg&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"git commit -m"&lt;/span&gt;
&lt;span class="nb"&gt;alias &lt;/span&gt;&lt;span class="nv"&gt;gitc&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"git checkout"&lt;/span&gt;
&lt;span class="nb"&gt;alias &lt;/span&gt;&lt;span class="nv"&gt;gitm&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"git checkout master"&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;As you can see, these are pretty simple aliases. But also, they reduce typing a lot everyday when you add up the number of keystrokes at the end of the day. So, that’s pretty much it.&lt;/p&gt;




&lt;p&gt;And if you like what you see here, or on my &lt;a href="https://medium.com/@contactsunny" rel="noopener noreferrer"&gt;Medium blog&lt;/a&gt; and &lt;a href="https://blog.contactsunny.com" rel="noopener noreferrer"&gt;personal blog&lt;/a&gt;, and would like to see more of such helpful technical posts in the future, consider supporting me on &lt;a href="https://www.patreon.com/contactsunny" rel="noopener noreferrer"&gt;Patreon&lt;/a&gt; and &lt;a href="https://github.com/sponsors/contactsunny" rel="noopener noreferrer"&gt;Github&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>windows</category>
      <category>ohmyzsh</category>
      <category>zsh</category>
      <category>wsl</category>
    </item>
    <item>
      <title>Installing Hadoop on the new M1 Pro and M1 Max MacBook Pro</title>
      <dc:creator>Sunny Srinidhi</dc:creator>
      <pubDate>Sat, 06 Nov 2021 02:57:27 +0000</pubDate>
      <link>https://dev.to/contactsunny/installing-hadoop-on-the-new-m1-pro-and-m1-max-macbook-pro-48e9</link>
      <guid>https://dev.to/contactsunny/installing-hadoop-on-the-new-m1-pro-and-m1-max-macbook-pro-48e9</guid>
      <description>&lt;p&gt;In the previous series of posts, I wrote about &lt;a href="https://blog.contactsunny.com/data-science/installing-hadoop-on-windows-11-with-wsl2"&gt;how to install the complete Hadoop stack on Windows 11 using WSL 2&lt;/a&gt;. And now that the new MacBook Pro laptops are available with the brand new M1 Pro and M1 Max SOCs, here’s a guide on how to install the same Hadoop stack on these laptops. Because both M1 Pro and M1 Max use the same architecture, the steps you need to follow to install Hadoop is the same. So it doesn’t matter which MacBook you got, the steps given here should work for you. So, let’s get started.&lt;/p&gt;




&lt;h2&gt;
  
  
  Installing Dependencies
&lt;/h2&gt;

&lt;p&gt;There are two important dependencies that you’ll need to install to make Hadoop work. These aren’t optional, unless you have them installed already. So make sure you install these dependencies.&lt;/p&gt;

&lt;h3&gt;
  
  
  Installing JDK
&lt;/h3&gt;

&lt;p&gt;As always, you need to install dependencies. To begin with, let’s get JDK 8 installed, because Hadoop is largely dependent on Java. There are two ways of installing JDK on an M1 Mac, using homebrew, or directly from a vendor. We’re going to install the OpenJDK implementation of Azul, which is super easy to install and is also a certified JDK. You can download the JDK from here. As you can see, there are multiple versions available. But let’s stick to 8 for now. Also, make sure you download the ARM 64-bit version of the JDK.&lt;/p&gt;

&lt;p&gt;Once you download the installer, installing itself is pretty easy. Just open the installer and follow the steps in the wizard. It shouldn’t take more than a couple of minutes. Once you are done, make sure to export the path to the Java home directory, as this will be used by not just Hadoop, but a lot of other packages as well. For this, get the installation path (which should be very similar to the one given below), and add this to your .zshrc file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;JAVA_HOME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/Library/Java/JavaVirtualMachines/zulu-8.jdk/Contents/Home/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I’m pretty sure you can find the JDK in the exact same path if you installed JDK 8. Anyway, let’s move on to the next dependency.&lt;/p&gt;

&lt;h3&gt;
  
  
  Enable SSH to localhost
&lt;/h3&gt;

&lt;p&gt;Unlike Linux or Windows, SSH is already installed on Macs. We only need to enable the feature and add our security keys so that we don’t need to provide our passwords every time. First, let’s enable SSH or remote login feature. For this, open up your System Preferences app and find the Sharing menu. From there, on the services list to the left, search for “Remote Login” and enable it. This will be disabled by default. Below is a screenshot for reference.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fduasrzpewcol71993gwh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fduasrzpewcol71993gwh.png" alt="enable remote logic Mac" width="800" height="646"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After this, we have to create a security key for being able to SSH into the localhost. Run the following command to generate a key. Follow the instructions on screen to provide all the required information.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ssh-keygen &lt;span class="nt"&gt;-t&lt;/span&gt; rsa &lt;span class="nt"&gt;-P&lt;/span&gt; &lt;span class="s1"&gt;''&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; ~/.ssh/id_rsa
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once the key is generated, we have to copy that over to the authorised keys file so that we authorise this key to be used for SSH without password. This is important because Hadoop expects password-less SSH to be available and enabled. So run the following command to copy over the key:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cat&lt;/span&gt; ~/.ssh/id_rsa.pub &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; ~/.ssh/authorized_keys
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In some cases, SSH might not work if the key has too much “public” access. To avoid this, run the following command to restrict permissions to the key:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;chmod &lt;/span&gt;0600 ~/.ssh/id_rsa.pub
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And that’s it. SSH should be working fine now. To be extra sure, let’s try it out with the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ssh localhost
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you don’t get any error for that command, it’s all working as expected. You’re now logged into another session in your terminal using SSH. So let’s logout from there and come back to our previous session. To do this, hit CTRL + d. You should see something similar to the following:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgp590vrqosjn7sqf8sl8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgp590vrqosjn7sqf8sl8.png" alt="ssh localhost Mac" width="626" height="214"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And we now have all dependencies installed and working.&lt;/p&gt;




&lt;h2&gt;
  
  
  Installing Hadoop
&lt;/h2&gt;

&lt;p&gt;First step to installing Hadoop is to actually download it. As of this writing, the latest version of Hadoop is version 3.3.1, and you can download it from here. You will be downloading a .tar.gz file from there. To decompress it, you can just double click the package and it’ll decompress and create a directory with all the contents. You can move this directory wherever you want to place the Hadoop installation.&lt;/p&gt;

&lt;p&gt;Because we’re installing Hadoop on our local machine, we’re going to do a single-node deployment, which is also known as pseudo-distributed mode deployment.&lt;/p&gt;

&lt;h3&gt;
  
  
  Setting the environment variables
&lt;/h3&gt;

&lt;p&gt;We have to set a bunch of environment variables. The best part is, you have to customize only one variable. The others are just copy-paste. Anyway, following are the variables I’m talking about:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Hadoop&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;HADOOP_HOME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/Users/sunny/programs/hadoop-3.3.1/
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;HADOOP_INSTALL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$HADOOP_HOME&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;HADOOP_MAPRED_HOME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$HADOOP_HOME&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;HADOOP_COMMON_HOME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$HADOOP_HOME&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;HADOOP_HDFS_HOME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$HADOOP_HOME&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;YARN_HOME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$HADOOP_HOME&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;HADOOP_COMMON_LIB_NATIVE_DIR&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$HADOOP_HOME&lt;/span&gt;/lib/native
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;PATH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$PATH&lt;/span&gt;:&lt;span class="nv"&gt;$HADOOP_HOME&lt;/span&gt;/sbin:&lt;span class="nv"&gt;$HADOOP_HOME&lt;/span&gt;/bin
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;HADOOP_OPTS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"-Djava.library.path=&lt;/span&gt;&lt;span class="nv"&gt;$HADOOP_HOME&lt;/span&gt;&lt;span class="s2"&gt;/lib/nativ"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you can see, you only have to change the value of the first environment variable, HADOOP_HOME. Set it to reflect the path where you have placed the Hadoop directory. Also, it is a good idea to place these export statements in the .zshrc file so that these variables are exported every time automatically instead of you having to do it. Once you place it in the file, make sure you source it so that it takes effect immediately:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;source&lt;/span&gt; ~/.zshrc
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Configuring Hadoop
&lt;/h3&gt;

&lt;p&gt;Next, we’ll have to edit a few files to change the config for various Hadoop components. Let’s start that with the file hadoop-env.sh. Run the following command to open the file in the editor:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;vim &lt;span class="nv"&gt;$HADOOP_HOME&lt;/span&gt;/etc/hadoop/hadoop-env.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, find the line that is exporting the $JAVA_HOME variable and uncomment it. Here, you have to provide the same path that you did when you installed Java earlier. For me, that’s the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;JAVA_HOME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/Library/Java/JavaVirtualMachines/zulu-8.jdk/Contents/Home/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, we have to edit the core-site.xml file. Here we have to provide the temporary directory for Hadoop and also the default name for the Hadoop file system. Open the file in the editor using the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;vim &lt;span class="nv"&gt;$HADOOP_HOME&lt;/span&gt;/etc/hadoop/core-site.xml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You’ll find an empty file here with a few comments and an empty configuration block. You can delete everything and replace it with the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&amp;lt;configuration&amp;gt;
    &amp;lt;property&amp;gt;
        &amp;lt;name&amp;gt;hadoop.tmp.dir&amp;lt;/name&amp;gt;
        &amp;lt;value&amp;gt;/Users/sunny/hdfs/tmp/&amp;lt;/value&amp;gt;
    &amp;lt;/property&amp;gt;
    &amp;lt;property&amp;gt;
        &amp;lt;name&amp;gt;fs.default.name&amp;lt;/name&amp;gt;
        &amp;lt;value&amp;gt;hdfs://127.0.0.1:9000&amp;lt;/value&amp;gt;
    &amp;lt;/property&amp;gt;
&amp;lt;/configuration&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Make sure you create the temp directory that you configure here. Next, we have to edit the HDFS config file hdfs-site.xml. To do this, open the file in the editor using the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;vim &lt;span class="nv"&gt;$HADOOP_HOME&lt;/span&gt;/etc/hadoop/hdfs-site.xml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this configuration file, we are setting the HDFS data node directory, HDFS name node directory, and the HDFS replication factor. Here again you should get a file with an empty configuration block. Replace that with the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&amp;lt;configuration&amp;gt;
  &amp;lt;property&amp;gt;
      &amp;lt;name&amp;gt;dfs.data.dir&amp;lt;/name&amp;gt;
      &amp;lt;value&amp;gt;/Users/sunny/hdfs/namenode&amp;lt;/value&amp;gt;
  &amp;lt;/property&amp;gt;
  &amp;lt;property&amp;gt;
      &amp;lt;name&amp;gt;dfs.data.dir&amp;lt;/name&amp;gt;
      &amp;lt;value&amp;gt;/Users/sunny/hdfs/datanode&amp;lt;/value&amp;gt;
  &amp;lt;/property&amp;gt;
  &amp;lt;property&amp;gt;
      &amp;lt;name&amp;gt;dfs.replication&amp;lt;/name&amp;gt;
      &amp;lt;value&amp;gt;1&amp;lt;/value&amp;gt;
  &amp;lt;/property&amp;gt;
&amp;lt;/configuration&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And again, make sure you create the data node and name node directories. Next, we have the MapReduce config file. To open this in the editor, run the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;vim &lt;span class="nv"&gt;$HADOOP_HOME&lt;/span&gt;/etc/hadoop/mapred-site.xml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can replace the configuration block with the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&amp;lt;configuration&amp;gt; 
  &amp;lt;property&amp;gt; 
    &amp;lt;name&amp;gt;mapreduce.framework.name&amp;lt;/name&amp;gt; 
    &amp;lt;value&amp;gt;yarn&amp;lt;/value&amp;gt; 
  &amp;lt;/property&amp;gt; 
&amp;lt;/configuration&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you can see, it’s a simple configuration which specifies the MapReduce framework name. And finally, we have the YARN configuration file, yarn-site.xml. Open the file in the editor using:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;vim &lt;span class="nv"&gt;$HADOOP_HOME&lt;/span&gt;/etc/hadoop/yarn-site.xml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Add the following configuration to the file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&amp;lt;configuration&amp;gt;
  &amp;lt;property&amp;gt;
    &amp;lt;name&amp;gt;yarn.nodemanager.aux-services&amp;lt;/name&amp;gt;
    &amp;lt;value&amp;gt;mapreduce_shuffle&amp;lt;/value&amp;gt;
  &amp;lt;/property&amp;gt;
  &amp;lt;property&amp;gt;
    &amp;lt;name&amp;gt;yarn.nodemanager.aux-services.mapreduce.shuffle.class&amp;lt;/name&amp;gt;
    &amp;lt;value&amp;gt;org.apache.hadoop.mapred.ShuffleHandler&amp;lt;/value&amp;gt;
  &amp;lt;/property&amp;gt;
  &amp;lt;property&amp;gt;
    &amp;lt;name&amp;gt;yarn.resourcemanager.hostname&amp;lt;/name&amp;gt;
    &amp;lt;value&amp;gt;127.0.0.1&amp;lt;/value&amp;gt;
  &amp;lt;/property&amp;gt;
  &amp;lt;property&amp;gt;
    &amp;lt;name&amp;gt;yarn.acl.enable&amp;lt;/name&amp;gt;
    &amp;lt;value&amp;gt;0&amp;lt;/value&amp;gt;
  &amp;lt;/property&amp;gt;
  &amp;lt;property&amp;gt;
    &amp;lt;name&amp;gt;yarn.nodemanager.env-whitelist&amp;lt;/name&amp;gt;   
    &amp;lt;value&amp;gt;JAVA_HOME,HADOOP_COMMON_HOME,HADOOP_HDFS_HOME,HADOOP_CONF_DIR,CLASSPATH_PERPEND_DISTCACHE,HADOOP_YARN_HOME,HADOOP_MAPRED_HOME&amp;lt;/value&amp;gt;
  &amp;lt;/property&amp;gt;
&amp;lt;/configuration&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There’s nothing to change in this configuration. And finally, we’re done configuring Hadoop. We can now move on to formatting the name node and starting Hadoop.&lt;/p&gt;

&lt;h3&gt;
  
  
  Formatting the HDFS name node
&lt;/h3&gt;

&lt;p&gt;It’s important to first format the HDFS name node before starting the Hadoop service the first time. This, obviously, makes sure there’s no junk anywhere in the name node. And once you start using HDFS more frequently, you’ll realize that you’re formatting the name node more often that you thought you would, at least on your development machine. Anyway, to format the name node, use the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;hdfs namenode &lt;span class="nt"&gt;-format&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once you get the shutdown notification for name node, the formatting is complete.&lt;/p&gt;

&lt;h3&gt;
  
  
  Starting all of Hadoop
&lt;/h3&gt;

&lt;p&gt;Finally, we’re at the best part of this activity, starting and using Hadoop. Now, there are many ways of starting Hadoop depending on what components you actually want to use. For example, you can start only YARN, or HDFS along with it, etc. For this activity, we’ll just start everything. To do this, the Hadoop distribution provides a handy script. And because you have already exported a bunch of environment variables earlier, you don’t even have to search for that script, it’s already in your path. Just run the following command and wait for it to finish:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;start-all.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will take a few seconds, as the script just waits for the first 10 seconds without doing anything to give you an option to cancel the operation if you started it by mistake. Just hold on and you should see output similar to the following screenshot:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F45upuxxeg7ud893azpdk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F45upuxxeg7ud893azpdk.png" alt="start-all.sh Mac" width="800" height="144"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This tells us that all components of Hadoop are up and running. To make sure, if you want to, you run the jps command to get a list of all the processes running. You should see at least the following services:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fu53964bng2rk8dxz73uf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fu53964bng2rk8dxz73uf.png" alt="jps" width="354" height="290"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And that’s it. You’re now running Hadoop on your Windows 11 PC using a Linux distro on WSL 1 or 2. To make sure, you can use the following simple HDFS command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;hdfs dfs &lt;span class="nt"&gt;-ls&lt;/span&gt; /
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This command will list all files and directories at the root HDFS directory. If it’s a brand new deployment, you shouldn’t find much there. You’ll get a list similar to the one shown below:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7ibxnxh54bh9sb8iuym4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7ibxnxh54bh9sb8iuym4.png" alt="hdfs dfs -ls /" width="800" height="73"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;That’s pretty much it. We’re done!&lt;/p&gt;




&lt;p&gt;And if you like what you see here, or on my &lt;a href="https://medium.com/@contactsunny"&gt;Medium blog&lt;/a&gt; and &lt;a href="https://blog.contactsunny.com"&gt;personal blog&lt;/a&gt;, and would like to see more of such helpful technical posts in the future, consider supporting me on &lt;a href="https://www.patreon.com/contactsunny"&gt;Patreon&lt;/a&gt; and &lt;a href="https://github.com/sponsors/contactsunny"&gt;Github&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>hadoop</category>
      <category>bigdata</category>
      <category>macbook</category>
      <category>programming</category>
    </item>
    <item>
      <title>Getting Started With Apache Airflow</title>
      <dc:creator>Sunny Srinidhi</dc:creator>
      <pubDate>Mon, 11 Oct 2021 09:27:05 +0000</pubDate>
      <link>https://dev.to/contactsunny/getting-started-with-apache-airflow-18fl</link>
      <guid>https://dev.to/contactsunny/getting-started-with-apache-airflow-18fl</guid>
      <description>&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Furxjmajo1mg8ns7h179x.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Furxjmajo1mg8ns7h179x.jpg" alt="workflow" width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Apache Airflow is another awesome tool that I discovered just recently. Just a couple of months after discovering it, I can’t imagine not using it now. It’s reliable, configurable, and dynamic. Because it’s all driven by code, you can version control it too. It’s just awesome! But wait, what the heck is Apache Airflow?&lt;/p&gt;

&lt;p&gt;Apache Airflow is a workflow orchestration tool, to put it simply. Using Airflow, you can define the various steps involved in your workflow (data projects or not), define the relations between these various steps, and then schedule those steps as well. But wait, this sounds more like defining CRON jobs. You wouldn’t be entirely wrong there. But obviously, this is much more than just a fancy CRON job. Let’s dive deep and understand it.&lt;/p&gt;




&lt;h2&gt;
  
  
  Apache Airflow
&lt;/h2&gt;

&lt;p&gt;The official definition of Airflow on it’s Apache homepage is:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Airflow is a platform created by the community to programmatically author, schedule and monitor workflows.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://airflow.apache.org/"&gt;Apache Airflow&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That should give a high-level understanding of what Airflow is. Like most Apache projects, this is community driven and open source. It can be programmatically authored, which means you can write some code to not just define the workflow, but also to schedule and update it. As I already mentioned, you can have versioning on Airflow code and easily update or rollback the code at will. Oh, by the way, Airflow DAGs are written in Python.&lt;/p&gt;

&lt;p&gt;Wait, what’s a DAG, you ask? DAG stands for Directed Acyclic Graph. This shouldn’t be new to you if you’ve already worked with tools such as Apache Spark. Spark jobs are internally converted to DAGs as well, which you can see visually from the web UI whenever a Spark job is running. Airflow uses the same concept to chain various operations together in a workflow.&lt;/p&gt;

&lt;p&gt;Each operation in Airflow is defined using an operator. And of course, there are &lt;a href="https://airflow.apache.org/docs/apache-airflow/stable/_api/airflow/operators/index.html?highlight=operator#module-airflow.operators"&gt;various operators&lt;/a&gt;. For example, in a DAG if you want to have an operation just to denote a virtual entity, you can use a DummyOperator. Similarly, if you want to run a execute a bash command or run a bash script file, you can use the BashOperator.&lt;/p&gt;

&lt;p&gt;Similar to operators, there’s support for &lt;a href="https://airflow.apache.org/docs/apache-airflow/stable/plugins.html"&gt;plugins&lt;/a&gt; as well, where you can integrate third party plugins to bring in more functionality to Airflow. Obviously you can write your own plugins as well. This should be fairly simple as writing a plugin is mostly as simple as writing a Python package. If you have experience with Python, you’d understand this looking at the &lt;a href="https://airflow.apache.org/docs/apache-airflow/stable/plugins.html"&gt;documentation&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Another way to extend the functionality of Airflow is to use &lt;a href="https://airflow.apache.org/docs/apache-airflow-providers/index.html"&gt;providers packages&lt;/a&gt;. This is different from plugins in the way that a providers package can include new operators, sensors, hooks, and transfer operators to extend Airflow. And as you can expect with any other popular open source platform, there are already a bunch of providers to provide a rich ecosystem. You can see the full list of current providers &lt;a href="https://airflow.apache.org/docs/#providers-packages-docs-apache-airflow-providers-index-html"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Now that we understand at a very high level what Apache Airflow is, let’s look a simple example workflow to see how we can utilize Airflow.&lt;/p&gt;




&lt;h2&gt;
  
  
  A Sample Airflow Workflow
&lt;/h2&gt;

&lt;p&gt;Before we get started, if you are anything like me, you would want to have Airflow setup locally so that you can just copy-paste the code and see if it works. If this is you, you’d want to checkout the installation &lt;a href="https://airflow.apache.org/docs/apache-airflow/stable/installation/index.html"&gt;instructions&lt;/a&gt;, because there are a bunch of ways to install Airflow. Once you have Airflow up and running, make sure you switch from Derby to either MySQL or PostgreSQL as the backend database. This is because Derby gives some issues with scheduling DAGs and running them, even locally. You can see how to make the switch &lt;a href="https://airflow.apache.org/docs/apache-airflow/stable/howto/set-up-database.html"&gt;here&lt;/a&gt;. Don’t worry, it’s pretty simple and shouldn’t take more than a couple of minutes if you already have MySQL or Postgres already installed.&lt;/p&gt;

&lt;h3&gt;
  
  
  Understanding the sample workflow
&lt;/h3&gt;

&lt;p&gt;Even though the dataset itself isn’t important for this Airflow workflow, I just want to touch upon this dataset as this is one of the most frequently used dataset in the world of data science. The NYC Taxi and Limousine Commission (TLC) exposes each month’s taxi data as CSVs, for free. So I’m using that, well a sub-set of that. To be more specific, I’m using the yellow taxi data from December of 2020. You can get more info on this over &lt;a href="https://www1.nyc.gov/site/tlc/about/tlc-trip-record-data.page"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Code
&lt;/h3&gt;

&lt;p&gt;Finally, let’s get into the juicy bit. First, we need to define a DAG in our python file. This DAG, which is an instance of the DAG class, will be the identifier that tells Airflow that this particular file should be scanned and listed as a DAG in Airflow’s database. And this DAG taken in a bunch of default arguments which are literally called default arguments, I’m not even kidding. So let’s start by defining these default arguments:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;default_args&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;owner&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;sunny&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;depends_on_past&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;email&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;sunny@contactsunny.com&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;email_on_failure&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;email_on_retry&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;retries&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;retry_delay&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;timedelta&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;minutes&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This should be pretty much self explanatory. I’m not going to talk much on each field in that object. Next, with this set of default arguments, we’ll define the DAG itself:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nc"&gt;DAG&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;load_nyc_taxi_data&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;default_args&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;default_args&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;description&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;DAG to load NYC Taxi data to Hive&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;schedule_interval&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nf"&gt;timedelta&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;days&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;start_date&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nf"&gt;days_ago&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;tags&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;sunny&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;sample&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;dag&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The first argument to the DAG here is called the DAG ID. This, as you could imagine, is what uniquely identifies a DAG. The second argument is the set of default arguments that we defined in the previous step. The others are again pretty self explanatory.&lt;/p&gt;

&lt;p&gt;The next step is to define the various operations that we want to perform in this workflow. So let’s talk about that first. For this example, I’m using Hive as the datastore for storing the yellow taxi data that I’m downloading. Why Hive? Well, why not? You can of course change any of all of this. Anyway, following is the operations that the DAG is going to perform, in the given order:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Create a database in Hive.&lt;/li&gt;
&lt;li&gt;Create a table in the database that we just created.&lt;/li&gt;
&lt;li&gt;Download the NYC yellow taxi data CSV.&lt;/li&gt;
&lt;li&gt;Import that data into the table we created in Hive.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;That’s pretty simple, right? Anyway, let’s look at each of these steps in detail.&lt;/p&gt;

&lt;h3&gt;
  
  
  Creating a database in Hive
&lt;/h3&gt;

&lt;p&gt;This is a simple &lt;code&gt;create database&lt;/code&gt; query in Hive. But I’m not going to login to the Hive shell and then execute this query. Instead, I have written a &lt;code&gt;.hql&lt;/code&gt; file that I’ll submit to the hive command, which will in turn execute the query that’s in the file. The &lt;code&gt;.hql&lt;/code&gt; itself is pretty simple:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;create&lt;/span&gt; &lt;span class="k"&gt;database&lt;/span&gt; &lt;span class="n"&gt;if&lt;/span&gt; &lt;span class="k"&gt;not&lt;/span&gt; &lt;span class="k"&gt;exists&lt;/span&gt; &lt;span class="n"&gt;nyc&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I have this query in a file called &lt;code&gt;create_database.hql&lt;/code&gt;. And how exactly do I submit this to hive? That’s pretty simple too:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="n"&gt;hive&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;mnt&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;code&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;poc&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;airflow&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;create_database&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;hql&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now that we know what exactly we’re doing in this operation, let’s define a task in Airflow. We’ll use the BashOperator to create this task, as we’re running a bash command to perform this operation. The Python code to define this task is as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;create_database&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;BashOperator&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;task_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;create_database&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;bash_command&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;hive -f /mnt/d/code/poc/airflow/create_database.hql&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;dag&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;dag&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The first argument to the BashOperator is a &lt;code&gt;task_id&lt;/code&gt;. This is the ID that will uniquely identify the task in the given DAG. So make sure you don’t have duplicate task IDs. The &lt;code&gt;bash_command&lt;/code&gt; is the actual bash command that will be executed when Airflow triggers this task. That’s pretty much it for this task. Let’s move to the next task.&lt;/p&gt;

&lt;h3&gt;
  
  
  Creating a table
&lt;/h3&gt;

&lt;p&gt;Now that we have the database, let’s create a table to hold the NYC yellow taxi data. I have created this table definition based on the data in the CSV file that I downloaded from the source. So if you are using some other dataset, make sure to change the table definition accordingly.&lt;/p&gt;

&lt;p&gt;Similar to the query for creating the database, I have the query for creating the table in a &lt;code&gt;.hql&lt;/code&gt; file called &lt;code&gt;create_table.hql&lt;/code&gt;. The query itself is as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;create&lt;/span&gt; &lt;span class="n"&gt;table&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;exists&lt;/span&gt; &lt;span class="n"&gt;nyc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;nyc_yellow_taxi_trips &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;VendorID&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;tpep_pickup_datetime&lt;/span&gt; &lt;span class="n"&gt;timestamp&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;tpep_dropoff_datetime&lt;/span&gt; &lt;span class="n"&gt;timestamp&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;passenger_count&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;trip_distance&lt;/span&gt; &lt;span class="n"&gt;double&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;RatecodeID&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;store_and_fwd_flag&lt;/span&gt; &lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;PULocationID&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;DOLocationID&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;payment_type&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;fare_amount&lt;/span&gt; &lt;span class="n"&gt;double&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;extra&lt;/span&gt; &lt;span class="n"&gt;double&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;mta_tax&lt;/span&gt; &lt;span class="n"&gt;double&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;tip_amount&lt;/span&gt; &lt;span class="n"&gt;double&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;tolls_amount&lt;/span&gt; &lt;span class="n"&gt;double&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;improvement_surcharge&lt;/span&gt; &lt;span class="n"&gt;double&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;total_amount&lt;/span&gt; &lt;span class="n"&gt;double&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;congestion_surcharge&lt;/span&gt; &lt;span class="n"&gt;double&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;COMMENT&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;NYC Yellow Taxi Trips&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
&lt;span class="n"&gt;ROW&lt;/span&gt; &lt;span class="n"&gt;FORMAT&lt;/span&gt; &lt;span class="n"&gt;DELIMITED&lt;/span&gt;
&lt;span class="n"&gt;FIELDS&lt;/span&gt; &lt;span class="n"&gt;TERMINATED&lt;/span&gt; &lt;span class="n"&gt;BY&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;,&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And the bash command to submit this query to Hive is also similar to the previous command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="n"&gt;hive&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;mnt&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;code&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;poc&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;airflow&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;create_table&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;hql&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, let’s define the task in Airflow:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="n"&gt;create_table&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;BashOperator&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;task_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'create_table'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;bash_command&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'hive -f /mnt/d/code/poc/airflow/create_table.hql'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;dag&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;dag&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Nothing new here, this is very similar to the previous task, jus the task ID and the command are different. Next, we download the sample data.&lt;/p&gt;

&lt;h3&gt;
  
  
  Downloading sample dataset
&lt;/h3&gt;

&lt;p&gt;You can skip this step if you want to manually download the CSV file. But because I’m automating a workflow, I thought it’ll be better if I made the download automated as well. In a real world workflow, getting the data for ingestion itself is a separate workflow. That’s another reason I wanted to have this as a task. Now, how am I downloading the dataset?&lt;/p&gt;

&lt;p&gt;I’m using the &lt;code&gt;wget&lt;/code&gt; command to get the &lt;code&gt;.csv&lt;/code&gt; file and place it in a path that I have already created. You don’t have to manually create the path because in most cases, the path will be constant. Anyway, the &lt;code&gt;wget&lt;/code&gt; command itself is straightforward:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;wget https://nyc-tlc.s3.amazonaws.com/trip+data/yellow_tripdata_2020-12.csv &lt;span class="nt"&gt;-P&lt;/span&gt; /mnt/d/Backup/hive_sample_datasets/nyc_taxi_trips/nyc_yellow_taxi_trips/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I have this command in a shell script called &lt;code&gt;download_dataset.sh&lt;/code&gt;. Now you might ask that because this is a bash command, and we have the BashOperator which can directly take the bash command, why are you creating a shell script to run this command? Valid question. Because we have a file for all the queries, I thought we’ll continue with that trend and create a file for this as well. You can directly use the bash command in the BashOperator itself. That should work the same way. Anyway, the task definition for this is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;download_nyc_yellow_taxi_data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;BashOperator&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;task_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;download_nyc_yellow_taxi_data&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;bash_command&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;/mnt/d/code/poc/airflow/download_dataset.sh &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;dag&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;dag&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Load data to table
&lt;/h3&gt;

&lt;p&gt;At this stage, we have the database, the table, and the sample dataset. The only part pending is loading the data into Hive. This is another Hive query that I have written in another &lt;code&gt;.hql&lt;/code&gt; file called &lt;code&gt;load_data_to_table.hql&lt;/code&gt;. The query itself is as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;load&lt;/span&gt; &lt;span class="k"&gt;data&lt;/span&gt; &lt;span class="k"&gt;local&lt;/span&gt; &lt;span class="n"&gt;inpath&lt;/span&gt; &lt;span class="s1"&gt;'/mnt/d/Backup/hive_sample_datasets/nyc_taxi_trips/nyc_yellow_taxi_trips/yellow_tripdata_2020-12.csv'&lt;/span&gt; &lt;span class="k"&gt;into&lt;/span&gt; &lt;span class="k"&gt;table&lt;/span&gt; &lt;span class="n"&gt;nyc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;nyc_yellow_taxi_trips&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you can see from the query, I’m loading the file from a local path. In a real world setup, you’d copy all the files to HDFS first and then load those files from HDFS to Hive. Because distributed, of course. You can do that here as well, but I didn’t see that adding to this demo. So I just skipped it. Now, the task definition for this step is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;load_data_to_table&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;BashOperator&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;task_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;load_data_to_table&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;bash_command&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;hive -f /mnt/d/code/poc/airflow/load_data_to_table.hql&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;dag&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;dag&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Orchestrating the workflow
&lt;/h3&gt;

&lt;p&gt;We have all the tasks defined now. So let’s define the sequence of events now. Doing that is pretty simple. You’ll use the &lt;code&gt;&amp;gt;&amp;gt;&lt;/code&gt; operand to chain two tasks together. For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;task1&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;task2&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What this means is that when the Airflow DAG is triggered, &lt;code&gt;task1&lt;/code&gt; will be executed first. Once that’s complete, &lt;code&gt;task2&lt;/code&gt; will be executed. This will happen in sequence. But what if you have another task, &lt;code&gt;task3&lt;/code&gt; that you want to trigger after &lt;code&gt;task1&lt;/code&gt; along with &lt;code&gt;task2&lt;/code&gt;? You can do that in parallel like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;task1&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;task2&lt;/span&gt;
&lt;span class="n"&gt;task1&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;task3&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this case, as soon as &lt;code&gt;task1&lt;/code&gt; is complete, tasks &lt;code&gt;task2&lt;/code&gt; and &lt;code&gt;task3&lt;/code&gt; will be executed in parallel. We don’t need this parallelism in our example though. For us, the sequence is as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;start&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;create_database&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;create_table&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;download_nyc_yellow_taxi_data&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;load_data_to_table&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Wait, we only defined the four tasks in the middle. What are the start and end tasks? Glad that you asked. Those two are DummyOperators used to represent the start and the end of the workflow. You can use DummyOperators even in the middle of the workflow, basically wherever you want. But they do absolutely nothing more than adding a node in the DAG.&lt;/p&gt;

&lt;p&gt;Now, if you open the Airflow web UI and open your DAG, you should see something similar to the following:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ft1lit4zeng1239j0pbcm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ft1lit4zeng1239j0pbcm.png" alt="Airflow DAG" width="800" height="178"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The green outline you see around each task indicates that the task completed successfully. If, for some reason, any of the task failed, you’d see a red outline around that task, like the example shown below:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgadd7pay5o8ak6tt3by9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgadd7pay5o8ak6tt3by9.png" alt="Airflow DAG Failed DAG" width="800" height="201"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Running A DAG
&lt;/h2&gt;

&lt;p&gt;Running a DAG is pretty straightforward, at least for our example as we don’t have any schedule in place. At the top right corner of the Airflow web UI, you’ll see the trigger DAG button, which looks like a play button. Click on that, and you’ll get a couple of options. The first option is to trigger the DAG, and the second is to trigger the DAG with some options. We’ll not look at the second option for now. You can see this in the screenshot below:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbsp9nh3o0xuc1khuni9t.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbsp9nh3o0xuc1khuni9t.png" alt="Running A DAG" width="297" height="216"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Click the Tigger DAG option and you should see the DAG trigger now. But for this to happen, make sure you have enabled the DAG first. By default, when a new DAG is discovered by Airflow, it doesn’t enable it. You have to do that manually the first time. And enabling the DAG is pretty simple. At the top left corner of the Airflow web UI, where you see the DAG ID displayed in large font, you’ll see a toggle to the left of the DAG ID, as shown in the screenshot below.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdbx7rgf81smw7dn1np6s.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdbx7rgf81smw7dn1np6s.png" alt="Enabling a DAG" width="666" height="201"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;That toggle will be off by default. Toggle it on to enable the DAG, it’s as simple as that. If you skip this step and trigger the DAG directly, Airflow will not throw a error or put up a notice that you have to enable it first. This is quite unfortunate, but do remember to do this for all new DAGs.&lt;/p&gt;

&lt;p&gt;When you trigger a DAG, Airflow takes you the tree view by default. You can see the tree view for our sample DAG in the screenshot below. You can change this behavior to instead take you to the graph view, but that’s a preference you’ll have to discover for yourself. I prefer to stay in the graph view when I trigger a DAG. Because as the workflows get complex and you more tasks to it, it becomes difficult to understand which task is running in the tree view. Graph view is much easier, at least for me.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F671npqd0rk6w8ti06bpf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F671npqd0rk6w8ti06bpf.png" alt="Tree View of a DAG" width="669" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If a task fails, you can click on it to get a modal which will have the option to either restart the task and all downstream tasks from that point, or check the logs, or just mark it as a success. Once you start writing more complex workflows, you’ll get a chance to experiment with various features of the tool.&lt;/p&gt;




&lt;p&gt;That’s pretty much it. If you want to look at the complete code base for this instead of stitching together all these pieces, have a look at my &lt;a href="https://github.com/contactsunny/apache_airflow_poc"&gt;Github repo&lt;/a&gt; which has the same code. And if you think my understanding is wrong anywhere with Airflow, please leave a comment to make sure you correct me.&lt;/p&gt;




&lt;p&gt;And if you like what you see here, or on my &lt;a href="https://medium.com/@contactsunny"&gt;Medium blog&lt;/a&gt; and &lt;a href="//blog.contactsunny.com"&gt;personal blog&lt;/a&gt;, and would like to see more of such helpful technical posts in the future, consider supporting me on &lt;a href="https://www.patreon.com/contactsunny"&gt;Patreon&lt;/a&gt; and &lt;a href="https://github.com/sponsors/contactsunny"&gt;Github&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>airflow</category>
      <category>bigdata</category>
      <category>datascience</category>
      <category>python</category>
    </item>
    <item>
      <title>Fake (almost) everything with Faker</title>
      <dc:creator>Sunny Srinidhi</dc:creator>
      <pubDate>Fri, 01 Oct 2021 05:48:22 +0000</pubDate>
      <link>https://dev.to/contactsunny/fake-almost-everything-with-faker-38ee</link>
      <guid>https://dev.to/contactsunny/fake-almost-everything-with-faker-38ee</guid>
      <description>&lt;p&gt;I was recently tasked with creating some random customer data, with names, phone numbers, addresses, and the usual other stuff. At first, I thought I'll just generate random strings and numbers (some gibberish) and call it a day. But then I remembered my colleagues using a package for that. I know, there's always a package for everything, well, almost everything.&lt;/p&gt;

&lt;p&gt;Anyway, I thought I'll give it a shot. I've started to write some serious Python code these days and thought it to be a good idea to explore the various packages available for Python. I executed the pip command, downloaded the package, and started generating some random people in my CSV files. It was fun. So I thought I'll document this process because given my history, I'll definitely forget about Faker.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fea0tmemjyh0kr8r0hfjr.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fea0tmemjyh0kr8r0hfjr.jpg" alt="Group of people" width="800" height="485"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;Installing Faker&lt;/h2&gt;

&lt;p&gt;Installing Faker is no different than installing any other Python package using pip. You can use any one of the following commands to install Faker.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;pip install faker
pip3 install faker
python -m pip install faker
python3 -m pip install faker&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Depending on the version of Python you have installed, use the appropriate command to install the Faker package. It shouldn't take more than a couple minutes.&lt;/p&gt;




&lt;h2&gt;Importing Faker into your code and initialising it&lt;/h2&gt;

&lt;p&gt;Importing the Faker package into your code is also nothing different. Simply add the following import statement at the beginning of your Python file and you should be good to go.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;from faker import Faker&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Once you have imported the package, you need to create a object of the Faker class. You can do that using the following command. The locale parameter is optional though. You can skip that and you'll totally be fine.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;faker = Faker(locale='en_US')&lt;/code&gt;&lt;/pre&gt;

&lt;h2&gt;Let's look at what it can do first&lt;/h2&gt;

&lt;p&gt;Before we dive into the code, let's have a look at what it can do for us first.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;My name is Mx. Linda Dunn III , I'm a gender neutral person. You can call me at 001-099-311-6470, or email me at caroljohnson@hotmail.com, or visit my home at 2703 Fitzpatrick Squares Suite 785
New Crystal, MN 18112

My name is Dr. John Harris MD , I'm a male. You can call me at (276)611-1727, or email me at combstiffany@brown-rivers.org, or visit my home at 7409 Peterson Locks Apt. 270
South Kimfurt, IL 79246

My name is Dr. Ann Huynh DVM , I'm a female. You can call me at 543.024.8936, or email me at timothy30@shea-poole.com, or visit my home at 5144 Rubio Island
South Kenneth, WI 22855&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;This is the output of a simple Python script that I wrote to generate fake customer data, or fake people. Looking at this, it's amazing how realistic it looks. And the code I used to get this output is the following:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;from faker import Faker

faker = Faker(locale='en_US')

print("My name is %s %s %s , I'm a gender neutral person. You can call me at %s, or email me at %s, or visit my home at %s" % 
    (faker.prefix_nonbinary(), faker.name_nonbinary(), faker.suffix_nonbinary(), faker.phone_number(), faker.ascii_free_email(), faker.address())
)

print("My name is %s %s %s , I'm a male. You can call me at %s, or email me at %s, or visit my home at %s" % 
    (faker.prefix_male(), faker.name_male(), faker.suffix_male(), faker.phone_number(), faker.ascii_company_email(), faker.address())
)

print("My name is %s %s %s , I'm a female. You can call me at %s, or email me at %s, or visit my home at %s" % 
    (faker.prefix_female(), faker.name_female(), faker.suffix_female(), faker.phone_number(), faker.company_email(), faker.address())
)&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;You can see now how easy it is to generate large amounts of fake customers, for testing of course. And the fun doesn't end here. There's a lot more where that came from. You can generate a whole company with for example:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;The company I just created!
David PLC
Providers of Horizontal value-added knowledge user
Phone: 001-891-255-4642x93803
Email: ksanchez@cochran.com
234 Torres Ports
West Rhonda, AL 96210&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;As you can see from the output above, we provide some great horizontal value-added knowledge user. That's supposed to be the company's catch phrase. &lt;/p&gt;

&lt;p&gt;And I kid you not, there's a method called &lt;strong&gt;bs()&lt;/strong&gt;. I don't know when you'd ever use, but you call Faker's bs() any time you want. See what I did there?&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fb564xqcwz67xeygv83h4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fb564xqcwz67xeygv83h4.png" alt="faker bs() method" width="278" height="164"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;How does this help?&lt;/h2&gt;

&lt;p&gt;Well, I thought you'd have already figured that part out. Anyway, when you need data to test, and you need that data to be as true to reality as possible (or as realistic) as possible, you can use Faker to easily and quickly generated test data. &lt;/p&gt;

&lt;p&gt;Actually, I'm not sure about the "quickly" part of my last sentence. It's definitely easy to generate the data. But to generate one million customer records with first name, last name, email, phone, etc., it took almost 350 seconds on a 2019 16-inch base model MacBook Pro. So make of it what you will. &lt;/p&gt;




&lt;h2&gt;Summary&lt;/h2&gt;

&lt;p&gt;Nonetheless, this is definitely a very handy and fun package to have in your arsenal. You can generate any number of customers or friends (swing how ever you swing) very easily with a complete offline and online profile for each person. You can generate home phone and email, work phone and email, home address, work address, interests, profiles, credit cards, license plate numbers, and a lot more. So do head over to the &lt;a href="https://github.com/joke2k/faker?ref=pythonrepo.com" rel="noreferrer noopener"&gt;package's Github repo&lt;/a&gt;, take a look around, and take it for a spin. The source code is pretty easy to understand as well.&lt;/p&gt;

&lt;p&gt;And if you like what you see here, or on my &lt;a rel="noreferrer noopener" href="https://medium.com/@contactsunny"&gt;Medium blog&lt;/a&gt;, and would like to see more of such helpful technical posts in the future, consider supporting me on &lt;a rel="noreferrer noopener" href="https://www.patreon.com/contactsunny"&gt;Patreon&lt;/a&gt; and &lt;a rel="noreferrer noopener" href="https://github.com/sponsors/contactsunny"&gt;Github&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.patreon.com/bePatron?u=28955887"&gt;Become a Patron!&lt;/a&gt;&lt;/p&gt;

</description>
      <category>python</category>
      <category>bigdata</category>
      <category>datascience</category>
      <category>programming</category>
    </item>
    <item>
      <title>out() vs. outE() – JanusGraph and Gremlin</title>
      <dc:creator>Sunny Srinidhi</dc:creator>
      <pubDate>Wed, 03 Mar 2021 03:24:43 +0000</pubDate>
      <link>https://dev.to/contactsunny/out-vs-oute-janusgraph-and-gremlin-m5n</link>
      <guid>https://dev.to/contactsunny/out-vs-oute-janusgraph-and-gremlin-m5n</guid>
      <description>&lt;p&gt;If you are new to JanusGraph and the Gremlin query language, like I am, you would be confused about the &lt;code&gt;out()&lt;/code&gt;, &lt;code&gt;outE()&lt;/code&gt;, &lt;code&gt;in()&lt;/code&gt;, and &lt;code&gt;inE()&lt;/code&gt; methods. If you look at examples of these functions, you’ll not be able to comprehend the difference easily. Or is it just me?&lt;/p&gt;

&lt;p&gt;Anyway, I got confused and it took me a while to understand there is a difference, and there isn’t. Let me explain.&lt;/p&gt;




&lt;h1&gt;
  
  
  The Sample Graph
&lt;/h1&gt;

&lt;p&gt;Before we look at the differences, let’s look at a sample graph.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpo1lyxrh1fmy12tt89js.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpo1lyxrh1fmy12tt89js.png" alt="Sample JanusGraph graph" width="800" height="509"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As you can see from the graph above, we have four vertices and three edges. The vertex in the middle with the property "name": "sunny" is the vertex from where we’ll start our traversal. The other three vertices are the items that I bought from an e-commerce website. They are a smartphone, a laptop, and a monitor. The relationship is represented with edges labelled bought.&lt;/p&gt;

&lt;p&gt;The edges have another property called count, and as you can tell, they represent the number of times I have bought these items. So I bought three smartphones, two laptops, and one monitor. This is the data we’re going to work with.&lt;/p&gt;

&lt;p&gt;Now, we’ll first get a reference to our starting vertex with the following query:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sunny = g.V().has('name', 'sunny').next()
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We now have all the data we need to understand the difference between these functions.&lt;/p&gt;




&lt;h1&gt;
  
  
  &lt;code&gt;out()&lt;/code&gt; vs. &lt;code&gt;outE()&lt;/code&gt;
&lt;/h1&gt;

&lt;p&gt;We already know that we use the &lt;code&gt;outE()&lt;/code&gt; function to traverse an edge that is going out of the current vertex. We pass in the label of one or more edges to the function. From our e-commerce example, if I want to get all the items that I have bought, I’ll run the following query:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;g.V(sunny).outE('bought').in()
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This would give us all the vertices which have a ‘bought’ relationship with the current vertex. But you’d have also seen the following query for the same use case:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;g.V(sunny).out('bought')
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So, they are performing the same traversal and returning the same results. I found out that when you’re using the &lt;code&gt;outE().in()&lt;/code&gt; combination, you can simply replace it with &lt;code&gt;out()&lt;/code&gt;. It’s a shorthand or an alias for the long form &lt;code&gt;outE().in()&lt;/code&gt;. But then, why would you use &lt;code&gt;outE()&lt;/code&gt; at all?&lt;/p&gt;

&lt;p&gt;Suppose you want to filter or limit the traversal based on other properties of the edge. For example, in our sample graph, I want to get only the items that have bought more than once. We have the count property for each of our bought edge. We can use that to filter our vertices. For this, the query is as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;g.V(sunny).outE('bought').has('count', gt(1)).inV()
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you can see, we can use the &lt;code&gt;has()&lt;/code&gt; function on edges as well to filter out edges with particular property. This ability to filter is not available when you use the &lt;code&gt;out()&lt;/code&gt; function. Because the result of the &lt;code&gt;out()&lt;/code&gt; function is vertices. So if you call the &lt;code&gt;has()&lt;/code&gt; function on that result, you’ll be filtering on the vertices and not edges. I hope I’m not complicating things.&lt;/p&gt;




&lt;h1&gt;
  
  
  &lt;code&gt;in()&lt;/code&gt; vs. &lt;code&gt;inE()&lt;/code&gt;
&lt;/h1&gt;

&lt;p&gt;It’s the same story with the &lt;code&gt;in()&lt;/code&gt; and &lt;code&gt;inE()&lt;/code&gt; functions as well. If you want to filter edges based on extra properties, you use the &lt;code&gt;inE()&lt;/code&gt; function instead of &lt;code&gt;in()&lt;/code&gt;.&lt;/p&gt;




&lt;h1&gt;
  
  
  There’s more…
&lt;/h1&gt;

&lt;p&gt;Using the &lt;code&gt;outE()&lt;/code&gt; or &lt;code&gt;inE()&lt;/code&gt; functions gives you access to more functions that can be used on edges, such as aliasing them using the &lt;code&gt;as()&lt;/code&gt; function, the &lt;code&gt;count()&lt;/code&gt; function, etc. You can have a look at the documentation to see the list of all functions available on edges.&lt;/p&gt;




&lt;p&gt;I hope this has not confused you more than you already are. I thought this would clear things our for at least a few people who have the same questions as me when you are still getting started with JanusGraph and Gremlin. Let me know if this helped, or didn’t.&lt;/p&gt;




&lt;p&gt;And if you like what you see here, or on my &lt;a href="https://blog.contactsunny.com"&gt;personal&lt;/a&gt; or &lt;a href="https://medium.com/@contactsunny"&gt;Medium&lt;/a&gt; blogs, and would like to see more of such helpful technical posts in the future, consider supporting me on &lt;a href="https://www.patreon.com/contactsunny"&gt;Patreon&lt;/a&gt; and &lt;a href="https://github.com/sponsors/contactsunny"&gt;Github&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>janusgraph</category>
      <category>graph</category>
      <category>gremlin</category>
      <category>datascience</category>
    </item>
    <item>
      <title>Getting Started With JanusGraph</title>
      <dc:creator>Sunny Srinidhi</dc:creator>
      <pubDate>Thu, 25 Feb 2021 11:14:16 +0000</pubDate>
      <link>https://dev.to/contactsunny/getting-started-with-janusgraph-3cm4</link>
      <guid>https://dev.to/contactsunny/getting-started-with-janusgraph-3cm4</guid>
      <description>&lt;p&gt;JanusGraph is a graph processing tool that can process graphs stored on clusters with multiple nodes. JanusGraph is designed for massive clusters and for real-time traversals and analytics queries.&lt;/p&gt;

&lt;p&gt;In this post, we’ll look at a few queries that you would want to run the very first time you install JanusGraph and start playing with the Gremlin console. I did this just yesterday, so it’s still fresh in my memory. I saw the need to write this as I didn’t find a few of my questions answered in the official documentation, and I had to hunt down the information on the world wide web, which took some quality time. So, let’s get started.&lt;/p&gt;




&lt;h1&gt;
  
  
  Installing JanusGraph
&lt;/h1&gt;

&lt;p&gt;Installing JanusGraph is pretty straightforward, almost. I did face one issue and confusion while I was trying to get the servers up on my local machine. But upon research, I found out that many people are facing the same issue, and that it could be a bug. I didn’t have enough time to get into the weeds, so I just did what I had to to get it started.&lt;/p&gt;

&lt;p&gt;To begin with, the official documentation is good enough for instructions on how to install and get the servers up. The issue I was talking about is that you need both Cassandra and Elasticsearch running to get the Gremlin server up. You can of course use alternatives to both Cassandra (such as HBase, Google Cloud Bigtable, Berkeley DB, or in-memory) and Elasticserach (such as Lucene, Solr, etc.), but I went the easiest route to get started with it.&lt;/p&gt;

&lt;p&gt;The documentation says that you can skip mixed index backends (Elasticsearch, Solr, Lucene, etc.) and use only storage backends, but it doesn’t work. You need a mixed index backend along with a storage backend. So I had to download Elasticsearch as well and run it locally to get the Gremlin server up. This was a bit inconvenient, but no big deal, I guess. Now that we have all the moving parts moving as expected, let’s look at the data we’re going to play with.&lt;/p&gt;




&lt;h1&gt;
  
  
  The Sample Dataset
&lt;/h1&gt;

&lt;p&gt;Here again I took the easy route of using the sample dataset that ships with JanusGraph. It’s an interesting one, called The Graph of the Gods. This data model, as seen below, is called a Property Graph Model. This particular property graph model shows a few Gods from the Roman pantheon, with their names, age, pets, places of living, and a few other relationships.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0sz6iiqrqbhitgyqkxnk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0sz6iiqrqbhitgyqkxnk.png" alt="property-graph-model-janusgraph" width="768" height="698"&gt;&lt;/a&gt;&lt;br&gt;
Source: JanusGraph&lt;br&gt;
You can see that there are few special symbols in the graph. They have special meanings, of course.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7o6j9rq26t5dodwawdum.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7o6j9rq26t5dodwawdum.png" alt="property graph model meaning" width="800" height="233"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now that we have the graph and know what the different edges mean, let’s see if we can traverse the graph. To do that, we first need to get a starting point, which is one vertex. We can use the &lt;code&gt;.has("property", "value")&lt;/code&gt; method on the graph to find a vertex with a particular value. I’ll again go with the official example here.&lt;/p&gt;

&lt;p&gt;We’ll start by getting the reference of the vertex with the “name” being “saturn.” For this, run the following query:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;saturn = g.V().has('name', 'saturn').next()
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Pretty straightforward. Now that we have the reference to the vertex in the &lt;code&gt;saturn&lt;/code&gt; variable, let’s see how we can traverse to Saturn’s grandkid, Hercules. To find the grandson of Saturn, we first need to find the son of Saturn. From the graph, we can see that there’s an incoming edge called “father” from Jupiter to Saturn. We can use this edge to traverse further. Because we have an incoming edge, we’ll use the &lt;code&gt;in()&lt;/code&gt; method on the &lt;code&gt;saturn&lt;/code&gt; variable. Following is the query for this.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;hercules = g.V(saturn).in('father').in('father')
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;in()&lt;/code&gt; method accepts one parameter, which is the edge that has to be traversed. In this example, it’s the ‘father’ edge. So from Saturn, we go to the next vertex which has a ‘father’ edge coming into Saturn, which in this case is Jupiter. From there, we again have the same traversal – &lt;code&gt;in('father')&lt;/code&gt;. This will take us to the son of Jupiter, which is Hercules.&lt;/p&gt;

&lt;p&gt;If you want to go the other way, that’s finding the grandfather of Hercules, you can run the following queries:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;hercules = g.V().has('name', 'hercules').next()
saturn = g.V(hercules).out('father').out('father')
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you can see, we’ve used the &lt;code&gt;out()&lt;/code&gt; method here instead of &lt;code&gt;in()&lt;/code&gt;. This is because we are traversing on the edge ‘father’ which is going out of the &lt;code&gt;hercules&lt;/code&gt; vertex. It took me a while to understand when to use which method, but it’s very logical once I figured it out.&lt;/p&gt;




&lt;h1&gt;
  
  
  Loops
&lt;/h1&gt;

&lt;p&gt;You would’ve observed in the last couple of queries, we did use the &lt;code&gt;in('father')&lt;/code&gt; and &lt;code&gt;out('father')&lt;/code&gt; methods twice in succession. This is a clear example where loops can be used instead of tailing the same method multiple times like this. And with JanusGraph, you can. There’s a simple &lt;code&gt;repeat()&lt;/code&gt; method that makes this very easy. Below are the same queries rewritten with the &lt;code&gt;repeat()&lt;/code&gt; method.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;hercules = g.V(saturn).repeat(__.in('father')).times(2).next()
saturn = g.V(hercules).repeat(__.out('father')).times(2).next()
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I don’t think it could be easier than this.&lt;/p&gt;




&lt;h1&gt;
  
  
  Selecting Multiple Vertices
&lt;/h1&gt;

&lt;p&gt;Sometimes you’d want to query for multiple vertices in a single query. For example, if you want to query for the parents of Hercules, you’ll have to query one parent first, and then the other. You can imagine how this could slow down things when you are working with a distributed graph. To makes things easier and quicker, you can pass in a comma-separated list of edges that you want to traverse using the &lt;code&gt;out()&lt;/code&gt; method. So if you want to query the parents of Hercules, you can just do this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;g.V(hercules).out('father', 'mother').values('name')
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So elegant.&lt;/p&gt;




&lt;p&gt;As I mentioned at the beginning of this post, this is just getting started. There’s a lot more to JanusGraph and Gremlin. I’ll be posting more about these two as and when I learn new things. So watch out for this space if you are interested.&lt;/p&gt;




&lt;p&gt;And if you like what you see here, or on my &lt;a href="https://blog.contactsunny.com"&gt;personal&lt;/a&gt; or &lt;a href="https://medium.com/@contactsunny"&gt;Medium&lt;/a&gt; blogs, and would like to see more of such helpful technical posts in the future, consider supporting me on &lt;a href="https://www.patreon.com/contactsunny"&gt;Patreon&lt;/a&gt; and &lt;a href="https://github.com/sponsors/contactsunny"&gt;Github&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>janusgraph</category>
      <category>bigdata</category>
      <category>graph</category>
      <category>datascience</category>
    </item>
    <item>
      <title>A few basic (but powerful) ImageMagick commands</title>
      <dc:creator>Sunny Srinidhi</dc:creator>
      <pubDate>Sat, 06 Feb 2021 13:04:02 +0000</pubDate>
      <link>https://dev.to/contactsunny/a-few-basic-but-powerful-imagemagick-commands-1gdc</link>
      <guid>https://dev.to/contactsunny/a-few-basic-but-powerful-imagemagick-commands-1gdc</guid>
      <description>&lt;h6&gt;
  
  
  Originally published on &lt;a href="https://blog.contactsunny.com/tech/a-few-basic-but-powerful-imagemagick-commands"&gt;my personal blog&lt;/a&gt;
&lt;/h6&gt;

&lt;p&gt;If you’re not sure what ImageMagick is, it’s one of the greatest tools you could have on your computer, to manipulate images and a few other types of files. In this post, I’m going to list out a few of the commands which come in very handy in a variety of situations.&lt;/p&gt;

&lt;p&gt;Recently I got the opportunity to work on a project where a lot of images had to be manipulated — changing the resolution and keeping the file size in check. I wrote the script in PHP and ImageMagick was the weapon of choice. Before you read any further, if you are not aware of ImageMagick and the features it offers, go through their website first.&lt;/p&gt;

&lt;p&gt;Also, I’d like to point out that this is more for personal reference, so that after a few months when I forget the commands or need to manipulate more images, I can come back here and get all the commands I need.&lt;/p&gt;

&lt;p&gt;One more thing, I work with Ubuntu — my personal laptop runs Ubuntu, my work laptop runs Ubuntu, my personal AWS EC2 instance runs Ubuntu, and almost all of the cloud computers I work on run Ubuntu. So the commands given below are all tested on Ubuntu (16.04 to be precise).&lt;/p&gt;

&lt;p&gt;Let’s get started then, shall we?&lt;/p&gt;




&lt;h1&gt;
  
  
  Change image resolution
&lt;/h1&gt;

&lt;p&gt;This is I think one of the most basic image manipulation we’d do. And therefore, the command is also pretty simple to understand and remember, even for a non-techie.&lt;/p&gt;

&lt;p&gt;Consider we have an image called image.jpg which is around 2816×2112 pixels, that’s like a 6MP image. If you want to reduce that to say around 1024×768 pixels, you just do it like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;convert image.jpg -resize 1024x768 output_file.jpg
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And that’s it. You’ll have a new image called output_file.jpg whose resolution is somewhere close to 1024×768 pixels. I say close to that resolution because by default, ImageMagick tries to maintain the aspect ration. So if the resolution that you specify doesn’t maintain the original aspect ratio of the image, ImageMagick will use a resolution which is close to what you specified while maintaining the aspect ratio.&lt;/p&gt;

&lt;p&gt;But this default behaviour is easy to override. You can use the ‘!’ symbol with the resolution to tell ImageMagick that the resolution has to be maintained strictly. So the command would now look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;convert image.jpg -resize 1024x768! output_file.jpg
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But this might not always work the way you expect. Because *nix systems have a special meaning for the symbol !. To make sure nothing funny happens and this works as expected, you need to “escape” the ! in the command. You can do that by adding a ‘\’ in front of the ! like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;convert image.jpg -resize 1024x768\! output_file.jpg
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That’s it. If your input file image.jpg is not corrupt, you’ll now have a new image with the resolution that you specified. It’s that simple.&lt;/p&gt;




&lt;h1&gt;
  
  
  Reducing an image’s file size, or reducing the quality of an image
&lt;/h1&gt;

&lt;p&gt;If you a developer who builds web app with a lot of images, you know the importance of file size. You can’t have a 5MB image on your webpage, which will take ages to download. This will for a horrible user experience. The solution is to keep the file size as small as possible. When it comes to images, “compressing” an image is the most common technique to achieve a smaller file size. This can be done in two way — reduce the image resolution (which we already discussed), and reduce the “quality” of the image.&lt;/p&gt;

&lt;p&gt;Since we already discussed how to reduce the resolution of an image, let’s see how you can reduce the quality of an image. ImageMagick provides an easy option with it’s convert command to set the quality of an image. You can do it like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;convert image.jpg -quality 75 output_file.jpg
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This command will reduce a 3MB file to less than 300KB. The quality option 75 provided in the command could be thought of as reducing the quality of the image to 75%, or by 25%.&lt;/p&gt;

&lt;p&gt;For JPG (JPEG) images, the quality ranges from 1 to 100. 1 will ensure highest compression but lowest image quality, and 100 will ensure highest image quality but doesn’t provide great compression. You can determine the quality of the input file, if not, the default value is 92.&lt;/p&gt;

&lt;p&gt;Experts also says that 75 is the best trade-off between image quality and compression, and in most cases, it gets the work done. For other image types (such as PNG), there are a lot of other options to use with the -quality option. For more on that, take a look at the official documentation, and you’ll be amazed at the possibilities.&lt;/p&gt;




&lt;h1&gt;
  
  
  Create a collage of images (montage)
&lt;/h1&gt;

&lt;p&gt;Another use case is creating collages of images. We see this kind of images a lot on social networks these days. If you are the kind of person who makes collages, you’ll love this command. No more uploading images to websites and waiting for them to convert and then downloading them again. You don’t even have to give your images to anybody else now.&lt;/p&gt;

&lt;p&gt;Say you have four images and you want to create a 2×2 collage of those four images. You use the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;montage image1.jpg image2.jpg image3.jpg image4.jpg output_montage.jpg
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will create a new image called output_montage.jpg, but it might not look how you expected it to look — it’ll have four very small images arranged in a 2×2 matrix. The images will be resized because of the default options that the montage command uses, which is “-geometry 120×120&amp;gt;+4+3.” Now what does that mean? It means, no matter what the input image resolution is, resize it to a 120×120 pixel image and use that to generate the collage. To override this behaviour, you can just provide the -geometry option yourself with the command.&lt;/p&gt;

&lt;p&gt;There’s also a -tile command, using which you can specify how many tiles you want. But if you aren’t specific about this and just want to have a collage of your images, you can let ImageMagick figure out the best possible tile arrangement.&lt;/p&gt;

&lt;p&gt;So, to get the best possible collage of your four images, use this command with the -geometry option:&lt;br&gt;
montage image1.jpg image2.jpg image3.jpg image4.jpg -geometry +2+2 output_montage.jpg&lt;br&gt;
I’m sure you’ll like the output. Let me know. 🙂&lt;/p&gt;


&lt;h1&gt;
  
  
  Convert an image to a PDF
&lt;/h1&gt;

&lt;p&gt;This is another most common task that we come across frequently. And I must say, conversion of images to PDFs is one of the most simplest commands ImageMagick provides. Let’s suppose you want to convert that awesome 2×2 collage you created earlier to a PDF. How do you do it? Like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;convert output_montage.jpg output_montage.pdf
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Wasn’t that easy? Go ahead, try it.&lt;/p&gt;




&lt;h1&gt;
  
  
  Bonus: Merging PDF files
&lt;/h1&gt;

&lt;p&gt;You might think that you could easily merge several PDF files into one using the convert command like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;convert pdf1.pdf pdf2.pdf output_pdf.pdf
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You actually can. But don’t! This is because if your input files are 10MB each, your output file will be close to 1GB big. This is very, very bad. So to merge several PDF files, I’ll give you another command using another tool called GhostScript. Most *nix systems come pre-installed with GhostScript. If you don’t have it already installed, head over to their website to get install instructions.&lt;/p&gt;

&lt;p&gt;Now, you can easily merge PDF files into one file using the command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;gs -dBATCH -dNOPAUSE -q -sDEVICE=pdfwrite -sOutputFile=temp.pdf pdf1.pdf pdf2.pdf
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Don’t bother what each option means, unless you’re a techie and interested to know how it works. The website has all the information you’d need.&lt;/p&gt;

&lt;p&gt;Most programming languages come with ImageMagick support, either built in or with a few third party packages. But you can be sure to make use of these commands in your project the next time you have to work with images.&lt;/p&gt;

&lt;p&gt;ImageMagick has capabilities with work with blobs as well. So you can just get the base64 string of an image and work with that. It’s really very versatile and handy.&lt;/p&gt;




&lt;p&gt;I hope this was of some use. Let me know in the comments if I made any mistake here, or if you have any more cool commands.&lt;/p&gt;

</description>
      <category>linux</category>
      <category>tooling</category>
      <category>editing</category>
      <category>imagemagick</category>
    </item>
  </channel>
</rss>
