<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Cloud Archives - Albert Nogués</title>
	<atom:link href="https://www.albertnogues.com/category/cloud/feed/" rel="self" type="application/rss+xml" />
	<link>https://www.albertnogues.com/category/cloud/</link>
	<description>Data and Cloud Freelancer</description>
	<lastBuildDate>Fri, 31 May 2024 11:17:08 +0000</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	

<image>
	<url>https://www.albertnogues.com/wp-content/uploads/2020/12/cropped-cropped-AlbertLogo2-32x32.png</url>
	<title>Cloud Archives - Albert Nogués</title>
	<link>https://www.albertnogues.com/category/cloud/</link>
	<width>32</width>
	<height>32</height>
</image> 
	<item>
		<title>Data Quality Checks with Soda-Core in Databricks</title>
		<link>https://www.albertnogues.com/data-quality-checks-with-soda-core-in-databricks/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=data-quality-checks-with-soda-core-in-databricks</link>
		
		<dc:creator><![CDATA[Albert]]></dc:creator>
		<pubDate>Fri, 31 May 2024 11:17:06 +0000</pubDate>
				<category><![CDATA[Azure]]></category>
		<category><![CDATA[Databricks]]></category>
		<category><![CDATA[Python]]></category>
		<category><![CDATA[data]]></category>
		<category><![CDATA[data quality]]></category>
		<category><![CDATA[python]]></category>
		<category><![CDATA[soda]]></category>
		<category><![CDATA[spark]]></category>
		<category><![CDATA[sql]]></category>
		<guid isPermaLink="false">https://www.albertnogues.com/?p=3439</guid>

					<description><![CDATA[<p>It&#8217;s easy to do data quality checks when working with spark with the soda-core library. The library has support for spark dataframes. I&#8217;ve tested it within a databricks environment and it worked quite easily for me. For the examples of this article i am loading the customers table from the tpch delta tables in the &#8230; </p>
<p>The post <a href="https://www.albertnogues.com/data-quality-checks-with-soda-core-in-databricks/">Data Quality Checks with Soda-Core in Databricks</a> appeared first on <a href="https://www.albertnogues.com">Albert Nogués</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p>It&#8217;s easy to do data quality checks when working with spark with the soda-core library. The library has support for spark dataframes. I&#8217;ve tested it within a databricks environment and it worked quite easily for me.</p>



<p>For the examples of this article i am loading the customers table from the tpch delta tables in the databricks-datasets folder.</p>



<p>First of all we need to install the library either scoped to our Databricks notebook or on our cluster. In my case i will install it notebook scoped:</p>



<pre class="wp-block-code"><code>%pip install soda-core-spark-df</code></pre>



<p>Then we create a dataframe from the tpch customers table:</p>



<pre class="wp-block-code"><code>#We create a table and read it into a dataframe
customer_df = spark.read.table("delta.`/databricks-datasets/tpch/delta-001/customer/`")</code></pre>



<p>We create a temporary view for our dataframe so soda can query the data and run the checks:</p>



<pre class="wp-block-code"><code>#We create a TempView
customer_df.createOrReplaceTempView("customer")</code></pre>



<p>And here it comes the whole soda core. We will define the checks using yaml syntax:</p>



<pre class="wp-block-code"><code>from soda.scan import Scan
scan = Scan()
scan.set_scan_definition_name("Databricks Test Notebook")
scan.set_data_source_name("customer")
scan.add_spark_session(spark, data_source_name="customer")
#YAML Format
checks = '''
checks for customer:
  - row_count > 0
  - invalid_percent(c_phone) = 0:
      valid regex: ^&#91;0-9]{2}&#91;-]&#91;0-9]{3}&#91;-]&#91;0-9]{3}&#91;-]&#91;0-9]{4}$
  - duplicate_count(c_phone) = 0:
      name: No duplicate phone numbers
  - invalid_count(c_mktsegment) = 0:
      invalid values: &#91;HOUSEHOLD]
      name: HOUSEHOLD is not allowed as a Market Segment
'''
# you can use add_sodacl_yaml_file(s). Useful if the tests are in a github repo or FS
scan.add_sodacl_yaml_str(checks)
scan.execute()
print(scan.get_logs_text())</code></pre>


<div class="wp-block-image">
<figure class="aligncenter size-full"><img fetchpriority="high" decoding="async" width="570" height="322" src="https://www.albertnogues.com/wp-content/uploads/2024/05/Output1.png" alt="" class="wp-image-3440" srcset="https://www.albertnogues.com/wp-content/uploads/2024/05/Output1.png 570w, https://www.albertnogues.com/wp-content/uploads/2024/05/Output1-300x169.png 300w, https://www.albertnogues.com/wp-content/uploads/2024/05/Output1-106x60.png 106w" sizes="(max-width: 570px) 100vw, 570px" /></figure>
</div>


<p>More info: <a href="https://docs.soda.io/soda/quick-start-databricks.html">Add Soda to a Databricks notebook | Soda Documentation</a></p>



<p>List of validations: <a href="https://docs.soda.io/soda-cl/validity-metrics.html">Validity metrics | Soda Documentation</a> and <a href="https://docs.soda.io/soda-cl/metrics-and-checks.html">SodaCL metrics and checks | Soda Documentation</a></p>



<p>We can somewhat enhance it and generate a Spark Dataframe all out of the list of our warnings or error validation checks:</p>



<pre class="wp-block-code"><code>from datetime import datetime
schema_checks = 'datasource STRING, table STRING, rule_name STRING, rule STRING, column STRING, check_status STRING, number_of_errors_in_sample INT, check_time TIMESTAMP'
list_of_checks = &#91;]
for c in scan.get_scan_results()&#91;'checks']:
    list_of_checks = list_of_checks + &#91;&#91;scan.get_scan_results()&#91;'defaultDataSource'], c&#91;'table'], c&#91;'name'], c&#91;'definition'], c&#91;'column'], c&#91;'outcome'], 0 if 'pass'in c&#91;'outcome'] else int(c&#91;'diagnostics']&#91;'blocks']&#91;0]&#91;'totalFailingRows']), datetime.strptime(scan.get_scan_results()&#91;'dataTimestamp'], '%Y-%m-%dT%H:%M:%S%z')]]
list_of_checks_df = spark.createDataFrame(list_of_checks,schema_checks)
display(list_of_checks_df)</code></pre>



<figure class="wp-block-image size-large is-resized"><img decoding="async" width="1024" height="403" src="https://www.albertnogues.com/wp-content/uploads/2024/05/DataFrameOutput-1024x403.png" alt="" class="wp-image-3441" style="width:840px;height:auto" srcset="https://www.albertnogues.com/wp-content/uploads/2024/05/DataFrameOutput-1024x403.png 1024w, https://www.albertnogues.com/wp-content/uploads/2024/05/DataFrameOutput-300x118.png 300w, https://www.albertnogues.com/wp-content/uploads/2024/05/DataFrameOutput-768x302.png 768w, https://www.albertnogues.com/wp-content/uploads/2024/05/DataFrameOutput-152x60.png 152w, https://www.albertnogues.com/wp-content/uploads/2024/05/DataFrameOutput.png 1328w" sizes="(max-width: 1024px) 100vw, 1024px" /></figure>



<p>In the case we have the yaml file in our github repo, we can read it and pass it. Or If we are working with Databricks repos and the file is part of out repo we can load it locally</p>



<p>Accessing a remote file and reading it with requests:</p>



<pre class="wp-block-code"><code>#Trying to use a remote yaml file to enforce rules. We can upload it to a github of our own and use it in opur notebook.
#I've created a public repo so i dont need to authenticate to github, but in a real world scenario we should use private repo + secret scopes
customer_quality_rules = 'https://raw.githubusercontent.com/anogues/soda-core-quality-rules/main/soda-core-quality-rules-customer.yaml'
import requests
scan.add_sodacl_yaml_str(requests.get(customer_quality_rules).text)</code></pre>



<p>Or we can load it locally if we are using databricks repos:</p>



<pre class="wp-block-code"><code>scan.add_sodacl_yaml_file("your_file.yaml")</code></pre>
<p>The post <a href="https://www.albertnogues.com/data-quality-checks-with-soda-core-in-databricks/">Data Quality Checks with Soda-Core in Databricks</a> appeared first on <a href="https://www.albertnogues.com">Albert Nogués</a>.</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Query Delta Tables in the DataLake from PowerBi with Databricks</title>
		<link>https://www.albertnogues.com/query-delta-tables-in-the-datalake-from-powerbi-with-databricks/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=query-delta-tables-in-the-datalake-from-powerbi-with-databricks</link>
		
		<dc:creator><![CDATA[Albert]]></dc:creator>
		<pubDate>Wed, 15 Nov 2023 18:47:00 +0000</pubDate>
				<category><![CDATA[Azure]]></category>
		<category><![CDATA[Databricks]]></category>
		<category><![CDATA[SQL]]></category>
		<category><![CDATA[databricks]]></category>
		<category><![CDATA[powerbi]]></category>
		<category><![CDATA[spark]]></category>
		<category><![CDATA[sql]]></category>
		<guid isPermaLink="false">https://www.albertnogues.com/?p=2195</guid>

					<description><![CDATA[<p>There are several ways to query delta tables from PowerBi. We are going to cover the 4th method here. To do it first we need a service princpal, a secret scope pointing to a databricks keyvault and the password of the SPN stored in this keyvault. Once we have this, the first step is to &#8230; </p>
<p>The post <a href="https://www.albertnogues.com/query-delta-tables-in-the-datalake-from-powerbi-with-databricks/">Query Delta Tables in the DataLake from PowerBi with Databricks</a> appeared first on <a href="https://www.albertnogues.com">Albert Nogués</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p>There are several ways to query delta tables from PowerBi.</p>



<ul class="wp-block-list">
<li>You can use snowflake with external stages reading delta data from the DL,</li>



<li>The parquet connector and directly query the data from the datalake (caution as this method does not support SPNs and also you can only use delta tables with only 1 version)</li>



<li>or with Delta Sharing plugin (Not possible currently in danone due not having unity catalog)</li>



<li>Or the recommended way, using databricks to do it (With a SPN + Databricks CLuster (either DataEngineering or SQL Warehouse))</li>
</ul>



<p>We are going to cover the 4th method here. To do it first we need a service princpal, a secret scope pointing to a databricks keyvault and the password of the SPN stored in this keyvault.</p>



<p>Once we have this, the first step is to set up the cluster with the credentials to access the datalake. For this we need to configure the spark variables of our databricks cluster. You can follow the guide <a href="https://learn.microsoft.com/en-us/azure/databricks/getting-started/connect-to-azure-storage">here</a>.</p>



<p>After that you cluster should have the credentials in the spark conf section, something like this:</p>



<figure class="wp-block-image size-large"><img decoding="async" width="677" height="1024" src="https://www.albertnogues.com/wp-content/uploads/2023/11/image-1-677x1024.png" alt="" class="wp-image-2197" srcset="https://www.albertnogues.com/wp-content/uploads/2023/11/image-1-677x1024.png 677w, https://www.albertnogues.com/wp-content/uploads/2023/11/image-1-198x300.png 198w, https://www.albertnogues.com/wp-content/uploads/2023/11/image-1-768x1161.png 768w, https://www.albertnogues.com/wp-content/uploads/2023/11/image-1-40x60.png 40w, https://www.albertnogues.com/wp-content/uploads/2023/11/image-1.png 979w" sizes="(max-width: 677px) 100vw, 677px" /></figure>



<p>The second step is creating an EXTERNAL table in Databricks to pint to our delta table(s). For this we connect to our databricks workspace with the previous configuration, and create a new notebook and define the external tables we want, something like this:</p>



<pre class="wp-block-code"><code>%sql
CREATE TABLE IF NOT EXISTS anogues.customers_external 
LOCATION 'abfss://raw@albertdatabricks001.dfs.core.windows.net/customers'</code></pre>



<p>Make sure the location is the right one otherwise when we query the data we will get either an error or no results.</p>



<p>Once we have this we can query our external table and verify we can see the data:</p>



<pre class="wp-block-code"><code>%sql
select * from anogues.customers_external LIMIT 5;</code></pre>



<p>And providing we did it right we should see the data. There is no need to define the table columns as delta uses parquet under the hood so it’s a self contained format where the schema is stored alongside with the data.</p>



<p></p>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="531" src="https://www.albertnogues.com/wp-content/uploads/2023/11/image-20230817-142556-1024x531.png" alt="" class="wp-image-2198" srcset="https://www.albertnogues.com/wp-content/uploads/2023/11/image-20230817-142556-1024x531.png 1024w, https://www.albertnogues.com/wp-content/uploads/2023/11/image-20230817-142556-300x156.png 300w, https://www.albertnogues.com/wp-content/uploads/2023/11/image-20230817-142556-768x398.png 768w, https://www.albertnogues.com/wp-content/uploads/2023/11/image-20230817-142556-1536x796.png 1536w, https://www.albertnogues.com/wp-content/uploads/2023/11/image-20230817-142556-116x60.png 116w, https://www.albertnogues.com/wp-content/uploads/2023/11/image-20230817-142556.png 1682w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>



<p>Once we confirmed this is working we can go to powerbi, try to import data using the Databricks Connector:</p>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="349" src="https://www.albertnogues.com/wp-content/uploads/2023/11/image-20230817-142627-1024x349.png" alt="" class="wp-image-2199" srcset="https://www.albertnogues.com/wp-content/uploads/2023/11/image-20230817-142627-1024x349.png 1024w, https://www.albertnogues.com/wp-content/uploads/2023/11/image-20230817-142627-300x102.png 300w, https://www.albertnogues.com/wp-content/uploads/2023/11/image-20230817-142627-768x262.png 768w, https://www.albertnogues.com/wp-content/uploads/2023/11/image-20230817-142627-176x60.png 176w, https://www.albertnogues.com/wp-content/uploads/2023/11/image-20230817-142627.png 1214w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>



<p>To configure the connector we need to get some details from our cluster. These can be found in the advanced options of our cluster, in the tab JDBC/ODBC</p>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="966" src="https://www.albertnogues.com/wp-content/uploads/2023/11/image-20230817-142733-1024x966.png" alt="" class="wp-image-2200" srcset="https://www.albertnogues.com/wp-content/uploads/2023/11/image-20230817-142733-1024x966.png 1024w, https://www.albertnogues.com/wp-content/uploads/2023/11/image-20230817-142733-300x283.png 300w, https://www.albertnogues.com/wp-content/uploads/2023/11/image-20230817-142733-768x726.png 768w, https://www.albertnogues.com/wp-content/uploads/2023/11/image-20230817-142733-64x60.png 64w, https://www.albertnogues.com/wp-content/uploads/2023/11/image-20230817-142733.png 1228w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>



<p>On the following screen we need to select our authentication options to connect to the databricks cluster. Since we have SAML + SCIM enabled in our workspaces the user and password option is not possible. Either we need a databricks PAT token or use Azure AD. I recommend the latter:</p>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="507" src="https://www.albertnogues.com/wp-content/uploads/2023/11/image-20230817-142917-1024x507.png" alt="" class="wp-image-2201" srcset="https://www.albertnogues.com/wp-content/uploads/2023/11/image-20230817-142917-1024x507.png 1024w, https://www.albertnogues.com/wp-content/uploads/2023/11/image-20230817-142917-300x149.png 300w, https://www.albertnogues.com/wp-content/uploads/2023/11/image-20230817-142917-768x381.png 768w, https://www.albertnogues.com/wp-content/uploads/2023/11/image-20230817-142917-121x60.png 121w, https://www.albertnogues.com/wp-content/uploads/2023/11/image-20230817-142917.png 1235w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>



<p>We click on it and select our AAD account. If all works well our session will be started. We should see it in the screen:</p>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="506" src="https://www.albertnogues.com/wp-content/uploads/2023/11/image-20230817-143025-1024x506.png" alt="" class="wp-image-2202" srcset="https://www.albertnogues.com/wp-content/uploads/2023/11/image-20230817-143025-1024x506.png 1024w, https://www.albertnogues.com/wp-content/uploads/2023/11/image-20230817-143025-300x148.png 300w, https://www.albertnogues.com/wp-content/uploads/2023/11/image-20230817-143025-768x379.png 768w, https://www.albertnogues.com/wp-content/uploads/2023/11/image-20230817-143025-121x60.png 121w, https://www.albertnogues.com/wp-content/uploads/2023/11/image-20230817-143025.png 1223w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>



<p>Then we click on connect, and we can see our data. Since we don’t have unity catalog, our table should appear in the hive_metastore catalog. There we can find our database and inside our table(s). We click and either load all the tables we want or start transforming them inside powerbi, like we will do with any other data source.</p>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="867" height="689" src="https://www.albertnogues.com/wp-content/uploads/2023/11/pbi.png" alt="" class="wp-image-2208" srcset="https://www.albertnogues.com/wp-content/uploads/2023/11/pbi.png 867w, https://www.albertnogues.com/wp-content/uploads/2023/11/pbi-300x238.png 300w, https://www.albertnogues.com/wp-content/uploads/2023/11/pbi-768x610.png 768w, https://www.albertnogues.com/wp-content/uploads/2023/11/pbi-76x60.png 76w" sizes="auto, (max-width: 867px) 100vw, 867px" /></figure>



<p>For more help here is the detail of the powerbi databricks connector: <a href="https://learn.microsoft.com/en-us/azure/databricks/partners/bi/power-bi#--connect-power-bi-desktop-to-azure-databricks-manually">Connect Power BI to Azure Databricks &#8211; Azure Databricks | Microsoft Learn</a></p>
<p>The post <a href="https://www.albertnogues.com/query-delta-tables-in-the-datalake-from-powerbi-with-databricks/">Query Delta Tables in the DataLake from PowerBi with Databricks</a> appeared first on <a href="https://www.albertnogues.com">Albert Nogués</a>.</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Smallest Analytical Platform Ever!</title>
		<link>https://www.albertnogues.com/smallest-analytical-platform-ever/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=smallest-analytical-platform-ever</link>
		
		<dc:creator><![CDATA[Albert]]></dc:creator>
		<pubDate>Sat, 07 May 2022 08:38:12 +0000</pubDate>
				<category><![CDATA[Azure]]></category>
		<category><![CDATA[Cloud]]></category>
		<category><![CDATA[Databricks]]></category>
		<category><![CDATA[DevOps]]></category>
		<category><![CDATA[Spark]]></category>
		<category><![CDATA[SQL]]></category>
		<category><![CDATA[databricks]]></category>
		<category><![CDATA[git]]></category>
		<category><![CDATA[python]]></category>
		<category><![CDATA[spark]]></category>
		<guid isPermaLink="false">https://www.albertnogues.com/?p=1440</guid>

					<description><![CDATA[<p>I&#8217;ve started working on some of my free time in a project to build the smallest useful analytics platform on the cloud (starting with azure). The purpose is to use it a sa PoC to show to colleagues, managers, prospective customers or just to have fun and play It&#8217;s publicly available on my github repo &#8230; </p>
<p>The post <a href="https://www.albertnogues.com/smallest-analytical-platform-ever/">Smallest Analytical Platform Ever!</a> appeared first on <a href="https://www.albertnogues.com">Albert Nogués</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p>I&#8217;ve started working on some of my free time in a project to build the smallest useful analytics platform on the cloud (starting with azure).</p>



<p>The purpose is to use it a sa PoC to show to colleagues, managers, prospective customers or just to have fun and play</p>



<p>It&#8217;s publicly available on my github repo and any collaboration is welcome. You can fork it, improve it, send PR&#8217;s and do whatever you want!</p>



<p>The first version will run solely on azure. The objective is to show the following technologies/disciplines:</p>



<p>* Infrastructure as a Code (IaaC), by using Terraform</p>



<p>* Cloud architecture anc Cloud Ops by using an azure cloud environment</p>



<p>* Data Engineering by using a Spark powered Databricks Notebook and an ADF Pipeline (future)</p>



<p>* DevOps to trigger some pipelines based on changes (future)</p>



<p>* Basic Security concepts (keyvault, service principals, least privileged rbac accesses&#8230;)</p>



<p>* FinOps keeping the costs at minimum and choosing the proper tools for the job</p>



<p>* Reporting and Dashboarding on data in the platform</p>



<p>* Data management: We will use an adls storage account and azure sql db</p>



<p>TOOLS:</p>



<p>* Terraform to deploy all the infra as a code</p>



<p>* Azure Cloud to host our resources</p>



<p>You have the code plus all the information on my github repo:</p>



<p><a href="https://github.com/anogues/ProjectZ">https://github.com/anogues/ProjectZ</a></p>



<p></p>
<p>The post <a href="https://www.albertnogues.com/smallest-analytical-platform-ever/">Smallest Analytical Platform Ever!</a> appeared first on <a href="https://www.albertnogues.com">Albert Nogués</a>.</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Implementing CI/CD in Databricks with Azure DevOps (Part 1)</title>
		<link>https://www.albertnogues.com/implementing-ci-cd-in-databricks-with-azure-devops-part-1/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=implementing-ci-cd-in-databricks-with-azure-devops-part-1</link>
		
		<dc:creator><![CDATA[Albert]]></dc:creator>
		<pubDate>Sat, 30 Apr 2022 15:10:29 +0000</pubDate>
				<category><![CDATA[Azure]]></category>
		<category><![CDATA[Databricks]]></category>
		<category><![CDATA[DevOps]]></category>
		<category><![CDATA[Spark]]></category>
		<category><![CDATA[Cloud]]></category>
		<category><![CDATA[databricks]]></category>
		<category><![CDATA[git]]></category>
		<category><![CDATA[spark]]></category>
		<guid isPermaLink="false">https://www.albertnogues.com/?p=1416</guid>

					<description><![CDATA[<p>There are many ways to implement some CI/CD with Databricks. We can use Azure DevOps, Github+Github Actions or any other combination of tools, including the dbx tool. But an easy way to just copy notebooks between workspaces can be implemented easily with Azure DevOps. We are going to use the git repos capability of Azure &#8230; </p>
<p>The post <a href="https://www.albertnogues.com/implementing-ci-cd-in-databricks-with-azure-devops-part-1/">Implementing CI/CD in Databricks with Azure DevOps (Part 1)</a> appeared first on <a href="https://www.albertnogues.com">Albert Nogués</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p>There are many ways to implement some CI/CD with Databricks. We can use Azure DevOps, Github+Github Actions or any other combination of tools, including the <a href="https://dbx.readthedocs.io/en/latest/templates/python_basic.html#project-file-structure" target="_blank" rel="noreferrer noopener">dbx tool</a>.</p>



<p>But an easy way to just copy notebooks between workspaces can be implemented easily with Azure DevOps.</p>



<p>We are going to use the git repos capability of Azure Databricks, so when a new code change is commited in a notebook an Azure DevOps pipeline will trigger the transport copy of the workbook from the first Databricks workspace (in our case a NonProd workspace) to the target one, again, in our case, the Prod workspace.</p>



<p>To achieve this we will use some more components from the Azure ecosystem, including the use of Keyvaults to keep all our secrets stored safely. The list of prerequisites is the following:</p>



<ul class="wp-block-list"><li>Two Databricks workspaces, one our source workspace (NonProd) and another, our Production one.</li><li>An Azure Keyvault (or two if we want to segregate the environments)</li><li>Azure Databricks repository configured at least in our source workspace, so when the change is commited we can triger the pipeline that will fetch the notebook and transport it to the prod workspace</li><li>Access to Azure DevOps (Something similar can be implemented with Github + Github Actions)</li></ul>



<p>Lets see how to implement it. First we need to make sure git repos in enabled our source workspace. We can verify by login with an admin privileged user to our workspace and make sure the option is checked as follows:</p>



<figure class="wp-block-image size-large is-resized"><img loading="lazy" decoding="async" src="https://www.albertnogues.com/wp-content/uploads/2022/04/DatabricksCICD1-924x1024.png" alt="" class="wp-image-1418" width="687" height="760" srcset="https://www.albertnogues.com/wp-content/uploads/2022/04/DatabricksCICD1-924x1024.png 924w, https://www.albertnogues.com/wp-content/uploads/2022/04/DatabricksCICD1-271x300.png 271w, https://www.albertnogues.com/wp-content/uploads/2022/04/DatabricksCICD1-768x851.png 768w, https://www.albertnogues.com/wp-content/uploads/2022/04/DatabricksCICD1-54x60.png 54w, https://www.albertnogues.com/wp-content/uploads/2022/04/DatabricksCICD1.png 1072w" sizes="auto, (max-width: 687px) 100vw, 687px" /><figcaption>Fig 1. Make sure that github repos is enabled in our workspace.</figcaption></figure>



<p>Secondly, we go to <a href="https://azure.microsoft.com/en-us/services/devops/" target="_blank" rel="noreferrer noopener">Azure DevOps services</a> and we create a new project. I&#8217;ve called it DatabricksCICD but feel free to call it whatever you need:</p>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="714" src="https://www.albertnogues.com/wp-content/uploads/2022/04/DatabricksCICD2-1024x714.png" alt="" class="wp-image-1419" srcset="https://www.albertnogues.com/wp-content/uploads/2022/04/DatabricksCICD2-1024x714.png 1024w, https://www.albertnogues.com/wp-content/uploads/2022/04/DatabricksCICD2-300x209.png 300w, https://www.albertnogues.com/wp-content/uploads/2022/04/DatabricksCICD2-768x536.png 768w, https://www.albertnogues.com/wp-content/uploads/2022/04/DatabricksCICD2-86x60.png 86w, https://www.albertnogues.com/wp-content/uploads/2022/04/DatabricksCICD2.png 1531w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /><figcaption>Fig 2. Create a new repo and initialize it</figcaption></figure>



<p>Once created we take the details for cloning our repo and copying them. We go to our databricks workspace and then we look for the Repos option on the left, and add a new repository. We need to paste the url to clone our newle Azure DevOps created repository:</p>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="319" src="https://www.albertnogues.com/wp-content/uploads/2022/04/DatabricksCICD3-1024x319.png" alt="" class="wp-image-1420" srcset="https://www.albertnogues.com/wp-content/uploads/2022/04/DatabricksCICD3-1024x319.png 1024w, https://www.albertnogues.com/wp-content/uploads/2022/04/DatabricksCICD3-300x93.png 300w, https://www.albertnogues.com/wp-content/uploads/2022/04/DatabricksCICD3-768x239.png 768w, https://www.albertnogues.com/wp-content/uploads/2022/04/DatabricksCICD3-1536x478.png 1536w, https://www.albertnogues.com/wp-content/uploads/2022/04/DatabricksCICD3-2048x637.png 2048w, https://www.albertnogues.com/wp-content/uploads/2022/04/DatabricksCICD3-193x60.png 193w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /><figcaption>Fig 3. Cloning our Azure DevOps Repository</figcaption></figure>



<p>Once we linked our Databricks workspace with our DevOps repo, now we can create a new notebook. In the same Repos section, click on the down arrow to create a new notebook, as shown below:</p>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="889" height="373" src="https://www.albertnogues.com/wp-content/uploads/2022/04/DatabricksCICD4.png" alt="" class="wp-image-1421" srcset="https://www.albertnogues.com/wp-content/uploads/2022/04/DatabricksCICD4.png 889w, https://www.albertnogues.com/wp-content/uploads/2022/04/DatabricksCICD4-300x126.png 300w, https://www.albertnogues.com/wp-content/uploads/2022/04/DatabricksCICD4-768x322.png 768w, https://www.albertnogues.com/wp-content/uploads/2022/04/DatabricksCICD4-143x60.png 143w" sizes="auto, (max-width: 889px) 100vw, 889px" /><figcaption>Fig 4.  Creating a new notebook.</figcaption></figure>



<p>The content of the notebook, you can put anything you want. I&#8217;m writing a print(&#8220;Hello from Albert&#8221;) statement. We will not run it, we just want to show it&#8217;s possible to transport it. Once done, click on the save now in the revision tab:</p>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="73" src="https://www.albertnogues.com/wp-content/uploads/2022/04/DatabricksCICD5-1024x73.png" alt="" class="wp-image-1423" srcset="https://www.albertnogues.com/wp-content/uploads/2022/04/DatabricksCICD5-1024x73.png 1024w, https://www.albertnogues.com/wp-content/uploads/2022/04/DatabricksCICD5-300x21.png 300w, https://www.albertnogues.com/wp-content/uploads/2022/04/DatabricksCICD5-768x55.png 768w, https://www.albertnogues.com/wp-content/uploads/2022/04/DatabricksCICD5-1536x110.png 1536w, https://www.albertnogues.com/wp-content/uploads/2022/04/DatabricksCICD5-2048x146.png 2048w, https://www.albertnogues.com/wp-content/uploads/2022/04/DatabricksCICD5-600x43.png 600w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /><figcaption>Fig 5. Saving our changes to the notebook.</figcaption></figure>



<p>Then click on the left in the main branch button, from there we will be commiting the changes to our repository:</p>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="314" src="https://www.albertnogues.com/wp-content/uploads/2022/04/DatabricksCICD6-1024x314.png" alt="" class="wp-image-1424" srcset="https://www.albertnogues.com/wp-content/uploads/2022/04/DatabricksCICD6-1024x314.png 1024w, https://www.albertnogues.com/wp-content/uploads/2022/04/DatabricksCICD6-300x92.png 300w, https://www.albertnogues.com/wp-content/uploads/2022/04/DatabricksCICD6-768x235.png 768w, https://www.albertnogues.com/wp-content/uploads/2022/04/DatabricksCICD6-1536x470.png 1536w, https://www.albertnogues.com/wp-content/uploads/2022/04/DatabricksCICD6-2048x627.png 2048w, https://www.albertnogues.com/wp-content/uploads/2022/04/DatabricksCICD6-196x60.png 196w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /><figcaption>Fig 6. Pushing our notebook to the DevOps repo</figcaption></figure>



<p>If we go back now to our Azure DevOps project we should see the file has been commited to the repository. This ends the first part of this tutorial.</p>



<p>In the second blog entry we will see how to trigger the pipeline after a modification of this notebook and passing the credentials of the second workspace to be able to deliver the changed notebook to our Production (target) workspace.</p>
<p>The post <a href="https://www.albertnogues.com/implementing-ci-cd-in-databricks-with-azure-devops-part-1/">Implementing CI/CD in Databricks with Azure DevOps (Part 1)</a> appeared first on <a href="https://www.albertnogues.com">Albert Nogués</a>.</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Using Azure Private Endpoints with Databricks</title>
		<link>https://www.albertnogues.com/using-azure-private-endpoints-with-databricks/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=using-azure-private-endpoints-with-databricks</link>
		
		<dc:creator><![CDATA[Albert]]></dc:creator>
		<pubDate>Thu, 09 Dec 2021 19:31:32 +0000</pubDate>
				<category><![CDATA[Azure]]></category>
		<category><![CDATA[Databricks]]></category>
		<category><![CDATA[Spark]]></category>
		<category><![CDATA[SQL]]></category>
		<category><![CDATA[Cloud]]></category>
		<category><![CDATA[databricks]]></category>
		<category><![CDATA[PrivateEndpoints]]></category>
		<category><![CDATA[spark]]></category>
		<guid isPermaLink="false">https://www.albertnogues.com/?p=1235</guid>

					<description><![CDATA[<p>In this article i will show how to avoing going outside to the internet when using resources inside azure, specially if they are in the same subscription and location (datacenter). Why we may want a private endpoint? Thats a good question. For oth security and performance. Just like using TSCM Equipment for optimal safety and &#8230; </p>
<p>The post <a href="https://www.albertnogues.com/using-azure-private-endpoints-with-databricks/">Using Azure Private Endpoints with Databricks</a> appeared first on <a href="https://www.albertnogues.com">Albert Nogués</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p>In this article i will show how to avoing going outside to the internet when using resources inside azure, specially if they are in the same subscription and location (datacenter).</p>



<p>Why we may want a private endpoint? Thats a good question. For oth security and performance. Just like using <a href="https://spyassociates.com/counter-surveillance">TSCM Equipment</a> for optimal safety and security. We dont want the traffic going outside to the internet to return again back to the azure datacenter if the resource we are trying to reach is already there. So with a PrivateLink the traffic will stay inside the Azure backbone network avoiding reaching the internet. More information about private endpoints <a href="https://azure.microsoft.com/en-us/services/private-link/" target="_blank" rel="noreferrer noopener">here</a> and <a href="https://docs.microsoft.com/en-us/azure/private-link/private-link-overview" target="_blank" rel="noreferrer noopener">here</a>.</p>



<p>Though its possible to create private endpoints to connect to services in other subcriptions we will use the same subscription and the West Europe Region in this article. The goal is to connect to both a AzureSQL database using private connectivity and to a datalake using private connectivity as well.</p>



<h2 class="wp-block-heading">Creating a Private Endpoint for AzureSQL and integrating in the databricks vnet</h2>



<p>For this, i created a Databricks workspace and selected to use an already existing VNET, so this way I can add a new subnet for my private endpoints. One of the good things of doing this way is that NICs between subnets see each other and are reacheable (unless we block it with a network security group) but by default traffic is open within the VNET. So I can create a Private endpoint in a specific subnet of the same VNET that hosts the databricks subnets.</p>



<p>Bear in mind that it&#8217;s not possible to add a private endpoint to a subnet managed by databricks. So the two subnets we created, when we deployed the databricks workspace (Bot public and private) should not be modified. We will create a new one as shown in the screen below:</p>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="145" src="https://www.albertnogues.com/wp-content/uploads/2021/12/SQlPep4-1024x145.png" alt="" class="wp-image-1236" srcset="https://www.albertnogues.com/wp-content/uploads/2021/12/SQlPep4-1024x145.png 1024w, https://www.albertnogues.com/wp-content/uploads/2021/12/SQlPep4-300x43.png 300w, https://www.albertnogues.com/wp-content/uploads/2021/12/SQlPep4-768x109.png 768w, https://www.albertnogues.com/wp-content/uploads/2021/12/SQlPep4-1536x218.png 1536w, https://www.albertnogues.com/wp-content/uploads/2021/12/SQlPep4-2048x290.png 2048w, https://www.albertnogues.com/wp-content/uploads/2021/12/SQlPep4-424x60.png 424w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /><figcaption class="wp-element-caption">Our  Databricks VNET. Among the two subnets created when the databricks workspace is created i added a new one to host our Private Endpoints</figcaption></figure>



<p>Once defined properly the VNET, we are going to create the private endpoint to reach our AzureSQL through it.</p>



<p>First we need to go to the Azure Portal, find our AzureSQL Server, and click on the left menu called Private Endpoint Connections and click on the plus sing on top to create a new one. We just need to select the subscription, the resource group the nameof the private endpoint and the region. We can fill it as shown in the following picture:</p>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="946" height="626" src="https://www.albertnogues.com/wp-content/uploads/2021/12/SQlPep1.png" alt="" class="wp-image-1237" srcset="https://www.albertnogues.com/wp-content/uploads/2021/12/SQlPep1.png 946w, https://www.albertnogues.com/wp-content/uploads/2021/12/SQlPep1-300x199.png 300w, https://www.albertnogues.com/wp-content/uploads/2021/12/SQlPep1-768x508.png 768w, https://www.albertnogues.com/wp-content/uploads/2021/12/SQlPep1-91x60.png 91w" sizes="auto, (max-width: 946px) 100vw, 946px" /><figcaption class="wp-element-caption">Private Endpoint Creation. Step 1</figcaption></figure>



<p>The second step requires a bit more of information, here we will define which resource we try to target with our private endpoint. As expected we need to find our AzureSQL Server here. We fill the combo boxes as usual</p>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="920" height="529" src="https://www.albertnogues.com/wp-content/uploads/2021/12/SQlPep2.png" alt="" class="wp-image-1238" srcset="https://www.albertnogues.com/wp-content/uploads/2021/12/SQlPep2.png 920w, https://www.albertnogues.com/wp-content/uploads/2021/12/SQlPep2-300x173.png 300w, https://www.albertnogues.com/wp-content/uploads/2021/12/SQlPep2-768x442.png 768w, https://www.albertnogues.com/wp-content/uploads/2021/12/SQlPep2-330x190.png 330w, https://www.albertnogues.com/wp-content/uploads/2021/12/SQlPep2-104x60.png 104w" sizes="auto, (max-width: 920px) 100vw, 920px" /><figcaption class="wp-element-caption">Private Endpoint Creation. Step 2</figcaption></figure>



<p>The third screen is the most important one. We need to select the VNET and Subnets that will host our private endpoint. In this case we want to use databricks so we need to use the VNET we created for databrickks, and then the subnet we created specifically to host the private endpoints.</p>



<p>Another important step here is to integrate it with the DNS. If we dont integrate it, when we use the AzureSQL hostname provided by azure we will still access through the public endpoint. By integrating it in the DNS. the dns queries over the public endpoint in this private zone, will resolve to the private IP of the NIC of the Private Endpoint</p>



<p>If we chose no for the DNS integration then we will have to add static entries in the /etc/hosts or somewhat, or use the private IP instead of the hostname when connecting to the AzureSQL server. To simplify we choose to integrate it.</p>



<figure class="wp-block-image size-large is-resized"><img loading="lazy" decoding="async" width="1024" height="583" src="https://www.albertnogues.com/wp-content/uploads/2021/12/SQlPep3-1024x583.png" alt="" class="wp-image-1239" style="width:840px;height:478px" srcset="https://www.albertnogues.com/wp-content/uploads/2021/12/SQlPep3-1024x583.png 1024w, https://www.albertnogues.com/wp-content/uploads/2021/12/SQlPep3-300x171.png 300w, https://www.albertnogues.com/wp-content/uploads/2021/12/SQlPep3-768x438.png 768w, https://www.albertnogues.com/wp-content/uploads/2021/12/SQlPep3-105x60.png 105w, https://www.albertnogues.com/wp-content/uploads/2021/12/SQlPep3.png 1241w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /><figcaption class="wp-element-caption"> Private Endpoint Creation. Step 3.</figcaption></figure>



<p>Once created we should see the private endpoint available. If you look at the right its implemented though a NIC (Network Interface card), and by clicking on it, we can find it and see the ip address assigned:</p>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="177" src="https://www.albertnogues.com/wp-content/uploads/2021/12/SQlPep5-1024x177.png" alt="" class="wp-image-1241" srcset="https://www.albertnogues.com/wp-content/uploads/2021/12/SQlPep5-1024x177.png 1024w, https://www.albertnogues.com/wp-content/uploads/2021/12/SQlPep5-300x52.png 300w, https://www.albertnogues.com/wp-content/uploads/2021/12/SQlPep5-768x132.png 768w, https://www.albertnogues.com/wp-content/uploads/2021/12/SQlPep5-1536x265.png 1536w, https://www.albertnogues.com/wp-content/uploads/2021/12/SQlPep5-2048x353.png 2048w, https://www.albertnogues.com/wp-content/uploads/2021/12/SQlPep5-348x60.png 348w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /><figcaption class="wp-element-caption">Our newly created Private Endpoint for Azure SQL</figcaption></figure>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="225" src="https://www.albertnogues.com/wp-content/uploads/2021/12/SQlPep6-1024x225.png" alt="" class="wp-image-1242" srcset="https://www.albertnogues.com/wp-content/uploads/2021/12/SQlPep6-1024x225.png 1024w, https://www.albertnogues.com/wp-content/uploads/2021/12/SQlPep6-300x66.png 300w, https://www.albertnogues.com/wp-content/uploads/2021/12/SQlPep6-768x168.png 768w, https://www.albertnogues.com/wp-content/uploads/2021/12/SQlPep6-1536x337.png 1536w, https://www.albertnogues.com/wp-content/uploads/2021/12/SQlPep6-2048x449.png 2048w, https://www.albertnogues.com/wp-content/uploads/2021/12/SQlPep6-274x60.png 274w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /><figcaption class="wp-element-caption">Finding the PrivateIP Address of the NIC that implements the private endpoint</figcaption></figure>



<h2 class="wp-block-heading">Test the AzureSQL DB Endpoint from Databricks</h2>



<p>Now we have it ready. We can still see from outside that VNET, that our old server still resolves to a public ip, as it was the case before even inside databricks. We can ping it for testing purposes:</p>



<pre class="wp-block-code"><code>C:\Users\Albert&gt;ping azure-sql-server-albert.database.windows.net

Haciendo ping a cr4.westeurope1-a.control.database.windows.net &#91;<strong>104.40.168.105</strong>] con 32 bytes de datos:</code></pre>



<p>As you can see we have a public ip, but lets try to ping it inside the cluster:</p>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="885" height="178" src="https://www.albertnogues.com/wp-content/uploads/2021/12/SQlPep7.png" alt="" class="wp-image-1244" srcset="https://www.albertnogues.com/wp-content/uploads/2021/12/SQlPep7.png 885w, https://www.albertnogues.com/wp-content/uploads/2021/12/SQlPep7-300x60.png 300w, https://www.albertnogues.com/wp-content/uploads/2021/12/SQlPep7-768x154.png 768w, https://www.albertnogues.com/wp-content/uploads/2021/12/SQlPep7-298x60.png 298w" sizes="auto, (max-width: 885px) 100vw, 885px" /><figcaption class="wp-element-caption">Private endpoint with the DNS integration working fine. Our dns record for the AzureSQL Db does not resolve to a public ip anymore but to the private IP of the PrivateEndpoint</figcaption></figure>



<p>So it&#8217;s working. Its using the private ip instead of the public one. Our last step is to see if we can fetch the data from the database:</p>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="363" src="https://www.albertnogues.com/wp-content/uploads/2021/12/SQlPep8-1024x363.png" alt="" class="wp-image-1245" srcset="https://www.albertnogues.com/wp-content/uploads/2021/12/SQlPep8-1024x363.png 1024w, https://www.albertnogues.com/wp-content/uploads/2021/12/SQlPep8-300x106.png 300w, https://www.albertnogues.com/wp-content/uploads/2021/12/SQlPep8-768x272.png 768w, https://www.albertnogues.com/wp-content/uploads/2021/12/SQlPep8-1536x544.png 1536w, https://www.albertnogues.com/wp-content/uploads/2021/12/SQlPep8-2048x726.png 2048w, https://www.albertnogues.com/wp-content/uploads/2021/12/SQlPep8-169x60.png 169w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /><figcaption class="wp-element-caption">Accessing AzureSQL Database though a private endpoint from databricks</figcaption></figure>



<h2 class="wp-block-heading">Creating an AzureDataLake PrivateEndpoint and saving our data to the DataLake through it.</h2>



<p>We are not done yet! We can still complicate matters and create a private endpoint as well to save data to our datalake.</p>



<p>I&#8217;ve created an ADLS Gen 2 storage account, and going back to databricks I see by default it&#8217;s using public access:</p>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="836" height="194" src="https://www.albertnogues.com/wp-content/uploads/2021/12/SQlPep9.png" alt="" class="wp-image-1246" srcset="https://www.albertnogues.com/wp-content/uploads/2021/12/SQlPep9.png 836w, https://www.albertnogues.com/wp-content/uploads/2021/12/SQlPep9-300x70.png 300w, https://www.albertnogues.com/wp-content/uploads/2021/12/SQlPep9-768x178.png 768w, https://www.albertnogues.com/wp-content/uploads/2021/12/SQlPep9-259x60.png 259w" sizes="auto, (max-width: 836px) 100vw, 836px" /><figcaption class="wp-element-caption">Datalake public access</figcaption></figure>



<p>But we can implement a Private Endpoint as well, and route all the traffic through the azure datacenter itself. Lets see how to do it. For achieving this, we go to our ADS Gen2 storage account, and on the left we click again in Networking, and the second tab is called Private Endpoint connections. We click the plus button to create a new one, and basically we follow the same steps as before with a subtle difference</p>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="930" height="760" src="https://www.albertnogues.com/wp-content/uploads/2021/12/SQlPep10.png" alt="" class="wp-image-1247" srcset="https://www.albertnogues.com/wp-content/uploads/2021/12/SQlPep10.png 930w, https://www.albertnogues.com/wp-content/uploads/2021/12/SQlPep10-300x245.png 300w, https://www.albertnogues.com/wp-content/uploads/2021/12/SQlPep10-768x628.png 768w, https://www.albertnogues.com/wp-content/uploads/2021/12/SQlPep10-73x60.png 73w" sizes="auto, (max-width: 930px) 100vw, 930px" /><figcaption class="wp-element-caption">Creation of a private endpoint for an ADLS Gen2 storage account.</figcaption></figure>



<p>The difference with a storage account is that we need to chose which api we want to create the private endpoint for. We can use the blob, the table, the queue, the file share and the dfs (DataLake) endpoint (And also the static website!).</p>



<p>We will use the dfs endpoint, and again we will place it in the Private Endpoint subnet of our Databricks vnet. Something like this:</p>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="814" height="990" src="https://www.albertnogues.com/wp-content/uploads/2021/12/SQlPep11.png" alt="" class="wp-image-1248" srcset="https://www.albertnogues.com/wp-content/uploads/2021/12/SQlPep11.png 814w, https://www.albertnogues.com/wp-content/uploads/2021/12/SQlPep11-247x300.png 247w, https://www.albertnogues.com/wp-content/uploads/2021/12/SQlPep11-768x934.png 768w, https://www.albertnogues.com/wp-content/uploads/2021/12/SQlPep11-49x60.png 49w" sizes="auto, (max-width: 814px) 100vw, 814px" /><figcaption class="wp-element-caption">Creating a Private Endpoint for our DataLake an dplacing it in the appropiate subnet</figcaption></figure>



<p>After a few minutes our private endpoint will be ready to be used. We can go again to see the NIC and check the private ip or go directly to databricks and ping the storage account url to see if now it&#8217;s resolving to our private endpoint:</p>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="796" height="184" src="https://www.albertnogues.com/wp-content/uploads/2021/12/SQlPep12.png" alt="" class="wp-image-1249" srcset="https://www.albertnogues.com/wp-content/uploads/2021/12/SQlPep12.png 796w, https://www.albertnogues.com/wp-content/uploads/2021/12/SQlPep12-300x69.png 300w, https://www.albertnogues.com/wp-content/uploads/2021/12/SQlPep12-768x178.png 768w, https://www.albertnogues.com/wp-content/uploads/2021/12/SQlPep12-260x60.png 260w" sizes="auto, (max-width: 796px) 100vw, 796px" /><figcaption class="wp-element-caption">As we can see now databricks resolves our storage account through the private endpoint</figcaption></figure>



<h2 class="wp-block-heading">Test the ADLS Gen2 SA endpoint from Databricks </h2>



<p>If we have the IAM credential Passthrough enabled in our cluster and we have permisison to write to the datalake, now we should be able to write there without going through the internet:</p>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="175" src="https://www.albertnogues.com/wp-content/uploads/2021/12/SQlPep13-1024x175.png" alt="" class="wp-image-1250" srcset="https://www.albertnogues.com/wp-content/uploads/2021/12/SQlPep13-1024x175.png 1024w, https://www.albertnogues.com/wp-content/uploads/2021/12/SQlPep13-300x51.png 300w, https://www.albertnogues.com/wp-content/uploads/2021/12/SQlPep13-768x132.png 768w, https://www.albertnogues.com/wp-content/uploads/2021/12/SQlPep13-1536x263.png 1536w, https://www.albertnogues.com/wp-content/uploads/2021/12/SQlPep13-2048x351.png 2048w, https://www.albertnogues.com/wp-content/uploads/2021/12/SQlPep13-350x60.png 350w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /><figcaption class="wp-element-caption">Writing to a DataLake through the Private Endpoint we just created</figcaption></figure>



<p>So this is the end of the tutorial. We created two private endpoints, one for AzureSQL Database and Another for our DataLake and used bot them from Databricks. We also confirmed we are effectively using them by pinging the hostnames of both resources and seeing a change from the public ip to the private one.</p>



<p>Happy Data pojects!</p>
<p>The post <a href="https://www.albertnogues.com/using-azure-private-endpoints-with-databricks/">Using Azure Private Endpoints with Databricks</a> appeared first on <a href="https://www.albertnogues.com">Albert Nogués</a>.</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Databricks connectivity to Azure SQL / SQL Server</title>
		<link>https://www.albertnogues.com/databricks-connectivity-to-azure-sql-sql-server/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=databricks-connectivity-to-azure-sql-sql-server</link>
		
		<dc:creator><![CDATA[Albert]]></dc:creator>
		<pubDate>Thu, 09 Dec 2021 10:45:34 +0000</pubDate>
				<category><![CDATA[Azure]]></category>
		<category><![CDATA[Databricks]]></category>
		<category><![CDATA[Python]]></category>
		<category><![CDATA[Spark]]></category>
		<category><![CDATA[SQL]]></category>
		<guid isPermaLink="false">https://www.albertnogues.com/?p=1224</guid>

					<description><![CDATA[<p>Most of the developments I see inside databricks rely on fetching or writing data to some sort of Database. Usually the preferred method for this is though the use of jdbc driver, as most databases offer some sort of jdbc driver. In some cases, though, its also possible to use some spark optimized driver. This &#8230; </p>
<p>The post <a href="https://www.albertnogues.com/databricks-connectivity-to-azure-sql-sql-server/">Databricks connectivity to Azure SQL / SQL Server</a> appeared first on <a href="https://www.albertnogues.com">Albert Nogués</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p>Most of the developments I see inside databricks rely on fetching or writing data to some sort of Database.</p>



<p>Usually the preferred method for this is though the use of jdbc driver, as most databases offer some sort of jdbc driver.</p>



<p>In some cases, though, its also possible to use some spark optimized driver. This is the case in Azure SQL / SQL Server. We have still the option to use the standard jdbc driver (what most people do because it&#8217;s standard to all databases) but we can improve the performance by using a specific spark driver. Till some time ago it was only supported with the Scala API but now it&#8217;s possible to be used in Python and R as well, so there is no reason not to give it a try.</p>



<p>In this article we will see the two options to make this connectivity. For the test purposes we will connect to an Azure SQL in the same region (West Europe).</p>



<h2 class="wp-block-heading">Connecting to AzureSQL through  jdbc driver. </h2>



<p>In this case the jdbc driver is already shipped in the databricks cluster, we do not need to install anything. We just can connect directly. Lets see how (We have a scala example <a href="https://docs.microsoft.com/es-es/azure/databricks/data/data-sources/sql-databases" target="_blank" rel="noreferrer noopener">here</a> but i will use python for this example)</p>



<pre class="wp-block-code"><code>#In a real development this should be fetched from a keyvault using a secret scope with: dbutils.secrets.get(scope = "sql_db", key = "username") and  dbutils.secrets.get(scope = "sql_db", key = "password")

jdbcDF = spark.read.format("jdbc") \
    .option("url", f"jdbc:sqlserver://azure-sql-server-albert.database.windows.net:1433;databaseName=databricksdata") \
    .option("dbtable", "SalesLT.Product") \
    .option("user", "anogues") \
    .option("password", "XXXXXX") \
    .option("driver", "com.microsoft.sqlserver.jdbc.SQLServerDriver") \
    .load()

jdbcDF.show()</code></pre>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="362" src="https://www.albertnogues.com/wp-content/uploads/2021/12/SQLDBR1-1024x362.png" alt="" class="wp-image-1226" srcset="https://www.albertnogues.com/wp-content/uploads/2021/12/SQLDBR1-1024x362.png 1024w, https://www.albertnogues.com/wp-content/uploads/2021/12/SQLDBR1-300x106.png 300w, https://www.albertnogues.com/wp-content/uploads/2021/12/SQLDBR1-768x272.png 768w, https://www.albertnogues.com/wp-content/uploads/2021/12/SQLDBR1-1536x543.png 1536w, https://www.albertnogues.com/wp-content/uploads/2021/12/SQLDBR1-2048x724.png 2048w, https://www.albertnogues.com/wp-content/uploads/2021/12/SQLDBR1-170x60.png 170w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /><figcaption>Spark Dataframe from a JDBC Azure SQL DB Source</figcaption></figure>



<p>So as we saw we have been able to connect successfully to our Azure SQL DB using the jdbc driver shipped with databricks. Lets now try to change to the spark optimized driver</p>



<h2 class="wp-block-heading">Connecting to AzureSQL through the spark optimized driver</h2>



<p>To connect using the spark optimized driver, first we need to install the driver in the cluster, as it&#8217;s not available by default.</p>



<p>The driver is available in Maven for both spark 2.X and 3.X. In the microsoft <a href="https://docs.microsoft.com/en-us/sql/connect/spark/connector?view=sql-server-ver15">website</a> we can find more information on where to get them and how to use them. For this exercise purposes we will inbstall it through databricks libraries, using maven. Just add in the coordinates box the following: com.microsoft.azure:spark-mssql-connector_2.12:1.2.0 as can be seen in the image below</p>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="323" src="https://www.albertnogues.com/wp-content/uploads/2021/12/SQLDBR2-1024x323.png" alt="" class="wp-image-1227" srcset="https://www.albertnogues.com/wp-content/uploads/2021/12/SQLDBR2-1024x323.png 1024w, https://www.albertnogues.com/wp-content/uploads/2021/12/SQLDBR2-300x95.png 300w, https://www.albertnogues.com/wp-content/uploads/2021/12/SQLDBR2-768x242.png 768w, https://www.albertnogues.com/wp-content/uploads/2021/12/SQLDBR2-1536x485.png 1536w, https://www.albertnogues.com/wp-content/uploads/2021/12/SQLDBR2-190x60.png 190w, https://www.albertnogues.com/wp-content/uploads/2021/12/SQLDBR2.png 2006w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /><figcaption>Installing the spark AzureSQL Driver from Maven</figcaption></figure>



<p>Once installed we should see a green dot next to the driver, and this will mean the driver is ready to be used. We go back to our notebook and try</p>



<pre class="wp-block-code"><code>#In a real development this should be fetched from a keyvault using a secret scope with: dbutils.secrets.get(scope = "sql_db", key = "username") and  dbutils.secrets.get(scope = "sql_db", key = "password")
jdbcDF = spark.read.format("com.microsoft.sqlserver.jdbc.spark") \
    .option("url", f"jdbc:sqlserver://azure-sql-server-albert.database.windows.net:1433;databaseName=databricksdata") \
    .option("dbtable", "SalesLT.Product") \
    .option("user", "anogues") \
    .option("password", "XXXXXX") \
    .load()

jdbcDF.show()</code></pre>



<p>If we see an error like java.lang.ClassNotFoundException: com.microsoft.sqlserver.jdbc.spark this means that the driver can&#8217;t be found, so probably it&#8217;s not properly installed. Check back the libraries in the cluster and make sure the status is installed. If all goes well we should see again our dataframe:</p>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="351" src="https://www.albertnogues.com/wp-content/uploads/2021/12/SQLDBR3-1-1024x351.png" alt="" class="wp-image-1229" srcset="https://www.albertnogues.com/wp-content/uploads/2021/12/SQLDBR3-1-1024x351.png 1024w, https://www.albertnogues.com/wp-content/uploads/2021/12/SQLDBR3-1-300x103.png 300w, https://www.albertnogues.com/wp-content/uploads/2021/12/SQLDBR3-1-768x263.png 768w, https://www.albertnogues.com/wp-content/uploads/2021/12/SQLDBR3-1-1536x526.png 1536w, https://www.albertnogues.com/wp-content/uploads/2021/12/SQLDBR3-1-2048x701.png 2048w, https://www.albertnogues.com/wp-content/uploads/2021/12/SQLDBR3-1-175x60.png 175w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /><figcaption> Spark Dataframe from a Spark Azure SQL DB Source </figcaption></figure>



<p>The reason why we should use the optimized spark driver is usually because of performance reasons. Microsoft claims its about 15x faster than the jdbc one. But there is more. The spark driver also allows AAD authentication either by using a service principal or an AAD account, apart of course from the native sql server authentication. Lets try if it works with an AAD account:</p>



<pre class="wp-block-code"><code>jdbcDF = spark.read \
    .format("com.microsoft.sqlserver.jdbc.spark") \
    .option("url", f"jdbc:sqlserver://azure-sql-server-albert.database.windows.net:1433;databaseName=databricksdata") \
    .option("dbtable", "SalesLT.Product") \
    .option("authentication", "ActiveDirectoryPassword") \
    .option("user", "sqluser@anogues4hotmail.onmicrosoft.com") \
    .option("password", "XXXXXX") \
    .option("encrypt", "true") \
    .option("hostNameInCertificate", "*.database.windows.net") \
    .load()
jdbcDF.show()</code></pre>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="366" src="https://www.albertnogues.com/wp-content/uploads/2021/12/SQLDBR4-1024x366.png" alt="" class="wp-image-1230" srcset="https://www.albertnogues.com/wp-content/uploads/2021/12/SQLDBR4-1024x366.png 1024w, https://www.albertnogues.com/wp-content/uploads/2021/12/SQLDBR4-300x107.png 300w, https://www.albertnogues.com/wp-content/uploads/2021/12/SQLDBR4-768x274.png 768w, https://www.albertnogues.com/wp-content/uploads/2021/12/SQLDBR4-1536x549.png 1536w, https://www.albertnogues.com/wp-content/uploads/2021/12/SQLDBR4-2048x732.png 2048w, https://www.albertnogues.com/wp-content/uploads/2021/12/SQLDBR4-168x60.png 168w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>



<p>For using a service principal you need to generate a token. In python this can be accomplished with the <a href="https://pypi.org/project/adal/" target="_blank" rel="noreferrer noopener">adal</a> library (That needs to be installed in the cluster as well from pypi). You have a sample notebook in microsoft spark driver github account <a href="https://github.com/microsoft/sql-spark-connector/tree/master/samples/Databricks-AzureSQL/DatabricksNotebooks">here</a>.</p>



<p>More information about the driver can be found on the microsoft github repository <a href="https://github.com/microsoft/sql-spark-connector" target="_blank" rel="noreferrer noopener">here</a>.</p>
<p>The post <a href="https://www.albertnogues.com/databricks-connectivity-to-azure-sql-sql-server/">Databricks connectivity to Azure SQL / SQL Server</a> appeared first on <a href="https://www.albertnogues.com">Albert Nogués</a>.</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Ansible playbook to configure Azure Red Hat VM&#8217;s</title>
		<link>https://www.albertnogues.com/ansible-playbook-to-configure-azure-red-hat-vms/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=ansible-playbook-to-configure-azure-red-hat-vms</link>
		
		<dc:creator><![CDATA[Albert]]></dc:creator>
		<pubDate>Wed, 21 Apr 2021 10:43:36 +0000</pubDate>
				<category><![CDATA[Ansible]]></category>
		<category><![CDATA[Azure]]></category>
		<category><![CDATA[Cloud]]></category>
		<category><![CDATA[Linux]]></category>
		<category><![CDATA[ansible]]></category>
		<category><![CDATA[linux]]></category>
		<category><![CDATA[lun]]></category>
		<category><![CDATA[machine]]></category>
		<category><![CDATA[python]]></category>
		<category><![CDATA[redhat]]></category>
		<category><![CDATA[ulimit]]></category>
		<category><![CDATA[vm]]></category>
		<guid isPermaLink="false">https://www.albertnogues.com/?p=1067</guid>

					<description><![CDATA[<p>In todays post I am going to share an ansible playbook to configure a new VM recently launched. This playbook contains the following: Change Admin password Create linux Group Add user to several groups Create a new user with specific salted password (Check point 3 for generating the hashed salt) Find all the mounted disks &#8230; </p>
<p>The post <a href="https://www.albertnogues.com/ansible-playbook-to-configure-azure-red-hat-vms/">Ansible playbook to configure Azure Red Hat VM&#8217;s</a> appeared first on <a href="https://www.albertnogues.com">Albert Nogués</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p>In todays post I am going to share an ansible playbook to configure a new VM recently launched. This playbook contains the following:</p>



<ul class="wp-block-list"><li>Change Admin password</li><li>Create linux Group</li><li>Add user to several groups</li><li>Create a new user with specific salted password (Check point 3 for generating the hashed salt)</li><li>Find all the mounted disks in any LUN and format them (create a xfs filesystem)</li><li>Create a mountpoint (in /opt)</li><li>Mount the disk formatted in LUN 0 PART 1 in the specified mountoint in fstab, so it will be persistent across reboots</li><li>Register the OS into RHEL Satellite so yum is usable</li><li>Disable some local repo in an external /mnt drive that crashes yum in the image</li><li>Install Telnet and other packages</li><li>Modify some Ulimits</li><li>Register Machine in a domain (TO BE DONE)</li></ul>



<p>To Start, lets create first the ansible inventory file and call it inventory.yaml (Or use init format if you want). The format of the file should be something like the folowing:</p>



<pre class="wp-block-code"><code>all:
  children:
    myservers:
      hosts:
        SERVER001:
          ansible_host: 10.10.1.1
        SERVER002:
          ansible_host: 10.10.1.2
        SERVER003:
          ansible_host: 10.10.1.3
        SERVER004:
          ansible_host: 10.10.1.4
        SERVER005:
          ansible_host: 10.10.1.5
    alberttest:
      hosts:
        testalbert001:
          ansible_host: 10.10.1.6</code></pre>



<p>Then we can create our playbook file (I will comment it below). This will be another yaml file with the following content:</p>



<pre class="wp-block-code"><code>---
- hosts: myservers
  collections:
    - ansible.posix
  tasks:
  - name: Ping the Server
    ping:

  - name: Change Password of the Admin user.
    user:
      name: Admin
      # python3 -c 'import crypt; print (crypt.crypt("Passw0rd", "$1$SomeSalt$"))'
      password: $1$SomeSalt$C7s11A7tyf8OKOg0JoCYp/

  - name: Create anogues group
    group:
      name: anogues
      state: present

  - name: Create anogues user
    user:
      name: anogues
      password: $1$SomeSalt$C7s11A7tyf8OKOg0JoCYp/
      shell: /bin/bash
      groups: infaedc, wheel
      append: yes

  - name: Create mountpoint
    file: path=/opt/data state=directory

  - name: Find all Luns
    find:
      paths: /dev/disk/azure/scsi1/
      file_type: link
      recurse: No
      patterns: "lun?"
    register: files_matched

  - name: Partition Disk to Max
    shell: "parted {{ item.path }} --script mklabel gpt mkpart xfspart xfs 0% 100%"
    args:
      executable: /bin/bash
    loop: "{{ files_matched.files|flatten(levels=1) }}"

  - name: Inform OS of partition table changes
    command: partprobe

  - name: find UUID of sdX1
    shell: |
      blkid -s UUID -o value $(readlink -f /dev/disk/azure/scsi1/lun0-part1)
    register: uuid

  - name: show real uuid
    debug:
      msg: "{{ uuid.stdout }}"

  - name: Mount disk drive in fstab
    mount:
      path: /opt/data
      src: 'UUID={{uuid.stdout}}'
      fstype: xfs
      opts: defaults,nofail
      dump: 1
      passno: 2
      state: mounted

  - name: Check disk Status
    shell: df -h | grep /dev/sd
    register: df2_status

  - name: Show mounted FS
    debug:
      msg: "{{ df2_status.stdout_lines }}"

  - name: Register RHEL
    redhat_subscription:
      state: present
      username: {{ lookup('env', 'RHEL_USER') }}
      password: {{ lookup('env', 'RHEL_PASSWORD') }}
      autosubscribe: yes

  - name: Disable Media Repo
    ini_file:
      dest: /etc/yum.repos.d/media.repo
      section: "{{item}}"
      option: enabled
      value: 0
    with_items:
      - LocalRepo_BaseOS
      - LocalRepo_AppStream

  - name: Install Telnet and other packags
    yum:
      name:
        - telnet
        - curl
        - zip
        - unzip
        - tar
        - wget
        - libcurl
      state: present

  - name: Add or modify nproc hard limit for the user anogues. Set 65k value.
    pam_limits:
      domain: anogues
      limit_type: hard
      limit_item: nproc
      value: 65000
    become: yes
    become_method: sudo
    become_user: root

  - name: Add or modify nproc soft limit for the user anogues. Set 65k value.
    pam_limits:
      domain: anogues
      limit_type: soft
      limit_item: nproc
      value: 65000
    become: yes
    become_method: sudo
    become_user: root</code></pre>



<p>Let&#8217;s have a look at how it works:</p>



<p>One of the playbook steps is to register RHEL into Satellite. To avoid hardcoding the user and password in the playbooy these values will be taken from your env vars, so please export them before</p>



<p>export RHEL_USER=<br>export RHEL_PASSWORD=</p>



<p>You need to install ansible-posix first (Tested with 1.2.0.). Download it from here: https://galaxy.ansible.com/ansible/posix<br>ansible-galaxy install ansible-posix-1.2.0.tar.gz</p>



<p>To create a crypted user with a salt you can do the following<br>python3 -c &#8216;import crypt; print (crypt.crypt(&#8220;YOUR_UNHASHED_PASSWORD&#8221;, &#8220;$1$SomeSalt$&#8221;))&#8217;<br>And copy the hashed salt into the playbook area for the user you want to set the password</p>



<p>Azure Mounts the Disk Drives by Luns. By default it starts in Lun0, So i am using the softlink to /dev/disk/azure to check the disks added. This playbook will only automount the first disk. If you need more simply modify the playbook and add as many luns as you need</p>



<p># tree /dev/disk/azure<br>/dev/disk/azure<br>├── resource -&gt; ../../sdb<br>├── resource-part1 -&gt; ../../sdb1<br>├── root -&gt; ../../sda<br>├── root-part1 -&gt; ../../sda1<br>├── root-part14 -&gt; ../../sda14<br>├── root-part15 -&gt; ../../sda15<br>├── root-part2 -&gt; ../../sda2<br>└── scsi1<br>├── lun0 -&gt; ../../../sdc<br>└── lun1 -&gt; ../../../sdd</p>



<p>Here there are two disks mounted in two luns, 0 and 1. No disk has been formatted yet as we only see sdc and sdd drives with no partition. Once the first one is formatted, you will see a difference:</p>



<p>/dev/disk/azure<br>├── resource -&gt; ../../sdb<br>├── resource-part1 -&gt; ../../sdb1<br>├── root -&gt; ../../sda<br>├── root-part1 -&gt; ../../sda1<br>├── root-part14 -&gt; ../../sda14<br>├── root-part15 -&gt; ../../sda15<br>├── root-part2 -&gt; ../../sda2<br>└── scsi1<br>├── lun0 -&gt; ../../../sdc<br>├── lun0-part1 -&gt; ../../../sdc1<br>└── lun1 -&gt; ../../../sdd</p>



<p>To run it (make sure you have installed ansible 2.9 from RHEL repo):</p>



<pre class="wp-block-code"><code>ansible-playbook -i inventory.yaml -k playbook.yaml</code></pre>



<p>The playbook also formats all drives found in any luns (All disks mounted in the VM), bout only mounts the first one as we only defined one mount point.</p>



<p>If we need to modify it, we can include all the tasks that ar enecessary (read the blkid, add entry in fstab into another loop block but previously we have to create as many directories for the mountpoints as necessay and feed them in the loop block, so they are assigned concurrently.</p>



<p>Links of interest:</p>



<p><a href="https://stackoverflow.com/questions/19292899/creating-a-new-user-and-password-with-ansible">https://stackoverflow.com/questions/19292899/creating-a-new-user-and-password-with-ansible</a></p>



<p><a href="https://stackoverflow.com/questions/49424967/how-to-create-azure-vm-with-data-disk-and-then-format-it-via-ansible">https://stackoverflow.com/questions/49424967/how-to-create-azure-vm-with-data-disk-and-then-format-it-via-ansible</a></p>
<p>The post <a href="https://www.albertnogues.com/ansible-playbook-to-configure-azure-red-hat-vms/">Ansible playbook to configure Azure Red Hat VM&#8217;s</a> appeared first on <a href="https://www.albertnogues.com">Albert Nogués</a>.</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Load data from azure blob storage and run TPC-DS queries on Azure Synapse.</title>
		<link>https://www.albertnogues.com/load-data-from-azure-blob-storage-and-run-tpc-ds-queries-on-azure-synapse/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=load-data-from-azure-blob-storage-and-run-tpc-ds-queries-on-azure-synapse</link>
		
		<dc:creator><![CDATA[Albert]]></dc:creator>
		<pubDate>Wed, 03 Feb 2021 15:35:20 +0000</pubDate>
				<category><![CDATA[Azure]]></category>
		<category><![CDATA[BigData]]></category>
		<category><![CDATA[Cloud]]></category>
		<category><![CDATA[SQL]]></category>
		<guid isPermaLink="false">http://www.albertnogues.com.preview.services/?p=1023</guid>

					<description><![CDATA[<p>In this article we will see how to provision an azure synapse cluster, load some large quantity of data from azure blob storage and run a query to see the contents and check performance. I plan to write a serie of articles arround data warehousing in the cloud so check out for new articles soon. &#8230; </p>
<p>The post <a href="https://www.albertnogues.com/load-data-from-azure-blob-storage-and-run-tpc-ds-queries-on-azure-synapse/">Load data from azure blob storage and run TPC-DS queries on Azure Synapse.</a> appeared first on <a href="https://www.albertnogues.com">Albert Nogués</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p>In this article we will see how to provision an azure synapse cluster, load some large quantity of data from azure blob storage and run a query to see the contents and check performance. I plan to write a serie of articles arround data warehousing in the cloud so check out for new articles soon.</p>



<p>I’ve split the article in 3 steps that cover diverse topics:</p>



<ul class="wp-block-list"><li>Part 1. Deploying a synapse cluster.</li><li>Part 2. Load TPC-DS data.</li><li>Part 3. Run queries to verify the performance.</li></ul>



<ol class="wp-block-list"><li>We need to create a synapse cluster. We head to the azure portal and we will create a Gen2: DW100c cluster, which is the cheapest option on sale for a bit more than 1.50 USD per hour. For this exercise I didnt create a synapse workspace, i just went with the &#8220;Dedicated SQL pool&#8221; because i am only interested in the synapse db warehouse, not spark or other engines this time. Check <a href="https://docs.microsoft.com/en-us/azure/synapse-analytics/sql-data-warehouse/sql-data-warehouse-overview-what-is" target="_blank" rel="noreferrer noopener">the following</a> documentation for more help. </li></ol>



<p>After a few minutes we will have the synapse warehouse ready.</p>



<p>Once created, we can take the url and connect from Dbeaver (or any other editor, you can use the free <a href="https://docs.microsoft.com/en-us/sql/azure-data-studio/download-azure-data-studio?view=sql-server-ver15" data-type="URL" data-id="https://docs.microsoft.com/en-us/sql/azure-data-studio/download-azure-data-studio?view=sql-server-ver15" target="_blank" rel="noreferrer noopener">Azure Data Studio too!</a>) to see if all is ok:</p>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="555" height="625" src="https://www.albertnogues.com/wp-content/uploads/2021/02/Synapse1.png" alt="" class="wp-image-1052" srcset="https://www.albertnogues.com/wp-content/uploads/2021/02/Synapse1.png 555w, https://www.albertnogues.com/wp-content/uploads/2021/02/Synapse1-266x300.png 266w, https://www.albertnogues.com/wp-content/uploads/2021/02/Synapse1-53x60.png 53w" sizes="auto, (max-width: 555px) 100vw, 555px" /><figcaption>Check connectivity with the newly created synapse instance</figcaption></figure>



<p>If you have trouble, make sure you have your ip added in the whitelist of the firewall section of your synapse instance and that is open to the public:</p>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="638" height="498" src="https://www.albertnogues.com/wp-content/uploads/2021/02/Synapse2.png" alt="" class="wp-image-1053" srcset="https://www.albertnogues.com/wp-content/uploads/2021/02/Synapse2.png 638w, https://www.albertnogues.com/wp-content/uploads/2021/02/Synapse2-300x234.png 300w, https://www.albertnogues.com/wp-content/uploads/2021/02/Synapse2-77x60.png 77w" sizes="auto, (max-width: 638px) 100vw, 638px" /><figcaption>Adding an IP to our synapse instance</figcaption></figure>



<p>2. Load TPC-DS Data</p>



<p>Unfortunatelly I haven&#8217;t been able to found any TPC-DS data in azure blob storage. The table structure I am going to use, is the fivetran table structure for azure synapse found in their repo <a rel="noreferrer noopener" href="https://github.com/fivetran/benchmark/blob/master/500-PopulateAzure.sql" data-type="URL" data-id="https://github.com/fivetran/benchmark/blob/master/500-PopulateAzure.sql" target="_blank">here</a>, albeit i will do a modification stated in <a rel="noreferrer noopener" href="https://www.atscale.com/wp-content/uploads/2020/07/AtScale-Cloud-Data-Warehouse-Benchmark-Azure-Synapse-Analytics-SQL-20200813-1.pdf" data-type="URL" data-id="https://www.atscale.com/wp-content/uploads/2020/07/AtScale-Cloud-Data-Warehouse-Benchmark-Azure-Synapse-Analytics-SQL-20200813-1.pdf" target="_blank">this atscale pdf</a> as I think it makes more sense. Basically, for the dimension tables we will replicate them and create a clustered columnstore index, and for the fact table, we will use a hash distribution by the sr_item_sk column. They also suggest a columnstore index order in the fact tables but for our test i dont think it&#8217;s necessary.</p>



<p>The table structure, extracted from fivetran repo that we are going to replicate is the same set of 4 tables i did for my previous article for redshift, that you can find <a href="http://www.albertnogues.com.preview.services/load-data-from-s3-and-run-tpc-ds-queries-on-amazon-redshift/" data-type="URL" data-id="http://www.albertnogues.com.preview.services/load-data-from-s3-and-run-tpc-ds-queries-on-amazon-redshift/">here</a>. Here is the modified list:</p>



<pre class="wp-block-code"><code>create table customer_address (
    ca_address_sk             bigint,
    ca_address_id             nvarchar(16),
    ca_street_number          nvarchar(10),
    ca_street_name            nvarchar(60),
    ca_street_type            nvarchar(15),
    ca_suite_number           nvarchar(10),
    ca_city                   nvarchar(60),
    ca_county                 nvarchar(30),
    ca_state                  nvarchar(2),
    ca_zip                    nvarchar(10),
    ca_country                nvarchar(20),
    ca_gmt_offset             float,
    ca_location_type          nvarchar(20)
)
WITH
( 
  DISTRIBUTION = REPLICATE,
  CLUSTERED COLUMNSTORE INDEX
)
GO

create table customer (
    c_customer_sk             bigint,
    c_customer_id             nvarchar(16),
    c_current_cdemo_sk        bigint,
    c_current_hdemo_sk        bigint,
    c_current_addr_sk         bigint,
    c_first_shipto_date_sk    bigint,
    c_first_sales_date_sk     bigint,
    c_salutation              nvarchar(10),
    c_first_name              nvarchar(20),
    c_last_name               nvarchar(30),
    c_preferred_cust_flag     nvarchar(1),
    c_birth_day               int,
    c_birth_month             int,
    c_birth_year              int,
    c_birth_country           nvarchar(20),
    c_login                   nvarchar(13),
    c_email_address           nvarchar(50),
    c_last_review_date        nvarchar(10)
)
WITH
( 
  DISTRIBUTION = REPLICATE,
  CLUSTERED COLUMNSTORE INDEX
)
GO

create table date_dim (
    d_date_sk                 bigint,
    d_date_id                 nvarchar(16),
    d_date                    nvarchar(10),
    d_month_seq               int,
    d_week_seq                int,
    d_quarter_seq             int,
    d_year                    int,
    d_dow                     int,
    d_moy                     int,
    d_dom                     int,
    d_qoy                     int,
    d_fy_year                 int,
    d_fy_quarter_seq          int,
    d_fy_week_seq             int,
    d_day_name                nvarchar(9),
    d_quarter_name            nvarchar(6),
    d_holiday                 nvarchar(1),
    d_weekend                 nvarchar(1),
    d_following_holiday       nvarchar(1),
    d_first_dom               int,
    d_last_dom                int,
    d_same_day_ly             int,
    d_same_day_lq             int,
    d_current_day             nvarchar(1),
    d_current_week            nvarchar(1),
    d_current_month           nvarchar(1),
    d_current_quarter         nvarchar(1),
    d_current_year            nvarchar(1) 
)
WITH
( 
  DISTRIBUTION = REPLICATE,
  CLUSTERED COLUMNSTORE INDEX
)
GO

create table web_returns (
    wr_returned_date_sk       bigint,
    wr_returned_time_sk       bigint,
    wr_item_sk                bigint,
    wr_refunded_customer_sk   bigint,
    wr_refunded_cdemo_sk      bigint,
    wr_refunded_hdemo_sk      bigint,
    wr_refunded_addr_sk       bigint,
    wr_returning_customer_sk  bigint,
    wr_returning_cdemo_sk     bigint,
    wr_returning_hdemo_sk     bigint,
    wr_returning_addr_sk      bigint,
    wr_web_page_sk            bigint,
    wr_reason_sk              bigint,
    wr_order_number           bigint,
    wr_return_quantity        int,
    wr_return_amt             float,
    wr_return_tax             float,
    wr_return_amt_inc_tax     float,
    wr_fee                    float,
    wr_return_ship_cost       float,
    wr_refunded_cash          float,
    wr_reversed_charge        float,
    wr_account_credit         float,
    wr_net_loss               float
)
WITH
( 
  DISTRIBUTION = HASH(wr_item_sk),
  CLUSTERED COLUMNSTORE INDEX
)
GO</code></pre>



<pre class="wp-block-code"><code>19:33:36Started executing query at Line 2
Commands completed successfully.
19:33:36Started executing query at Line 23
Commands completed successfully.
19:33:36Started executing query at Line 50
Commands completed successfully.
19:33:36Started executing query at Line 87
Commands completed successfully.
Total execution time: 00:00:25.515</code></pre>



<p>So we are good for the table structure. Its now time to import the data. We can try to copy it from an azure blob storage where fivetran has left the data already generated. I tried with their suggested COPY INTO COMMAND but didnt work for me because the row terminator was not specified, so if you have problems use the following statements that worked for me:</p>



<pre class="wp-block-code"><code>copy into date_dim
from 'https://fivetranbenchmark.blob.core.windows.net/tpcds/tpcds_1000_dat/date_dim/'
with (file_type = 'CSV', fieldterminator = '|', ENCODING = 'UTF8', ROWTERMINATOR='0X0A');


copy into customer
from 'https://fivetranbenchmark.blob.core.windows.net/tpcds/tpcds_1000_dat/customer/'
with (file_type = 'CSV', fieldterminator = '|', ENCODING = 'UTF8', ROWTERMINATOR='0X0A');

copy into customer_address
from 'https://fivetranbenchmark.blob.core.windows.net/tpcds/tpcds_1000_dat/customer_address/'
with (file_type = 'CSV', fieldterminator = '|', ENCODING = 'UTF8', ROWTERMINATOR='0X0A');


copy into web_returns
from 'https://fivetranbenchmark.blob.core.windows.net/tpcds/tpcds_1000_dat/web_returns/'
with (file_type = 'CSV', fieldterminator = '|', ENCODING = 'UTF8', ROWTERMINATOR='0X0A');</code></pre>



<p>And after a few minutes (the fact table is a bit more than 10GB big) you will have your data loaded:</p>



<pre class="wp-block-code"><code>20:07:41Started executing query at Line 125
(73049 rows affected)
(12000000 rows affected)
(6000000 rows affected)
(71997522 rows affected)
Total execution time: 00:06:53.032</code></pre>



<p>Note that we are using the 1TB set for Azure Synapse vs the 3 TB test we used for redshift so the comparison wouln&#8217;t be fair with this dataset. You can see the difference as our fact table has approximately 1/3 of the rows than in the <a rel="noreferrer noopener" href="http://www.albertnogues.com.preview.services/load-data-from-s3-and-run-tpc-ds-queries-on-amazon-redshift/" data-type="URL" data-id="http://www.albertnogues.com.preview.services/load-data-from-s3-and-run-tpc-ds-queries-on-amazon-redshift/" target="_blank">redshift test</a>. But still with that, we have a table with almost 72 million rows. If you want to generate another bigger set of data you can do it by using the <a rel="noreferrer noopener" href="https://github.com/fivetran/benchmark/blob/master/002-GenerateData.sh" data-type="URL" data-id="https://github.com/fivetran/benchmark/blob/master/002-GenerateData.sh" target="_blank">generate_data.sh</a> provided by fivetran and load the data to your own azure blob storage container.</p>



<p>3. Start running the queries.</p>



<p>As I did for the previous test with redshift, I will use query30 to test this. The query can be found in <a rel="noreferrer noopener" href="https://github.com/fivetran/benchmark/blob/master/microsoft_sql/query30.sql" data-type="URL" data-id="https://github.com/fivetran/benchmark/blob/master/microsoft_sql/query30.sql" target="_blank">fivetran github repo</a> updated to run with synapse, but you can adapt the original query as you want.</p>



<pre class="wp-block-code"><code>WITH customer_total_return 
     AS (SELECT wr_returning_customer_sk AS ctr_customer_sk, 
                ca_state                 AS ctr_state, 
                Sum(wr_return_amt)       AS ctr_total_return 
         FROM   web_returns, 
                date_dim, 
                customer_address 
         WHERE  wr_returned_date_sk = d_date_sk 
                AND d_year = 2000 
                AND wr_returning_addr_sk = ca_address_sk 
         GROUP  BY wr_returning_customer_sk, 
                   ca_state) 
SELECT TOP 100 c_customer_id, 
               c_salutation, 
               c_first_name, 
               c_last_name, 
               c_preferred_cust_flag, 
               c_birth_day, 
               c_birth_month, 
               c_birth_year, 
               c_birth_country, 
               c_login, 
               c_email_address, 
               c_last_review_date, 
               ctr_total_return 
FROM   customer_total_return ctr1, 
       customer_address, 
       customer 
WHERE  ctr1.ctr_total_return &gt; (SELECT Avg(ctr_total_return) * 1.2 
                                FROM   customer_total_return ctr2 
                                WHERE  ctr1.ctr_state = ctr2.ctr_state) 
       AND ca_address_sk = c_current_addr_sk 
       AND ca_state = 'IN' 
       AND ctr1.ctr_customer_sk = c_customer_sk 
ORDER  BY c_customer_id, 
          c_salutation, 
          c_first_name, 
          c_last_name, 
          c_preferred_cust_flag, 
          c_birth_day, 
          c_birth_month, 
          c_birth_year, 
          c_birth_country, 
          c_login, 
          c_email_address, 
          c_last_review_date, 
          ctr_total_return</code></pre>



<p>And the result:</p>



<pre class="wp-block-code"><code>20:31:22Started executing query at Line 145
(100 rows affected)
Total execution time: 00:01:40.416</code></pre>



<p>And a pic with the 100 first rows:</p>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="269" src="https://www.albertnogues.com/wp-content/uploads/2021/02/Synapse3-1024x269-1.png" alt="" class="wp-image-1054" srcset="https://www.albertnogues.com/wp-content/uploads/2021/02/Synapse3-1024x269-1.png 1024w, https://www.albertnogues.com/wp-content/uploads/2021/02/Synapse3-1024x269-1-300x79.png 300w, https://www.albertnogues.com/wp-content/uploads/2021/02/Synapse3-1024x269-1-768x202.png 768w, https://www.albertnogues.com/wp-content/uploads/2021/02/Synapse3-1024x269-1-228x60.png 228w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>
<p>The post <a href="https://www.albertnogues.com/load-data-from-azure-blob-storage-and-run-tpc-ds-queries-on-azure-synapse/">Load data from azure blob storage and run TPC-DS queries on Azure Synapse.</a> appeared first on <a href="https://www.albertnogues.com">Albert Nogués</a>.</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Unload data from AWS Redshift to S3 in Parquet</title>
		<link>https://www.albertnogues.com/unload-data-from-aws-redshift-to-s3-in-parquet/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=unload-data-from-aws-redshift-to-s3-in-parquet</link>
		
		<dc:creator><![CDATA[Albert]]></dc:creator>
		<pubDate>Thu, 31 Dec 2020 09:59:14 +0000</pubDate>
				<category><![CDATA[AWS]]></category>
		<category><![CDATA[BigData]]></category>
		<category><![CDATA[Blog]]></category>
		<category><![CDATA[Cloud]]></category>
		<category><![CDATA[SQL]]></category>
		<category><![CDATA[aws]]></category>
		<category><![CDATA[parquet]]></category>
		<category><![CDATA[redshift]]></category>
		<category><![CDATA[s3]]></category>
		<category><![CDATA[snappy]]></category>
		<category><![CDATA[unload]]></category>
		<guid isPermaLink="false">http://192.168.1.40/?p=1007</guid>

					<description><![CDATA[<p>Following the previous redshift articles in this one I will explain how to export data from redshift to parquet in s3. This can be interesting when we want to archive (infrequently queried) data to be queried cheaper with spectrum, or to store in s3 archive, or to export to another storage solution like glacier. The &#8230; </p>
<p>The post <a href="https://www.albertnogues.com/unload-data-from-aws-redshift-to-s3-in-parquet/">Unload data from AWS Redshift to S3 in Parquet</a> appeared first on <a href="https://www.albertnogues.com">Albert Nogués</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p>Following the previous redshift articles in this one I will explain how to export data from redshift to parquet in s3. This can be interesting when we want to archive (infrequently queried) data to be queried cheaper with spectrum, or to store in s3 archive, or to export to another storage solution like glacier.</p>



<p>The first thing we need to do is to modify our redshift cluster iam role to allow write to s3. We go to our cluster in the redshift panel, we click on properties, and then we will see the link to the iam role attached to the cluster. we click on it and it will open the IAM role page.</p>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="183" src="https://www.albertnogues.com/wp-content/uploads/2020/12/RedsiftParquet1-1024x183.png" alt="" class="wp-image-1008" srcset="https://www.albertnogues.com/wp-content/uploads/2020/12/RedsiftParquet1-1024x183.png 1024w, https://www.albertnogues.com/wp-content/uploads/2020/12/RedsiftParquet1-300x54.png 300w, https://www.albertnogues.com/wp-content/uploads/2020/12/RedsiftParquet1-768x137.png 768w, https://www.albertnogues.com/wp-content/uploads/2020/12/RedsiftParquet1-1536x275.png 1536w, https://www.albertnogues.com/wp-content/uploads/2020/12/RedsiftParquet1-336x60.png 336w, https://www.albertnogues.com/wp-content/uploads/2020/12/RedsiftParquet1.png 1628w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>



<p>Then we add the policy named AmazonS3ReadOnlyAccess as shown in the following pic:</p>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="329" height="143" src="https://www.albertnogues.com/wp-content/uploads/2020/12/RedsiftParquet2.png" alt="" class="wp-image-1009" srcset="https://www.albertnogues.com/wp-content/uploads/2020/12/RedsiftParquet2.png 329w, https://www.albertnogues.com/wp-content/uploads/2020/12/RedsiftParquet2-300x130.png 300w, https://www.albertnogues.com/wp-content/uploads/2020/12/RedsiftParquet2-138x60.png 138w" sizes="auto, (max-width: 329px) 100vw, 329px" /></figure>



<p>And with this we already have all the required permissions ready. The next step now is to make sure we have an available s3 bucket. I&#8217;ve created one for the demo purposes with a folder called parquet_exports.</p>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="889" height="422" src="https://www.albertnogues.com/wp-content/uploads/2020/12/RedsiftParquet3.png" alt="" class="wp-image-1010" srcset="https://www.albertnogues.com/wp-content/uploads/2020/12/RedsiftParquet3.png 889w, https://www.albertnogues.com/wp-content/uploads/2020/12/RedsiftParquet3-300x142.png 300w, https://www.albertnogues.com/wp-content/uploads/2020/12/RedsiftParquet3-768x365.png 768w, https://www.albertnogues.com/wp-content/uploads/2020/12/RedsiftParquet3-126x60.png 126w" sizes="auto, (max-width: 889px) 100vw, 889px" /></figure>



<p>For starting the extraction, we will use the customers table we used in the previous articles. This table was loaded as well from the TPC-DS test data from s3 in a gzip file but now it sits inside our redshift node. The instruction to unload the data is called <a rel="noreferrer noopener" href="https://docs.aws.amazon.com/es_es/redshift/latest/dg/r_UNLOAD.html" data-type="URL" data-id="https://docs.aws.amazon.com/es_es/redshift/latest/dg/r_UNLOAD.html" target="_blank">UNLOAD</a> <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f642.png" alt="🙂" class="wp-smiley" style="height: 1em; max-height: 1em;" /></p>



<p>Since we want our data in parquet + snappy format, which is usually the recommended way (avro is not supported in redshift UNLOAD, only CSV and parquet), we need to express it in the unload statement.</p>



<p>Contrary to spectrum, here we can unload data to buckets in another region. So if thats your need make sure you fill the bucket region as well in the unload statement. The statement is as follows</p>



<pre class="wp-block-code"><code>UNLOAD ('<em>select-statement</em>')
TO '<em>s3://object-path/name-prefix</em>'
<em>authorization</em>
&#91; <em>option</em> &#91; ... ] ]

where <em>option</em> is
{ &#91; FORMAT &#91; AS ] ] CSV | PARQUET
| PARTITION BY ( <em>column_name</em> &#91;, ... ] ) &#91; INCLUDE ]
| MANIFEST &#91; VERBOSE ] 
| HEADER           
| DELIMITER &#91; AS ] '<em>delimiter-char</em>' 
| FIXEDWIDTH &#91; AS ] '<em>fixedwidth-spec</em>'   
| ENCRYPTED &#91; AUTO ]
| BZIP2  
| GZIP 
| ZSTD
| ADDQUOTES 
| NULL &#91; AS ] '<em>null-string</em>'
| ESCAPE
| ALLOWOVERWRITE
| PARALLEL &#91; { ON | TRUE } | { OFF | FALSE } ]
| MAXFILESIZE &#91;AS] <em>max-size</em> &#91; MB | GB ] 
| REGION &#91;AS] 'aws-region' }</code></pre>



<p>As you can see, it allows to pass a select statement instead of a table name. This way we can project the required columns and there is no need to export the whole table if we do not need it. We can also specify the max filesize. Usually with parquet 256 MB is a good split size. And make sure with Parquet not to specify any compression format as otherwise it will crash, as by default redshift already compresses it with Snappy.</p>



<p>To run the export we also need to fetch the arn of our redshift role, and incliude it just after the bucket path:</p>



<pre class="wp-block-code"><code>UNLOAD ('select * from customer')
TO 's3://albertnogues-parquet/parquet_exports/'
iam_role 'arn:aws:iam::742123541312:role/Redshift_Albertnogues.com'
FORMAT AS PARQUET
MAXFILESIZE 256 MB</code></pre>



<p>After about two minutes, the query finished sucessfully. We can go to our s3 bucket to see the parquet files there and check that the split file size is the one we requested</p>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="430" src="https://www.albertnogues.com/wp-content/uploads/2020/12/RedsiftParquet4-1024x430.png" alt="" class="wp-image-1011" srcset="https://www.albertnogues.com/wp-content/uploads/2020/12/RedsiftParquet4-1024x430.png 1024w, https://www.albertnogues.com/wp-content/uploads/2020/12/RedsiftParquet4-300x126.png 300w, https://www.albertnogues.com/wp-content/uploads/2020/12/RedsiftParquet4-768x322.png 768w, https://www.albertnogues.com/wp-content/uploads/2020/12/RedsiftParquet4-1536x644.png 1536w, https://www.albertnogues.com/wp-content/uploads/2020/12/RedsiftParquet4-143x60.png 143w, https://www.albertnogues.com/wp-content/uploads/2020/12/RedsiftParquet4.png 1664w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>



<p>We can check first the size of the table in redshift with the following query:</p>



<pre class="wp-block-code"><code>SELECT "table", tbl_rows, size size_in_MB FROM SVV_TABLE_INFO
order by 1</code></pre>



<figure class="wp-block-table"><table><tbody><tr><td><strong>Table</strong></td><td><strong>Num Rows</strong></td><td><strong>Size (MB)</strong></td></tr><tr><td>customer</td><td>30.000.000</td><td>2098</td></tr></tbody></table></figure>



<p>So its quite clear that the export looks ok as the size is similar. We can now download one fo the parquet files and inspect it with some parquet tool analyzer. I tend to use the python version of parquet-tools based on apache arrow project. You can install it with:</p>



<pre class="wp-block-code"><code>pip install parquet-tools</code></pre>



<p>And then we will inspect the file with the following:</p>



<pre class="wp-block-code"><code>parquet-tools inspect 0001_part_03.parquet</code></pre>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="627" height="620" src="https://www.albertnogues.com/wp-content/uploads/2020/12/RedsiftParquet5.png" alt="" class="wp-image-1012" srcset="https://www.albertnogues.com/wp-content/uploads/2020/12/RedsiftParquet5.png 627w, https://www.albertnogues.com/wp-content/uploads/2020/12/RedsiftParquet5-300x297.png 300w, https://www.albertnogues.com/wp-content/uploads/2020/12/RedsiftParquet5-61x60.png 61w" sizes="auto, (max-width: 627px) 100vw, 627px" /></figure>



<p>And if we scroll down a little bit we can see the total number of files on our parquet file:</p>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="531" height="614" src="https://www.albertnogues.com/wp-content/uploads/2020/12/RedsiftParquet6.png" alt="" class="wp-image-1013" srcset="https://www.albertnogues.com/wp-content/uploads/2020/12/RedsiftParquet6.png 531w, https://www.albertnogues.com/wp-content/uploads/2020/12/RedsiftParquet6-259x300.png 259w, https://www.albertnogues.com/wp-content/uploads/2020/12/RedsiftParquet6-52x60.png 52w" sizes="auto, (max-width: 531px) 100vw, 531px" /></figure>
<p>The post <a href="https://www.albertnogues.com/unload-data-from-aws-redshift-to-s3-in-parquet/">Unload data from AWS Redshift to S3 in Parquet</a> appeared first on <a href="https://www.albertnogues.com">Albert Nogués</a>.</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Use Redshift Spectrum to query infrequently used data on S3</title>
		<link>https://www.albertnogues.com/use-redshift-spectrum-to-query-infrequently-used-data-on-s3/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=use-redshift-spectrum-to-query-infrequently-used-data-on-s3</link>
		
		<dc:creator><![CDATA[Albert]]></dc:creator>
		<pubDate>Wed, 30 Dec 2020 11:14:01 +0000</pubDate>
				<category><![CDATA[AWS]]></category>
		<category><![CDATA[BigData]]></category>
		<category><![CDATA[Blog]]></category>
		<category><![CDATA[Cloud]]></category>
		<category><![CDATA[aws]]></category>
		<category><![CDATA[datawarehousing]]></category>
		<category><![CDATA[redshift]]></category>
		<category><![CDATA[spectrum]]></category>
		<guid isPermaLink="false">http://192.168.1.40/?p=994</guid>

					<description><![CDATA[<p>Redshift spectrum lets us to query data in s3 buckets using redshift. This scenario is specially interesting in large datawarehouses with data that we do not need to query often but it may be nevessary from time to time to run some of our queries. In this situation, probably we dont want the data to &#8230; </p>
<p>The post <a href="https://www.albertnogues.com/use-redshift-spectrum-to-query-infrequently-used-data-on-s3/">Use Redshift Spectrum to query infrequently used data on S3</a> appeared first on <a href="https://www.albertnogues.com">Albert Nogués</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p>Redshift spectrum lets us to query data in s3 buckets using redshift. This scenario is specially interesting in large datawarehouses with data that we do not need to query often but it may be nevessary from time to time to run some of our queries. In this situation, probably we dont want the data to be loaded into our redshift cluster as this may push us to provision larger redshift clusters that the ones we need for procession our usual queries (take into account that redshift clusters are set with specific cpu, ram and space and they can not be totally tailored to our needs, so only few combinations are available)</p>



<p>There are a few requirements however to be able to use redshift spectrum. First, the data loaded will be like an external table in other systems (RDBMS) or like hive external tables. So this means that we can forget about updating or deleting our data. We will only get read only capability, but usually thats we are looking for (think in historical data from already closed exercices, archived data and so on), where the cost of storing it in s3 even in a cool tier can ve several orders of magnitude cheaper than keeping it inside redshift.</p>



<p>There are other technical limitations tough. For example, <strong>the s3 bucket has to be in the same region as our redshift cluster</strong>, so plan in advance or move the data to another s3 bucket. There are other requirements like permissions and so on, you can read it <a rel="noreferrer noopener" href="https://docs.aws.amazon.com/redshift/latest/dg/c-using-spectrum.html" data-type="URL" data-id="https://docs.aws.amazon.com/redshift/latest/dg/c-using-spectrum.html" target="_blank">here</a>.</p>



<p>Before starting we need to attach a role to our redshift cluster that grants access to read s3 buckets. If you followed my previous article on redshift you will already have this role, otherwise check how to do it <a href="https://www.albertnogues.com/load-data-from-s3-and-run-tpc-ds-queries-on-amazon-redshift/" data-type="URL" data-id="https://www.albertnogues.com/load-data-from-s3-and-run-tpc-ds-queries-on-amazon-redshift/">here</a>.</p>



<p>If you followed my previous article, apart of the s3 read only permisison, you need to add the glue catalog permission to create a table. For this, modify the role (or if you&#8217;re creating a new one, add the following permission: <code>AWSGlueConsoleFullAccess</code></p>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="633" src="https://www.albertnogues.com/wp-content/uploads/2020/12/Spectrum0-1024x633.png" alt="" class="wp-image-997" srcset="https://www.albertnogues.com/wp-content/uploads/2020/12/Spectrum0-1024x633.png 1024w, https://www.albertnogues.com/wp-content/uploads/2020/12/Spectrum0-300x185.png 300w, https://www.albertnogues.com/wp-content/uploads/2020/12/Spectrum0-768x475.png 768w, https://www.albertnogues.com/wp-content/uploads/2020/12/Spectrum0-97x60.png 97w, https://www.albertnogues.com/wp-content/uploads/2020/12/Spectrum0.png 1031w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>



<p>For the sake of this exercise I&#8217;ve load part of the TPC-DS customer data into a s3 bucket i made public on the Paris region. Then we will create an external customer table pointing to the data in the bucket (which is in fact compressed in gz) and we will query that data from redshift.</p>



<p>Once the bucket data is loaded, we are ready to go back to our redshift cluster. I&#8217;ve used <a href="https://redshift-downloads.s3.amazonaws.com/TPC-DS/2.13/3TB/customer/customer_1_14.dat.gz" data-type="URL" data-id="https://redshift-downloads.s3.amazonaws.com/TPC-DS/2.13/3TB/customer/customer_1_14.dat.gz" target="_blank" rel="noreferrer noopener">this file</a> for the tests. We will create an external schema and an external customers table. For this we need first to copy the arn of the role we created with the s3 read access. To get that let&#8217;s go to our cluster, then click on properties and later on click in Copy Amazon Resource Name (ARN):</p>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="383" src="https://www.albertnogues.com/wp-content/uploads/2020/12/Spectrum1-1024x383.png" alt="" class="wp-image-996" srcset="https://www.albertnogues.com/wp-content/uploads/2020/12/Spectrum1-1024x383.png 1024w, https://www.albertnogues.com/wp-content/uploads/2020/12/Spectrum1-300x112.png 300w, https://www.albertnogues.com/wp-content/uploads/2020/12/Spectrum1-768x287.png 768w, https://www.albertnogues.com/wp-content/uploads/2020/12/Spectrum1-1536x574.png 1536w, https://www.albertnogues.com/wp-content/uploads/2020/12/Spectrum1-161x60.png 161w, https://www.albertnogues.com/wp-content/uploads/2020/12/Spectrum1.png 1787w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>



<p>Once we have the arn of our role, then run the folloowing in your editor. It will create the external schema in a new spectrum database (feel free to choose any other database name if you need)</p>



<pre class="wp-block-code"><code>create external schema spectrum_albertnogues 
from data catalog 
database 'spectrum' 
iam_role 'arn:aws:iam::742123541312:role/Redshift_Albertnogues.com'
create external database if not exists;</code></pre>



<p>Then we are ready to create our external table:</p>



<pre class="wp-block-code"><code>create external table spectrum_albertnogues.customer
(
  c_customer_sk int4,                 
  c_customer_id char(16),             
  c_current_cdemo_sk int4 ,   
  c_current_hdemo_sk int4 ,   
  c_current_addr_sk int4 ,    
  c_first_shipto_date_sk int4 ,                 
  c_first_sales_date_sk int4 ,
  c_salutation char(10) ,     
  c_first_name char(20) ,     
  c_last_name char(30) ,      
  c_preferred_cust_flag char(1) ,               
  c_birth_day int4 ,          
  c_birth_month int4 ,        
  c_birth_year int4 ,         
  c_birth_country varchar(20) ,                 
  c_login char(13) ,          
  c_email_address char(50) ,  
  c_last_review_date_sk int4
)
row format delimited
fields terminated by '|'
stored as textfile
location 's3://redshift-spectrum-albertnogues/customers/';</code></pre>



<p>As you can see it&#8217;s not really 100% the same customers table from the TPC-DS data. This is because external tables do not support primary keys, not null syntax and some other keywords that do not make sense in external tables. Make sure you used the right separator and selected the right format of data (compression is determined by the file extension).</p>



<p>Then, as you will see the table creation takes virtually nothing. This is because in fact, the data is not loaded, its only a shortcut to our s3 data. But to make sure it&#8217;s working we can run a few queries to our new table:</p>



<pre class="wp-block-code"><code>select count(*) from spectrum_albertnogues.customer;</code></pre>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="448" height="163" src="https://www.albertnogues.com/wp-content/uploads/2020/12/Spectrum2.png" alt="" class="wp-image-998" srcset="https://www.albertnogues.com/wp-content/uploads/2020/12/Spectrum2.png 448w, https://www.albertnogues.com/wp-content/uploads/2020/12/Spectrum2-300x109.png 300w, https://www.albertnogues.com/wp-content/uploads/2020/12/Spectrum2-165x60.png 165w" sizes="auto, (max-width: 448px) 100vw, 448px" /></figure>



<p>And to make sure data is properly loaded, we can query it as well:</p>



<pre class="wp-block-code"><code>select * from spectrum_albertnogues.customer LIMIT 5;</code></pre>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="189" src="https://www.albertnogues.com/wp-content/uploads/2020/12/Spectrum3-1024x189.png" alt="" class="wp-image-999" srcset="https://www.albertnogues.com/wp-content/uploads/2020/12/Spectrum3-1024x189.png 1024w, https://www.albertnogues.com/wp-content/uploads/2020/12/Spectrum3-300x55.png 300w, https://www.albertnogues.com/wp-content/uploads/2020/12/Spectrum3-768x142.png 768w, https://www.albertnogues.com/wp-content/uploads/2020/12/Spectrum3-324x60.png 324w, https://www.albertnogues.com/wp-content/uploads/2020/12/Spectrum3.png 1238w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>



<p>As a last example, if you run my previous blog post on redshift, we can now join our new external customers table with our web returns fact table, to see how many objects these users returned depending on their origin country, to see if there is any pattern or not.</p>



<pre class="wp-block-code"><code>select c_birth_country as customer_birth_country, sum(wr_return_quantity) as qty_returned_total
 from web_returns,
      date_dim,
     spectrum_albertnogues.customer
 where wr_returned_date_sk = d_date_sk 
   and d_year =2002
   and wr_returning_customer_sk = c_customer_sk
 group by c_birth_country
 order by 2 desc;</code></pre>



<p>Of course the query should take a bit more since the data is not inside redshift, and needs to be fetched from s3 but we can get our output quite fast and easily:</p>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="709" height="547" src="https://www.albertnogues.com/wp-content/uploads/2020/12/Spectrum4.png" alt="" class="wp-image-1000" srcset="https://www.albertnogues.com/wp-content/uploads/2020/12/Spectrum4.png 709w, https://www.albertnogues.com/wp-content/uploads/2020/12/Spectrum4-300x231.png 300w, https://www.albertnogues.com/wp-content/uploads/2020/12/Spectrum4-78x60.png 78w" sizes="auto, (max-width: 709px) 100vw, 709px" /></figure>



<p>Thats it!. For more advanced topics like performance improvements when querying from s3 and others, you can read <a rel="noreferrer noopener" href="https://docs.aws.amazon.com/redshift/latest/dg/c-spectrum-external-performance.html" data-type="URL" data-id="https://docs.aws.amazon.com/redshift/latest/dg/c-spectrum-external-performance.html" target="_blank">here</a> and <a href="https://blog.openbridge.com/10-simple-tips-that-help-you-quickly-find-success-adopting-amazon-redshift-spectrum-810db089abbe" data-type="URL" data-id="https://blog.openbridge.com/10-simple-tips-that-help-you-quickly-find-success-adopting-amazon-redshift-spectrum-810db089abbe" target="_blank" rel="noreferrer noopener">here</a>. I recommend basically partitioning, a splittable file format like parquet compressed with snappy or some other codec, small files (but not very small), add as many filers as you can to avoid retrieving unnecesary data, and if you use a columnar format like parquet, you will avoid fetching unused columns in your select statement. This is important (and can have a huge impact in costs) because now it&#8217;s the time of discussing the price of all this.</p>



<p>Bear in mind that <strong>AWS charges 5 dollars for each Terabyte of data scanned from s3 with spectrum</strong>, so make sure you do the right usage on this (only data infrequently queried) and do not overestimate these charges as they grew quickly.</p>



<p>For expert advice or project requests you can contact me <a rel="noreferrer noopener" href="https://www.albertnogues.com/contact/" data-type="URL" data-id="https://www.albertnogues.com/contact/" target="_blank">here</a>. Happy Querying!</p>
<p>The post <a href="https://www.albertnogues.com/use-redshift-spectrum-to-query-infrequently-used-data-on-s3/">Use Redshift Spectrum to query infrequently used data on S3</a> appeared first on <a href="https://www.albertnogues.com">Albert Nogués</a>.</p>
]]></content:encoded>
					
		
		
			</item>
	</channel>
</rss>
