<?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: Jan Gazda</title>
    <description>The latest articles on DEV Community by Jan Gazda (@1oglop1).</description>
    <link>https://dev.to/1oglop1</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%2F428534%2F25d5d137-ec6f-443f-838e-d6a8a4079ac6.jpeg</url>
      <title>DEV Community: Jan Gazda</title>
      <link>https://dev.to/1oglop1</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/1oglop1"/>
    <language>en</language>
    <item>
      <title>Remote/Virtual pair programming</title>
      <dc:creator>Jan Gazda</dc:creator>
      <pubDate>Tue, 18 May 2021 05:10:03 +0000</pubDate>
      <link>https://dev.to/1oglop1/virtual-pair-programming-bj5</link>
      <guid>https://dev.to/1oglop1/virtual-pair-programming-bj5</guid>
      <description>&lt;p&gt;Whenever you are learning or working together on a common codebase with your friends and colleagues you often need to discuss "things" and code review on GitHub is not enough.&lt;/p&gt;

&lt;p&gt;Fortunately, other people had the same ideas and thought how nice it would be if you could:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;See the code on your computer (not via sharing the screen)&lt;/li&gt;
&lt;li&gt;Edit the code together&lt;/li&gt;
&lt;li&gt;Follow the cursor of one another while navigating the text&lt;/li&gt;
&lt;li&gt;Being able to interact with an integrated terminal on the other side&lt;/li&gt;
&lt;li&gt;Speak with each other without using another app&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In May 2017 Microsoft has announced &lt;a href="https://visualstudio.microsoft.com/services/live-share/"&gt;Visual Studio Live Share&lt;/a&gt; which changed how you can interact with your co-coder. &lt;/p&gt;

&lt;p&gt;Live share is available for VSCode as well as for visual studio.&lt;/p&gt;

&lt;p&gt;As a long time PyCharm user I was longing to have the same feature and often opened the VSCode just so I can share the code and explain the thought process. &lt;br&gt;
After a long time, they finally delivered a similar feature with their &lt;a href="https://www.jetbrains.com/code-with-me/"&gt;Code with me&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Comparison
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Live Share
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Uses Microsoft servers hosted on Azure (requires public internet access)&lt;/li&gt;
&lt;li&gt;To initiate the live share session you must sign in with Microsoft or GitHub account.&lt;/li&gt;
&lt;li&gt;Share the code via VSCode, Visual Studio or a Web browser&lt;/li&gt;
&lt;li&gt;Main Features:

&lt;ul&gt;
&lt;li&gt;Co-editing - let's multiple participants edit the code&lt;/li&gt;
&lt;li&gt;Following and focusing - follow the cursor of others&lt;/li&gt;
&lt;li&gt;Share terminals - see/use the terminal of the session organiser&lt;/li&gt;
&lt;li&gt;Share server/Share port - expose a local server (eg. &lt;a href="https://localhost:8000"&gt;https://localhost:8000&lt;/a&gt;) to the participants so they can access your app.&lt;/li&gt;
&lt;li&gt;Unlimited session length&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Read &lt;a href="https://docs.microsoft.com/en-us/visualstudio/liveshare/overview/features#features"&gt;more detailed documentation about the Live Share features&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Code With Me
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Uses Jetbrains servers, however, offers an on-premise solution for closed networks.&lt;/li&gt;
&lt;li&gt;Does not require any account to share the session&lt;/li&gt;
&lt;li&gt;Free session is limited to 3 participants and 30 minutes&lt;/li&gt;
&lt;li&gt;Not all InteliJ IDEs are yet supported&lt;/li&gt;
&lt;li&gt;Requires Code With me client app (included in IDE or standalone)&lt;/li&gt;
&lt;li&gt;Main features:

&lt;ul&gt;
&lt;li&gt;Audio and Video Calls - hear and see each other&lt;/li&gt;
&lt;li&gt;Simultaneous editing - let's multiple participants edit the code&lt;/li&gt;
&lt;li&gt;Following - follow the cursor of others &lt;/li&gt;
&lt;li&gt;Terminal access - see/use the terminal of the session organiser&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Read &lt;a href="https://www.jetbrains.com/code-with-me/"&gt;more detailed documentation about the Code With Me&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;I used Live Share for quite a long time and it has helped me a lot and I cannot imagine working in a team without it!&lt;br&gt;
It's quite easy to use however sometimes VSCode required me to sign in before sharing each session which I find a bit annoying mainly because I want to sing-in in different than my default web browser. &lt;br&gt;
I also noticed that it sometimes takes quite a long to connect or the session does not start correctly so you have to re-create the session.&lt;br&gt;
Also switching between two IDEs "just to share" the code wasn't ideal.&lt;br&gt;
Performance-wise I haven't noticed any difference and working on remote or local editor worked as expected.&lt;/p&gt;

&lt;p&gt;When Code With me became publicly available I switched to it and was very pleasantly surprised how smooth the integration with PyCharm is. I haven't tried it on a large codebase to comment on performance yet.&lt;br&gt;
Free session 30 minutes could be a limiting factor but also motivation to keep the meetings short!&lt;/p&gt;

&lt;p&gt;As you can see both solutions offer a quite similar set of features with few differences so you may still need to switch between IDEs in some cases but concurrency in this sector will lead to better tools and support.&lt;/p&gt;

&lt;p&gt;Let me know in the comment section which solution you use and why what features you'd like to have. And leave a like if you liked the article!&lt;/p&gt;

&lt;p&gt;update 09-11-2021 - code with me can now also do port forwarding and screen sharing&lt;/p&gt;

</description>
      <category>codenewbie</category>
      <category>remote</category>
      <category>vscode</category>
      <category>codereview</category>
    </item>
    <item>
      <title>AWS Glue first experience - part 5 - Glue Workflow, monitoring and rants</title>
      <dc:creator>Jan Gazda</dc:creator>
      <pubDate>Tue, 01 Sep 2020 08:23:46 +0000</pubDate>
      <link>https://dev.to/1oglop1/aws-glue-first-experience-part-5-glue-workflow-monitoring-and-rants-51jo</link>
      <guid>https://dev.to/1oglop1/aws-glue-first-experience-part-5-glue-workflow-monitoring-and-rants-51jo</guid>
      <description>&lt;p&gt;In this episode, we are going to look at AWS Glue Workflow, mention time-consuming tasks during development and wrap up.&lt;/p&gt;

&lt;h2&gt;
  
  
  Challenge number 7: the Workflow
&lt;/h2&gt;

&lt;p&gt;To define ETL pipelines AWS Glue offers a feature called Workflow, where you can orchestrate your Crawlers and Jobs into a flow using predefined triggers. Workflow provides a visual representation of your ETL pipeline and some level of monitoring.&lt;/p&gt;

&lt;p&gt;This, in theory, is a nice thing to have for your ETL pipeline however I discovered a lot of problems prevented me from using it's promised potential.&lt;/p&gt;

&lt;h3&gt;
  
  
  Definition
&lt;/h3&gt;

&lt;p&gt;Defining the workflow via the AWS Console is quite simple.&lt;/p&gt;

&lt;p&gt;The user interface resembles AWS CloudFormation template Designer however with a very limited set of features.&lt;/p&gt;

&lt;p&gt;Adding your jobs and triggers to the workflow graph feel quite bulky because the graph screen does not scale well in the browser (at least on 13" MacBook).&lt;/p&gt;

&lt;p&gt;Due to the number of required parameters, each click brings up a modal window where you need to fill something or select. The navigation supports mouse only and there aren't any keyboard shortcuts supported which require a lot clicking around, especially if you just want to remove a few nodes. It is not possible to select multiple components or delete the previous chain.&lt;/p&gt;

&lt;p&gt;Delete action on triggers is immediately performed on an actual resource which can take quite some time. Workflow graph is automatically saved after each activity which can be potentially dangerous if one is not careful when editing if the workflow is triggered by the cron.&lt;/p&gt;

&lt;p&gt;Also undo and redo buttons are not present so the only way to recover modified workflow is from the previous run (if there was any).&lt;/p&gt;

&lt;p&gt;Defining the workflow with IaC either Terraform or AWS CloudFormation gets much more difficult. Just due to the fact that your workflow is defined as a set of trigger resources bound to the workflow resource, where each trigger contains a reference to the Job or Crawler resource, so the final flow not really obvious. So the best way to define the workflow is to use the user interface and then export it using &lt;code&gt;aws glue get-workflow --name&lt;/code&gt; or same API function. Then adapt the graph JSON into your IaC code.&lt;/p&gt;

&lt;h3&gt;
  
  
  State management
&lt;/h3&gt;

&lt;p&gt;Like jobs have &lt;code&gt;DefaultArguments&lt;/code&gt;, the workflow has run properties which can be accessed or modified prior to or during the workflow run. This provides a good way of sharing the state or specific config.&lt;/p&gt;

&lt;p&gt;Workflow properties are submitted to your job via system arguments &lt;code&gt;'--WORKFLOW_NAME', 'my-workflow', '--WORKFLOW_RUN_ID', 'wr_1e927676cc826831704712dc067b46480042ca7aead5caa6bea4fc587311e29d'&lt;/code&gt;. Yet again this was inconsistent between PySpark and Python Shell jobs but seems it's not anymore.&lt;/p&gt;

&lt;h3&gt;
  
  
  Execution &amp;amp; Monitoring
&lt;/h3&gt;

&lt;p&gt;There are several ways to start the workflow but the first node has to be the trigger &lt;code&gt;on-demand&lt;/code&gt; or &lt;code&gt;schedule&lt;/code&gt;(cron). In case you select &lt;code&gt;event&lt;/code&gt; and add your job as a first node, workflow won't start.&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%2Fgist.githubusercontent.com%2F1oglop1%2Ff5ee29229f3b9b0a10d354c2f59aa7ad%2Fraw%2Ff7bb5e1ec723c92c7b34928aeae7b5e59fc2f366%2Fworkflow-not-start.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%2Fgist.githubusercontent.com%2F1oglop1%2Ff5ee29229f3b9b0a10d354c2f59aa7ad%2Fraw%2Ff7bb5e1ec723c92c7b34928aeae7b5e59fc2f366%2Fworkflow-not-start.png" alt="AWS Glue Workflow Console view"&gt;&lt;/a&gt;&lt;br&gt;AWS Glue Workflow Console view
  &lt;/p&gt;

&lt;p&gt;When the workflow is started (manually or by the trigger) it still takes some time to kick off the first job (I experienced times between 1 - 4 minutes). There is also a delay in between jobs during the workflow run which is around 1 minute.&lt;/p&gt;

&lt;p&gt;From my observation, it looks like that workflow appears to be a pull-based system instead of the expected event-based system.&lt;/p&gt;

&lt;p&gt;Which also means that any events you'd like to know about eg. workflow started, the job started, job finished, have to be implemented inside the code of the job itself.&lt;/p&gt;

&lt;p&gt;This also brings me to another point that workflow is not able to provide information whether it finished successfully or not. The only states you can get at the moment are &lt;code&gt;'RUNNING'|'COMPLETED'|'STOPPING'|'STOPPED'&lt;/code&gt;. To find out which job or crawler failed via AWS Console you have to navigate to the &lt;code&gt;Workflows&lt;/code&gt;, select workflow and then &lt;code&gt;History&lt;/code&gt; tab, then select &lt;code&gt;workflow run&lt;/code&gt; you want to investigate and click &lt;code&gt;View run details&lt;/code&gt;. After that, you can see which job has failed.&lt;/p&gt;

&lt;p&gt;In case your workflow has been running for some time and accumulated a lot of &lt;code&gt;workflow runs&lt;/code&gt; rendering of the &lt;code&gt;History&lt;/code&gt; tab can take a noticeable amount of time.&lt;/p&gt;

&lt;p&gt;To investigate the workflow via CLI/API with a function &lt;code&gt;get_workflow_run&lt;/code&gt; (include graph), you then have to iterate through two arrays &lt;code&gt;JobDetails.JobRuns&lt;/code&gt; and &lt;code&gt;CrawlerDetails.Crawls&lt;/code&gt; to find out what failed.&lt;/p&gt;

&lt;h2&gt;
  
  
  Challenge number 8: Time
&lt;/h2&gt;

&lt;p&gt;As a developer, your most valuable thing is your time and ability to iterate as fast and as much as possible. With AWS Glue you should prepare yourself for quite long breaks.&lt;/p&gt;

&lt;p&gt;This is a rough summary of how long take certain operations.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Start PySpark Glue 1.0 job - it takes a minimum 10 minutes to start the cluster and run your job. And then a minute or two to run your code. This can be really frustrating if you've made a typo which wasn't caught by the linter.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Start PySpark Glue 2.0 job - Fortunately the startup time has been significantly reduced and it took roughly 4-6 minutes (often much less) to run the code.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Start Python Shell Glue 1.0 job - this takes roughly 2 minutes to start the execution of your code&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Preparing dependencies - If you decide not to use online editor, you will be required to perform a lot of operations to get your code up to the cloud. So this was the first task to automate.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Checking logs in AWS CloudWatch Logs - there is another ~1-2 minutes delay before the logs are populated&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Workflow run takes longer than expected due to polling mechanism.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;This was my first experience with AWS Glue ETL service.&lt;br&gt;
In general, it was a good experience and it is a fully managed service it simplifies a lot, especially when it comes to creating and maintenance of a cluster.&lt;/p&gt;

&lt;p&gt;The service is being actively developed and things may change without you knowing. It is nice in general but has yet a long way to go to become more user and developer-friendly.&lt;/p&gt;

&lt;p&gt;Here is the list of my grumbles:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The inability to update C based libraries (NumPy/Pandas) in PySpark jobs may pose a problem.&lt;/li&gt;
&lt;li&gt;Packaging and defining and supplying dependencies for the jobs is clumsy.&lt;/li&gt;
&lt;li&gt;Startup time of PySpark Glue 1.0 is really long.&lt;/li&gt;
&lt;li&gt;The inconsistency in API and parameters prevents better implementations.&lt;/li&gt;
&lt;li&gt;Unnecessary workaround needed to implement optional arguments.&lt;/li&gt;
&lt;li&gt;Open-source code may not be up to date.&lt;/li&gt;
&lt;li&gt;Workflow interface and definition could have been more intuitive.&lt;/li&gt;
&lt;li&gt;Event-driven or extended functionality of the workflow must be developed outside Glue service eg. using AWS Lambda and step functions.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;From the future versions of AWS Glue, I'd like to see little more focus on development experience.&lt;br&gt;
Especially addressing the inconsistent API between jobs, packaging/deployment and &lt;a href="https://github.com/awslabs/aws-glue-libs" rel="noopener noreferrer"&gt;AWS Glue lib&lt;/a&gt; maintenance.&lt;/p&gt;

&lt;p&gt;The code for the examples in this article can be found in my GitHub repository &lt;a href="https://github.com/1oglop1/aws-glue-monorepo-style" rel="noopener noreferrer"&gt;aws-glue-monorepo-style&lt;/a&gt;&lt;/p&gt;

</description>
      <category>aws</category>
      <category>datascience</category>
      <category>python</category>
      <category>serverless</category>
    </item>
    <item>
      <title>AWS Glue first experience - part 4 - Deployment &amp; packaging</title>
      <dc:creator>Jan Gazda</dc:creator>
      <pubDate>Sat, 29 Aug 2020 18:13:00 +0000</pubDate>
      <link>https://dev.to/1oglop1/aws-glue-first-experience-part-4-deployment-packaging-5708</link>
      <guid>https://dev.to/1oglop1/aws-glue-first-experience-part-4-deployment-packaging-5708</guid>
      <description>&lt;p&gt;In this episode, we are going to explore how can we reuse our code and how to deploy AWS Glue application which consists of more than one file.&lt;br&gt;
I expected the workflow to be very similar to AWS Lambda which is already well known and optimised for python but due to involvement of Spark this is not entirely true for AWS Glue.&lt;/p&gt;
&lt;h2&gt;
  
  
  Challenge number 5: Stay DRY
&lt;/h2&gt;

&lt;p&gt;Because the initialisation process for all data sources and jobs was similar I decided it would be a good idea not having to repeat myself much and create a library with a set of functions which take care of parsing arguments, getting configuration data, simplify the PySpark or Pandas interface.&lt;/p&gt;

&lt;p&gt;Due to the nature of different types of dependencies each job type requires. PySpark - &lt;code&gt;.py&lt;/code&gt;, &lt;code&gt;.zip&lt;/code&gt;, Python Shell - &lt;code&gt;.egg&lt;/code&gt;, &lt;code&gt;.whl&lt;/code&gt;. And the fact that all our code is held in monorepo.&lt;/p&gt;

&lt;p&gt;I decided to create a simple python package with &lt;code&gt;setuptools&lt;/code&gt; and follow the &lt;a href="https://blog.ionelmc.ro/2014/05/25/python-packaging/"&gt;src structure&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This gives me enough flexibility to produce needed formats and also reference to the library from inside &lt;code&gt;requirements.txt&lt;/code&gt;.&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;h2&gt;
  
  
  Challenge number 6: Deployment &amp;amp; packaging
&lt;/h2&gt;

&lt;p&gt;Okay so now that I have all the necessary components covered let's put them together and deploy with &lt;a href="https://www.terraform.io"&gt;Terraform&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;For each data source, we have defined two transitions &lt;code&gt;raw to refined&lt;/code&gt; and &lt;code&gt;refined to curated&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;AWS Glue requires 1 &lt;code&gt;.py&lt;/code&gt; file as an entry point and rest of the files must be plain &lt;code&gt;.py&lt;/code&gt; or contained inside &lt;code&gt;.zip&lt;/code&gt; or &lt;code&gt;.whl&lt;/code&gt; and each job should be able to have a different set of requirements.&lt;/p&gt;

&lt;p&gt;Another requirement from AWS Glue is that entry point script file and dependencies have to be uploaded to S3.&lt;/p&gt;

&lt;p&gt;Anything uploaded to S3 then also has to be listed in &lt;a href="https://www.terraform.io"&gt;Terraform&lt;/a&gt; as a &lt;a href="https://docs.aws.amazon.com/glue/latest/dg/aws-glue-programming-etl-glue-arguments.html"&gt;Special parameter&lt;/a&gt; &lt;code&gt;--extra-py-files&lt;/code&gt; in form of comma separated list of S3 URLs, eg. &lt;code&gt;s3://bucket/dep1.zip, s3://bucket/deb2.zip&lt;/code&gt; or &lt;code&gt;s3://bucket/dep1.whl, s3://bucket/deb2.whl&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Since this list can be very dynamic it's best to keep it as short as possible. As you can see there are a number of operations require from the developer and developing more than one job requires a significant effort. Therefore I decided to use the following structure&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/
├── glue/
│   ├── data_sources/
│   ├── ├── ds1/
│   │   └── ├── raw_to_refined/
│   │       │   ├── Makefile
│   │       │   ├── config.py
│   │       │   ├── raw_to_refined.py
│   │       │   └── requirements.txt
│   │       └── refined_to_curated/
│   │           ├── Makefile
│   │           ├── config.py
│   │           ├── another_dependency.py
│   │           ├── refined_to_curated.py
│   │           └── requirements.txt
│   └── shared/
│       └── glue_shared_lib/
│           ├── Makefile
│           ├── setup.py
│           ├── src
│           │   └── glue_shared/__init__.py
│           └── tests/
└── terraform/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Let's describe the structure above.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;/glue/&lt;/code&gt; holds all the python code&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;/glue/data_sources/&lt;/code&gt; holds the code of jobs for each data source&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;/glue/data_sources/ds1/&lt;/code&gt; - is a directory of 1 particular data source containing transformation&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;/glue/data_sources/ds1/raw_to_refined&lt;/code&gt; and &lt;code&gt;/glue/data_sources/ds1/raw_to_refined&lt;/code&gt;&lt;br&gt;
are the transformations whose content is then deployed as a particular AWS Glue Job&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;/glue/shared/&lt;/code&gt; - contains shared items among the glue (jobs, files, etc...)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;/glue/shared/glue_shared_lib&lt;/code&gt; - is the library used by the jobs, contains configuration interface and other useful functions&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;/terraform/&lt;/code&gt; holds all resources required to be deployed our Glue Jobs, IAM roles, lambda functions, etc...&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now that we understand the structure, we can look closer at a particular job.&lt;/p&gt;
&lt;h3&gt;
  
  
  Glue Job structure
&lt;/h3&gt;

&lt;p&gt;This is a standard blueprint which fit my purpose of developing and deploying several AWS Glue jobs.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ds1/
└── raw_to_refined/
   ├── Makefile
   ├── config.py
   ├── raw_to_refined.py
   └── requirements.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;In this case, we are looking at a transformation job from raw zone to refined zone.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;Makefile&lt;/code&gt; - contains several make targets which names are common across all jobs, &lt;code&gt;clean, package, test, upload-job, upload, deploy&lt;/code&gt; then implementation of each target is job-specific.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;clean&lt;/code&gt; - Cleans up the local temporary files.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;package&lt;/code&gt; - For PySpark job creates a &lt;code&gt;.zip&lt;/code&gt; file with dependencies. For Python shell job it runs &lt;code&gt;pip&lt;/code&gt; and downloads all the wheel files.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;upload-job&lt;/code&gt; - uploads entry point script to S3 - useful for quick updates during the development in case you are not doing any changes inside dependent files.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;upload&lt;/code&gt; - upload all related files &lt;code&gt;.zip&lt;/code&gt;, &lt;code&gt;.whl&lt;/code&gt; and entrypoint &lt;code&gt;.py&lt;/code&gt; file to S3.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;deploy&lt;/code&gt; - performs &lt;code&gt;clean&lt;/code&gt;, &lt;code&gt;package&lt;/code&gt; and &lt;code&gt;upload&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;config.py&lt;/code&gt; - is responsible for creating a configuration object. This is an &lt;code&gt;extra .py&lt;/code&gt; file which is later packaged and used as a dependency. For the sake of saving some time I used python dictionary but with growing complexity of the job I'd recommend spending time on creating a better approach.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;raw_to_refined.py&lt;/code&gt; - this is the main entry point file executed by AWS Glue. U can use this file execute the code in dependencies or directly implement transformation logic. The name of this file is purposely the same as it's parent directory which will be explained later.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;requirements.txt&lt;/code&gt; - Is a standard &lt;a href="https://pip.pypa.io/en/latest/user_guide/#requirements-files"&gt;requirements file&lt;/a&gt; It's a very simple way of managing your dependencies.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This setup gives me enough flexibility as a developer to run and update jobs in the cloud from within my local environment as well as using CI/CD. Another benefit is that if you have PySpark with Glue running locally, you can use that as well!&lt;/p&gt;
&lt;h3&gt;
  
  
  Terraform part
&lt;/h3&gt;

&lt;p&gt;This is an example of deploying PySpark Job via Terraform, Python Shell job follows the same process with a slight difference (mentioned later).&lt;/p&gt;

&lt;p&gt;To create or update the job via Terraform we need to supply several parameters Glue API which Terraform resource requires. Plus the parameters our job expects.&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;



&lt;p&gt;We need to provide:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;Command.ScriptLocation&lt;/code&gt; - represented as &lt;code&gt;ds1_raw_to_refined_job_script_location&lt;/code&gt; - this is our entrypoint script&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;DefaultArguments&lt;/code&gt; - represented as map &lt;code&gt;ds1_raw_to_refined_job_default_arguments&lt;/code&gt; -- this holds the main configuration&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Key &lt;code&gt;--extra-py-files&lt;/code&gt; in the map &lt;code&gt;ds1_raw_to_refined_job_default_arguments&lt;/code&gt; is a comma separated string of S3 urls pointing to our dependencies eg. &lt;code&gt;s3://bucket/dep1.zip,s3://bucket/deb2.zip&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;All extra dependencies fit in 1 &lt;code&gt;.zip&lt;/code&gt; file and once you get the shape of these parameters there is no need to change it.&lt;/p&gt;

&lt;p&gt;This brings a potential problem of human oversight, especially with Python Shell jobs. Where dependencies are wheels and by default, wheel name contains a version number &lt;code&gt;numpy-1.19.0-cp36-cp36m-manylinux2010_x86_64.whl&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Then any change in &lt;code&gt;requirements.txt&lt;/code&gt; or job arguments also requires a change in Terraform resource which is maintained in a different directory.&lt;/p&gt;

&lt;p&gt;I haven't solved this problem during the project but this could be potentially avoided by maintaining a list of dependencies in the S3 bucket in the form of a file which could be generated during the &lt;code&gt;make&lt;/code&gt;. And then Terraform would just download this information. However this theoretical the solution can lead to chicken-egg problems and I wish AWS Glue had a better option to maintain dependencies and the job config. Just allowing to use S3 prefix instead of the full URL would be a good start.&lt;/p&gt;

&lt;p&gt;The code for the examples in this article can be found in my GitHub repository &lt;a href="https://github.com/1oglop1/aws-glue-monorepo-style"&gt;aws-glue-monorepo-style&lt;/a&gt;&lt;/p&gt;

</description>
      <category>aws</category>
      <category>python</category>
      <category>datascience</category>
      <category>serverless</category>
    </item>
    <item>
      <title>AWS Glue first experience - part 3 - Arguments &amp; Logging</title>
      <dc:creator>Jan Gazda</dc:creator>
      <pubDate>Fri, 28 Aug 2020 13:28:13 +0000</pubDate>
      <link>https://dev.to/1oglop1/aws-glue-first-experience-part-3-arguments-logging-120</link>
      <guid>https://dev.to/1oglop1/aws-glue-first-experience-part-3-arguments-logging-120</guid>
      <description>&lt;h2&gt;
  
  
  Challenge number 3: Arguments &amp;amp; Config
&lt;/h2&gt;

&lt;p&gt;Almost every application requires some kind of config or parameters to start with the expected state, AWS Glue applications are no different.&lt;/p&gt;

&lt;p&gt;Our code is supposed to run in 3 different environments (accounts), DEV, TEST, PROD and several configuration values were required eg. log level, SNS topic (for status updates) and few more.&lt;/p&gt;

&lt;p&gt;Documentation mentions &lt;a href="https://docs.aws.amazon.com/glue/latest/dg/aws-glue-programming-etl-glue-arguments.html" rel="noopener noreferrer"&gt;special parameters&lt;/a&gt;  however they are not all arguments you can expect to get. We will explore this later in this section.&lt;/p&gt;

&lt;p&gt;During my work on a project, there was only one set of &lt;code&gt;DefaultArguments&lt;/code&gt; which could have been overwritten prior to job start. By the time of writing this article, there are now two sets of these &lt;code&gt;DefaultArguments&lt;/code&gt; and &lt;code&gt;NonOverridableArguments&lt;/code&gt; where the latter has been added recently.&lt;/p&gt;

&lt;p&gt;Some of these arguments were supplied as SSM Parameters while others were submitted as &lt;code&gt;DefaultArguments&lt;/code&gt;. This could be very useful in case the job fails and we'd like to run the job again with a different log level eg. default &lt;code&gt;WARN&lt;/code&gt; vs non-default &lt;code&gt;DEBUG&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;To add or change an argument of the job prior to it's run you can either use a console&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Security configuration, script libraries, and job parameters -&amp;gt; Job parameters&lt;/code&gt;&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%2Fgist.githubusercontent.com%2F1oglop1%2Ff5ee29229f3b9b0a10d354c2f59aa7ad%2Fraw%2Fc4f3ea6f063db2903d522612f3e053f29dcab4e7%2Fjob-parameters.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%2Fgist.githubusercontent.com%2F1oglop1%2Ff5ee29229f3b9b0a10d354c2f59aa7ad%2Fraw%2Fc4f3ea6f063db2903d522612f3e053f29dcab4e7%2Fjob-parameters.png" alt="AWS Glue Job parameters"&gt;&lt;/a&gt;&lt;br&gt;AWS Glue Job parameters
  &lt;/p&gt;

&lt;p&gt;Or when using CLI/API add your argument into the section of &lt;code&gt;DefaultArguments&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Then inside the code of your job you can use built-in &lt;code&gt;argparse&lt;/code&gt; module or function provided by &lt;a href="https://github.com/awslabs/aws-glue-libs" rel="noopener noreferrer"&gt;aws-glue-lib&lt;/a&gt; &lt;a href="https://docs.aws.amazon.com/glue/latest/dg/aws-glue-api-crawler-pyspark-extensions-get-resolved-options.html" rel="noopener noreferrer"&gt;getResolvedOptions&lt;/a&gt; (&lt;code&gt;awsglue.utils.getResolvedOptions&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;When I started with my journey the function &lt;code&gt;getResolvedOptions&lt;/code&gt; was not available for Python Shell jobs and I also planned to create a config object which holds the necessary configuration for the job. It got implemented later.&lt;/p&gt;

&lt;p&gt;There is a difference between the implementation of &lt;code&gt;getResolvedOptions&lt;/code&gt; between &lt;code&gt;awsglue&lt;/code&gt; present in PySpark jobs and &lt;code&gt;awsglue&lt;/code&gt; present in Python Shell jobs.&lt;/p&gt;

&lt;p&gt;The code of &lt;code&gt;awsglue&lt;/code&gt; used in PySpark jobs can be located at GitHub inside &lt;a href="https://github.com/awslabs/aws-glue-libs" rel="noopener noreferrer"&gt;aws-glue-lib&lt;/a&gt; repository. The main difference is that PySpark job handles some cases of &lt;a href="https://docs.aws.amazon.com/glue/latest/dg/aws-glue-programming-etl-glue-arguments.html" rel="noopener noreferrer"&gt;reserved arguments&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The code used inside Python Shell jobs is this.&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;The main problem of this function is that it makes all &lt;code&gt;DefaultArguments&lt;/code&gt; &lt;strong&gt;required&lt;/strong&gt;. Which is rather clumsy considering the fact that it also requires you to use &lt;code&gt;--&lt;/code&gt; (double dash) in front of your argument which is generally used for optional arguments.&lt;/p&gt;

&lt;p&gt;It is possible to re-implement optional argument this by wrapping this function as suggested in &lt;a href="https://stackoverflow.com/a/58083506" rel="noopener noreferrer"&gt;this StackOverflow answer&lt;/a&gt;. However, this is rather a workaround which may break if the AWS team decides to fix this.&lt;/p&gt;

&lt;p&gt;Also when specifying &lt;code&gt;DefaultArguments&lt;/code&gt; via console it feels more natural not to include &lt;code&gt;--&lt;/code&gt; as the UI is not mentioning this at all.&lt;/p&gt;

&lt;h3&gt;
  
  
  Missing arguments in sys.argv
&lt;/h3&gt;

&lt;p&gt;My first few jobs were only using PySpark and I discovered that there are some additional arguments present in &lt;code&gt;sys.argv&lt;/code&gt; which are used in examples inside developers guide but not described. To get a description of these arguments one should visit &lt;a href="https://docs.aws.amazon.com/glue/latest/webapi/WebAPI_Welcome.html" rel="noopener noreferrer"&gt;AWS Glue API docs&lt;/a&gt; page which is a bit hidden because there is only 1 direct link pointing there from the developers' guide.&lt;/p&gt;

&lt;p&gt;Here are arguments present in &lt;code&gt;sys.argv&lt;/code&gt; for PySpark job (Glue 1.0).&lt;/p&gt;

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

[
  'script_2020-06-24-07-06-36.py',
  '--JOB_NAME', 'my-pyspark-job',
  '--JOB_ID', 'j_dfbe1590b8a1429eb16a4a7883c0a99f1a47470d8d32531619babc5e283dffa7',
  '--JOB_RUN_ID', 'jr_59e400f5f1e77c8d600de86c2c86cefab9e66d8d64d3ae937169d766d3edce52',
  '--job-bookmark-option', 'job-bookmark-disable',
  '--TempDir', 's3://aws-glue-temporary-&amp;lt;accountID&amp;gt;-us-east-1/admin'
]


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

&lt;/div&gt;

&lt;p&gt;Parameters &lt;code&gt;JOB_NAME&lt;/code&gt;, &lt;code&gt;JOB_ID&lt;/code&gt;, &lt;code&gt;JOB_RUN_ID&lt;/code&gt; can be used for self-reference from inside the job without hard coding the &lt;code&gt;JOB_NAME&lt;/code&gt; in your code.&lt;/p&gt;

&lt;p&gt;This could be a very useful feature for self-configuration or some sort of state management. For example, you could use &lt;code&gt;boto3&lt;/code&gt; client to access the job's connections and use it inside your code. Without specifying the connection name in your code directly. Or if your job has been triggered from the workflow it would be possible to refer to the current workflow and its properties.&lt;/p&gt;

&lt;p&gt;Let's explore &lt;code&gt;sys.argv&lt;/code&gt; of Python Shell jobs&lt;/p&gt;

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

[
  '/tmp/glue-python-scripts-7pbpva1h/my_pyshell_job.py',
  '--job-bookmark-option', 'job-bookmark-disable',
  '--scriptLocation', 's3://aws-glue-scripts-133919474178-us-east-1/my_pyshell_job.py',
  '--job-language', 'python'
]


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

&lt;/div&gt;

&lt;p&gt;Above we can see that set arguments available in Python Shell job.&lt;/p&gt;

&lt;p&gt;The arguments are a bit different from what we've got in PySpark job but the major problem is that arguments &lt;code&gt;JOB_NAME&lt;/code&gt;, &lt;code&gt;JOB_ID&lt;/code&gt;, &lt;code&gt;JOB_RUN_ID&lt;/code&gt; are not available.&lt;/p&gt;

&lt;p&gt;This creates a very inconsistent developer experience and &lt;strong&gt;prevents&lt;/strong&gt; the self-reference from inside the job which diminishes the potential of these parameters.&lt;/p&gt;

&lt;h2&gt;
  
  
  Challenge number 4: Logging
&lt;/h2&gt;

&lt;p&gt;Like I already mentioned AWS Glue Job logs are sent to AWS CloudWatch logs.&lt;/p&gt;

&lt;p&gt;There are two log groups for each job. &lt;code&gt;/aws-glue/python-jobs/output&lt;/code&gt; which contains the &lt;code&gt;stdout&lt;/code&gt; and &lt;code&gt;/aws-glue/python-jobs/error&lt;/code&gt; for &lt;code&gt;stderr&lt;/code&gt;. Inside log groups you can find the log stream of your job named with &lt;code&gt;JOB_RUN_ID&lt;/code&gt; eg. &lt;code&gt;/aws-glue/python-jobs/output/jr_3c9c24f19d1d2d5f9114061b13d4e5c97881577c26bfc45b99089f2e1abe13cc&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;When the job is started there are already 2 links helping you to navigate to the particular log.&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%2Fgist.githubusercontent.com%2F1oglop1%2Ff5ee29229f3b9b0a10d354c2f59aa7ad%2Fraw%2Fc4f3ea6f063db2903d522612f3e053f29dcab4e7%2Flog-links.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%2Fgist.githubusercontent.com%2F1oglop1%2Ff5ee29229f3b9b0a10d354c2f59aa7ad%2Fraw%2Fc4f3ea6f063db2903d522612f3e053f29dcab4e7%2Flog-links.png" alt="the picture of job with links to logs"&gt;&lt;/a&gt;&lt;br&gt;Showcase of aws console.
  &lt;/p&gt;

&lt;p&gt;Even though the links are present, the log streams are not created until the job starts.&lt;/p&gt;

&lt;p&gt;When using logging in your jobs, you may want to avoid logging to &lt;code&gt;stderr&lt;/code&gt; or redirect it to &lt;code&gt;stdout&lt;/code&gt; because &lt;code&gt;error&lt;/code&gt; log stream is &lt;strong&gt;only&lt;/strong&gt; created when the job finishes with failure.&lt;/p&gt;

&lt;p&gt;Glue 1.0  PySpark job logs are very verbose and contain a lot of "clutter", unrelated to your code. This clutter comes from Spark underlying services. This issue has been addressed in Glue 2.0 where the exposure to the logs of unrelated services is minimal and you can comfortably focus on your own logs. Good job AWS Team!&lt;/p&gt;

&lt;p&gt;Python Shell jobs do not suffer from this condition and you can expect to get exactly what you log.&lt;/p&gt;

&lt;p&gt;And that's it about the config and logging. In the next episode, we are going to look into packaging and deployment.&lt;/p&gt;

&lt;p&gt;The code for the examples in this article can be found in my GitHub repository &lt;a href="https://github.com/1oglop1/aws-glue-monorepo-style" rel="noopener noreferrer"&gt;aws-glue-monorepo-style&lt;/a&gt;&lt;/p&gt;

</description>
      <category>aws</category>
      <category>python</category>
      <category>datascience</category>
      <category>serverless</category>
    </item>
    <item>
      <title>AWS Glue first experience - part 2 - Dependencies and guts</title>
      <dc:creator>Jan Gazda</dc:creator>
      <pubDate>Tue, 25 Aug 2020 21:25:02 +0000</pubDate>
      <link>https://dev.to/1oglop1/aws-glue-first-experience-part-2-dependencies-and-guts-29l</link>
      <guid>https://dev.to/1oglop1/aws-glue-first-experience-part-2-dependencies-and-guts-29l</guid>
      <description>&lt;h2&gt;
  
  
  Challenge number 2: Dependencies
&lt;/h2&gt;

&lt;p&gt;In the previous episode, we have learned that it's rather simple to put the code in one python file and run it. However, this is often not the best solution and it makes more sense to split the code into separate modules or use the code from the existing libraries. I was about to call this part external dependencies but you will soon find out why I left the &lt;code&gt;external&lt;/code&gt; out.&lt;/p&gt;

&lt;p&gt;There are multiple documentation pages mentioning how to work with dependencies and differences between &lt;code&gt;PySpark&lt;/code&gt; and &lt;code&gt;Python Shell&lt;/code&gt; jobs. This may not be really obvious when you browse the docs for the first time.&lt;/p&gt;

&lt;p&gt;For PySpark jobs, there is this a page: &lt;a href="https://docs.aws.amazon.com/glue/latest/dg/aws-glue-programming-python-libraries.html" rel="noopener noreferrer"&gt;https://docs.aws.amazon.com/glue/latest/dg/aws-glue-programming-python-libraries.html&lt;/a&gt;&lt;br&gt;
For Python Shell jobs there is another page: &lt;a href="https://docs.aws.amazon.com/glue/latest/dg/add-job-python.html#python-shell-supported-library" rel="noopener noreferrer"&gt;https://docs.aws.amazon.com/glue/latest/dg/add-job-python.html#python-shell-supported-library&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Inconsistencies&lt;/strong&gt;: Each of the pages can be found in completely different sections and therefore easily missed.&lt;/p&gt;
&lt;h3&gt;
  
  
  Providing dependencies for PySpark Jobs
&lt;/h3&gt;

&lt;p&gt;English isn't my native language and documentation has confused me here with an explanation instead of giving a simple example.&lt;/p&gt;

&lt;p&gt;At the moment Glue supports only pure python libraries which means we are not able to use C based libraries (&lt;code&gt;pandas&lt;/code&gt;, &lt;code&gt;numpy&lt;/code&gt;) or extensions from other languages.&lt;/p&gt;

&lt;p&gt;The dependencies can be supplied in two forms.&lt;/p&gt;

&lt;p&gt;a. Single &lt;code&gt;.py&lt;/code&gt; file&lt;br&gt;
b. &lt;code&gt;.zip&lt;/code&gt; an archive containing python packages.&lt;/p&gt;

&lt;p&gt;Packages inside the &lt;code&gt;.zip&lt;/code&gt; archive needs to be in the root of the archive.&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

$ zipinfo -1 dependencies.zip
pkg2/
pkg2/__init__.py
pkg1/
pkg1/__init__.py


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

&lt;/div&gt;
&lt;p&gt;Each dependency has to be uploaded to S3 Bucket and then supplied as an "&lt;code&gt;Special parameter&lt;/code&gt;_" &lt;code&gt;--extra-py-files&lt;/code&gt; to the particular job in a format of comma-separated S3 URLs.&lt;/p&gt;

&lt;p&gt;You have to specify each file separately&lt;br&gt;
&lt;code&gt;s3://bucket/prefix/lib_A.zip,s3://bucket_B/prefix/lib_X.zip&lt;/code&gt; which becomes annoyingly clumsy if your job has more than one dependency.&lt;/p&gt;
&lt;h4&gt;
  
  
  Behind the scenes
&lt;/h4&gt;

&lt;p&gt;Your &lt;code&gt;.py&lt;/code&gt; or &lt;code&gt;.zip&lt;/code&gt; file is copied into a &lt;code&gt;/tmp&lt;/code&gt; directory accessible during the job runtime. Injected into a &lt;code&gt;PYTHONPATH&lt;/code&gt; and also added as an argument &lt;code&gt;--py-files&lt;/code&gt; to &lt;code&gt;spark-submit&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Note:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;We have been given access to the Glue 2.0 preview which has broken the &lt;code&gt;spark-submit&lt;/code&gt; argument and Spark did could not see the code provided inside the zip file. Fortunately, it is possible to supply the &lt;code&gt;py-files&lt;/code&gt; during the job runtime to the &lt;code&gt;SparkSession&lt;/code&gt;. This has been later fixed by AWS team and we were notified about the fix when our jobs started to fail due to applied workaround above. It's worth mentioning that Glue 2.0 is running Python 3.7.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h4&gt;
  
  
  Providing dependencies for Python Shell Jobs
&lt;/h4&gt;

&lt;p&gt;Beware:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The documentation of Python Shell jobs is really tricky and sometimes confusing mainly because the examples are provided without enough context and some code examples are written in legacy python while others in Python 3.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;To provide external dependencies documentation advise using &lt;code&gt;.egg&lt;/code&gt; or &lt;code&gt;.whl&lt;/code&gt; files. Upload them to S3 and list as &lt;code&gt;—extra-py-files&lt;/code&gt; argument.&lt;/p&gt;

&lt;p&gt;The runtime environment includes several pre-installed packages.&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

Boto3
collections
CSV
gzip
multiprocessing
NumPy
pandas (required to be installed via the python setuptools configuration, setup.py)
pickle
PyGreSQL
re
SciPy
sklearn
sklearn.feature_extraction
sklearn.preprocessing
xml.etree.ElementTree
zipfile


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

&lt;/div&gt;
&lt;p&gt;Due to my past experience with AWS Lambda, I suspected that versions of pre-installed packages may be outdated.&lt;/p&gt;

&lt;p&gt;So I decided to create a simple job &lt;code&gt;my-pyshell-job&lt;/code&gt; and edit the code using the online editor, just to get versions of specific libraries. The code is very simple:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;The result of the code above&lt;/p&gt;

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

boto3 1.9.203
numpy 1.16.2
pandas 0.24.2


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

&lt;/div&gt;

&lt;p&gt;confirmed that my assumption was correct and the versions are a bit dated so you won't get the latest stable versions.&lt;/p&gt;

&lt;p&gt;Which left me no other choice than installing it.&lt;/p&gt;

&lt;p&gt;This is the point where the documentation is lacking an explanation and I was left with trial and error approach.&lt;/p&gt;

&lt;h4&gt;
  
  
  The installation
&lt;/h4&gt;

&lt;p&gt;Knowing that I need to provide &lt;code&gt;.whl&lt;/code&gt; I ran the command &lt;code&gt;pip wheel pandas&lt;/code&gt; on my macOS. And started to experiments.&lt;/p&gt;

&lt;p&gt;This has downloaded multiple wheel files:&lt;/p&gt;

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

numpy-1.19.0-cp36-cp36m-macosx_10_9_x86_64.whl
python_dateutil-2.8.1-py2.py3-none-any.whl
six-1.15.0-py2.py3-none-any.whl
pandas-1.0.5-cp36-cp36m-macosx_10_9_x86_64.whl
pytz-2020.1-py2.py3-none-any.whl


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

&lt;/div&gt;

&lt;p&gt;I took my sample code from above and called it &lt;code&gt;my_pyshell_job.py&lt;/code&gt; and together with&lt;br&gt;
the &lt;code&gt;.whl&lt;/code&gt; files above uploaded to S3 bucket.&lt;/p&gt;

&lt;p&gt;Updated a job with AWS CLI command.&lt;/p&gt;

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

  aws glue update-job --job-name my-pyshell-job --job-update '{
    "Role": "MyGlueRole",
    "Command": {"Name": "my-pyshell-job", "ScriptLocation": "s3://bucket/my_pyshell_job.py"},
    "DefaultArguments": {
      "--extra-py-files": "s3://bucket/dependencies/library/numpy-1.19.0-cp36-cp36m-macosx_10_9_x86_64.whl,s3://bucket/dependencies/library/python_dateutil-2.8.1-py2.py3-none-any.whl,s3://bucket/dependencies/library/six-1.15.0-py2.py3-none-any.whl,s3://bucket/dependencies/library/pandas-1.0.5-cp36-cp36m-macosx_10_9_x86_64.whl,s3://bucket/dependencies/library/pytz-2020.1-py2.py3-none-any.whl"
    }
  }'


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

&lt;/div&gt;

&lt;p&gt;And started the job via console.&lt;/p&gt;

&lt;p&gt;The job finished with error &lt;code&gt;ImportError:&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;AWS Glue uses AWS Cloudwatch logs, there are two log groups for each job. &lt;code&gt;/aws-glue/python-jobs/output&lt;/code&gt; which contains the &lt;code&gt;stdout&lt;/code&gt; and &lt;code&gt;/aws-glue/python-jobs/error&lt;/code&gt; for &lt;code&gt;stderr&lt;/code&gt;.&lt;br&gt;
Inside log groups you can find the log stream of your job named with &lt;code&gt;JOB_RUN_ID&lt;/code&gt; eg. &lt;code&gt;/aws-glue/python-jobs/output/jr_3c9c24f19d1d2d5f9114061b13d4e5c97881577c26bfc45b99089f2e1abe13cc&lt;/code&gt;. I also found out that &lt;code&gt;error&lt;/code&gt; log is only created if the job finishes with error so if you are planning to log errors which won't fail the job log it to &lt;code&gt;stdout&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The content of the &lt;code&gt;output&lt;/code&gt; log contains &lt;code&gt;pip&lt;/code&gt; logs. And reported that all libraries were successfully installed. However, the &lt;code&gt;error&lt;/code&gt; log contains the whole stack trace of this error.&lt;/p&gt;

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

  ModuleNotFoundError: No module named 'numpy.core._multiarray_umath'{% raw %}`.

  ImportError:

  IMPORTANT: PLEASE READ THIS FOR ADVICE ON HOW TO SOLVE THIS ISSUE!

  Importing the numpy C-extensions failed. This error can happen for
  many reasons, often due to issues with your setup or how NumPy was
  installed.
```

The error message tells us that this is most likely caused by OS mismatch.

So I ran another simple job to give me more details about the platform.

{% gist https://gist.github.com/1oglop1/f5ee29229f3b9b0a10d354c2f59aa7ad file=print_platform.py %}

I tried this with both PySpark and Python Shell jobs and the results were a bit surprising.

Python Shell jobs run on `debian`: `Linux-4.14.123-86.109.amzn1.x86_64-x86_64-with-debian-10.2`
while PySpark jobs run on Amazon Linux `Linux-4.14.133-88.112.amzn1.x86_64-x86_64-with-glibc2.3.4` likely to be a `amazoncorretto`.

The next step was clear, I needed a `wheel` with `numpy` built on `Debian` Linux. Since the plan was to deploy using CI/CD I decided to use [official python docker image][docker_python] `python:3.6-slim-buster`.

Run `pip wheel pandas` again to get the correct packages and remove those already installed.

```
numpy-1.19.0-cp36-cp36m-manylinux2010_x86_64.whl
pandas-1.0.5-cp36-cp36m-manylinux1_x86_64.whl
```

Upload `wheels` to S3.
Update and run the job.

The job has PASSED!
And this is how the result looks:

```
boto3 1.9.203
numpy 1.19.0
pandas 1.0.5
```

But wait there is more in the log.

```
Processing ./glue-python-libs-psoetpzo/numpy-1.19.0-cp36-cp36m-manylinux2010_x86_64.whl
Installing collected packages: numpy
Successfully installed numpy-1.19.0
Processing ./glue-python-libs-psoetpzo/pandas-1.0.5-cp36-cp36m-manylinux1_x86_64.whl
Collecting pytz&amp;gt;=2017.2
Downloading pytz-2020.1-py2.py3-none-any.whl (510 kB)
Collecting numpy&amp;gt;=1.13.3
Downloading numpy-1.19.0-cp36-cp36m-manylinux2010_x86_64.whl (14.6 MB)
Collecting python-dateutil&amp;gt;=2.6.1
Downloading python_dateutil-2.8.1-py2.py3-none-any.whl (227 kB)
Collecting six&amp;gt;=1.5
Downloading six-1.15.0-py2.py3-none-any.whl (10 kB)
Installing collected packages: pytz, numpy, six, python-dateutil, pandas
Successfully installed numpy-1.19.0 pandas-1.0.5 python-dateutil-2.8.1 pytz-2020.1 six-1.15.0
```

If you look closely, you can notice that `numpy` has been installed twice, the question is why?

The answer lies inside another file called `/tmp/runscript.py`. This file is responsible for orchestrating the installation and running your code as well as providing the stack trace in case the exception.

##### /tmp/runscript.py

Out of pure curiosity, I decided to print out the content of this file to find out why `numpy` has been installed twice.

The content of this file was enlightening but also shocking.

After the first 12 lines of imports, there was a comment on line 14

`##TODO: add basic unittest`

Okay, this is a bit scary because a comment like this either suggests that developer did not have enough time to write the tests when rushing into production and the reviewer left this to slip.

If your code does not contain enough tests you should keep it internally or loudly warn your customers about potential consequences.

By further examination, I noticed that this script is also used for PySpark jobs.

Little further into the file, there is a function `download_and_install`. The comment above function describes it's purpose `# Download extra py files and add them to the python path` (might as well have used the docstring right?).

The function takes each file supplied in `--extra-py-files` argument and installs it separately with `pip`.

This tells me a couple of things:

1. Dependencies mentioned in `setup.py` are automatically downloaded from PyPI, so since `pandas` depend on `numpy` it will download it as well hence the second installation.

2. Using a private package repository where dependencies are located there as well won't work flawlessly.

3. The order of dependencies supplied matters


### AWS Glue examples and libs

Speaking of dependencies AWS Glue provides its core functionality via a library called [awsglue][aws-glue-lib]. The code is located on GitHub. Even though the code is public, the repository maintainers do not seem to be interested in community ideas and pull requests because there are many pull requests without any kind of response from AWS Glue team. Judging from the reactions on the [unmaintained issue][unmaintained] I am not the only one who feels this way.

**Friendly warning**, if you are going attempt anything using this repository on your local machine I strongly suggest doing it in an isolated environment either using `docker` container or VM. The code modifies some local directories and `PYTHONPATH`.

During my work on this project, the repository contained 2 versions one in each branch `glue-0.9` and `glue-1.0` + readme file which was a bit confusing because it described the process for both versions.

The "installation" process is also somewhat painful if the only thing you want is code completion in your IDE because the library does not provide any mechanism for a simple installation. And the only way how to run this code is to zip `awsglue` directory `zip -r PyGlue.zip awsglue`. And add it to your `PYTHONPATH` (see the `bin/setup.sh` for example). As you may have noticed the repository contains `bash` commands and Windows is not currently supported (there is [an open pull request][pr_open]).

In case you are visiting the repository to look for the examples of tests you will be very disappointed because there aren't any tests at all!

#### Coding practices

Since the repository does not contain any kind of automation for linting and testing there are a lot of lint errors and potentially dangerous lines.

One example is using immutable as a default argument:

{% gist https://gist.github.com/1oglop1/f5ee29229f3b9b0a10d354c2f59aa7ad file=glue_immutable.py %}

This is the score of default pylint configuration `Your code has been rated at 2.94/10` Using a plain text and pylint score is not always ideal when presenting the errors to a wider audience. With [codeac.io][codeac] we can see the report in numbers but also track issue counts over time.

I forked the repository and run the report for you.
Please keep in mind that I left all configuration on default and therefore the results might not be 100% accurate but should give the idea about the current state.

&amp;lt;figure&amp;gt;
  &amp;lt;img src="https://gist.githubusercontent.com/1oglop1/f5ee29229f3b9b0a10d354c2f59aa7ad/raw/0653a962d1912d28e4fda6eb2a3926c0f9774e9a/codeac.png" 
  alt="codeac io for aws-glue-libs" style="width:50%"&amp;gt;
  &amp;lt;figcaption&amp;gt;Results of codeac.io for &amp;lt;a href="https://github.com/awslabs/aws-glue-libs"&amp;gt;aws-glue-libs&amp;lt;/a&amp;gt;&amp;lt;/figcaption&amp;gt;
&amp;lt;/figure&amp;gt;


Apart from dangerous default values, you can find missing docstrings, `#TODO` comments, too long lines, unused imports and many others.

Before the latest update on May 5, 2020, there were several import errors preventing the code from even running or testing it. This update has patched a few holes however there is still a mix of imports present waiting to cause more problems. If you are curious about how python imports work I suggest reading [this article from realpython][article_rp]

AWS maintains another repository called [aws-glue-samples][aws-glue-samples]. The story of this repository is slightly better, it appears to be actively maintained [I reported and issue][my_issue] about bad coding practices of using `import *` and received a response 2 days later what I consider a rather quick action in an open-source world.

Although I did not find examples particularly useful for myself and the code quality is a bit better than mentioned `aws-glue-libs`. The examples usually contain a lot of comments which could be really useful if you are
new to AWS Glue and data science in particular.

As you can see trivial example of executing one script file without dependencies can quickly escalate and catch you off guard. In the next episode, we are going to explore how to parametrise and configure the glue application.

The code for the examples in this article can be found in my GitHub repository [aws-glue-monorepo-style]


[aws-glue-monorepo-style]: https://github.com/1oglop1/aws-glue-monorepo-style

[docker_python]: https://hub.docker.com/_/python
[aws-glue-lib]: https://github.com/awslabs/aws-glue-libs
[unmaintained]: https://github.com/awslabs/aws-glue-libs/issues/51
[pr_open]: https://github.com/awslabs/aws-glue-libs/pull/54
[codeac]: https://codeac.io
[article_rp]: https://realpython.com/absolute-vs-relative-python-imports/
[aws-glue-samples]: https://github.com/aws-samples/aws-glue-samples/
[my_issue]: https://github.com/aws-samples/aws-glue-samples/issues/62
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

</description>
      <category>aws</category>
      <category>datascience</category>
      <category>python</category>
      <category>serverless</category>
    </item>
    <item>
      <title>AWS Glue first experience - part 1 - How to run your code?</title>
      <dc:creator>Jan Gazda</dc:creator>
      <pubDate>Tue, 25 Aug 2020 21:24:45 +0000</pubDate>
      <link>https://dev.to/1oglop1/aws-glue-first-experience-part-1-how-to-run-your-code-3pe3</link>
      <guid>https://dev.to/1oglop1/aws-glue-first-experience-part-1-how-to-run-your-code-3pe3</guid>
      <description>&lt;h1&gt;
  
  
  My first project with AWS Glue
&lt;/h1&gt;

&lt;p&gt;I have received an assignment to help build a data lake with a data pipeline using AWS Glue.&lt;br&gt;
It was my first exposure to this service, and there were many challenges along the way worth sharing.&lt;/p&gt;

&lt;p&gt;AWS Glue is a serverless, fully managed extract, transform,&lt;br&gt;
and load (ETL) service to prepare and load data for analytics. Supports data stored in Amazon Aurora and all other Amazon RDS engines, Amazon Redshift, and Amazon S3, as well as common database engines and databases running on Amazon EC2. Helps you to construct a Data Catalog using Crawlers and pre-built Classifiers then suggests schemas and transformations and generates the code.&lt;/p&gt;

&lt;h2&gt;
  
  
  The goal of the project
&lt;/h2&gt;

&lt;p&gt;The data lake followed typical 3 zone architecture: Raw, Refined and Curated.&lt;/p&gt;

&lt;p&gt;Data for raw and refined zones are stored in S3 bucket while curated data is written to PostgreSQL database running on AWS Aurora.&lt;/p&gt;

&lt;p&gt;The idea was to process and transform data incoming from 4 different data sources. All data sources dropped data into a raw zone S3 bucket. Where they have been picked up by individual Glue jobs.&lt;/p&gt;

&lt;p&gt;The glue jobs itself have been orchestrated using Glue Workflow feature.&lt;/p&gt;

&lt;h2&gt;
  
  
  First steps
&lt;/h2&gt;

&lt;p&gt;Since we have identified our data sources and ETL zones it was time to write 8 Glue jobs. Understand four for jobs per transition: 4x &lt;code&gt;raw to refined&lt;/code&gt;, 4x &lt;code&gt;refined to curated&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Glue supports multiple run times: Apache Spark with Scala, Apache spark with Python - PySpark, Python shell - pure python (3.6 by the time of writing) interpreter.&lt;/p&gt;

&lt;p&gt;We will stick with Python and use PySpark and python shell.&lt;/p&gt;

&lt;p&gt;AWS Glue provides an extension (soft wrapper) around &lt;code&gt;pyspark.sql.context&lt;/code&gt; and adds Glue specific features such as &lt;code&gt;DynamicFrame&lt;/code&gt;, etc. To provide maximum portability I'm going to avoid using AWS Glue specific features.&lt;/p&gt;

&lt;h2&gt;
  
  
  Challenge number 1: How to run your code?
&lt;/h2&gt;

&lt;p&gt;As a python developer, I'm used to splitting the code into modules and packages to prevent large files.&lt;/p&gt;

&lt;p&gt;The Glue documentation is pretty straight forward in pointing you to use the&lt;br&gt;
pre-generated code or write it using an online editor built-in AWS Glue Console.&lt;/p&gt;

&lt;p&gt;Apart from that, there is a short page about &lt;a href="https://docs.aws.amazon.com/glue/latest/dg/console-custom-created.html"&gt;providing your scripts&lt;/a&gt;.&lt;br&gt;
Most of the documentation shows the examples within the console and our code was expected to be deployed via&lt;br&gt;
&lt;a href="https://www.terraform.io"&gt;Terraform&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The Glue Job code requires a script file to be stored in an S3 bucket.&lt;/p&gt;

&lt;p&gt;Then you have to point your Terraform resource: &lt;code&gt;aws_glue_job&lt;/code&gt; to the &lt;code&gt;script_location&lt;/code&gt;&lt;br&gt;
which contains an S3 URL to your file eg. &lt;code&gt;s3://code-bucket/glue_job.py&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;We can now write the code in 1 file, which is enough for small ETL scripts based purely on Spark. In the next episode, we are going to explore how to deal with dependencies.&lt;/p&gt;

&lt;p&gt;The code for the examples in this article can be found in my GitHub repository &lt;a href="https://github.com/1oglop1/aws-glue-monorepo-style"&gt;aws-glue-monorepo-style&lt;/a&gt;&lt;/p&gt;

</description>
      <category>aws</category>
      <category>datascience</category>
      <category>python</category>
      <category>serverless</category>
    </item>
  </channel>
</rss>
