<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:discourse="http://discourse.org/rss/modules/discourse/"><channel><title>César Meira on DevBlog</title><link>https://devblog.criticalmanufacturing.com/author/c%C3%A9sar-meira/</link><description>Recent content in César Meira on DevBlog</description><image><url>https://devblog.criticalmanufacturing.com/uploads/og.webp</url><link>https://devblog.criticalmanufacturing.com/uploads/og.webp</link></image><generator>Hugo -- gohugo.io</generator><language>en-us</language><copyright>A template by [Heksagon](https://www.heksagon.net). Implemented for [Critical Manufacturing](https://www.criticalmanufacturing.com/).</copyright><lastBuildDate>Mon, 30 Jun 2025 00:00:00 +0000</lastBuildDate><atom:link href="https://devblog.criticalmanufacturing.com/author/c%C3%A9sar-meira/index.xml" rel="self" type="application/rss+xml"/><item><title>Service Performance: Analyzing through SQL</title><link>https://devblog.criticalmanufacturing.com/blog/20250630_performance_analysis/</link><pubDate>Mon, 30 Jun 2025 00:00:00 +0000</pubDate><dc:creator>César Meira</dc:creator><guid>https://devblog.criticalmanufacturing.com/blog/20250630_performance_analysis/</guid><description>Overview and SQL scripts for diagnosing service performance issues in MES systems</description><content:encoded><![CDATA[<hr>
<p>As services grow in complexity, pinpointing performance issues becomes more challenging.
This post shows how to analyze the behavior of your service by querying the database.
We&rsquo;ll explore the SQL scripts used, interpret the results, and understand what should be reviewed for possible enhancements.</p>
<h2 id="overview-and-motivation">Overview and Motivation</h2>
<p>In a fully extensible system like ours — with numerous extension points and layers of custom logic — it&rsquo;s not uncommon for performance issues to sneak in through one of many possible code paths.
When customer-specific logic is added on top, pinpointing the exact line (or feature) responsible for slowness can quickly become a daunting task.</p>
<p>📝 While this SQL-based approach helps identify heavy dead time and performance gaps, it’s only an alternative approach instead of using observability. With observability, you gain access to a full execution timeline, including failed requests (which SQL doesn’t capture) and read operations (loads) that often go unnoticed but can hurt performance. In other words, observability provides a complete, real-time view of your system’s behavior, allowing you to identify and address issues more effectively.</p>
<h2 id="finding-problematic-services">Finding Problematic Services</h2>
<p>This is your starting point for identifying slow or potentially problematic services.</p>
<p>If you&rsquo;re looking to get a high-level overview of your system&rsquo;s performance — and quickly spot which services might be worth optimizing — this script is for you.</p>
<p>The script analyzes service execution times and highlights the ones that may require further investigation.
Naturally, some services are inherently slow due to the complexity of their operations, and cluttering your report with those isn&rsquo;t helpful.
That&rsquo;s why the script allows you to exclude specific services by adding them to an ignore list.</p>
<p>The script outputs a summary table with the following metrics:</p>
<ul>
<li>Service Name</li>
<li>Total Number of Calls</li>
<li>Minimum Execution Time (ms)</li>
<li>Maximum Execution Time (ms)</li>
<li>Average Execution Time (ms)</li>
</ul>
<p>By default, the script analyzes service calls from the last 24 hours, but you can easily adjust the timeframe to suit your needs.</p>
<p><strong>Reminder:</strong> The script is a base for your analysis feel free to adapt it to your needs.</p>
<h3 id="script">Script</h3>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-sql" data-lang="sql"><span style="display:flex;"><span><span style="color:#66d9ef">DECLARE</span> <span style="color:#f92672">@</span><span style="color:#66d9ef">From</span> DATETIME <span style="color:#f92672">=</span> DATEADD(DD, <span style="color:#f92672">-</span><span style="color:#ae81ff">1</span>, <span style="color:#66d9ef">CAST</span>(GETDATE() <span style="color:#66d9ef">as</span> date))   <span style="color:#75715e">-- ANALYZE ONLY LAST DAY
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>;<span style="color:#66d9ef">WITH</span> ServiceData <span style="color:#66d9ef">AS</span>(
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">SELECT</span>
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">-- Service Name  
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>    SH.ServiceName
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">-- Start Date/Time of the Service Call    
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>    , SH.ServiceStartTime
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">-- End Date/Time of the Service Call    
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>    , SH.ServiceEndTime
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">-- Total elapsed time of the Service Call in milliseconds
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>    , DATEDIFF(MILLISECOND, ServiceStartTime, ServiceEndTime) <span style="color:#66d9ef">AS</span> ElapsedMilliSeconds
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">FROM</span> dbo.T_ServiceHistory SH
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">WHERE</span> 
</span></span><span style="display:flex;"><span>    ServiceStartTime <span style="color:#f92672">&gt;</span> <span style="color:#f92672">@</span><span style="color:#66d9ef">FROM</span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">AND</span>
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">-- THIS IS THE IGNORE LIST
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>    <span style="color:#75715e">--  ADD ANY OTHER SERVICE THAT IS NOT RELEVANT FOR THE ANALYSIS
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>    ServiceName <span style="color:#66d9ef">NOT</span> <span style="color:#66d9ef">IN</span> (
</span></span><span style="display:flex;"><span>      <span style="color:#e6db74">&#39;ReseedIdentityColumns&#39;</span>,
</span></span><span style="display:flex;"><span>      <span style="color:#e6db74">&#39;Cmf.FullBackup.Ods 10.2.7 Installation&#39;</span>,
</span></span><span style="display:flex;"><span>      <span style="color:#e6db74">&#39;Cmf.FullBackup.Dwh 10.2.7 Installation&#39;</span>,
</span></span><span style="display:flex;"><span>      <span style="color:#e6db74">&#39;Cmf.Database.Jobs 10.2.7 Installation&#39;</span>,
</span></span><span style="display:flex;"><span>      <span style="color:#e6db74">&#39;ProcessIntegrationEntry&#39;</span>
</span></span><span style="display:flex;"><span>    )
</span></span><span style="display:flex;"><span>)
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">SELECT</span>
</span></span><span style="display:flex;"><span>  ServiceName, 
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">COUNT</span>(<span style="color:#f92672">*</span>) <span style="color:#66d9ef">AS</span> NumberOfCalls, 
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">MIN</span>(ElapsedMilliSeconds) <span style="color:#66d9ef">AS</span> MinElapsedMilliSeconds,
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">MAX</span>(ElapsedMilliSeconds) <span style="color:#66d9ef">AS</span> MaxElapsedMilliSeconds,
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">AVG</span>(ElapsedMilliSeconds) <span style="color:#66d9ef">AS</span> AvgElapsedMilliSeconds
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">FROM</span> ServiceData
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">GROUP</span> <span style="color:#66d9ef">BY</span> ServiceName
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">ORDER</span> <span style="color:#66d9ef">BY</span> AvgElapsedMilliSeconds <span style="color:#66d9ef">DESC</span>
</span></span></code></pre></div><h3 id="example">Example</h3>
<table>
<thead>
<tr>
<th style="text-align:left">Service Name</th>
<th># of Calls</th>
<th>Min Elapsed (ms)</th>
<th>Max Elapsed (ms)</th>
<th>Avg Elapsed (ms)</th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align:left">StartPrepareEquipment</td>
<td>10</td>
<td>3667</td>
<td>5214</td>
<td>4506</td>
</tr>
<tr>
<td style="text-align:left">ComplexReworkMaterial</td>
<td>64</td>
<td>31</td>
<td>78051</td>
<td>4322</td>
</tr>
<tr>
<td style="text-align:left">ComplexMoveMaterialsToNextStep</td>
<td>3</td>
<td>562</td>
<td>8286</td>
<td>3467</td>
</tr>
<tr>
<td style="text-align:left">AbortMaterialProcessing</td>
<td>38</td>
<td>13</td>
<td>30692</td>
<td>1994</td>
</tr>
</tbody>
</table>
<h2 id="finding-a-request-to-analyze">Finding a Request to Analyze</h2>
<p>Once you&rsquo;ve identified a service that deserves a closer look, the next step is to dive into specific requests — individual executions that might reveal what&rsquo;s really going on under the hood.</p>
<p>The script in this section assumes you&rsquo;ve already selected a target service (and you know the Service Name) and want to examine how it&rsquo;s behaving in real-world scenarios.</p>
<p>This script returns a summary of recent executions of the selected service, including:</p>
<ul>
<li>Service History Id</li>
<li>Service Name</li>
<li>Start Date/Time</li>
<li>End Date/Time</li>
<li>Elapsed Time (ms)</li>
</ul>
<p>This gives you a short list of candidates to dig deeper into — especially those with unusually high execution times.</p>
<p><strong>Bonus:</strong> Need to focus only on the most recent runs? Just uncomment the ServiceStartTime WHERE clause in the script to narrow down to the latest executions. Super handy when you&rsquo;re checking the effect of recent performance improvements.</p>
<h3 id="script-1">Script</h3>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-sql" data-lang="sql"><span style="display:flex;"><span><span style="color:#66d9ef">DECLARE</span> <span style="color:#f92672">@</span>CriticalServiceTime BIGINT <span style="color:#f92672">=</span> <span style="color:#ae81ff">5000</span> <span style="color:#75715e">-- CONSIDER ONLY EXECUTIONS THAT TOOK LONGER THAN
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span><span style="color:#66d9ef">DECLARE</span> <span style="color:#f92672">@</span>ServiceName NVARCHAR(<span style="color:#66d9ef">MAX</span>) <span style="color:#f92672">=</span> <span style="color:#e6db74">&#39;ComplexReworkMaterial&#39;</span>  <span style="color:#75715e">-- HERE YOU SELECT THE SERVICE YOU WANT TO CHECK
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span><span style="color:#66d9ef">SELECT</span>
</span></span><span style="display:flex;"><span>  <span style="color:#75715e">-- Service History Id of the Service Call  
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>  SH.ServiceHistoryId
</span></span><span style="display:flex;"><span>  <span style="color:#75715e">-- Service Name  
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>  , SH.ServiceName
</span></span><span style="display:flex;"><span>  <span style="color:#75715e">-- Start Date/Time of the Service Call    
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>  , SH.ServiceStartTime
</span></span><span style="display:flex;"><span>  <span style="color:#75715e">-- End Date/Time of the Service Call    
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>  , SH.ServiceEndTime
</span></span><span style="display:flex;"><span>  <span style="color:#75715e">-- Total elapsed time of the Service Call in milliseconds
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>  , DATEDIFF(MILLISECOND, ServiceStartTime, ServiceEndTime) <span style="color:#66d9ef">AS</span> ElapsedMilliSeconds
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">FROM</span> dbo.T_ServiceHistory SH
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">WHERE</span> 
</span></span><span style="display:flex;"><span>  ServiceName <span style="color:#f92672">=</span> <span style="color:#f92672">@</span>ServiceName
</span></span><span style="display:flex;"><span>  <span style="color:#75715e">-- AND ServiceStartTime &gt; &#39;2025-04-23 09:07:33.037&#39; -- UNCOMMENT IF YOU WANT TO CHECK LATEST EXECUTIONS ONLY
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span><span style="color:#66d9ef">AND</span> DATEDIFF(MILLISECOND, ServiceStartTime, ServiceEndTime) <span style="color:#f92672">&gt;</span> <span style="color:#f92672">@</span>CriticalServiceTime
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">ORDER</span> <span style="color:#66d9ef">BY</span> ElapsedMilliSeconds <span style="color:#66d9ef">DESC</span>
</span></span></code></pre></div><h3 id="example-1">Example</h3>
<table>
<thead>
<tr>
<th>Service History Id</th>
<th>Service Name</th>
<th>Start Date/Time</th>
<th>End Date/Time</th>
<th>Elapsed (ms)</th>
</tr>
</thead>
<tbody>
<tr>
<td>2504230000010152315</td>
<td>StartPrepareEquipment</td>
<td>2025-04-23 09:30:00.567</td>
<td>2025-04-23 09:30:05.781</td>
<td>5214</td>
</tr>
<tr>
<td>2504230000010129953</td>
<td>StartPrepareEquipment</td>
<td>2025-04-23 07:48:43.373</td>
<td>2025-04-23 07:48:48.303</td>
<td>4930</td>
</tr>
<tr>
<td>2504160000010112003</td>
<td>StartPrepareEquipment</td>
<td>2025-04-16 06:04:31.553</td>
<td>2025-04-16 06:04:36.276</td>
<td>4723</td>
</tr>
<tr>
<td>2504230000010146189</td>
<td>StartPrepareEquipment</td>
<td>2025-04-23 09:04:10.897</td>
<td>2025-04-23 09:04:15.554</td>
<td>4657</td>
</tr>
</tbody>
</table>
<h2 id="operation-level-performance-analysis">Operation-Level Performance Analysis</h2>
<p>In this section, we dive into the <strong>operation execution time</strong> of a specific service request. The goal?<br>
To spot which operations are taking longer than expected — and might be silently sabotaging your performance. 🕵️‍♂️</p>
<p>This script returns a detailed breakdown of all operations involved in a service execution.<br>
While it brings a lot of data, the real gold lies in identifying two key performance villains:</p>
<ul>
<li><strong>Slow operations</strong>: Operations with high elapsed times.</li>
<li><strong>Dead time</strong>: Gaps between operations where nothing (at least, nothing tracked) is happening — but time keeps ticking.</li>
</ul>
<p>But what is dead time? Great question!
As you may know, the system only logs operations that write to the database (like <code>Track-In</code>, <code>LogEvent</code>, etc.).
So not everything leaves a trace.<br>
Individual loads in collection, uncontrolled lazy loading, expensive queries, inefficient loops, and other non-persistent operations can consume serious time — without ever appearing in your logs.</p>
<p>That <strong>gap between one operation ending and the next starting</strong>?<br>
Yep, that&rsquo;s your <strong>dead time</strong> — sneaky and invisible, but very real.</p>
<p>The script returns a breakdown of operations for the selected <strong>Service History ID</strong>, including:</p>
<ul>
<li><code>Service History Id</code></li>
<li><code>Service Name</code></li>
<li><code>Operation Name</code>
<ul>
<li>The <code>&lt;Service&gt;</code> entry is added by the script to represent the root operation — the entire service scope.</li>
</ul>
</li>
<li><code>Entity Type Name</code></li>
<li><code>Operation Start Time</code></li>
<li><code>Operation End Time</code></li>
<li><code>Operation History Seq</code></li>
<li><code>Parent Operation Sequence</code> <em>(ID of the operation that triggered this one)</em></li>
<li><code>LagOperationStartTime</code> <em>(dead time: time between previous op end and current op start)</em></li>
<li><code>Operation Elapsed Milliseconds</code> <em>(includes time of any sub-operations)</em></li>
</ul>
<p>Think of this as an <strong>x-ray</strong> of your service request — a step-by-step timeline of what&rsquo;s really happening under the hood.<br>
Perfect for spotting hidden inefficiencies, mysterious lags, and operations that deserve a little tough love 💪</p>
<h3 id="script-2">Script</h3>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-sql" data-lang="sql"><span style="display:flex;"><span><span style="color:#66d9ef">DECLARE</span> <span style="color:#f92672">@</span>ServiceHistoryId BIGINT <span style="color:#f92672">=</span> <span style="color:#ae81ff">2504230000010132939</span>  <span style="color:#75715e">-- CHANGE THIS FOR THE SERVICE ID TO BE ANALYZED
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span><span style="color:#66d9ef">DECLARE</span> <span style="color:#f92672">@</span>OverMs BIGINT <span style="color:#f92672">=</span> <span style="color:#ae81ff">0</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>;<span style="color:#66d9ef">WITH</span> Ops <span style="color:#66d9ef">AS</span> (
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">SELECT</span>
</span></span><span style="display:flex;"><span>        SH.ServiceHistoryId,
</span></span><span style="display:flex;"><span>        SH.ServiceName,
</span></span><span style="display:flex;"><span>        <span style="color:#e6db74">&#39;&lt;Service&gt;&#39;</span> <span style="color:#66d9ef">AS</span> OperationName,
</span></span><span style="display:flex;"><span>        <span style="color:#e6db74">&#39;-&#39;</span> <span style="color:#66d9ef">AS</span> EntityTypeName,
</span></span><span style="display:flex;"><span>        SH.ServiceStartTime <span style="color:#66d9ef">AS</span> OperationStartTime,
</span></span><span style="display:flex;"><span>        SH.ServiceEndTime <span style="color:#66d9ef">AS</span> OperationEndTime,
</span></span><span style="display:flex;"><span>        <span style="color:#ae81ff">0</span> <span style="color:#66d9ef">AS</span> OperationHistorySeq,
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">NULL</span> <span style="color:#66d9ef">AS</span> ParentOperationSequence,
</span></span><span style="display:flex;"><span>        DATEDIFF(MILLISECOND, SH.ServiceStartTime, SH.ServiceEndTime) <span style="color:#66d9ef">AS</span> OperationElapsedMilliseconds
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">FROM</span> dbo.T_ServiceHistory SH
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">WHERE</span> SH.ServiceHistoryId <span style="color:#f92672">=</span> <span style="color:#f92672">@</span>ServiceHistoryId
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">UNION</span> <span style="color:#66d9ef">ALL</span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">SELECT</span>
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">DISTINCT</span>
</span></span><span style="display:flex;"><span>        OH.ServiceHistoryId,
</span></span><span style="display:flex;"><span>        SH.ServiceName,
</span></span><span style="display:flex;"><span>        OH.OperationName,
</span></span><span style="display:flex;"><span>        OH.EntityTypeName,
</span></span><span style="display:flex;"><span>        OH.OperationStartTime,
</span></span><span style="display:flex;"><span>        OH.OperationEndTime,
</span></span><span style="display:flex;"><span>        OH.OperationHistorySeq,
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">CASE</span>
</span></span><span style="display:flex;"><span>            <span style="color:#66d9ef">WHEN</span> OH.ParentOperationSequence <span style="color:#66d9ef">IS</span> <span style="color:#66d9ef">NULL</span> <span style="color:#66d9ef">THEN</span> <span style="color:#ae81ff">0</span>
</span></span><span style="display:flex;"><span>            <span style="color:#66d9ef">ELSE</span> OH.ParentOperationSequence
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">END</span> <span style="color:#66d9ef">AS</span> ParentOperationSequence,
</span></span><span style="display:flex;"><span>        DATEDIFF(MILLISECOND, OH.OperationStartTime, OH.OperationEndTime) <span style="color:#66d9ef">AS</span> OperationElapsedMilliseconds
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">FROM</span> dbo.T_OperationHistory OH
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">INNER</span> <span style="color:#66d9ef">JOIN</span> dbo.T_ServiceHistory SH
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">ON</span> OH.ServiceHistoryId <span style="color:#f92672">=</span> SH.ServiceHistoryId
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">WHERE</span> SH.ServiceHistoryId <span style="color:#f92672">=</span> <span style="color:#f92672">@</span>ServiceHistoryId
</span></span><span style="display:flex;"><span>),
</span></span><span style="display:flex;"><span>OpsWithRowNumber <span style="color:#66d9ef">AS</span> (
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">SELECT</span>
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">*</span>,
</span></span><span style="display:flex;"><span>        ROW_NUMBER() OVER (PARTITION <span style="color:#66d9ef">BY</span> OperationHistorySeq <span style="color:#66d9ef">ORDER</span> <span style="color:#66d9ef">BY</span> OperationStartTime) <span style="color:#66d9ef">AS</span> rn
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">FROM</span> Ops
</span></span><span style="display:flex;"><span>),
</span></span><span style="display:flex;"><span>SiblingLag <span style="color:#66d9ef">AS</span> (
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">SELECT</span>
</span></span><span style="display:flex;"><span>        O.<span style="color:#f92672">*</span>,
</span></span><span style="display:flex;"><span>        P.OperationStartTime <span style="color:#66d9ef">AS</span> ParentStartTime,
</span></span><span style="display:flex;"><span>        LAG(O.OperationEndTime) OVER (
</span></span><span style="display:flex;"><span>            PARTITION <span style="color:#66d9ef">BY</span> O.ParentOperationSequence
</span></span><span style="display:flex;"><span>            <span style="color:#66d9ef">ORDER</span> <span style="color:#66d9ef">BY</span> O.OperationStartTime, O.OperationHistorySeq
</span></span><span style="display:flex;"><span>        ) <span style="color:#66d9ef">AS</span> PrevSiblingEndTime,
</span></span><span style="display:flex;"><span>        P.rn <span style="color:#66d9ef">AS</span> ParentRowNumber
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">FROM</span> OpsWithRowNumber O
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">LEFT</span> <span style="color:#66d9ef">JOIN</span> OpsWithRowNumber P
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">ON</span> O.ParentOperationSequence <span style="color:#f92672">=</span> P.OperationHistorySeq <span style="color:#66d9ef">AND</span> P.rn <span style="color:#f92672">=</span> <span style="color:#ae81ff">1</span>
</span></span><span style="display:flex;"><span>),
</span></span><span style="display:flex;"><span>FinalData <span style="color:#66d9ef">AS</span> (
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">SELECT</span>
</span></span><span style="display:flex;"><span>        ServiceHistoryId,
</span></span><span style="display:flex;"><span>        ServiceName,
</span></span><span style="display:flex;"><span>        OperationName,
</span></span><span style="display:flex;"><span>        EntityTypeName,
</span></span><span style="display:flex;"><span>        OperationStartTime,
</span></span><span style="display:flex;"><span>        OperationEndTime,
</span></span><span style="display:flex;"><span>        OperationHistorySeq,
</span></span><span style="display:flex;"><span>        ParentOperationSequence,
</span></span><span style="display:flex;"><span>        
</span></span><span style="display:flex;"><span>        <span style="color:#75715e">-- Calculate Lag:
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>        <span style="color:#66d9ef">CASE</span>
</span></span><span style="display:flex;"><span>            <span style="color:#66d9ef">WHEN</span> ParentOperationSequence <span style="color:#66d9ef">IS</span> <span style="color:#66d9ef">NULL</span> <span style="color:#66d9ef">THEN</span> <span style="color:#66d9ef">NULL</span>
</span></span><span style="display:flex;"><span>            <span style="color:#66d9ef">WHEN</span> PrevSiblingEndTime <span style="color:#66d9ef">IS</span> <span style="color:#66d9ef">NULL</span> <span style="color:#66d9ef">THEN</span> DATEDIFF(MILLISECOND, ParentStartTime, OperationStartTime)  <span style="color:#75715e">-- First sibling
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>            <span style="color:#66d9ef">ELSE</span> DATEDIFF(MILLISECOND, PrevSiblingEndTime, OperationStartTime)  <span style="color:#75715e">-- Sibling lag
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>        <span style="color:#66d9ef">END</span> <span style="color:#66d9ef">AS</span> LagOperationStartTime,
</span></span><span style="display:flex;"><span>        OperationElapsedMilliseconds
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">FROM</span> SiblingLag
</span></span><span style="display:flex;"><span>)
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">SELECT</span> <span style="color:#f92672">*</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">FROM</span> FinalData
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">WHERE</span> 
</span></span><span style="display:flex;"><span>    OperationElapsedMilliseconds <span style="color:#f92672">&gt;</span> <span style="color:#f92672">@</span>OverMs <span style="color:#66d9ef">OR</span> LagOperationStartTime <span style="color:#f92672">&gt;</span> <span style="color:#f92672">@</span>OverMs
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">ORDER</span> <span style="color:#66d9ef">BY</span> OperationStartTime, OperationHistorySeq;
</span></span></code></pre></div><h3 id="example--deep-dive">Example + Deep Dive</h3>
<p><strong>💡 Quick note:</strong></p>
<ul>
<li>The provided data is filtered to show relevant operations.</li>
<li>Some columns have been excluded from the table to reduce clutter and focus on the most relevant performance indicators (ServiceHistoryId, ServiceName, OperationStartTime, OperationEndTime).</li>
</ul>
<table>
<thead>
<tr>
<th>OperationName</th>
<th>EntityTypeName</th>
<th>OperationHistorySeq</th>
<th>ParentOperationSequence</th>
<th>LagOperationStartTime</th>
<th>OperationElapsedMilliseconds</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>&lt;Service&gt;</code></td>
<td>-</td>
<td>0</td>
<td>NULL</td>
<td>NULL</td>
<td>19267</td>
</tr>
<tr>
<td>Rework</td>
<td>Material</td>
<td>62</td>
<td>0</td>
<td>80</td>
<td>643</td>
</tr>
<tr>
<td>ExecuteAction</td>
<td>CustomExecuteMaterialActions</td>
<td>74</td>
<td>62</td>
<td>0</td>
<td>357</td>
</tr>
<tr>
<td>Create</td>
<td>MaterialOffFlow</td>
<td>219</td>
<td>62</td>
<td>275</td>
<td>2</td>
</tr>
<tr>
<td>ExecuteAction</td>
<td>CustomExecuteMaterialActions</td>
<td>288</td>
<td>0</td>
<td>0</td>
<td>18529</td>
</tr>
<tr>
<td>ExecuteAction</td>
<td>CustomRequestNewMaterialOnMoveToReworkStep</td>
<td>311</td>
<td>288</td>
<td>0</td>
<td>18017</td>
</tr>
<tr>
<td>Sort</td>
<td>SortRuleSet</td>
<td>1343</td>
<td>311</td>
<td>570</td>
<td>7</td>
</tr>
<tr>
<td>ChangeProductionOrder</td>
<td>Material</td>
<td>17355</td>
<td>311</td>
<td>16087</td>
<td>217</td>
</tr>
<tr>
<td>EvaluateAction</td>
<td>CustomSetAttributesOnProductionOrderChange</td>
<td>17356</td>
<td>17355</td>
<td>0</td>
<td>210</td>
</tr>
<tr>
<td>ExecuteAction</td>
<td>CustomTrackObjectStateModelHandler</td>
<td>17395</td>
<td>311</td>
<td>0</td>
<td>225</td>
</tr>
<tr>
<td>SetMainStateModel</td>
<td>Material</td>
<td>17417</td>
<td>17395</td>
<td>3</td>
<td>221</td>
</tr>
<tr>
<td>ChangeProductionOrder</td>
<td>Material</td>
<td>17526</td>
<td>311</td>
<td>0</td>
<td>206</td>
</tr>
<tr>
<td>Create</td>
<td>MaterialProductionOrder</td>
<td>17563</td>
<td>17526</td>
<td>202</td>
<td>1</td>
</tr>
<tr>
<td>ChangeFlowAndStep</td>
<td>Material</td>
<td>17656</td>
<td>311</td>
<td>0</td>
<td>255</td>
</tr>
<tr>
<td>CheckAndPerformFutureActions</td>
<td>Material</td>
<td>17896</td>
<td>311</td>
<td>0</td>
<td>197</td>
</tr>
<tr>
<td>EvaluateAction</td>
<td>CustomTriggerRequestOnMaterialSelection</td>
<td>17979</td>
<td>311</td>
<td>0</td>
<td>202</td>
</tr>
<tr>
<td>ExecuteAction</td>
<td>CustomChangeMaterialProductionOrderRework</td>
<td>18228</td>
<td>288</td>
<td>0</td>
<td>241</td>
</tr>
<tr>
<td>ChangeProductionOrder</td>
<td>Material</td>
<td>18272</td>
<td>18228</td>
<td>226</td>
<td>9</td>
</tr>
<tr>
<td>EvaluateAction</td>
<td>CustomDeleteQualityCertificateOnMoveToRework</td>
<td>18503</td>
<td>288</td>
<td>220</td>
<td>0</td>
</tr>
</tbody>
</table>
<p><strong>Deep Dive</strong></p>
<p>From the example above, the following code/logic areas should be reviewed:</p>
<p><strong>CustomExecuteMaterialActions</strong> DEE Action <em>(Operation 288)</em></p>
<ul>
<li>
<p>Consuming over <strong>96% of the total execution time</strong> (~18.5 out of 19 seconds).</p>
</li>
<li>
<p>This operation clearly dominates the execution timeline. We need to dig deeper and understand where this time is being spent.</p>
<ul>
<li>💡 <em>Reminder:</em> The elapsed time includes all sub-operation times — the slowness might be hiding in nested calls.</li>
</ul>
</li>
<li>
<p>Operation 288 triggers Operation 311, which is another DEE Action: <strong>CustomRequestNewMaterialOnMoveToReworkStep</strong></p>
<ul>
<li>Inside 311, we hit a <strong>major lag spike</strong>: <code>16,087 ms</code> — nearly <strong>~84%</strong> of the total execution time!</li>
<li>We&rsquo;ve got a lead: the DEE is a prime suspect 🕵️</li>
<li>Analyze the DEE thoroughly — trace what happens from the start until the <strong>ChangeProductionOrder</strong> operation.</li>
<li>Then? Optimize it, and re-run the analysis to validate improvements. 🔁</li>
</ul>
</li>
</ul>
<p><strong>Rework</strong> Operation <em>(Operation 62)</em></p>
<ul>
<li>
<p>Responsible for around <strong>3% of the total execution time</strong> (~1 second out of 19 seconds)</p>
</li>
<li>
<p>Inside this:</p>
<ul>
<li><strong>CustomExecuteMaterialActions</strong> <em>(Operation 74)</em> takes over 50% of Rework&rsquo;s time (~0.3 seconds).
<ul>
<li>No sub-operations are recorded — this could mean inefficient data access, excessive loops, or invisible logic bottlenecks.</li>
</ul>
</li>
<li><strong>Create MaterialOffFlow</strong> <em>(Operation 219)</em> took only 2 ms — but:
<ul>
<li>There&rsquo;s a <strong>dead time</strong> of <code>275 ms</code> between op 74 and 219.</li>
<li>That&rsquo;s idle time — probably burned on non-database-writing logic like expensive loads, loops, or waits.</li>
</ul>
</li>
</ul>
</li>
</ul>
<p><strong>TL;DR</strong></p>
<ul>
<li>You&rsquo;re staring at two hot zones: <strong>Operation 288</strong> and <strong>Operation 74</strong></li>
<li>Both are DEE actions — mysterious black boxes until opened</li>
<li>One&rsquo;s taking too much time (<strong>288</strong>), the other seems to do too little (<strong>74</strong>)</li>
<li>Time to investigate the DEE logic, optimize the flows, and run the scripts again 🛠️✨</li>
</ul>
<h2 id="final-takeaways">Final Takeaways</h2>
<ul>
<li>🔍 <strong>Start with the biggest bottleneck</strong>
<ul>
<li>Focus on the slowest operation first — it might not just be slow on its own, but could be dragging other operations down by hogging resources or causing delays further down the chain.</li>
</ul>
</li>
<li>🧾 <strong>Track your improvements</strong>
<ul>
<li>Performance tuning is often a time sink. Make sure you&rsquo;re capturing before-and-after metrics so you can see what&rsquo;s working (and prove that it is). It&rsquo;s your own version of a “glow-up” 📈.</li>
</ul>
</li>
<li>📣 <strong>Share what you learn</strong>
<ul>
<li>If you stumble upon a common pitfall or a sneaky performance killer, document it! Blog it! Help your team (and your future self) avoid déjà vu slowdowns.</li>
</ul>
</li>
<li>📋 <strong>Don&rsquo;t sacrifice functionality for speed</strong>
<ul>
<li>A fast service that skips steps is just a glorified <code>return true;</code>. Always double-check that your fixes don&rsquo;t break what the system is <em>supposed</em> to be doing.</li>
</ul>
</li>
<li>🛠️ <strong>Use the scripts — they&rsquo;re your friend</strong>
<ul>
<li>Dive in, tweak, explore. If you get stuck or something doesn&rsquo;t make sense, don&rsquo;t suffer in silence — shoot me a message and let&rsquo;s untangle it together. 🧵😄</li>
</ul>
</li>
</ul>
<p><br></br></p>
<hr>
<p><br></br></p>
<h2 id="author">Author</h2>
<h3 id="hello-my-name-is-césar-meira-">Hello, my name is César Meira 👋</h3>
<p>I&rsquo;ve been working for some years at Critical Manufacturing.
You can check me at <a href="https://www.linkedin.com/in/cesarmeira/" target="_blank" rel="noopener">
<img src="https://i.stack.imgur.com/gVE0j.png" alt="Linkedin" />

 LinkedIn</a></p>
<p>
<figure class="text-center">
  <img
    src="/blogPosts/contributors/blogger_cesar_meira.jpeg"
    alt="César Meira"
    title="Senior Software Engineer"
  />
  <figcaption style="text-align: center">Senior Software Engineer</figcaption>
</figure>

</p>
]]></content:encoded></item><item><title>MES Code Performance Quick Tips</title><link>https://devblog.criticalmanufacturing.com/blog/20240722_mes_code_performance_tips/</link><pubDate>Mon, 22 Jul 2024 00:00:00 +0000</pubDate><dc:creator>César Meira</dc:creator><guid>https://devblog.criticalmanufacturing.com/blog/20240722_mes_code_performance_tips/</guid><description>Overview and analysis of the performance impact that our coding may have in the MES application</description><content:encoded><![CDATA[<p>As developers, we often focus on functionality, ensuring that our code achieves the desired outcomes. However, how this code is written can significantly influence the speed, resource consumption, and scalability of the applications we build.</p>
<p>In this post, we will explore some ways in which code quality can impact system performance.</p>
<h2 id="overview-and-motivation">Overview and Motivation</h2>
<p>Not knowing exactly how the framework that we use works may jeopardize our code performance and result in not efficient algorithms.
As we develop on top of an existing framework that manages the application communication with the database, it is important to understand how it is done, and how it may impact the performance of our code.</p>
<p>This blog post focus on the performance impact between the Host application and the Database Layer. In the future, some analysis can be performed on the performance impact between UI and Host application.</p>
<p>
<img src="/blogPosts/posts/20240722_mes_code_performance_tips/20240722_mes_system_architecture.jpg" alt="MES Architecture" />

</p>
<p>The analysis presented, in this blog post, was performed using a DEE Action in local environment (application and database in the same machine).</p>
<h2 id="loading-and-lazy-loading">Loading and Lazy Loading</h2>
<h3 id="lazy-loading">Lazy Loading</h3>
<blockquote>
<p>Lazy loading is a design pattern commonly used in computer programming to defer initialization of an object until the point at which it is explicitly needed.</p>
</blockquote>
<p>Below you can see a generic overview of the Lazy Loading pattern in the framework for the <code>Product</code> property in the <code>Material</code> class <em>(not actual code of the property)</em>.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-c#" data-lang="c#"><span style="display:flex;"><span><span style="color:#66d9ef">private</span> IProduct _Product;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">public</span> IProduct Product
</span></span><span style="display:flex;"><span>{
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">get</span>
</span></span><span style="display:flex;"><span>    {
</span></span><span style="display:flex;"><span>        Int64 propertyId = <span style="color:#66d9ef">this</span>.GetNativeValue&lt;Int64&gt;(<span style="color:#e6db74">&#34;Product&#34;</span>);
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">if</span> (_Product == <span style="color:#66d9ef">null</span>)
</span></span><span style="display:flex;"><span>        {
</span></span><span style="display:flex;"><span>            _Product = _entityFactory.Create&lt;IProduc&gt;();
</span></span><span style="display:flex;"><span>            _Product.DefinitionId = propertyId;
</span></span><span style="display:flex;"><span>        }
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">if</span> (_Product?.Id &gt; <span style="color:#ae81ff">0</span> &amp;&amp; <span style="color:#66d9ef">string</span>.IsNullOrEmpty(_Product.Name))
</span></span><span style="display:flex;"><span>            _Product.Load();
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">return</span> _Product;
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">set</span>
</span></span><span style="display:flex;"><span>    {
</span></span><span style="display:flex;"><span>        _Product = <span style="color:#66d9ef">value</span>;
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>The Lazy Loading pattern is automatically implemented in every Reference property (Product, Resource, &hellip;), even in custom entity types.</p>
<h3 id="load-method-and-the-impact-of-levelstoload">Load method and the impact of levelsToLoad</h3>
<p>All entities have the <code>Load</code> method that is equally implemented, and it has one parameter called <code>levelsToLoad</code> that may have a tremendous impact in the system performance.</p>
<p>The method is responsible for loading all information of the instance, and it may retrieve it from the database, or the application cache.</p>
<p>Consider the following representation of the Load method for an entity instance.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-c#" data-lang="c#"><span style="display:flex;"><span><span style="color:#66d9ef">public</span> <span style="color:#66d9ef">void</span> Load(<span style="color:#66d9ef">int</span> id, <span style="color:#66d9ef">int</span> levelsToLoad) {
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// Retrieves the information from the database</span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">var</span> dbReader = ReadFromDatabase( <span style="color:#66d9ef">this</span>, id );
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// foreach property in the Entity Type that is Active and is not an Attribute</span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">var</span> properties = <span style="color:#66d9ef">this</span>.EntityType.Properties;
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">foreach</span> (<span style="color:#66d9ef">var</span> property <span style="color:#66d9ef">in</span> properties) {
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">this</span>[property.Name] = database[property.Name];
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">if</span> ( property <span style="color:#66d9ef">is</span> EntityType &amp;&amp; levelsToLoad &gt; <span style="color:#ae81ff">0</span> ) <span style="color:#75715e">// Product, Resource, etc. </span>
</span></span><span style="display:flex;"><span>        {
</span></span><span style="display:flex;"><span>            <span style="color:#66d9ef">var</span> instance = <span style="color:#66d9ef">new</span>();
</span></span><span style="display:flex;"><span>            instance.Load(database[property.Name], levelsToLoad - <span style="color:#ae81ff">1</span>);
</span></span><span style="display:flex;"><span>        }
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><h3 id="analysis">Analysis</h3>
<p>Loading one Material instance and accessing the <code>Product</code> property using lazy loading, took in average <strong>5 milliseconds</strong>.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-c#" data-lang="c#"><span style="display:flex;"><span>material.Load(levelsToLoad: <span style="color:#ae81ff">0</span>);
</span></span><span style="display:flex;"><span><span style="color:#75715e">// access the property while NULL, to force the lazy loading logic</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">if</span> (material.Product != <span style="color:#66d9ef">null</span>) { }
</span></span></code></pre></div><p>Loading one Material instance, with levels to load as one, and accessing the Product property using lazy loading, took in average <strong>20 milliseconds</strong>.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-c#" data-lang="c#"><span style="display:flex;"><span>material.Load(levelsToLoad: <span style="color:#ae81ff">1</span>);
</span></span><span style="display:flex;"><span><span style="color:#75715e">// access the property while not NULL, as the references were loaded by levelsToLoad: 1</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">if</span> (material.Product != <span style="color:#66d9ef">null</span>) { }
</span></span></code></pre></div><script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<script> Chart.defaults.color = '#fff'; </script><div class="chart">
    <canvas id="fcd19df25411ed02"></canvas>
</div>
<script>
    document.addEventListener('DOMContentLoaded', () => {
        var ctx = document.getElementById('fcd19df25411ed02')
        var options = 
{
    type: 'bar',
    data: {
        labels: [
          'Lazy Loading',
          'LevelsToLoad as 1'
        ],
        datasets: [{
            label: 'ms',
            data: [5, 20]
        }]
    },
    options: {
        indexAxis: 'y',
        plugins: {
            legend: {
                display: false
            },
            title: {
                display: true,
                text: 'Loading one Material and accessing the Product property'
            }
        }
    }
};
        new Chart(ctx, options);
    });
</script>
<p>Individually, loading 500 Material instances and accessing the Product property using lazy loading, took in average <strong>2900 milliseconds</strong>.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-c#" data-lang="c#"><span style="display:flex;"><span><span style="color:#75715e">// rows are retrieved from a query execution</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">foreach</span>(DataRow dr <span style="color:#66d9ef">in</span> rows) {
</span></span><span style="display:flex;"><span>    material.Load(dr.Field&lt;<span style="color:#66d9ef">string</span>&gt;(<span style="color:#e6db74">&#34;Name&#34;</span>), levelsToLoad: <span style="color:#ae81ff">0</span>);
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// access the property while NULL, to force the lazy loading logic</span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">if</span> (material.Product != <span style="color:#66d9ef">null</span>) { }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>Individually, loading 500 Material instances, with levels to load as one, and accessing the Product property using lazy loading, took in average <strong>5000 milliseconds</strong>.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-c#" data-lang="c#"><span style="display:flex;"><span><span style="color:#75715e">// rows are retrieved from a query execution</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">foreach</span>(DataRow dr <span style="color:#66d9ef">in</span> rows) {
</span></span><span style="display:flex;"><span>    material.Load(dr.Field&lt;<span style="color:#66d9ef">string</span>&gt;(<span style="color:#e6db74">&#34;Name&#34;</span>), levelsToLoad: <span style="color:#ae81ff">1</span>);
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// access the property while not NULL, as the references were loaded by levelsToLoad: 1</span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">if</span> (material.Product != <span style="color:#66d9ef">null</span>) { }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><div class="chart">
    <canvas id="41e172d2bf87e60e"></canvas>
</div>
<script>
    document.addEventListener('DOMContentLoaded', () => {
        var ctx = document.getElementById('41e172d2bf87e60e')
        var options = 
{
    type: 'bar',
    data: {
        labels: [
          'Lazy Loading',
          'LevelsToLoad as 1'
        ],
        datasets: [{
            label: 'ms',
            data: [2900, 5000]
        }]
    },
    options: {
        indexAxis: 'y',
        plugins: {
            legend: {
                display: false
            },
            title: {
                display: true,
                text: 'Loading 500 Materials, one by one, and accessing the Product property'
            }
        }
    }
};
        new Chart(ctx, options);
    });
</script>
<h2 id="loading-single-instance-or-collection">Loading Single Instance or Collection</h2>
<p>Although this section focus on the loading operations, keep in mind that this can be applied in any operation (Save, Track-In, etc.).</p>
<p>Whenever a list of instances is important for your functionality, always keep in mind that performing actions in a collection is always better than performing them individually, to reduce the number of communications with the database.</p>
<p>The framework supports collection operations for almost everything, take advantage of them.</p>
<h3 id="analysis-1">Analysis</h3>
<p>Individually, loading 500 Material instances to have a loaded collection of materials, took in average <strong>1400 milliseconds</strong>.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-c#" data-lang="c#"><span style="display:flex;"><span><span style="color:#75715e">// rows are retrieved from a query execution</span>
</span></span><span style="display:flex;"><span>IMaterialCollection materials = _entityFactory.CreateCollection&lt;IMaterialCollection&gt;();
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">foreach</span>(DataRow dr <span style="color:#66d9ef">in</span> rows) {
</span></span><span style="display:flex;"><span>    IMaterial material = _entityFactory.Create&lt;IMaterial&gt;();
</span></span><span style="display:flex;"><span>    material.Name = dr.Field&lt;<span style="color:#66d9ef">string</span>&gt;(<span style="color:#e6db74">&#34;Name&#34;</span>); 
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// Load is performed individually</span>
</span></span><span style="display:flex;"><span>    material.Load();
</span></span><span style="display:flex;"><span>    materials.Add(material);
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>Collectively, loading 500 Material instances to have a loaded collection of materials, took in average <strong>90 milliseconds</strong>.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-c#" data-lang="c#"><span style="display:flex;"><span><span style="color:#75715e">// rows are retrieved from a query execution</span>
</span></span><span style="display:flex;"><span>IMaterialCollection materials = _entityFactory.CreateCollection&lt;IMaterialCollection&gt;();
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">foreach</span>(DataRow dr <span style="color:#66d9ef">in</span> rows) {
</span></span><span style="display:flex;"><span>    IMaterial material = _entityFactory.Create&lt;IMaterial&gt;();
</span></span><span style="display:flex;"><span>    material.Name = dr.Field&lt;<span style="color:#66d9ef">string</span>&gt;(<span style="color:#e6db74">&#34;Name&#34;</span>); 
</span></span><span style="display:flex;"><span>    materials.Add(material);
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span><span style="color:#75715e">// Load is performed in collection using IMaterialCollection</span>
</span></span><span style="display:flex;"><span>materials.Load();
</span></span></code></pre></div><div class="chart">
    <canvas id="794c842321ad2b59"></canvas>
</div>
<script>
    document.addEventListener('DOMContentLoaded', () => {
        var ctx = document.getElementById('794c842321ad2b59')
        var options = 
{
    type: 'bar',
    data: {
        labels: [
          'Loading in collection',
          'Loading one by one'
        ],
        datasets: [{
            label: 'ms',
            data: [90, 1400]
        }]
    },
    options: {
        indexAxis: 'y',
        plugins: {
            legend: {
                display: false
            },
            title: {
                display: true,
                text: 'Loading 500 Materials'
            }
        }
    }
};
        new Chart(ctx, options);
    });
</script>
<h2 id="using-native-value-and-lazy-loading-for-comparisons">Using Native Value and Lazy Loading for comparisons</h2>
<p>Several times we want to check if some conditions are met, in order to apply our custom logic.</p>
<p>In this section, I use a collection of 500 materials (previously loaded with levelsToLoad as zero), and want to validate which ones are of Product <code>MyProduct</code>.</p>
<p>Using the Lazy Loading approach, and accessing the name of the instance, the check takes in average <strong>1600ms</strong>.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-c#" data-lang="c#"><span style="display:flex;"><span><span style="color:#66d9ef">foreach</span> (IMaterial material <span style="color:#66d9ef">in</span> materials) {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">if</span> (material.Product.Name == <span style="color:#e6db74">&#34;MyProduct&#34;</span>) {
</span></span><span style="display:flex;"><span>        <span style="color:#75715e">// code goes here</span>
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>Using the NativeValue approach, and checking using the Product DefinitionId <em>(because it is a versionable object, otherwise it would be the Id)</em>, the check takes in average <strong>5ms</strong> <em>(basically the time to load the Product once)</em>.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-c#" data-lang="c#"><span style="display:flex;"><span>IProduct myProduct = _entityFactory.Create&lt;IProduct&gt;();
</span></span><span style="display:flex;"><span>myProduct.Load(<span style="color:#e6db74">&#34;MyProduct&#34;</span>);
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">foreach</span> (IMaterial material <span style="color:#66d9ef">in</span> materials) {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">if</span> (material.GetNativeValue&lt;<span style="color:#66d9ef">long</span>&gt;(<span style="color:#e6db74">&#34;Product&#34;</span>) == myProduct.DefinitionId) {
</span></span><span style="display:flex;"><span>        <span style="color:#75715e">// code goes here</span>
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>If the NativeValue approach is not possible, and is not possible or effective to compare with Ids, it is also viable to have a local cache to reduce the call stack complexity.
This approach takes around <strong>50 milliseconds</strong> (where the number of different products is 5 or 6).</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-c#" data-lang="c#"><span style="display:flex;"><span>Dictionary&lt;<span style="color:#66d9ef">long</span>, IProduct&gt; localProductCache = <span style="color:#66d9ef">new</span>(); <span style="color:#75715e">// holds a pointer for the IProduct reference for each different Id found</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">foreach</span> (IMaterial material <span style="color:#66d9ef">in</span> materials) {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">long</span> id = material.GetNativeValue&lt;<span style="color:#66d9ef">long</span>&gt;(<span style="color:#e6db74">&#34;Product&#34;</span>);
</span></span><span style="display:flex;"><span>    localProductCache.TryGetValue(id, <span style="color:#66d9ef">out</span> IProduct materialProduct);
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">if</span> (materialProduct == <span style="color:#66d9ef">null</span>) {
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">value</span> = material.Product;
</span></span><span style="display:flex;"><span>        localProductCache[id] = <span style="color:#66d9ef">value</span>;
</span></span><span style="display:flex;"><span>    } <span style="color:#66d9ef">else</span> {
</span></span><span style="display:flex;"><span>        material.Product = materialProduct;
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">if</span> (material.Product.Type == <span style="color:#e6db74">&#34;myProductType&#34;</span>) {
</span></span><span style="display:flex;"><span>        <span style="color:#75715e">// code goes here</span>
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><div class="chart">
    <canvas id="56d89537b979f4b7"></canvas>
</div>
<script>
    document.addEventListener('DOMContentLoaded', () => {
        var ctx = document.getElementById('56d89537b979f4b7')
        var options = 
{
    type: 'bar',
    data: {
        labels: [
          'Native Value approach',
          'Local cache approach',
          'Lazy Loading approach'
        ],
        datasets: [{
            label: 'ms',
            data: [5, 50, 1600]
        }]
    },
    options: {
        indexAxis: 'y',
        plugins: {
            legend: {
                display: false
            },
            title: {
                display: true,
                text: 'Reference comparison methods'
            }
        }
    }
};
        new Chart(ctx, options);
    });
</script>
<h2 id="final-takeaway">Final Takeaway</h2>
<ul>
<li><strong>Collection is always better</strong> over instance operations</li>
<li>Try to reduce and group database operations as much as possible</li>
<li>Casually <strong>perform some performance checks</strong> on services where your customizations are running
<ul>
<li><em>You can use the MES System Performance report for that, in your integration environment</em></li>
</ul>
</li>
<li>Keep in mind that <strong>everything stacks up</strong>, your code normally runs on top of the framework code that may already be performance heavy</li>
</ul>
<p><br></br></p>
<hr>
<p><br></br></p>
<h2 id="author">Author</h2>
<h3 id="hello-my-name-is-césar-meira-">Hello, my name is César Meira 👋</h3>
<p>I&rsquo;ve been working for some years at Critical Manufacturing. I am working in Deployment Services for M1 Team.
You can check me at <a href="https://www.linkedin.com/in/cesarmeira/" target="_blank" rel="noopener">LinkedIn</a></p>
<p>
<figure class="text-center">
  <img
    src="/blogPosts/contributors/blogger_cesar_meira.jpeg"
    alt="César Meira"
    title="M1 Tech Architect"
  />
  <figcaption style="text-align: center">M1 Tech Architect</figcaption>
</figure>

</p>
]]></content:encoded></item><item><title>Taking advantage of Orchestration Extra Parameters</title><link>https://devblog.criticalmanufacturing.com/blog/20240626_ui_taking_advantage_of_extra_parameters/</link><pubDate>Wed, 26 Jun 2024 00:00:00 +0000</pubDate><dc:creator>César Meira</dc:creator><guid>https://devblog.criticalmanufacturing.com/blog/20240626_ui_taking_advantage_of_extra_parameters/</guid><description>Small demo showcase of using Orchestration Extra Parameters in UI Pages</description><content:encoded><![CDATA[<p>We are going to do a walkthrough on how to take advantage of the Orchestration Extra Parameters, to avoid creating customized orchestrations to perform out-of-the-box actions.
This is not focused on any specific process, but rather it focuses on showing how to build the blocks.</p>
<h2 id="overview-and-motivation">Overview and Motivation</h2>
<p>Some out-of-the-box orchestrations require complex objects and structures that are not easy to be built in Low Code implementations, e.g. UI Pages, IoT workflows.
Which forced us to build customized orchestrations that receive simple inputs, and invoked OOTB orchestrations.</p>
<p>In recent MES versions, all MES Orchestrations have a new property called <code>ExtraParameters</code> of type <code>Dictionary&lt;string, object&gt;</code> this allows the projects to re-use the OOTB Orchestrations, and have custom logic based on those extra parameters by having before (Pre) or after (Post) DEE&rsquo;s on the MES Orchestration.</p>
<h2 id="examples">Examples</h2>
<h3 id="ui-page-example">UI Page example</h3>
<p>
<img src="/blogPosts/posts/20240626_ui_taking_advantage_of_extra_parameters/20240626_ui_taking_advantage_of_extra_parameters_uipage_example.png" alt="UI Page example" />

</p>
<p>In this example, the UI Page Step provides to the user a form to input some information as <code>Name</code>, <code>Description</code>, <code>Type</code> and provides that information as <code>ExtraParameters</code> to the service <em>(in this case CreateFacility)</em>.
A DEE Action can be created to be triggered on the <code>Pre</code> action group of the service, that would be responsible for reading the <code>ExtraParameters</code> and defining the required properties of the <code>Input</code> of the service <em>(in this case, the Facility instance)</em>.</p>
<h4 id="building-the-extraparameters">Building the ExtraParameters</h4>
<p>By using the converter <code>setMapValue</code> it is possible to set any <code>key</code> into a <code>Dictionary</code>, this converter can be used multiple times to the same <code>Dictionary</code>.
The converter requires a parameter, that must be defined as a property in the page.</p>
<h3 id="iot-workflow">IoT Workflow</h3>
<p>
<img src="/blogPosts/posts/20240626_ui_taking_advantage_of_extra_parameters/20240626_ui_taking_advantage_of_extra_parameters_iotworkflow_example.png" alt="IoT Workflow example" />

</p>
<p>In this example, an IoT command is expected to perform a Track-In of the Material, but from the command response it is not possible to identify the MES Material, as only the Batch is provided.
So, it is possible to provide the Batch as <code>ExtraParameters</code>, and retrieve the Material within a DEE in the <code>Pre</code> of the <code>TrackIn</code> service.</p>
<h4 id="building-the-extraparameters-1">Building the ExtraParameters</h4>
<p>In this example, the <code>Code</code> task is used to build a <code>Dictionary</code> with the required information to be provided to the <code>ExtraParameters</code> service.</p>
<p><br></br></p>
<hr>
<p><br></br></p>
<h2 id="author">Author</h2>
<h3 id="hello-my-name-is-césar-meira-">Hello, my name is César Meira 👋</h3>
<p>I&rsquo;ve been working for some years at Critical Manufacturing. I am working in Deployment Services for M1 Team.
You can check me at <a href="https://www.linkedin.com/in/cesarmeira/" target="_blank" rel="noopener">LinkedIn</a></p>
<p>
<figure class="text-center">
  <img
    src="/blogPosts/contributors/blogger_cesar_meira.jpeg"
    alt="César Meira"
    title="M1 Tech Architect"
  />
  <figcaption style="text-align: center">M1 Tech Architect</figcaption>
</figure>

</p>
]]></content:encoded></item></channel></rss>