<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Mukund Raghav Sharma (Moko)</title>
    <description>The latest articles on DEV Community by Mukund Raghav Sharma (Moko) (@mrsharm).</description>
    <link>https://dev.to/mrsharm</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F740260%2F6d887282-312a-45ca-94f6-68c6a6d4ca13.png</url>
      <title>DEV Community: Mukund Raghav Sharma (Moko)</title>
      <link>https://dev.to/mrsharm</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/mrsharm"/>
    <language>en</language>
    <item>
      <title>My Capstone Project: Deep Learning to Detect Bugs in Code using Graph Based Neural Networks</title>
      <dc:creator>Mukund Raghav Sharma (Moko)</dc:creator>
      <pubDate>Sun, 16 Jan 2022 23:01:05 +0000</pubDate>
      <link>https://dev.to/mrsharm/my-capstone-project-deep-learning-to-detect-bugs-in-code-using-graph-based-neural-networks-1jam</link>
      <guid>https://dev.to/mrsharm/my-capstone-project-deep-learning-to-detect-bugs-in-code-using-graph-based-neural-networks-1jam</guid>
      <description>&lt;p&gt;Over this long weekend, I decided to revisit details of my capstone project for my Master's in Data Science (graduated May 2020) using Deep Learning to detect bugs in code. The paper can be found &lt;a href="https://drive.google.com/file/d/1x5G8eKDVW8MTHLEE585I_UTOC4idNmoO/view?usp=sharing"&gt;here&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;This paper involved conducting a comparative study of the efficacy (based on test accuracy) of Gated Graph Neural Networks (GGNNs) vs. Relational Graph Convolutional Networks (RGCNs) on a task to automatically detect the misuse of a variable for the top 25 trending C# repositories on Github.&lt;/p&gt;

&lt;p&gt;The results showed that RGCNs outperformed GGNNs for all cases (did a considerable amount of randomized hyperparameter tuning but wasn't fruitful to shake up the results), albeit, within &amp;lt; 5%.&lt;/p&gt;

&lt;p&gt;My work was based on multiple papers by Microsoft Research (particularly &lt;a href="https://lnkd.in/gd5kTEEv"&gt;https://lnkd.in/gd5kTEEv&lt;/a&gt;) and used Tensorflow to conduct the analysis. In a nutshell, the training data obtained was a modified version of the Abstract Syntax Tree generated by the Roslyn compiler.&lt;/p&gt;

&lt;p&gt;Some lessons I picked up from this experience are:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Sticking with a white paper even if you don't understand any of it in the beginning.&lt;/li&gt;
&lt;li&gt;Digesting material through different media is a good to way to switch it up: I heavily relied on YouTube videos of Deep Learning conferences (&lt;a href="https://lnkd.in/gzcTRGhG"&gt;https://lnkd.in/gzcTRGhG&lt;/a&gt;) to get a more lecture based approach to bolster my learning.&lt;/li&gt;
&lt;li&gt;Treating data as a first class citizen. If possible, using version control / backing up data is a definitely a lesson I learnt through this. Clobbering my old weights was something I did more times than I like to admit.&lt;/li&gt;
&lt;li&gt;I am a big advocate of self describing code, however, since this was such a new space for me with a steep learning curve, commenting as much as I could made a significant difference.&lt;/li&gt;
&lt;li&gt;Testing on small byte sized chunks saved me a considerable amount of time down the road: prototypical testing saved me countless hours because of the countless runtime errors that I could have faced if I hadn't done an E2E run.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Any feedback would be greatly appreciated! Happy to answer any questions.&lt;/p&gt;

</description>
      <category>deeplearning</category>
      <category>python</category>
      <category>dotnet</category>
    </item>
    <item>
      <title>Goal Achieved: 45/45 Books Read This Year</title>
      <dc:creator>Mukund Raghav Sharma (Moko)</dc:creator>
      <pubDate>Sun, 26 Dec 2021 06:46:20 +0000</pubDate>
      <link>https://dev.to/mrsharm/goal-achieved-4545-books-read-this-year-4cf5</link>
      <guid>https://dev.to/mrsharm/goal-achieved-4545-books-read-this-year-4cf5</guid>
      <description>&lt;p&gt;I have reached my goal I set for myself by reading 45 books! &lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--xw7kTttM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7ler4v7595bdi9hpngmn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--xw7kTttM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7ler4v7595bdi9hpngmn.png" alt="Image description" width="550" height="413"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Notes
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;A tracker such as the below helped me considerably. The code that generated that chart can be found &lt;a href="https://github.com/MokoSan/BookReviews_2021/blob/main/Progress%20Tracker.ipynb"&gt;here&lt;/a&gt;. &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Audiobooks help scale - bumping up the playback rate has been helpful.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Don't feel bad if you don't want to continue reading a book. &lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Top 5:
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Atomic Habits - James Clear&lt;/li&gt;
&lt;li&gt;Mindset - Carol Dweck&lt;/li&gt;
&lt;li&gt;Stillness is the Key - Ryan Holiday&lt;/li&gt;
&lt;li&gt;The War of Art - Steven Pressfield&lt;/li&gt;
&lt;li&gt;Man's Search For Meaning - Viktor Frankl &lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  All the Books
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Autobiography of a Yogi - Parmahansa Yogananda&lt;/li&gt;
&lt;li&gt;The Subtle Art of Not Giving A F_ck - Mark Mason&lt;/li&gt;
&lt;li&gt;Unfu_k Yourself - Gary John Bishop&lt;/li&gt;
&lt;li&gt;The Art of War - Sun Tzu&lt;/li&gt;
&lt;li&gt;The Case Against Sugar - Gary Taubes&lt;/li&gt;
&lt;li&gt;Mindset: The New Psychology of Success - Carol Dweck&lt;/li&gt;
&lt;li&gt;Supermind - Norman E. Rosenthal&lt;/li&gt;
&lt;li&gt;A Guide To The Good Life - William B. Irvine&lt;/li&gt;
&lt;li&gt;Stillness Is The Key - Ryan Holiday&lt;/li&gt;
&lt;li&gt;Good To Great - Jim Collins&lt;/li&gt;
&lt;li&gt;Zero To One - Peter Thiel&lt;/li&gt;
&lt;li&gt;Deep Work - Cal Newport&lt;/li&gt;
&lt;li&gt;The 4 Disciplines of Execution - Franklin Covey&lt;/li&gt;
&lt;li&gt;Irresistible - Adam Adler&lt;/li&gt;
&lt;li&gt;Man's Search For Meaning - Viktor Frankl&lt;/li&gt;
&lt;li&gt;The Science Of Getting Rich - Wallace D. Wattles&lt;/li&gt;
&lt;li&gt;The Buddha In Your Mirror - Greg Martin et. al&lt;/li&gt;
&lt;li&gt;Eat Fat, Get Thin - Mark Hyman&lt;/li&gt;
&lt;li&gt;Steal Like An Artist - Austin Kleon&lt;/li&gt;
&lt;li&gt;7 Deadly Innocent Frauds of Economic Policy - Warren Mosler&lt;/li&gt;
&lt;li&gt;The Greatness of Saturn - Robert Svoboda&lt;/li&gt;
&lt;li&gt;Breath - James Nestor&lt;/li&gt;
&lt;li&gt;Masters Of Doom - David Kushner&lt;/li&gt;
&lt;li&gt;Caffeine - Michael Pollan&lt;/li&gt;
&lt;li&gt;How To Talk To Anyone, Anytime Anywhere - Larry King&lt;/li&gt;
&lt;li&gt;Show Your Work - Austin Kleon&lt;/li&gt;
&lt;li&gt;Brain Maker - David Perlmutter&lt;/li&gt;
&lt;li&gt;Three Questions - Leo Tolstoy&lt;/li&gt;
&lt;li&gt;The Beggar - Anton Chekhov&lt;/li&gt;
&lt;li&gt;The War Of Art - Steven Pressfield&lt;/li&gt;
&lt;li&gt;How To Win Friends And Influence People - Dale Carnegie&lt;/li&gt;
&lt;li&gt;The Bet - Anton Chekhov&lt;/li&gt;
&lt;li&gt;The Kybalion - The Three Initiates&lt;/li&gt;
&lt;li&gt;Atomic Habits - James Clear&lt;/li&gt;
&lt;li&gt;Your Invisible Power - Geneviève Behrend&lt;/li&gt;
&lt;li&gt;The Nameless City - HP Lovecraft&lt;/li&gt;
&lt;li&gt;The Law of Success - Parmahansa Yogananda&lt;/li&gt;
&lt;li&gt;The Feeling Is The Secret - Neville Goddard&lt;/li&gt;
&lt;li&gt;Invent And Wander - Jeff Bezos&lt;/li&gt;
&lt;li&gt;The Secret of Dreams - Yacki Raizizun&lt;/li&gt;
&lt;li&gt;The Power of Sexual Transmutation - Mitch Horowitz&lt;/li&gt;
&lt;li&gt;The Heart of Business - Hubert Joly&lt;/li&gt;
&lt;li&gt;August Heat - W. F. Harvey&lt;/li&gt;
&lt;li&gt;The Egyptian Book of the Dead - Ani Osiris&lt;/li&gt;
&lt;li&gt;The Miracle Club - Mitch Horowitz&lt;/li&gt;
&lt;/ol&gt;

</description>
      <category>books</category>
    </item>
    <item>
      <title>Perf Avore: A Rule Based CrossPlatform Performance Minded Monitoring and Analysis Tool </title>
      <dc:creator>Mukund Raghav Sharma (Moko)</dc:creator>
      <pubDate>Mon, 20 Dec 2021 17:54:16 +0000</pubDate>
      <link>https://dev.to/mrsharm/perf-avore-a-rule-based-crossplatform-performance-based-monitoring-and-analysis-tool-2bdl</link>
      <guid>https://dev.to/mrsharm/perf-avore-a-rule-based-crossplatform-performance-based-monitoring-and-analysis-tool-2bdl</guid>
      <description>&lt;p&gt;For my 2021 F# Advent Submission (5 years of submissions!!!!), I developed a Performance Based Monitoring and Analysis Tool called "&lt;em&gt;Perf-Avore&lt;/em&gt;" that applies user specified &lt;strong&gt;Rules&lt;/strong&gt; that consists of &lt;strong&gt;Conditions&lt;/strong&gt; to match based on Trace Events from either an .ETL trace or a real time session and if the conditions are met, &lt;strong&gt;Actions&lt;/strong&gt; specified in the rule are invoked. The types of &lt;strong&gt;Conditions&lt;/strong&gt; could include to check if the trace event property is an anomaly i.e. a deviant point based on an anomaly detection algorithm or is simply above a threshold value specified in the rule. Similarly, types of &lt;strong&gt;Actions&lt;/strong&gt; could be specified that lead to different outputs such as printing out the callstacks, charting the data point or just simply alerting the user that a condition is met.  &lt;/p&gt;

&lt;p&gt;The &lt;strong&gt;purpose&lt;/strong&gt; of Perf Avore is to provide users an easy and configurable way to detect and diagnose performance issues effectively by specifying details that are pertinent to performance issues in the rule itself. A use case, for example, is detecting spikes in memory allocations that can put unwanted pressure on the Garbage Collector and inevitably slow down the process. By specifying a rule that tracks &lt;code&gt;AllocationAmount&lt;/code&gt; on the &lt;code&gt;GC/AllocationTick&lt;/code&gt; event if it goes above a specified amount and then printing out the callstack for it can shed light on the impetus behind the increased pressure.&lt;/p&gt;

&lt;h2&gt;
  
  
  High Level Overview
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--L5HAY0Ot--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/afzw8q56l3lo0fi8gnes.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--L5HAY0Ot--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/afzw8q56l3lo0fi8gnes.png" alt="Image description" width="880" height="317"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Users provide rules.

&lt;ol&gt;
&lt;li&gt;Rules consist of conditions and actions.&lt;/li&gt;
&lt;li&gt;Conditions Include: 

&lt;ol&gt;
&lt;li&gt;The Name of the Trace Event and the property they'd like to track. &lt;/li&gt;
&lt;li&gt;The condition or case for which they'd like to act on.&lt;/li&gt;
&lt;/ol&gt;


&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;Trace Events are proffered to the rules engine to apply the rules to.&lt;/li&gt;
&lt;li&gt;Based on either a given trace or by real time monitoring, conditions are checked for and actions are invoked based on a stream of trace events.&lt;/li&gt;
&lt;li&gt;Examples of Rules:

&lt;ol&gt;
&lt;li&gt;&lt;code&gt;GC/AllocationTick.AllocationAmount &amp;gt; 200000 : Print Alert&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ThreadPoolWorkerThreadAdjustment/Stats.Throughput &amp;lt; 4 : Print CallStack&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;GC/HeapStats.GenerationSize0 isAnomaly DetectIIDSpike : Print Chart&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;


&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The code is available &lt;a href="https://github.com/MokoSan/FSharpAdvent_2021/tree/main/src/PerfAvore/PerfAvore.Console"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;To &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Directly jump into the details without reading about the experience developing in FSharp and the motivation, click &lt;a href="https://nbviewer.org/github/MokoSan/PerfAvore/blob/main/AdventSubmission.ipynb#Plan"&gt;here&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Start learning how to run Perf Avore click &lt;a href="https://nbviewer.org/github/MokoSan/PerfAvore/blob/main/AdventSubmission.ipynb#How-To-Run-Perf-Avore"&gt;here&lt;/a&gt;. &lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Experience Developing in FSharp
&lt;/h2&gt;

&lt;p&gt;F#, once again, didn't fail to deliver an incredible development experience! &lt;br&gt;
Despite not developing in F# for an extended period of time (much to my regret - I kicked myself about this during last year's &lt;a href="https://bit.ly/3hhhRjq"&gt;submission&lt;/a&gt; as well), I was able to let the muscle memory from my previous projects kick in and reached a productive state surprisingly quickly; I'd like to underscore that this is more of a testament to the ease of usage of the language speaking volumes about the user-friendly nature of the language itself (and not necessarily my some-what-sophomoric acumen). &lt;/p&gt;

&lt;p&gt;Granted, I didn't make use of all the bells and whistles the language had to offer, what I did make use of was incredibly easy to get stuff done with. &lt;br&gt;
The particular aspects of the language that made it easy to develop a Domain Specific Language, a parser for that domain specific language and dynamic application of the actions are Pattern Matching and Immutable Functional Data Structures such as Records and Discriminated Unions that make expressing the domain succinctly and lucidly not only for the developer but also the reader.&lt;/p&gt;

&lt;p&gt;An image that typifies the incredibly accessible nature of F# is the following one filched from a presentation by &lt;a href="https://twitter.com/dsymetweets"&gt;Don Syme&lt;/a&gt; and &lt;a href="https://twitter.com/KathleenDollard"&gt;Kathleen Dollard&lt;/a&gt; during this year's .NET Conf in November:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--7j8FcQtU--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vojjsd65jpjmf13gw0v3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--7j8FcQtU--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vojjsd65jpjmf13gw0v3.png" alt="Image description" width="608" height="430"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Inspiration For the Project
&lt;/h2&gt;

&lt;p&gt;Perf Avore was heavily inspired by &lt;a href="https://twitter.com/maoni0"&gt;maoni0's&lt;/a&gt; &lt;a href="https://github.com/Maoni0/realmon"&gt;realmon&lt;/a&gt;, a monitoring tool that tells you when GCs happen in a process and some characteristics about these GCs. My contributions and associated interactions for realmon definitely were incredibly instrumental in coming up with this idea and it's implementation.&lt;/p&gt;

&lt;p&gt;Additionally, as a Perf Engineer, I find that there are times where I need to arduously load traces in Perf View, resolve symbols and wait until all the windows open up to do basic things such as look up a single call stack for a single event or look up the payload value of a single event. By devising a simpler solution, I wish to reduce my perf investigation time as I build on this project.&lt;/p&gt;
&lt;h2&gt;
  
  
  5 Years Going Strong!
&lt;/h2&gt;

&lt;p&gt;It has been 5 years of submissions to the FSharp Advent event and it has been an awesome experience. Here are links to my previous posts:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;a href="https://bit.ly/3hhhRjq"&gt;2020: Bayesian Inference in F#&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://t.co/KqE8kfaZQ7"&gt;2019: Building A Simple Recommendation System in F#&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://t.co/fdssLnvzLX"&gt;2018: An Introduction to Probabilistic Programming in F#&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;2017: The Lord of The Rings: An F# Approach

&lt;ol&gt;
&lt;li&gt;&lt;a href="https://t.co/8qGEiwNniY"&gt;Introduction&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://t.co/UtFQRj3W3X"&gt;The Path of the Hobbits&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://t.co/6AzIg7voAb"&gt;The Path of the Wizard&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://t.co/ko6bubJqsw"&gt;The Path of the King&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Now that a basic overview and other auxiliary topics have been covered, without much more ceremony, I'll be diving into how I built Perf Avore. &lt;/p&gt;
&lt;h2&gt;
  
  
  Plan
&lt;/h2&gt;

&lt;p&gt;The plan to get rule applications working is threefold:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Parse Rules&lt;/strong&gt;: Convert the user inputted string based rules to a domain defined Rule.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Process Trace Events&lt;/strong&gt;: Retrieve trace events from either a trace or a real time process.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Apply Rules&lt;/strong&gt;: If the conditions of a rule are met, invoke the action associated with the rule.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--bvvtGwvX--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2816nyycou4fa33kger5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--bvvtGwvX--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2816nyycou4fa33kger5.png" alt="Image description" width="880" height="410"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;However, before implementation is presented, it is of paramount importance to define the domain.&lt;/p&gt;
&lt;h2&gt;
  
  
  The Domain
&lt;/h2&gt;

&lt;p&gt;A Rule is defined as having a &lt;strong&gt;Condition&lt;/strong&gt; and an &lt;strong&gt;Action&lt;/strong&gt;. &lt;/p&gt;

&lt;p&gt;&lt;code&gt;GC/AllocationTick.AllocationAmount &amp;gt; 200000 : Print Alert&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Here, the user requests that for the said process, an alert will be printed if the &lt;code&gt;AllocationAmount&lt;/code&gt; of the &lt;code&gt;GC/AllocationTick&lt;/code&gt; event is greater than 200,000 bytes. The action if the condition is met is that of alerting the user by outputting a message. &lt;/p&gt;

&lt;p&gt;A rule, more generally, is of the following format: &lt;/p&gt;

&lt;p&gt;&lt;code&gt;EventName.PropertyName ConditionalOperator ConditionalOperand : ActionOperator ActionOperand&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;where:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Part&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Event Name&lt;/td&gt;
&lt;td&gt;The event name from the trace / real time analysis for which we want to look up the property.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Property Name&lt;/td&gt;
&lt;td&gt;A double property (this may change in the future) for which we'd want to construct a rule for.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Conditional Operator&lt;/td&gt;
&lt;td&gt;An operator that, along with the Conditional Operand, will dictate situation for which we'll invoke an action for.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Conditional Operand&lt;/td&gt;
&lt;td&gt;The value or name of the anomaly detection operator along with the Conditional Operator that'll dictate the situation for which we'll invoke an action for.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Action Operator&lt;/td&gt;
&lt;td&gt;The operator that, along with the action operand will be invoked if a condition is met.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Action Operand&lt;/td&gt;
&lt;td&gt;The operand for which the action operator will be applied to in case a condition is met.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The &lt;strong&gt;Condition&lt;/strong&gt; is modeled as the following combination of records and discriminated unions:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// src/PerfAvore/PerfAvore.Console/RulesEngine/Domain.fs

type Condition = 
    {  Conditioner      : Conditioner
       ConditionType    : ConditionType
       ConditionalValue : ConditionalValue }
and Conditioner = 
    { ConditionerEvent    : ConditionerEvent 
      ConditionerProperty : ConditionerProperty }
and ConditionType = 
    | LessThan
    | LessThanEqualTo
    | GreaterThan
    | GreaterThanEqualTo
    | Equal
    | NotEqual
    | IsAnomaly
and ConditionalValue =
    | Value of double
    | AnomalyDetectionType of AnomalyDetectionType 
and ConditionerEvent    = string
and ConditionerProperty = string
and AnomalyDetectionType =
    | DetectIIDSpike
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To accommodate Anomaly Detection algorithms we add a &lt;code&gt;IsAnomaly&lt;/code&gt; as a &lt;code&gt;ConditionType&lt;/code&gt; which, rather than relying on a hardcoded threshold for the Conditional Value will relegate invoking an action onto an Anomaly Detection algorithm. The one that's implemented for this submission is that of an Independently and Identically Distributed Spike anomaly detection algorithm; more details are given below.&lt;/p&gt;

&lt;p&gt;For the sake of completeness, the conditions we define are the following:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Condition Operation&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;IsAnomaly&lt;/td&gt;
&lt;td&gt;The condition to match on an anomaly detection algorithm.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&amp;gt; &amp;gt;= &amp;lt; &amp;lt;= != =&lt;/td&gt;
&lt;td&gt;Self explanatory conditional matching based on the value of the event property specified by the rule&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;It is worth noting, right now the library only accepts numeric payloads.&lt;/p&gt;

&lt;p&gt;An Action is modeled as a record of an &lt;strong&gt;ActionOperator&lt;/strong&gt; and an &lt;strong&gt;ActionOperand&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// src/PerfAvore/PerfAvore.Console/RulesEngine/Domain.fs

type Action = 
    { ActionOperator: ActionOperator; ActionOperand: ActionOperand }
and ActionOperator = 
    |  Print
and ActionOperand =
    | Alert
    | CallStack
    | Chart
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The following are the currently implemented action operands:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Name of Action Operands&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Alert&lt;/td&gt;
&lt;td&gt;Alerting Mechanism that'll print out pertinent details about the rule invoked and why it was invoked.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Call Stack&lt;/td&gt;
&lt;td&gt;If a call stack is available, it will be printed out on the console.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Chart&lt;/td&gt;
&lt;td&gt;A chart of data points preceding and including the one that triggered the condition of the rule is generated and rendered as an html file&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;As of now, &lt;code&gt;Print&lt;/code&gt; is the only operator that simply outputs the operand to the Console.&lt;/p&gt;

&lt;p&gt;The Rule, a combination of a Condition and a Action along with an identifier and the original rule passed in by the user and therefore is modeled as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// src/PerfAvore/PerfAvore.Console/RulesEngine/Domain.fs

type Rule = 
    { Id           : Guid
      Condition    : Condition
      Action       : Action 
      InputRule    : string }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now that the domain is defined, the rule parsing logic can be explained; this makes extensive use of pattern matching after deserializing a list of rules from a specified JSON file that could look like the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[ 
    "GC/AllocationTick.AllocationAmount &amp;gt; 108000: Print Alert",
    "GC/AllocationTick.AllocationAmount isAnomaly DetectIIDSpike : Print CallStack"
]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 1: Parse Rule
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--5gIWvAf8--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0ukn3goo0jydsydfb3cw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--5gIWvAf8--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0ukn3goo0jydsydfb3cw.png" alt="Image description" width="880" height="410"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This first step's goal is take the user inputted rule as a string to a Rule defined in the domain. The parsing logic is broken into two main functions that break up the logic of parsing the Condition and Action separately. The &lt;code&gt;parseCondition&lt;/code&gt; function is defined as the following and constructs the condition based on the aforementioned constituents:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// src/PerfAvore/PerfAvore.Console/RulesEngine/Parser.fs

let parseCondition (conditionAsString : string) : Condition = 

    let splitCondition : string[] = conditionAsString.Split(" ", StringSplitOptions.RemoveEmptyEntries)

    // Precondition check
    if splitCondition.Length &amp;lt;&amp;gt; 3
    then invalidArg (nameof conditionAsString) ("Incorrect format of the condition. Format is: Event.Property Condition ConditionalValue. For example: GCEnd.SuspensionTimeMSec &amp;gt;= 298")

    // Condition Event and Property
    let parseConditioner : Conditioner = 
        let splitConditioner : string[] = splitCondition.[0].Split(".", StringSplitOptions.RemoveEmptyEntries)
        let parseConditionEvent : ConditionerEvent = splitConditioner.[0]
        let parseConditionProperty : ConditionerProperty = splitConditioner.[1]

        { ConditionerEvent = parseConditionEvent; ConditionerProperty = parseConditionProperty }

    // Condition Type
    let parseConditionType : ConditionType =
        match splitCondition.[1].ToLower() with
        | "&amp;gt;"  | "greaterthan"                                 -&amp;gt; ConditionType.GreaterThan 
        | "&amp;lt;"  | "lessthan"                                    -&amp;gt; ConditionType.LessThan
        | "&amp;gt;=" | "greaterthanequalto" | "greaterthanorequalto" -&amp;gt; ConditionType.GreaterThanEqualTo
        | "&amp;lt;=" | "lessthanequalto"    | "lessthanorequalto"    -&amp;gt; ConditionType.LessThanEqualTo
        | "="  | "equal"              | "equals"               -&amp;gt; ConditionType.Equal
        | "!=" | "notequal"                                    -&amp;gt; ConditionType.NotEqual
        | "isanomaly"                                          -&amp;gt; ConditionType.IsAnomaly
        | _                                                    -&amp;gt; invalidArg (nameof splitCondition) ("${splitCondition.[1]} is an unrecognized condition type.")

    // Condition Value
    let parseConditionValue : ConditionalValue =
        let conditionalValueAsString = splitCondition.[2].ToLower()
        let checkDouble, doubleValue = Double.TryParse conditionalValueAsString 
        match checkDouble, doubleValue with
        | true, v -&amp;gt; ConditionalValue.Value(v)
        | false, _ -&amp;gt; 
            match conditionalValueAsString with
            | "detectiidspike" -&amp;gt; ConditionalValue.AnomalyDetectionType(AnomalyDetectionType.DetectIIDSpike)
            | _                -&amp;gt; invalidArg (nameof splitCondition) ($"{conditionalValueAsString} is an unrecognized anomaly detection type.")

    { Conditioner = parseConditioner; ConditionType = parseConditionType; ConditionalValue = parseConditionValue }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Similarly, the action parsing logic is implemented via &lt;code&gt;parseAction&lt;/code&gt; function:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// src/PerfAvore/PerfAvore.Console/RulesEngine/Parser.fs

let parseAction (actionAsAString : string) : Action = 
    let splitAction : string[] = actionAsAString.Split(" ", StringSplitOptions.RemoveEmptyEntries)

    // ActionOperator
    let parseActionOperator : ActionOperator = 
        match splitAction.[0].ToLower() with
        | "print" -&amp;gt; ActionOperator.Print
        | _       -&amp;gt; invalidArg (nameof splitAction) ($"{splitAction.[0]} is an unrecognized Action Operator.")

    // ActionOperand 
    let parseActionOperand : ActionOperand = 
        match splitAction.[1].ToLower() with
        | "alert"     -&amp;gt; ActionOperand.Alert
        | "callstack" -&amp;gt; ActionOperand.CallStack
        | "chart"     -&amp;gt; ActionOperand.Chart
        | _           -&amp;gt; invalidArg (nameof splitAction) ($"{splitAction.[1]} is an unrecognized Action Operand.")


    { ActionOperator = parseActionOperator; ActionOperand = parseActionOperand }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally, these 2 parsing functions are combined to parse a particular rule:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// src/PerfAvore/PerfAvore.Console/RulesEngine/Parser.fs

let parseRule (ruleAsString : string) : Rule = 
    let splitRuleAsAString : string[] = ruleAsString.Split(":")
    let condition : Condition = parseCondition splitRuleAsAString.[0]
    let action : Action = parseAction splitRuleAsAString.[1]
    { Condition = condition; Action = action; InputRule = ruleAsString; Id = Guid.NewGuid() }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now that we have the functionality of parsing a rule, we want to move on to Step 2 i.e. Processing Trace Events. &lt;/p&gt;

&lt;h2&gt;
  
  
  Step 2: Process Trace Events
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--cN7qAivn--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9tcft5zw2tnsp9nxknj5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--cN7qAivn--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9tcft5zw2tnsp9nxknj5.png" alt="Image description" width="880" height="410"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Since both reading Trace Events from a .ETL file and real time event processing had to be accomodated for, a split in the logic is made using a command line parameter &lt;code&gt;TracePath&lt;/code&gt;; the absence of this command line parameter will indicate we want to kick off the real time processing logic.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Argu&lt;/code&gt;, an F# specific command line argument parsing library is used pattern match based the types of the command line args such as the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// src/PerfAvore/PerfAvore.Console/CommandLine.fs

#r "nuget:Argu" // Added specifically for this notebook.

open Argu

type Arguments = 
    | [&amp;lt;Mandatory&amp;gt;] ProcessName of string
    | TracePath of Path : string
    | RulesPath of Path : string

    interface IArgParserTemplate with
        member s.Usage =
            match s with
            | TracePath   _ -&amp;gt; "Specify a Path to the Trace."
            | ProcessName _ -&amp;gt; "Specify a Process Name."
            | RulesPath   _ -&amp;gt; "Specify a Path to a Json File With the Rules."
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The usage of the trace path is incorporated like the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// src/PerfAvore/PerfAvore.Console/Program.fs

// This is passed in from the command line but for the sake of demonstration, we'll include this as a literal []&amp;lt;string&amp;gt;.
let argv              = [| "--tracepath"; "Path.etl"; "--processname"; "Test.exe"|]

let parser            = ArgumentParser.Create&amp;lt;Arguments&amp;gt;()
let parsedCommandline = parser.Parse(inputs = argv)

let containsTracePath : bool = parsedCommandline.Contains TracePath
containsTracePath
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;True&lt;/p&gt;

&lt;p&gt;To interface with the Trace Events, the &lt;code&gt;Microsoft.Diagnostics.Tracing.TraceEvent&lt;/code&gt; library that contains the &lt;code&gt;TraceLog&lt;/code&gt; API is used to read events from both the .ETL file and for real time processing for Windows. Inspired by &lt;a href="https://twitter.com/dsymetweets/status/1472655546885058570"&gt;this&lt;/a&gt; tweet highlighting &lt;a href="https://carpenoctem.dev/blog/fsharp-for-linux-people/"&gt;this&lt;/a&gt; blogpost about F# integration in Linux, I pursued adding rule application functionality for Linux and MacOS; that &lt;a href="https://www.nuget.org/packages/Microsoft.Diagnostics.NETCore.Client/"&gt;API&lt;/a&gt;, &lt;code&gt;Microsoft.Diagnostics.NETCore.Client&lt;/code&gt; is different from the TraceLog one and will be highlighted below in the code.&lt;/p&gt;

&lt;p&gt;For further details about the TraceLog API, refer to &lt;a href="https://github.com/microsoft/perfview/blob/main/documentation/TraceEvent/TraceEventProgrammersGuide.md#higher-level-processing-tracelog"&gt;this&lt;/a&gt; doc. The logic to get the stream of events is achieved by the following two functions based on if the &lt;code&gt;tracepath&lt;/code&gt; is specified as a command line argument. &lt;/p&gt;

&lt;p&gt;The code that retrieves the &lt;code&gt;TraceLog&lt;/code&gt; abstraction if the &lt;code&gt;tracepath&lt;/code&gt; arg is specified is the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// src/PerfAvore/PerfAvore.Console/TraceSession.fs

#r "nuget: Microsoft.Diagnostics.Tracing.TraceEvent"

open Microsoft.Diagnostics.Tracing.Etlx

let getTraceLogFromTracePath (tracePath : string) : TraceLog = 
    TraceLog.OpenOrConvert tracePath
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Installed Packages&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;&lt;li&gt;&lt;span&gt;Microsoft.Diagnostics.Tracing.TraceEvent, 2.0.74&lt;/span&gt;&lt;/li&gt;&lt;/ul&gt;

&lt;p&gt;And, the code that retrieves the &lt;code&gt;TraceEventDispatcher&lt;/code&gt; and &lt;code&gt;Session&lt;/code&gt; abstraction for that's responsible for real time processing and if the &lt;code&gt;tracepath&lt;/code&gt; arg isn't specified is the following with support for Windows and Linux/MacOS is added:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// src/PerfAvore/PerfAvore.Console/TraceSession.fs

// Needed for compilation
#r "nuget:Microsoft.Diagnostics.NETCore.Client"

// Ignore this impl for now. More details about this in Step 3 but for the sake of success compilation, we need this.
let applyRule (rule: Rule) (traceEvent : TraceEvent) : unit = 
    ()

open System
open System.Collections.Generic
open System.Runtime.InteropServices

open Microsoft.Diagnostics.NETCore.Client
open Microsoft.Diagnostics.Tracing.Etlx
open Microsoft.Diagnostics.Tracing.Session
open Microsoft.Diagnostics.Tracing.Parsers
open Microsoft.Diagnostics.Tracing

open System.Diagnostics

let getProcessIdForProcessName (processName : string) : int =
        let processes = Process.GetProcessesByName(processName)
        if processes.Length &amp;lt; 1 then invalidArg processName $"No processes with name: {processName} exists."
        // For the sake of simplicity, choose the first process available with the said name. 
        else processes.[0].Id

let getRealTimeSession (processName : string) (parsedRules : Rule list) : TraceEventDispatcher * IDisposable = 

    let callbackForAllEvents (processId : int): Action&amp;lt;TraceEvent&amp;gt; = 
        Action&amp;lt;TraceEvent&amp;gt;(fun traceEvent -&amp;gt; 
            parsedRules
            |&amp;gt; List.iter(fun rule -&amp;gt;
                if processId = traceEvent.ProcessID then applyRule rule traceEvent))

    let processId = getProcessIdForProcessName processName

    // Windows.
    if RuntimeInformation.IsOSPlatform OSPlatform.Windows then
        let traceEventSession : TraceEventSession = new TraceEventSession($"Session_{Guid.NewGuid()}");

        let keywords : uint64 = uint64(ClrTraceEventParser.Keywords.All) 

        traceEventSession.EnableKernelProvider(KernelTraceEventParser.Keywords.All, KernelTraceEventParser.Keywords.None) |&amp;gt; ignore
        traceEventSession.EnableProvider(ClrTraceEventParser.ProviderGuid, TraceEventLevel.Verbose, keywords)             |&amp;gt; ignore

        // Once the pertinent providers are enabled, create the trace log event source. 
        let traceLogEventSource = TraceLog.CreateFromTraceEventSession traceEventSession

        // Add all the necessary callbacks.
        traceLogEventSource.Clr.add_All(callbackForAllEvents processId)    |&amp;gt; ignore
        traceLogEventSource.Kernel.add_All(callbackForAllEvents processId) |&amp;gt; ignore

        // TODO: Enable the GLAD events - only available for real time processing.
        // ala: https://devblogs.microsoft.com/dotnet/556-2/
        traceLogEventSource, traceEventSession

    // Linux / MacOS.
    else
        let keywords : int64 = int64(ClrTraceEventParser.Keywords.All) 
        let eventPipeProvider : EventPipeProvider = 
            EventPipeProvider("Microsoft-Windows-DotNETRuntime", Tracing.EventLevel.Informational, keywords)
        let providers = List&amp;lt;EventPipeProvider&amp;gt;()
        providers.Add eventPipeProvider

        // For the sake of simplicity, choose the first process available with the said name. 
        let processId        = getProcessIdForProcessName processName
        let client           = DiagnosticsClient(processId)
        let eventPipeSession = client.StartEventPipeSession(providers, false)
        let source           = new EventPipeEventSource(eventPipeSession.EventStream)

        source.Clr.add_All(callbackForAllEvents processId)     |&amp;gt; ignore
        source.Kernel.add_All(callbackForAllEvents processId ) |&amp;gt; ignore

        source, eventPipeSession
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Installed Packages&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;&lt;li&gt;&lt;span&gt;Microsoft.Diagnostics.NETCore.Client, 0.2.257301&lt;/span&gt;&lt;/li&gt;&lt;/ul&gt;

&lt;p&gt;This function is a bit more involved and requires turning on the Kernel and Clr Providers for those type of events. The events will flow in via callbacks that are subscribed to via the &lt;code&gt;callbackForAllEvents&lt;/code&gt; function. Finally, the &lt;code&gt;TraceEventDispatcher&lt;/code&gt; that'll be used for callstack retrieval and the session that'll need to be disposed once the session ends else, we'll run into a session leak is returned. &lt;/p&gt;

&lt;p&gt;It is worth noting, to enable the Kernel provider, admin privileges are needed; this implies for real time processing in Windows, the process must be started with admin privileges. &lt;/p&gt;

&lt;p&gt;Finally, in the main program, the events are subscribed to in the following manner:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// src/PerfAvore/PerfAvore.Console/Program.fs

 // Hard coded values for the sake of successful compilation.
 let processName = "Test.exe"
 let processID   = -1
 let parsedRules = [ parseRule "GC/AllocationTick.AllocationAmount &amp;gt; 110000 : Print CallStack" ]

let startProcessingEvents() : unit = 
    // If the trace log file is provided, use the Trace Log API to traverse through all events.
    if containsTracePath then 
        let tracePathArgs = parsedCommandline.GetResult TracePath
        let traceLog = getTraceLogFromTracePath tracePathArgs
        let events = traceLog.Events 
        let eventNamesToFilter = parsedRules |&amp;gt; List.map(fun r -&amp;gt; r.Condition.Conditioner.ConditionerEvent.ToString())

        let applyRulesForAllEvents (events : TraceEvent seq) (rules : Rule list) = 
            events
            // Consider events with name of the process and if they contain the events defined in the rules.
            |&amp;gt; Seq.filter(fun e -&amp;gt; e.ProcessID = processID &amp;amp;&amp;amp; 
                                    eventNamesToFilter |&amp;gt; List.contains(e.EventName))
            |&amp;gt; Seq.iter(fun e -&amp;gt; 
                rules
                |&amp;gt; List.iter(fun rule -&amp;gt; applyRule rule e ))
        applyRulesForAllEvents events parsedRules

    // Else, start a Real Time Session.
    // Requires admin privileges
    else
        let traceLogEventSource, session = getRealTimeSession processName parsedRules
        Console.CancelKeyPress.Add(fun e -&amp;gt; session.Dispose() |&amp;gt; ignore )

        traceLogEventSource.Process() |&amp;gt; ignore
        ()
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note that the TraceLog API allows iteration through Events as if it is a plain-vanilla &lt;code&gt;seq&amp;lt;TraceEvent&amp;gt;&lt;/code&gt; unlike the real time session that requires callback registration.&lt;/p&gt;

&lt;p&gt;The next step is to start applying rules and go into details about the implementation of the Action Engine and the different types of actions as well as the anomaly detection logic.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 3: Apply Rules
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--aSF0z9dn--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4hevdz9j5aidq83ef7bl.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--aSF0z9dn--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4hevdz9j5aidq83ef7bl.png" alt="Image description" width="880" height="410"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Lastly, the logic that's responsible for applying the rule if the condition is met is added. The application of the rule for a particular &lt;code&gt;TraceEvent&lt;/code&gt; instance and a Rule is to check if the condition specified in the rule matches and then invoking the action.&lt;/p&gt;

&lt;p&gt;The condition checking logic is as follows:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Check if the name of the &lt;code&gt;TraceEvent&lt;/code&gt; matches the condition of the Rule.&lt;/li&gt;
&lt;li&gt;Check if the property we want to match on is in the &lt;code&gt;TraceEvent&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Check if the condition matches based on the &lt;code&gt;TraceEvent&lt;/code&gt; and the rules conditions.
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// src/PerfAvore/PerfAvore.Console/RulesEngine/ActionEngine.fs

open System
open System.Linq

let applyRule (rule : Rule) (traceEvent : TraceEvent) : unit =

    // Helper fn checks if the condition is met for the traceEvent.
    let checkCondition : bool =
        let condition : Condition = rule.Condition

        // Match the event name.
        let matchEventName (traceEvent : TraceEvent) : bool = 
            traceEvent.EventName = condition.Conditioner.ConditionerEvent

        // Check if the specified payload exists.
        let checkPayload (traceEvent : TraceEvent) : bool = 
            if traceEvent.PayloadNames.Contains condition.Conditioner.ConditionerProperty then true
            else false

        // Early return if the payload is unavailable since it will except later if we let it slide. 
        if ( checkPayload traceEvent ) = false then 
            false
        else
            let payload : double = Double.Parse (traceEvent.PayloadByName(condition.Conditioner.ConditionerProperty).ToString())

            // Check if the condition matches.
            let checkConditionValue (rule : Rule) (traceEvent : TraceEvent) : bool =
                let conditionalValue : ConditionalValue = rule.Condition.ConditionalValue

                match conditionalValue with
                | ConditionalValue.Value value -&amp;gt;
                    match condition.ConditionType with
                    | ConditionType.Equal              -&amp;gt; payload = value
                    | ConditionType.GreaterThan        -&amp;gt; payload &amp;gt; value
                    | ConditionType.GreaterThanEqualTo -&amp;gt; payload &amp;gt;= value
                    | ConditionType.LessThan           -&amp;gt; payload &amp;lt; value
                    | ConditionType.LessThanEqualTo    -&amp;gt; payload &amp;lt;= value
                    | ConditionType.NotEqual           -&amp;gt; payload &amp;lt;&amp;gt; value
                    | ConditionType.IsAnomaly          -&amp;gt; false // This case should technically not be reached but adding it to prevent warnings.
                | ConditionalValue.AnomalyDetectionType anomalyDetectionType -&amp;gt;
                    match anomalyDetectionType with
                    | AnomalyDetectionType.DetectIIDSpike -&amp;gt;
                        // We'll be going over this logic below. Right now simply return false.
                        false

            // Match on Event Name, if the payload exists and the condition based on the trace event is met.
            matchEventName traceEvent &amp;amp;&amp;amp; checkPayload traceEvent &amp;amp;&amp;amp; checkConditionValue rule traceEvent

            // ... =&amp;gt; Apply Actions. 
    ()
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The simple ConditionType checks should be self-explanatory. The AnomalyDetection based approach, however, is a bit more involved. In general, for a point to be considered an anomaly, the context of "amongst which other values is that data point an anomaly" is needed; this implies that the history of the TraceEvents immediately before the said point to check the condition for should be kept in memory. As an aside, &lt;code&gt;Microsoft.ML&lt;/code&gt; and &lt;code&gt;Microsoft.ML.TimeSeries&lt;/code&gt; are the two nuget packages that are used for anomaly detection computation.&lt;/p&gt;

&lt;p&gt;To accommodate this logic, a rolling window of the last 'n - 1' points before the TraceEvent in question should be made available at the time of the anomaly detection computation. The implementation of the abstraction involves a queue with a capacity and an eviction policy that dequeues the oldest element and enqueues the incoming element if the queue is at capacity. This abstraction is named  &lt;code&gt;FixedSizeQueue&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// src/PerfAvore/PerfAvore.Console/RulesEngine/AnomalyDetection/Service.fs

open System
open System.Collections.Concurrent

type FixedSizeQueue&amp;lt;'T&amp;gt; (capacity : int) =
    // Concurrency might not be necessary but better to be safe than sorry.
    let queue = ConcurrentQueue&amp;lt;'T&amp;gt;()

    member this.Capacity : int = capacity
    member this.Count    : int = queue.Count
    member this.Print() : unit = 
        let stringRepr : string = String.Join(",", queue.ToArray())
        printfn "%A" stringRepr

    member this.Insert (item : 'T) : unit = 
        // If we are at capacity, evict the first item.
        if queue.Count = capacity then 
            queue.TryDequeue() |&amp;gt; ignore

        // Enqueue the new item to the list.
        queue.Enqueue(item)

    member this.GetAll() : seq&amp;lt;'T&amp;gt; = 
        queue
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The abstraction that is responsible for the orchestration of the retrieval of the last 'n' events will maintain a &lt;code&gt;FixedSizeQueue&lt;/code&gt; for each rule. Before defining the said service, the Anomaly Detection domain must be defined. To keep things as simple as possible, an anomaly detection algorithm takes in a &lt;code&gt;Context&lt;/code&gt; and a &lt;code&gt;Result&lt;/code&gt; returned.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;Context&lt;/code&gt; will, therefore, have to consist of details about the point in question and the associated Rule. The output would be a result indicating if the point is an anomaly and the confidence with which the algorithm believes the point is an anomaly. The input will consist of the payload value and the associated timestamp.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// src/PerfAvore/PerfAvore.Console/RulesEngine/Domain.fs

#r "nuget:Microsoft.ML"

open Microsoft.ML.Data

type AnomalyDetectionInput() =
    [&amp;lt;DefaultValue&amp;gt;]
    [&amp;lt;LoadColumn(0)&amp;gt;]
    val mutable public timestamp : double 

    [&amp;lt;DefaultValue&amp;gt;]
    [&amp;lt;LoadColumn(1)&amp;gt;]
    val mutable public value : float32 

type AnomalyDetectionContext = 
    { Rule  : Rule 
      Input : AnomalyDetectionInput }
type AnomalyDetectionResult = 
    { Context   : AnomalyDetectionContext
      IsAnomaly : bool
      PValue    : double }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Installed Packages&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;&lt;li&gt;&lt;span&gt;Microsoft.ML, 1.7.0&lt;/span&gt;&lt;/li&gt;&lt;/ul&gt;

&lt;p&gt;Now that we have the Anomaly Detection domain defined, the service mentioned before that'll retrieve the fixed size queue can be implemented.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// src/PerfAvore/PerfAvore.Console/RulesEngine/AnomalyDetection/Service.fs

open System
open System.Collections.Concurrent

type AnomalyDetectionContextService(capacity : int) = 
    // Keyed on the Rule Id and Value is a FixedSizeQueueForTraceEvents.
    // Each Rule that has Anomaly Detection associated with it must have its own Fixed Size Queue.
    let cache = ConcurrentDictionary&amp;lt;Guid, FixedSizeQueue&amp;lt;AnomalyDetectionInput&amp;gt;&amp;gt;()

    static member AnomalyPValueHistoryLength : int    = 30
    static member AnomalyConfidence          : double = 95.

    member this.Upsert (ruleId : Guid) (item : AnomalyDetectionInput) : unit =
        let queueExists, queue = cache.TryGetValue ruleId
        match queueExists, queue with
        | true, q  -&amp;gt; 
            q.Insert item
        | false, _ -&amp;gt; 
            cache.[ruleId] &amp;lt;- FixedSizeQueue( capacity )
            cache.[ruleId].Insert item

    member this.TryRetrieve(ruleId : Guid) : AnomalyDetectionInput seq option = 
        let queueExists, queue = cache.TryGetValue ruleId
        match queueExists, queue with
        | true, q  -&amp;gt; Some (q.GetAll())
        | false, _ -&amp;gt; None
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The one Anomaly Detection algorithm implemented is that of &lt;a href="https://github.com/dotnet/machinelearning/blob/510f0112d4fbb4d3ee233b9ca95c83fae1f9da91/src/Microsoft.ML.TimeSeries/IidSpikeDetector.cs"&gt;Independent and Identically Distributed Spike Detector&lt;/a&gt; from &lt;code&gt;Microsoft.ML.TimeSeries&lt;/code&gt; that makes use of adaptive kernel density estimation to compute p-values to decide how much of an anomaly a certain point is. &lt;/p&gt;

&lt;p&gt;The computation of the kernel p-value can be found &lt;a href="https://github.com/dotnet/machinelearning/blob/510f0112d4fbb4d3ee233b9ca95c83fae1f9da91/src/Microsoft.ML.TimeSeries/SequentialAnomalyDetectionTransformBase.cs#L475"&gt;here&lt;/a&gt;. To be put as simply as possible, the difference of the value of the point in question and all other points in the fixed size queue is computed and we consider a point an anomaly if there is a huge difference. Of course, I am trivializing the details, however, my intention here is to highlight the intuition more than the gory statistical details. &lt;/p&gt;

&lt;p&gt;Now that all ducks in a row with regard to the Anomaly Detection computation, the rest of the implementation is the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// src/PerfAvore/PerfAvore.Console/RulesEngine/AnomalyDetection/IIDSpike.fs

#r "nuget:Microsoft.ML.TimeSeries"

open Microsoft.ML
open Microsoft.ML.Data
open Microsoft.ML.Transforms.TimeSeries
open System.Collections.Generic

open System.Linq

let ctx : MLContext = MLContext()

type Prediction() = 
    [&amp;lt;DefaultValue&amp;gt;]
    [&amp;lt;VectorType(3)&amp;gt;] // prediction i.e. 0/1 + value i.e. payload + p-value
    val mutable public Prediction : double[]

let getAnomaliesUsingIIDSpikeEstimation (input : AnomalyDetectionContext) 
                                        (service : AnomalyDetectionContextService) 
                                        : AnomalyDetectionResult =
    let retrievedInput = service.TryRetrieve input.Rule.Id 
    let buffer =          
        match retrievedInput with
        | Some b -&amp;gt; b 
        | None   -&amp;gt; failwith $"Failed to look up Anomaly Detection Buffer for rule: {input.Rule.InputRule}" 

    let dataView = 
        ctx.Data.LoadFromEnumerable&amp;lt;AnomalyDetectionInput&amp;gt;(buffer)

    // If p-value &amp;lt; (1 - confidence / 100.0) -&amp;gt; Alert i.e. anomaly.
    let anomalyPipeline : IidSpikeEstimator =
        ctx.Transforms.DetectIidSpike(
        outputColumnName    = "Prediction",
        inputColumnName     = "value",
        side                = AnomalySide.Positive,
        confidence          = AnomalyDetectionContextService.AnomalyConfidence,  //  Alert Threshold = 1 - options.Confidence / 100;
        pvalueHistoryLength = AnomalyDetectionContextService.AnomalyPValueHistoryLength )

    // For this model, fitting doesn't matter.
    let trainedAnomalyModel : IidSpikeDetector 
        = anomalyPipeline.Fit(ctx.Data.LoadFromEnumerable(List&amp;lt;AnomalyDetectionInput&amp;gt;()))
    let transformedAnomalyData : IDataView 
        = trainedAnomalyModel.Transform(dataView)
    let anomalies : Prediction seq = 
        ctx.Data.CreateEnumerable&amp;lt;Prediction&amp;gt;(transformedAnomalyData, reuseRowObject = false)

    // Last one in the buffer since it's the most recent one. 
    let inputPoint = anomalies.Last()
    { Context   = input 
      IsAnomaly = inputPoint.Prediction[0] = 1
      PValue    = inputPoint.Prediction[2] }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Installed Packages&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;&lt;li&gt;&lt;span&gt;Microsoft.ML.TimeSeries, 1.7.0&lt;/span&gt;&lt;/li&gt;&lt;/ul&gt;

&lt;p&gt;Tying the anomaly detection computation all together with the rest of the condition matching logic:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// src/PerfAvore/PerfAvore.Console/RulesEngine/ActionEngine.fs

let anomalyDetectionContextService : AnomalyDetectionContextService = 
    AnomalyDetectionContextService(AnomalyDetectionContextService.AnomalyPValueHistoryLength)

let applyRule (rule : Rule) (traceEvent : TraceEvent) : unit =

    // Helper fn checks if the condition is met for the traceEvent.
    let checkCondition : bool =
        let condition : Condition = rule.Condition

        // Match the event name.
        let matchEventName (traceEvent : TraceEvent) : bool = 
            traceEvent.EventName = condition.Conditioner.ConditionerEvent

        // Check if the specified payload exists.
        let checkPayload (traceEvent : TraceEvent) : bool = 
            if traceEvent.PayloadNames.Contains condition.Conditioner.ConditionerProperty then true
            else false

        // Early return if the payload is unavailable since it will except later if we let it slide. 
        if ( checkPayload traceEvent ) = false then 
            false
        else
            let payload : double = Double.Parse (traceEvent.PayloadByName(condition.Conditioner.ConditionerProperty).ToString())

            // Add the new data point to the anomaly detection dict.
            let anomalyDetectionInput : AnomalyDetectionInput = 
                AnomalyDetectionInput(timestamp = traceEvent.TimeStampRelativeMSec, value = float32(payload))
            anomalyDetectionContextService.Upsert rule.Id anomalyDetectionInput |&amp;gt; ignore

            // Check if the condition matches.
            let checkConditionValue (rule : Rule) (traceEvent : TraceEvent) : bool =
                let conditionalValue : ConditionalValue = rule.Condition.ConditionalValue

                match conditionalValue with
                | ConditionalValue.Value value -&amp;gt;
                    match condition.ConditionType with
                    | ConditionType.Equal              -&amp;gt; payload = value
                    | ConditionType.GreaterThan        -&amp;gt; payload &amp;gt; value
                    | ConditionType.GreaterThanEqualTo -&amp;gt; payload &amp;gt;= value
                    | ConditionType.LessThan           -&amp;gt; payload &amp;lt; value
                    | ConditionType.LessThanEqualTo    -&amp;gt; payload &amp;lt;= value
                    | ConditionType.NotEqual           -&amp;gt; payload &amp;lt;&amp;gt; value
                    | ConditionType.IsAnomaly          -&amp;gt; false // This case should technically not be reached but adding it to prevent warnings.
                | ConditionalValue.AnomalyDetectionType anomalyDetectionType -&amp;gt;
                    match anomalyDetectionType with
                    | AnomalyDetectionType.DetectIIDSpike -&amp;gt;
                        let context = { Rule = rule; Input = anomalyDetectionInput }
                        let result  = getAnomaliesUsingIIDSpikeEstimation context anomalyDetectionContextService 
                        result.IsAnomaly

            // Match on Event Name, if the payload exists and the condition based on the trace event is met.
            matchEventName traceEvent &amp;amp;&amp;amp; checkPayload traceEvent &amp;amp;&amp;amp; checkConditionValue rule traceEvent

    ()
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, the Action implementation logic is added. The actions that are implemented are the following:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Name of Action Type&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1. Alert&lt;/td&gt;
&lt;td&gt;Alerting Mechanism that'll print out pertinent details about the rule invoked and why it was invoked.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2. Callstack&lt;/td&gt;
&lt;td&gt;If a call stack is available, it will be printed out on the console.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3. Chart&lt;/td&gt;
&lt;td&gt;A chart of data points preceding and including the one that triggered the condition of the rule is generated and rendered as an html file&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Alerts are fairly straight forward and are implemented in the following manner using the &lt;code&gt;Spectre.Console&lt;/code&gt; library for it's aesthetic appeal:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// src/PerfAvore/PerfAvore.Console/RulesEngine/Actions/Alerts.fs

#r "nuget:Spectre.Console"

open Microsoft.Diagnostics.Tracing
open Spectre.Console

// Added this back to distinguish between this Rule and the open imported from
// opening up Spectre.Console.
type Rule = 
    { Id           : Guid
      Condition    : Condition
      Action       : Action 
      InputRule    : string }

let printAlert (rule : Rule) (traceEvent : TraceEvent) : unit = 

    // Create a table
    let table = Spectre.Console.Table();
    table.Title &amp;lt;- Spectre.Console.TableTitle "[underline red] Alert! [/]"

    table.AddColumn("Input Rule")      |&amp;gt; ignore
    table.AddColumn("Timestamp")       |&amp;gt; ignore
    table.AddColumn("Event Name")      |&amp;gt; ignore
    table.AddColumn("Event Property")  |&amp;gt; ignore
    table.AddColumn("Payload")         |&amp;gt; ignore

    table.AddRow( rule.InputRule, 
                  traceEvent.TimeStampRelativeMSec.ToString(), 
                  traceEvent.EventName,
                  rule.Condition.Conditioner.ConditionerProperty,
                  traceEvent.PayloadByName(rule.Condition.Conditioner.ConditionerProperty).ToString() ) |&amp;gt; ignore

    table.Border &amp;lt;- Spectre.Console.TableBorder.Square

    // Render the table to the console
    Spectre.Console.AnsiConsole.Write(table);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Installed Packages&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;&lt;li&gt;&lt;span&gt;Spectre.Console, 0.43.0&lt;/span&gt;&lt;/li&gt;&lt;/ul&gt;

&lt;p&gt;For &lt;strong&gt;Callstack&lt;/strong&gt; actions, symbol resolution is an important step and is highlighted below; a recursive function that walks the stack frame-by-frame and prints out the module and full method name after resolving symbols is used:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// src/PerfAvore/PerfAvore.Console/RulesEngine/Actions/CallStack.fs

open System.IO

open Microsoft.Diagnostics.Tracing
open Microsoft.Diagnostics.Tracing.Etlx
open Microsoft.Diagnostics.Symbols

open Spectre.Console

// Added this back to distinguish between this Rule and the open imported from
// opening up Spectre.Console.
type Rule = 
    { Id           : Guid
      Condition    : Condition
      Action       : Action 
      InputRule    : string }

let symbolReader : SymbolReader = new SymbolReader(TextWriter.Null, SymbolPath.SymbolPathFromEnvironment)

// Helper fn responsible for getting the call stack from a particular trace event.
let printCallStack (rule: Rule) (traceEvent : TraceEvent) : unit =

    let callStack = traceEvent.CallStack()
    if isNull callStack then 
        printfn $"Rule: {rule.InputRule} invoked for Event: {traceEvent} however, the call stack associated with the event is null." 
        ()

    let root = Tree(Rule(rule.InputRule.EscapeMarkup()))

    let printStackFrame (callStack : TraceCallStack) : unit =
        if not (isNull callStack.CodeAddress.ModuleFile)
        then
            callStack.CodeAddress.CodeAddresses.LookupSymbolsForModule(symbolReader, callStack.CodeAddress.ModuleFile)
            let frameValue = sprintf "%s!%s" callStack.CodeAddress.ModuleName callStack.CodeAddress.FullMethodName
            root.AddNode ( frameValue.EscapeMarkup() ) |&amp;gt; ignore

    let rec processFrame (callStack : TraceCallStack) : unit =
        if isNull callStack then ()
        else
            printStackFrame callStack
            processFrame callStack.Caller

    processFrame callStack
    AnsiConsole.Write root
    printfn "\n"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;An example of a printed callstack is the following:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--5z7mIoBw--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://github.com/MokoSan/PerfAvore/blob/main/Images/Example_Callstack.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--5z7mIoBw--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://github.com/MokoSan/PerfAvore/blob/main/Images/Example_Callstack.jpeg" alt="Call Stack" width="" height=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It is worth noting that currently support for callstack resolution doesn't exist in its full capacity for Linux/MacOS.&lt;/p&gt;

&lt;p&gt;Lastly, charting is made possible using &lt;code&gt;FSharp.Plotly&lt;/code&gt; in the following manner:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// src/PerfAvore/PerfAvore.Console/RulesEngine/Actions/Chart.fs

#r "nuget:Fsharp.Plotly"

open System.Linq
open FSharp.Plotly

let printChart (rule : Rule) (service : AnomalyDetectionContextService) : unit = 

    let v = service.TryRetrieve(rule.Id).Value
    let x = 
        v
        |&amp;gt; Seq.map(fun i -&amp;gt; i.timestamp)
    let y = 
        v
        |&amp;gt; Seq.map(fun i -&amp;gt; i.value)
    let input = Seq.zip x y
    let point = v.Last()
    let scatterPoint = seq { point.timestamp, point.value }

    [
        Chart.Line (input, Name = $"Trend for {point.timestamp}") 
        Chart.Scatter (scatterPoint, mode = StyleParam.Mode.Markers, Name="Anomaly Point")
    ]
    |&amp;gt; Chart.Combine
    |&amp;gt; Chart.withX_AxisStyle(title = "Relative Timestamp (ms)")
    |&amp;gt; Chart.withY_AxisStyle(title = $"{rule.Condition.Conditioner.ConditionerProperty}")
    |&amp;gt; Chart.Show
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Installed Packages&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;&lt;li&gt;&lt;span&gt;Fsharp.Plotly, 1.2.2&lt;/span&gt;&lt;/li&gt;&lt;/ul&gt;

&lt;p&gt;An example of an chart is:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--hAaVZQEB--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4yfis0e91el2c9hgusda.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--hAaVZQEB--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4yfis0e91el2c9hgusda.png" alt="Image description" width="868" height="681"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  How To Run Perf Avore
&lt;/h2&gt;

&lt;p&gt;Now that all the components of Perf Avore are covered, this section will cover how a user can run the Console App. Perf-Avore can be run by cd'ing into the &lt;code&gt;src/PerfAvore/PerfAvore.Console&lt;/code&gt; directory and then:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;code&gt;dotnet restore&lt;/code&gt;&lt;/li&gt;
&lt;li&gt; &lt;code&gt;dotnet run -- --processname &amp;lt;ProcessName&amp;gt; [--tracepath &amp;lt;TracePath&amp;gt;] [--rulespath &amp;lt;RulesPath&amp;gt;]&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Command Line Arguments
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Command Line Option&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;processname&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Name of the Process to analyze. This is the only mandatory parameter.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;tracepath&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;The path of the trace file (.ETL / .ETLX). The absence of this command line will trigger a real time session. Note: For real time sessions, admin privileges are required.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;rulespath&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;The path to a json file that contains a list of all the rules. By default, the &lt;code&gt;SampleRules.json&lt;/code&gt; file will be used if this argument isn't specified. The location of this file is &lt;code&gt;src\PerfAvore\PerfAvore.Console\SampleRules\SampleRules.json&lt;/code&gt; for Windows and &lt;code&gt;src\PerfAvore\PerfAvore.Console\SampleRules\LinuxSampleRules.json&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Prototypes
&lt;/h2&gt;

&lt;p&gt;Prior to writing this Console App, I prototyped functionality to test out smaller components that can be found &lt;a href="https://github.com/MokoSan/PerfAvore/tree/main/src/Prototypes"&gt;here&lt;/a&gt;. Some of the prototypes include:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;a href="https://github.com/MokoSan/PerfAvore/blob/main/src/Prototypes/RuleEngineDSL.ipynb"&gt;Rule Engine based DSL Parsing&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/MokoSan/PerfAvore/blob/main/src/Prototypes/AnomalyDetection_TraceLog.ipynb"&gt;Anomaly Detection With Trace Log API&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/MokoSan/PerfAvore/blob/main/src/Prototypes/AnomalyDetection_ML.NET.ipynb"&gt;Anomaly Detection with ML.NET&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/MokoSan/PerfAvore/blob/main/src/Prototypes/PrototypingTraceLog.ipynb"&gt;Prototyping the Trace Log API&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Testing
&lt;/h2&gt;

&lt;p&gt;I tested the effectiveness with a rouge process given &lt;a href="https://github.com/MokoSan/PerfAvore/blob/main/src/PerfAvore/RougePrograms/TimedExcessiveAllocs/Program.cs"&gt;here&lt;/a&gt; that excessively allocates to both the SOH and the LOH on a timer and was able to get all the actions invoked. &lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Finally, done! This submission took a lot of work and I feel I have a reasonable base to continue to build on top of. As a disclaimer, the project is still under development and is without unit tests. &lt;/p&gt;

&lt;p&gt;To reiterate:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Build a parser for a domain we defined that encapsulates details about rules.&lt;/li&gt;
&lt;li&gt;A mechanism to digest &lt;code&gt;TraceEvent&lt;/code&gt; instances from a trace file or real time process for Windows, Linux and MacOS systems.&lt;/li&gt;
&lt;li&gt;Action invocation logic if the conditions of a rule are met.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Would very much appreciate any feedback or suggestions that can improve the product or if you spot any mistakes! Building Perf Avore was an incredibly rewarding learning experience!&lt;/p&gt;

&lt;p&gt;Thanks to the organizers of #fsadvent particularly, &lt;a href="https://twitter.com/sergey_tiho"&gt;Sergey Tihon&lt;/a&gt;! Happy Holidays to all!&lt;/p&gt;

&lt;h2&gt;
  
  
  Tools Used
&lt;/h2&gt;

&lt;p&gt;Perf Avore was developed on VSCode using the &lt;a href="https://github.com/ionide/ionide-vscode-fsharp"&gt;ionide&lt;/a&gt; plugin and dotnet cli. &lt;/p&gt;

&lt;p&gt;The version of dotnet used to develop is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;❯ dotnet --version
6.0.100
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I tested the linux use case using WSL with the following version:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;MokoSan:~:% lsb_release -a
No LSB modules are available.
Distributor ID: Ubuntu
Description:    Ubuntu 20.04 LTS
Release:        20.04
Codename:       focal
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Dependencies
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Dependency Name&lt;/th&gt;
&lt;th&gt;Reasons&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Argu&lt;/td&gt;
&lt;td&gt;Command line parsing&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;FSharp.Plotly&lt;/td&gt;
&lt;td&gt;Charting&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Microsoft.Diagnostics.NETCore.Client&lt;/td&gt;
&lt;td&gt;Linux / MacOS Trace Event Support&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Microsoft.Diagnostics.Tracing.TraceEvent&lt;/td&gt;
&lt;td&gt;Trace Event Support&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Microsoft.ML&lt;/td&gt;
&lt;td&gt;Basic abstractions used for the Anomaly Detection side of things&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Microsoft.ML.TimeSeries&lt;/td&gt;
&lt;td&gt;Anomaly Detection Algorithm&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Spectre.Console&lt;/td&gt;
&lt;td&gt;Prettifying the Console&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;System.Text.Json&lt;/td&gt;
&lt;td&gt;Parsing the JSON rules file&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Next Steps
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Added Unit Tests&lt;/li&gt;
&lt;li&gt;Add the ability to create an Audit of all the Actions Invoked.&lt;/li&gt;
&lt;li&gt;Clean up some of the interfaces and add more documentation.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;&lt;a href="https://www.codesuji.com/2019/05/24/F-and-MLNet-Anomaly/"&gt;Taking Stock of Anomalies with F# And ML.NET&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://lowleveldesign.org/2020/10/13/a-cpu-sampling-profiler-in-less-than-200-lines/"&gt;A CPU Sampling Profiler in Less Than 200 Lines&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.microsoft.com/en-us/dotnet/machine-learning/tutorials/phone-calls-anomaly-detection"&gt;Tutorial: Detect anomalies in time series with ML.NET&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://arxiv.org/pdf/1204.3251.pdf"&gt;Plug-in martingales for testing exchangeability on-line: arXiv:1204.3251&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://atlemann.github.io/fsharp/2021/12/11/fs-crypto.html"&gt;Atle Rudshaug's Submission of a Console App that helped me significantly design my app&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/Maoni0/realmon/tree/main/src"&gt;realmon&lt;/a&gt;
&lt;/li&gt;
&lt;/ol&gt;

</description>
      <category>fsharp</category>
      <category>dotnet</category>
      <category>performance</category>
    </item>
    <item>
      <title>[Work in Progress] Outlier Analysis</title>
      <dc:creator>Mukund Raghav Sharma (Moko)</dc:creator>
      <pubDate>Sat, 04 Dec 2021 20:02:02 +0000</pubDate>
      <link>https://dev.to/mrsharm/work-in-progress-outlier-analysis-55lg</link>
      <guid>https://dev.to/mrsharm/work-in-progress-outlier-analysis-55lg</guid>
      <description>&lt;h2&gt;
  
  
  1. Z-Value:
&lt;/h2&gt;

&lt;p&gt;

&lt;/p&gt;
&lt;div class="katex-element"&gt;
  &lt;span class="katex-display"&gt;&lt;span class="katex"&gt;&lt;span class="katex-mathml"&gt;Zi=∣Xi−μ∣σZ_i = \frac{| X_i - \mu |}{\sigma} &lt;/span&gt;&lt;span class="katex-html"&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mord mathnormal"&gt;Z&lt;/span&gt;&lt;span class="msupsub"&gt;&lt;span class="vlist-t vlist-t2"&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="sizing reset-size6 size3 mtight"&gt;&lt;span class="mord mathnormal mtight"&gt;i&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-s"&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mrel"&gt;=&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mopen nulldelimiter"&gt;&lt;/span&gt;&lt;span class="mfrac"&gt;&lt;span class="vlist-t vlist-t2"&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mord mathnormal"&gt;σ&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="frac-line"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mord"&gt;∣&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mord mathnormal"&gt;X&lt;/span&gt;&lt;span class="msupsub"&gt;&lt;span class="vlist-t vlist-t2"&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="sizing reset-size6 size3 mtight"&gt;&lt;span class="mord mathnormal mtight"&gt;i&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-s"&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mbin"&gt;−&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mord mathnormal"&gt;μ&lt;/span&gt;&lt;span class="mord"&gt;∣&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-s"&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="mclose nulldelimiter"&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/div&gt;


&lt;h3&gt;
  
  
  Assumptions:
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Underlying distribution is Normally Distributed. Violating this will result in poor anomalies.&lt;/li&gt;
&lt;li&gt;Good proxy: 
&lt;span class="katex-element"&gt;
  &lt;span class="katex"&gt;&lt;span class="katex-mathml"&gt;Zi≥3Z_i \geq 3 &lt;/span&gt;&lt;span class="katex-html"&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mord mathnormal"&gt;Z&lt;/span&gt;&lt;span class="msupsub"&gt;&lt;span class="vlist-t vlist-t2"&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="sizing reset-size6 size3 mtight"&gt;&lt;span class="mord mathnormal mtight"&gt;i&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-s"&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mrel"&gt;≥&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;3&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/span&gt;
&lt;/li&gt;
&lt;li&gt;For few samples, use the &lt;em&gt;Student's t-distribution&lt;/em&gt;. &lt;/li&gt;
&lt;/ol&gt;

</description>
    </item>
    <item>
      <title>Checking In: Joshua Tree</title>
      <dc:creator>Mukund Raghav Sharma (Moko)</dc:creator>
      <pubDate>Mon, 22 Nov 2021 05:18:33 +0000</pubDate>
      <link>https://dev.to/mrsharm/checking-in-joshua-tree-573m</link>
      <guid>https://dev.to/mrsharm/checking-in-joshua-tree-573m</guid>
      <description>&lt;p&gt;Had a blast at Joshua Tree today. Been behind on posting here and my WebDev learning but this trip was totally worth it. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--pXCyFxLx--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/92zq7cn173572y40z6nm.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--pXCyFxLx--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/92zq7cn173572y40z6nm.jpeg" alt="Image description" width="880" height="1173"&gt;&lt;/a&gt; &lt;/p&gt;

</description>
    </item>
    <item>
      <title>How To Lie With Performance Data: Part 1 - How To Lie With A Small Number of Data Points</title>
      <dc:creator>Mukund Raghav Sharma (Moko)</dc:creator>
      <pubDate>Sun, 07 Nov 2021 21:54:53 +0000</pubDate>
      <link>https://dev.to/mrsharm/how-to-lie-with-performance-data-part-1-how-to-lie-with-a-small-number-of-data-points-5g62</link>
      <guid>https://dev.to/mrsharm/how-to-lie-with-performance-data-part-1-how-to-lie-with-a-small-number-of-data-points-5g62</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;As a Performance Engineer, I have observed multiple cases where my intuition has failed me while trying to make inferences about data. Since it is imperative to get good at making decisions with data, emboldening your "BS" detection skillset is of paramount importance for any engineer. &lt;/p&gt;

&lt;p&gt;This blog post is the first of 3 that goes over some ways you or others can lie with data from performance analysis such as Benchmarks or Regression detection based tooling.&lt;/p&gt;

&lt;h2&gt;
  
  
  How To Lie With Small Number of Data Points
&lt;/h2&gt;

&lt;p&gt;A data set with a small number of data points isn't sufficient enough to represent a true distribution especially if the data is littered with anomalous values. &lt;/p&gt;

&lt;h3&gt;
  
  
  Visual Perspective: Histogram
&lt;/h3&gt;

&lt;p&gt;Consider the two data points both generated from a &lt;a href="https://www.scribbr.com/statistics/standard-normal-distribution/"&gt;Standard Normal Distribution&lt;/a&gt; (As an aside, I have never observed any real performance data that follows a normal distribution but for the sake of exposition, I'll stick with this example):&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;For a sample size of n = 10&lt;/em&gt;&lt;/strong&gt; &lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--aadzMnpL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/v4si7zrmf717fjnrs72r.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--aadzMnpL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/v4si7zrmf717fjnrs72r.png" alt="Low # of Samples" width="372" height="248"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Visually, the histogram has no semblance of the standard normal distribution or even a normal distribution.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;For a sample size of n = 10,000&lt;/em&gt;&lt;/strong&gt; &lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--PgLuHISL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/afjy6hv21vh7ojx1n7zs.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--PgLuHISL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/afjy6hv21vh7ojx1n7zs.png" alt="Image description" width="378" height="248"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This distribution is more like the standard normal distribution, the real population from which we sample from indicating that the more the samples, the better we are off in terms of making decisions.&lt;/p&gt;

&lt;p&gt;The code that generated these is the following:&lt;/p&gt;


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


&lt;h3&gt;
  
  
  Statistical Perspective: Standard Error, Margin of Error and Confidence Intervals
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Standard Error
&lt;/h4&gt;

&lt;p&gt;The &lt;strong&gt;Standard Error&lt;/strong&gt; gives us a measure of &lt;strong&gt;accuracy&lt;/strong&gt; of how close the sample is to the true mean. The higher the standard error the less accurate we are in terms of guessing the true mean of a population.&lt;/p&gt;

&lt;p&gt;The formula for standard error is:&lt;/p&gt;

&lt;p&gt;

&lt;/p&gt;
&lt;div class="katex-element"&gt;
  &lt;span class="katex-display"&gt;&lt;span class="katex"&gt;&lt;span class="katex-mathml"&gt;se=snse = \frac{s}{\sqrt{n}} &lt;/span&gt;&lt;span class="katex-html"&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord mathnormal"&gt;se&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mrel"&gt;=&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mopen nulldelimiter"&gt;&lt;/span&gt;&lt;span class="mfrac"&gt;&lt;span class="vlist-t vlist-t2"&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mord sqrt"&gt;&lt;span class="vlist-t vlist-t2"&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span class="svg-align"&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mord mathnormal"&gt;n&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="hide-tail"&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-s"&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="frac-line"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mord mathnormal"&gt;s&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-s"&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="mclose nulldelimiter"&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/div&gt;


&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Variable&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;se&lt;/td&gt;
&lt;td&gt;Standard Error&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;s&lt;/td&gt;
&lt;td&gt;Standard Deviation of the Sample&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;n&lt;/td&gt;
&lt;td&gt;Number of Samples&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;From the formula, we notice that a lower 'n' under the condition of a lower number of samples, we are less accurate in guessing the true statistic such as the mean of a population as the Standard Error is higher.&lt;/p&gt;

&lt;h4&gt;
  
  
  Margin of Error
&lt;/h4&gt;

&lt;p&gt;The Margin of Error is the Standard Error multiplied by a critical value: 
&lt;/p&gt;
&lt;div class="katex-element"&gt;
  &lt;span class="katex-display"&gt;&lt;span class="katex"&gt;&lt;span class="katex-mathml"&gt;t∗t^{*} &lt;/span&gt;&lt;span class="katex-html"&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mord mathnormal"&gt;t&lt;/span&gt;&lt;span class="msupsub"&gt;&lt;span class="vlist-t"&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="sizing reset-size6 size3 mtight"&gt;&lt;span class="mord mtight"&gt;&lt;span class="mord mtight"&gt;∗&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/div&gt;
, a magic constant whose value is derived from a table based on the the number of samples and a confidence %. This table can be found &lt;a href="https://www.coconino.edu/resources/files/pdfs/academics/sabbatical-reports/kate-kozak/appendix_table.pdf"&gt;here&lt;/a&gt; on page 3. 

&lt;p&gt;Formulaically, the margin of error is given by:&lt;/p&gt;


&lt;div class="katex-element"&gt;
  &lt;span class="katex-display"&gt;&lt;span class="katex"&gt;&lt;span class="katex-mathml"&gt;t∗×snt^{*} \times \frac{s}{\sqrt{n}} &lt;/span&gt;&lt;span class="katex-html"&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mord mathnormal"&gt;t&lt;/span&gt;&lt;span class="msupsub"&gt;&lt;span class="vlist-t"&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="sizing reset-size6 size3 mtight"&gt;&lt;span class="mord mtight"&gt;&lt;span class="mord mtight"&gt;∗&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mbin"&gt;×&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mopen nulldelimiter"&gt;&lt;/span&gt;&lt;span class="mfrac"&gt;&lt;span class="vlist-t vlist-t2"&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mord sqrt"&gt;&lt;span class="vlist-t vlist-t2"&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span class="svg-align"&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mord mathnormal"&gt;n&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="hide-tail"&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-s"&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="frac-line"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mord mathnormal"&gt;s&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-s"&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="mclose nulldelimiter"&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/div&gt;


&lt;h4&gt;
  
  
  Confidence Intervals
&lt;/h4&gt;

&lt;p&gt;Confidence Intervals are ranges between which the population statistic can be in with the margin of error as the "radius" or allowable leverage of error. For example, a confidence interval of 99% indicates that 99% of all confidence intervals with confidence level = 99% include the true population statistic.&lt;/p&gt;

&lt;p&gt;For example, for the mean of a population based on a sample is given by:&lt;/p&gt;


&lt;div class="katex-element"&gt;
  &lt;span class="katex-display"&gt;&lt;span class="katex"&gt;&lt;span class="katex-mathml"&gt;[xˉ−t×sn,xˉ+t×sn][ \bar{x} - t^{} \times \frac{s}{\sqrt{n}}, \bar{x} + t^{} \times \frac{s}{\sqrt{n}} ] &lt;/span&gt;&lt;span class="katex-html"&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mopen"&gt;[&lt;/span&gt;&lt;span class="mord accent"&gt;&lt;span class="vlist-t"&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="mord mathnormal"&gt;x&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="accent-body"&gt;&lt;span class="mord"&gt;ˉ&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mbin"&gt;−&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mord mathnormal"&gt;t&lt;/span&gt;&lt;span class="msupsub"&gt;&lt;span class="vlist-t"&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="sizing reset-size6 size3 mtight"&gt;&lt;span class="mord mtight"&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mbin"&gt;×&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mopen nulldelimiter"&gt;&lt;/span&gt;&lt;span class="mfrac"&gt;&lt;span class="vlist-t vlist-t2"&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mord sqrt"&gt;&lt;span class="vlist-t vlist-t2"&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span class="svg-align"&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mord mathnormal"&gt;n&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="hide-tail"&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-s"&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="frac-line"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mord mathnormal"&gt;s&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-s"&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="mclose nulldelimiter"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="mpunct"&gt;,&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mord accent"&gt;&lt;span class="vlist-t"&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="mord mathnormal"&gt;x&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="accent-body"&gt;&lt;span class="mord"&gt;ˉ&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mbin"&gt;+&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mord mathnormal"&gt;t&lt;/span&gt;&lt;span class="msupsub"&gt;&lt;span class="vlist-t"&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="sizing reset-size6 size3 mtight"&gt;&lt;span class="mord mtight"&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mbin"&gt;×&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mopen nulldelimiter"&gt;&lt;/span&gt;&lt;span class="mfrac"&gt;&lt;span class="vlist-t vlist-t2"&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mord sqrt"&gt;&lt;span class="vlist-t vlist-t2"&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span class="svg-align"&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mord mathnormal"&gt;n&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="hide-tail"&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-s"&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="frac-line"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mord mathnormal"&gt;s&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-s"&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="mclose nulldelimiter"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="mclose"&gt;]&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/div&gt;


&lt;p&gt;Since the confidence interval does inherently rely on the number of samples, the conjecture that the "higher number of samples will give us a higher level of confidence about the said population statistic" is valid.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Here are the lessons I have learnt over the years regarding the number of samples:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Be sure to ask the number of data points the descriptive statistics were based off of. If the number of samples are low, inaccurate inferences from the data are possible.&lt;/li&gt;
&lt;li&gt;Make sure that even with seemingly high number of samples, the data is littered with anomalies; find out how to decrease these.&lt;/li&gt;
&lt;li&gt;Sometimes it's infeasible to get more samples - for these cases, make sure your anomalous values are as low as possible.&lt;/li&gt;
&lt;li&gt;Most importantly: always question the data. &lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>performance</category>
      <category>datascience</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Pusher to get Real Time Updates For Your WebApp.</title>
      <dc:creator>Mukund Raghav Sharma (Moko)</dc:creator>
      <pubDate>Sun, 07 Nov 2021 09:36:25 +0000</pubDate>
      <link>https://dev.to/mrsharm/pusher-to-get-real-time-updates-for-your-webapp-4c40</link>
      <guid>https://dev.to/mrsharm/pusher-to-get-real-time-updates-for-your-webapp-4c40</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;I recently discovered &lt;a href="https://pusher.com/"&gt;Pusher&lt;/a&gt; that provides real time communication amongst different processes whether it be server to server or server to client. &lt;/p&gt;

&lt;p&gt;I followed &lt;a href="https://pusher.com/tutorials/realtime-results-nodejs/"&gt;this&lt;/a&gt; tutorial for a MERN stack based messaging app I am currently working on. With just a few lines of code after setting up the Pusher project, I was able to receive updates of my MongoDb instance on my frontend. &lt;/p&gt;

&lt;h2&gt;
  
  
  Basics
&lt;/h2&gt;

&lt;p&gt;Pusher allows you to send and receive &lt;strong&gt;events&lt;/strong&gt; from &lt;strong&gt;channels&lt;/strong&gt;. &lt;/p&gt;

&lt;h3&gt;
  
  
  Server Side
&lt;/h3&gt;

&lt;p&gt;From the &lt;strong&gt;server&lt;/strong&gt; side, you trigger events based on the event names on a particular channel such as the following:&lt;/p&gt;


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


&lt;p&gt;The library to be used on the server can be installed by the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm i pusher
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  Client Side
&lt;/h3&gt;

&lt;p&gt;The &lt;strong&gt;client&lt;/strong&gt; side, you subscribe to particular channels and bind to events both based on the respective names such as the following &lt;code&gt;useEffect&lt;/code&gt;:&lt;/p&gt;


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



&lt;p&gt;The library to be used on the client side can be installed by the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm i pusher-js
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;To summarize in the context of MERN apps, in your server, you observe changes to your data from MongoDB and send those updates on particular channels and with specific event names. And on your front end, you subscribe to the channel and bind to the event names for your real time updates.&lt;/p&gt;

&lt;p&gt;The setup was smooth and the free tier &lt;a href="https://pusher.com/channels/pricing"&gt;benefits&lt;/a&gt; are definitely generous with great &lt;a href="https://pusher.com/docs/"&gt;documentation&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;I'd be interested to hear about your experience with other similar technologies or any other information about Pusher.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>webdev</category>
      <category>react</category>
      <category>programming</category>
    </item>
    <item>
      <title>Finished 90 Days of Meditation</title>
      <dc:creator>Mukund Raghav Sharma (Moko)</dc:creator>
      <pubDate>Sun, 07 Nov 2021 02:30:55 +0000</pubDate>
      <link>https://dev.to/mrsharm/finished-90-days-of-meditation-26ph</link>
      <guid>https://dev.to/mrsharm/finished-90-days-of-meditation-26ph</guid>
      <description>&lt;p&gt;Just finished 90 days of following 2 x 20 minutes / day - first time been this consistent with it and I feel nothing short of great! Used the &lt;a href="https://jamesclear.com/stop-procrastinating-seinfeld-strategy" rel="noopener noreferrer"&gt;Seinfeld Strategy&lt;/a&gt; and &lt;a href="https://www.franklincovey.com/the-4-disciplines/" rel="noopener noreferrer"&gt;4 Disciples of Execution&lt;/a&gt;.&lt;/p&gt;

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

&lt;p&gt;The specific type of meditation I follow is &lt;a href="https://www.tm.org/" rel="noopener noreferrer"&gt;Transcendental Meditation&lt;/a&gt;, however, advocate for any type of stillness you can bring into your life.&lt;/p&gt;

&lt;h2&gt;
  
  
  Observed Benefits
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Clarity of Thought &lt;/li&gt;
&lt;li&gt;Much more energy during the day.&lt;/li&gt;
&lt;li&gt;Better at handling impulses. &lt;/li&gt;
&lt;li&gt;Easier to get into flow.&lt;/li&gt;
&lt;li&gt;More receptive to situations.&lt;/li&gt;
&lt;li&gt;Life becomes easier and repetitious activities no longer seem as mundane as before.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The tracker I used can be found &lt;a href="https://t.co/igmxnzcYWI?amp=1" rel="noopener noreferrer"&gt;here&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;Will gladly answer any questions and help others as this technique has unequivocally changed my life.  &lt;/p&gt;

</description>
      <category>productivity</category>
      <category>motivation</category>
      <category>performance</category>
      <category>writing</category>
    </item>
    <item>
      <title>Random Facts: Async Programming in .NET</title>
      <dc:creator>Mukund Raghav Sharma (Moko)</dc:creator>
      <pubDate>Sat, 06 Nov 2021 05:48:41 +0000</pubDate>
      <link>https://dev.to/mrsharm/random-facts-async-programming-in-net-d1p</link>
      <guid>https://dev.to/mrsharm/random-facts-async-programming-in-net-d1p</guid>
      <description>&lt;p&gt;This post aggregates random tidbits and their sources to pick up on a couple of Async Programming Concepts. I did a deep dive whose notes can be found &lt;a href="https://github.com/MokoSan/DeepDives/tree/main/AsyncAwaitProgrammingInDotNet"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h1&gt;
  
  
  ConfigureAwait
&lt;/h1&gt;

&lt;p&gt;General rule of thumb, use &lt;code&gt;ConfigureAwait(false)&lt;/code&gt; while awaiting a task if the control doesn’t need to go to the original captured context. Without &lt;code&gt;ConfigureAwait(false)&lt;/code&gt;, the next steps are posted on the original captured context i.e. &lt;code&gt;SynchronizationContext.Post(..)&lt;/code&gt;. This can also reduce impact on the UI thread and prevent deadlock conditions.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“As a general rule, every piece of code that is not in a view model and/or that does not need to go back on the main thread should use &lt;code&gt;ConfigureAwait&lt;/code&gt; false.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Good references: &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;a href="https://johnthiriet.com/configure-await/"&gt;https://johnthiriet.com/configure-await/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://channel9.msdn.com/Series/Three-Essential-Tips-for-Async/Async-library-methods-should-consider-using-Task-ConfigureAwait-false-"&gt;https://channel9.msdn.com/Series/Three-Essential-Tips-for-Async/Async-library-methods-should-consider-using-Task-ConfigureAwait-false-&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h1&gt;
  
  
  Thread Affinity
&lt;/h1&gt;

&lt;p&gt;Thread that instantiates the object is the only thread that is allowed to access and mutate the members.&lt;/p&gt;

&lt;p&gt;Good reference:&lt;br&gt;
&lt;a href="https://www.linkedin.com/learning/advanced-threading-in-c-sharp/thread-affinity?u=3322"&gt;https://www.linkedin.com/learning/advanced-threading-in-c-sharp/thread-affinity?u=3322&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  AutoResetEvent vs. ManualResetEvent
&lt;/h1&gt;

&lt;p&gt;Thread-safe signaling mechanism where &lt;code&gt;WaitOne()&lt;/code&gt;: Blocks the current thread until the &lt;code&gt;EventWaitHandle&lt;/code&gt; sends the signal of Set().&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The difference between a tollbooth and a door. The &lt;code&gt;ManualResetEvent&lt;/code&gt; is the door, which needs to be closed (reset) manually. The &lt;code&gt;AutoResetEvent&lt;/code&gt; is a tollbooth, allowing one car to go by and automatically closing before the next one can get through.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Good Reference: &lt;br&gt;
&lt;a href="https://stackoverflow.com/questions/153877/what-is-the-difference-between-manualresetevent-and-autoresetevent-in-net"&gt;https://stackoverflow.com/questions/153877/what-is-the-difference-between-manualresetevent-and-autoresetevent-in-net&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  CountdownEvent
&lt;/h1&gt;

&lt;p&gt;&lt;code&gt;IDisposable&lt;/code&gt; synchronization primitive that signals when the count reaches 0. &lt;code&gt;Signal()&lt;/code&gt; decrements the count and the threads that are blocked via &lt;code&gt;Wait()&lt;/code&gt; are released when the count = 0.&lt;/p&gt;

&lt;h1&gt;
  
  
  TaskCompletionSource
&lt;/h1&gt;

&lt;p&gt;&lt;code&gt;TaskCompletionSource&lt;/code&gt; is a class which wraps a Task whose state we can manually control.&lt;/p&gt;

&lt;p&gt;Good References:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;a href="http://www.levibotelho.com/development/async-processes-with-taskcompletionsource/"&gt;http://www.levibotelho.com/development/async-processes-with-taskcompletionsource/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Use Case: &lt;a href="https://stackoverflow.com/questions/15316613/when-should-taskcompletionsourcet-be-used"&gt;https://stackoverflow.com/questions/15316613/when-should-taskcompletionsourcet-be-used&lt;/a&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h1&gt;
  
  
  IO vs. CPU Bound and Async Programming in CSharp
&lt;/h1&gt;

&lt;p&gt;For I/O-bound code, you await an operation which returns a &lt;code&gt;Task&lt;/code&gt; or &lt;code&gt;Task&amp;lt;T&amp;gt;&lt;/code&gt; inside of an async method.&lt;br&gt;
For CPU-bound code, you await an operation which is started on a background thread with the &lt;code&gt;Task.Run&lt;/code&gt; method.&lt;/p&gt;

&lt;h1&gt;
  
  
  Background Threads
&lt;/h1&gt;

&lt;p&gt;The background threads will stop executing when it either finishes executing the delegate, or when there are no more foreground threads in the entire process i.e. the process will disallow the continuation of the background thread execution if the foreground threads have reached the end of the process.&lt;/p&gt;

</description>
      <category>csharp</category>
      <category>dotnet</category>
      <category>programming</category>
    </item>
    <item>
      <title>My First React App Deployed on Firebase: Dog Facts</title>
      <dc:creator>Mukund Raghav Sharma (Moko)</dc:creator>
      <pubDate>Sat, 06 Nov 2021 03:50:01 +0000</pubDate>
      <link>https://dev.to/mrsharm/my-first-react-app-deployed-on-firebase-dog-facts-5fo3</link>
      <guid>https://dev.to/mrsharm/my-first-react-app-deployed-on-firebase-dog-facts-5fo3</guid>
      <description>&lt;h1&gt;
  
  
  Introduction
&lt;/h1&gt;

&lt;p&gt;After developing and deploying my first Javascript based API that I wrote about &lt;a href="https://dev.to/mrsharm/my-first-javascript-rest-api-dog-facts-deployed-to-heroku-522p"&gt;here&lt;/a&gt;, I decided to create a a simple frontend for the API with React.js. Once the application was complete, I deployed it to firebase: &lt;a href="https://dog-facts-2277d.web.app/" rel="noopener noreferrer"&gt;https://dog-facts-2277d.web.app/&lt;/a&gt;. &lt;/p&gt;

&lt;h2&gt;
  
  
  Development
&lt;/h2&gt;

&lt;p&gt;What made development smooth was learning about:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;a href="https://reactjs.org/docs/components-and-props.html" rel="noopener noreferrer"&gt;React Components&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://getbem.com/naming/" rel="noopener noreferrer"&gt;BEM Naming Convention&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.youtube.com/watch?v=JJSoEo8JSnc" rel="noopener noreferrer"&gt;Tutorial on CSS Flex Box&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Additionally, making use of react-bootstrap's Card component simplified a lot of the styling of the individual facts. Granted this website is incredibly simple, I am still happy with the results:&lt;/p&gt;

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

&lt;p&gt;The Github repo for this project can be found &lt;a href="https://github.com/MokoSan/DogFacts" rel="noopener noreferrer"&gt;here&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Deployment
&lt;/h2&gt;

&lt;p&gt;Deployment with Firebase was a breeze. I used the Firebase CLI to easily initialize the project and then deploy. What was cool was the integration with Github using Actions i.e. with each push there is an automatic deployment of the new app.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Any feedback would greatly be appreciated! &lt;/p&gt;

</description>
      <category>react</category>
      <category>javascript</category>
      <category>webdev</category>
      <category>css</category>
    </item>
    <item>
      <title>38/45 Books: Read! 7 more to Go!</title>
      <dc:creator>Mukund Raghav Sharma (Moko)</dc:creator>
      <pubDate>Fri, 05 Nov 2021 01:02:15 +0000</pubDate>
      <link>https://dev.to/mrsharm/3845-books-read-7-more-to-go-11g9</link>
      <guid>https://dev.to/mrsharm/3845-books-read-7-more-to-go-11g9</guid>
      <description>&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--kG2nqyEq--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3cukak5lbkc42rq1ugfu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--kG2nqyEq--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3cukak5lbkc42rq1ugfu.png" alt="Image description" width="567" height="487"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;7 more to go till the end of the year. Tracking my progress &lt;a href="https://github.com/MokoSan/BookReviews_2021/blob/main/Progress%20Tracker.ipynb"&gt;here&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;Top 5 Books:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Atomic Habits - James Clear&lt;/li&gt;
&lt;li&gt;Mindset - Carol Dweck&lt;/li&gt;
&lt;li&gt;Stillness is the Key - Ryan Holiday&lt;/li&gt;
&lt;li&gt;The War of Art - Steven Pressfield&lt;/li&gt;
&lt;li&gt;Man's Search For Meaning - Viktor Frankl &lt;/li&gt;
&lt;/ol&gt;

</description>
      <category>books</category>
    </item>
    <item>
      <title>My First JavaScript REST API: Dog Facts Deployed To Heroku</title>
      <dc:creator>Mukund Raghav Sharma (Moko)</dc:creator>
      <pubDate>Thu, 04 Nov 2021 23:20:47 +0000</pubDate>
      <link>https://dev.to/mrsharm/my-first-javascript-rest-api-dog-facts-deployed-to-heroku-522p</link>
      <guid>https://dev.to/mrsharm/my-first-javascript-rest-api-dog-facts-deployed-to-heroku-522p</guid>
      <description>&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--8uXRDkLt--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7efdyzuarzo4x9fxlrcw.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--8uXRDkLt--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7efdyzuarzo4x9fxlrcw.jpg" alt="Dogs" width="880" height="664"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;I recently deployed my first Javascript REST API that returns facts about dogs. The impetus behind creating this project was to get a working front end solution with the API providing the data. While working on the front end, I started making use of the python API by DukeNgn that can be found &lt;a href="https://github.com/DukeNgn/Dog-facts-API"&gt;here&lt;/a&gt; and ran into &lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS"&gt;CORs&lt;/a&gt; issues that prevented me from getting the data. &lt;/p&gt;

&lt;h2&gt;
  
  
  Development
&lt;/h2&gt;

&lt;p&gt;My main goal was to get the API deployed as quickly as possible and therefore, relied on the simplest solution making use of &lt;code&gt;Express&lt;/code&gt; and the Javascript &lt;code&gt;fs&lt;/code&gt; library to read a JSON file with all the facts. I also used the &lt;code&gt;cors&lt;/code&gt; javascript library to make sure I wouldn't run into the same issue as the python library:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// App config.
const app = express();
app.use(express.json());
app.use(Cors());
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;All the code is available &lt;a href="https://github.com/MokoSan/DogFacts-API"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Deployment with Heroku
&lt;/h2&gt;

&lt;p&gt;The deployment to Heroku was smooth following &lt;a href="https://devcenter.heroku.com/articles/git"&gt;this&lt;/a&gt; tutorial. The only issue I got into was hardcoding a port that wasn't agreeable with Heroku for which configuring the listening port accordingly did the trick:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const port = process.env.PORT || 5000;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One of the coolest Heroku features was to hook up my GitHub repository to Heroku resulting in an automatic deployment whenever I pushed changes.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--hpEzyBZ9--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/cd56u85y7574nejuw10n.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--hpEzyBZ9--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/cd56u85y7574nejuw10n.png" alt="Image description" width="880" height="221"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Usage
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://dogfacts-api.herokuapp.com/api/v1/resources/dogs"&gt;https://dogfacts-api.herokuapp.com/api/v1/resources/dogs&lt;/a&gt; for all the dog facts.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://dogfacts-api.herokuapp.com/api/v1/resources/dogs?number=%7Bnumber%7D"&gt;https://dogfacts-api.herokuapp.com/api/v1/resources/dogs?number={number}&lt;/a&gt; for a particular number of random dog facts.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://dogfacts-api.herokuapp.com/api/v1/resources/dogs?index=%7Bindex%7D"&gt;https://dogfacts-api.herokuapp.com/api/v1/resources/dogs?index={index}&lt;/a&gt; for a dog fact associated with the index - there are 435 total facts.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Example
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://dogfacts-api.herokuapp.com/api/v1/resources/dogs?number=2"&gt;https://dogfacts-api.herokuapp.com/api/v1/resources/dogs?number=2&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Output:
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[
    {"fact":"Dogs are direct descendants of wolves."},
    {"fact":"Small quantities of grapes and raisins can cause renal failure in dogs. Chocolate, macadamia nuts, cooked onions, or anything with caffeine can also be harmful."}
]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Building the API was an awesome learning experience! Any feedback about the code or this blogpost would be greatly appreciated.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>programming</category>
      <category>opensource</category>
      <category>heroku</category>
    </item>
  </channel>
</rss>
