<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Tech | Jacob Aloysious</title><link>https://jacobaloysious.in/tag/tech/</link><atom:link href="https://jacobaloysious.in/tag/tech/index.xml" rel="self" type="application/rss+xml"/><description>Tech</description><generator>Source Themes Academic (https://sourcethemes.com/academic/)</generator><language>en-us</language><lastBuildDate>Fri, 03 Dec 2021 00:00:00 +0000</lastBuildDate><image><url>https://jacobaloysious.in/images/icon_hu4591c05f594249c11c1e99a3a8f1f246_3759739_512x512_fill_lanczos_center_2.png</url><title>Tech</title><link>https://jacobaloysious.in/tag/tech/</link></image><item><title>PKCE</title><link>https://jacobaloysious.in/post/tech_pkce/</link><pubDate>Fri, 03 Dec 2021 00:00:00 +0000</pubDate><guid>https://jacobaloysious.in/post/tech_pkce/</guid><description>&lt;p>
&lt;a href="https://jacobaloysious.in/post/tech_oauth/" title="OAuth">Prerequisite: OAuth2.0&lt;/a>&lt;/p>
&lt;p>PKCE is pronounced as &amp;lsquo;pixie&amp;rsquo;. PKCE - Proof Key for Code Exchange =&amp;gt;
&lt;a href="https://www.youtube.com/watch?v=5cQNwifDq1U" target="_blank" rel="noopener">Credits&lt;/a>&lt;/p>
&lt;h3 id="concepts">Concepts:&lt;/h3>
&lt;h4 id="what-is-confidential-client">What is Confidential Client?&lt;/h4>
&lt;p>Any backend application written in languages like .Net, Java, NodeJS can be considered as Confidential Client. As they have the ability to &amp;ldquo;store variables&amp;rdquo; in our case Secret Keys - without exposing them to the end user - as the code is running on the server side. The secret key would be used to generate the Access Token in the OAuth Flow.&lt;/p>
&lt;h4 id="what-is-public-client">What is public Client?&lt;/h4>
&lt;ol>
&lt;li>
&lt;p>Any Single Page Application (SPA), where all the code is downloaded to the users browser would be considered a Public Client. Coz: the secret key cannot be securely stored as the hacker could do a &amp;ldquo;view source code&amp;rdquo; and look at the secret key which is usually in plain text/string format.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Another example are Mobile Apps - the user can download and de-compile/reverse engineer - the application and find the secret key.&lt;/p>
&lt;/li>
&lt;/ol>
&lt;h3 id="why-do-you-need-pkce">Why do you need PKCE?&lt;/h3>
&lt;p>Once the secret key is available - the hacker can impersonate as the Client and generate the access token (note: clientID is public and secret key is private). Once the access token is available - the hacker can access lot more confidential information about the user. So the existing
&lt;a href="https://jacobaloysious.in/post/tech_oauth/" title="OAuth">OAuth2.0&lt;/a> model is not secure for both SPA and mobile apps. To solve this challenge -
&lt;a href="https://www.rfc-editor.org/rfc/pdfrfc/rfc7636.txt.pdf" target="_blank" rel="noopener">PKCE&lt;/a> was introduced.&lt;/p>
&lt;h4 id="oauth-20-normal-flow">OAuth 2.0 Normal Flow:&lt;/h4>
&lt;p>&lt;img src="oauth-normal-flow.jpg" alt="alt Components" title="OAuth2.0 Normal Flow">&lt;/p>
&lt;h4 id="pkce-flow">PKCE Flow:&lt;/h4>
&lt;p>&lt;img src="pkce-flow-2.jpg" alt="alt Components" title="PKCE Flow">&lt;/p>
&lt;h3 id="what-is-new-in-pkce">What is new in PKCE?&lt;/h3>
&lt;ol>
&lt;li>In the PKCE flow: Instead of a constant Secret Key - Everytime an OAuth flow is initiated - &lt;strong>A new &amp;ldquo;Secret Key&amp;rdquo;&lt;/strong> is generated and Hashed.&lt;/li>
&lt;li>Hash is shared with the Authentication Server(AS) when requesting for the Temporary Code. AS would remember this Hash value.&lt;/li>
&lt;li>When the client request for the Access Token - it sends the newly generated &amp;ldquo;Secret Key&amp;rdquo; - the AS would check, if the secret matches the Hash (it already remembers - #2).&lt;/li>
&lt;/ol>
&lt;h3 id="reference">Reference:&lt;/h3>
&lt;p>
&lt;a href="https://www.rfc-editor.org/rfc/pdfrfc/rfc7636.txt.pdf" target="_blank" rel="noopener">RFC7636&lt;/a>,
&lt;a href="https://www.youtube.com/watch?v=aU9RsE4fcRM" target="_blank" rel="noopener">Oauth Hack&lt;/a>,
&lt;a href="https://www.youtube.com/watch?v=5cQNwifDq1U" target="_blank" rel="noopener">What&amp;rsquo;s the difference between Confidential and Public clients&lt;/a>&lt;/p></description></item><item><title>Designated Authentication - OAuth2.0</title><link>https://jacobaloysious.in/post/tech_oauth/</link><pubDate>Thu, 02 Dec 2021 00:00:00 +0000</pubDate><guid>https://jacobaloysious.in/post/tech_oauth/</guid><description>&lt;p>
&lt;a href="https://www.youtube.com/watch?v=996OiexHze0" target="_blank" rel="noopener">OAuth2.0 and OpenID&lt;/a> - is one of the best videos (62 mins) on this topic - Highly recommended. This blog is just a quick summary (4 mins read) of it.&lt;/p>
&lt;h3 id="what-is-oauth">What is OAuth?&lt;/h3>
&lt;p>OAuth is a security standard where you give one application permission to access your data from another application i.e. you authorized one application to use data from another application on your behalf, without giving them your username and password. The steps to grant permission(s) or consent is often reffered to as authorization or delegated authorization. Permissions are scoped e.g. client can be given access to read your GMail contacts but cannot Read/Write your email. OAuth is not exactly used for user identification (who loggged in), instead its used to access API&amp;rsquo;s for resources like gmail contact list.&lt;/p>
&lt;p>&lt;img src="access-card-example.jpg" alt="alt Components" title="Access Card Example">&lt;/p>
&lt;ul>
&lt;li>You check-in to a hotel.&lt;/li>
&lt;li>At the front desk - You give your details like Passport and Reservation&lt;/li>
&lt;li>Front Desk would in turn - give you the Key Card i.e. Access Token&lt;/li>
&lt;li>You can use the Key Card to access your reserved Room&lt;/li>
&lt;/ul>
&lt;p>Here, the Front Desk is the Authentication Server - the key card is your Access token used to acess your Room (Resources API). The API doesn&amp;rsquo;t really care who is accessing the Resource as long as they have the access token.&lt;/p>
&lt;h3 id="oauth-terminologies">OAuth Terminologies:&lt;/h3>
&lt;p>Lets assume you are giving Yelp access to your Gmail account contacts..&lt;/p>
&lt;ol>
&lt;li>&lt;strong>Resource Owner:&lt;/strong> Represent you the owner of the gmail account; who can give permission/authorize.&lt;/li>
&lt;li>&lt;strong>Client&lt;/strong>: Represent Yelp i.e. the application which would like to perform action on behalf of you.&lt;/li>
&lt;li>&lt;strong>Authorization Server:&lt;/strong> One who validates the gmail uname/password and authenticate - in this case &lt;em>accounts.google.com&lt;/em>. Resource owner will already have an account with this server (gmail).&lt;/li>
&lt;li>&lt;strong>Resource Server:&lt;/strong> Server (API) which has the actual content/resources like contact list in this case &lt;em>contacts.google.com&lt;/em>. Which client would talk to get the actual data.&lt;/li>
&lt;li>&lt;strong>Authorization Grant:&lt;/strong> Once authenticated an &lt;em>Authorizatiion Code&lt;/em> or temporary code is sent back to he client. So, the client has been granted relevant access to the resource owners gmail contacts. This is short lived, this is used to inturn get the access token (long lived).&lt;/li>
&lt;li>&lt;strong>Redirect URI:&lt;/strong> Also known as Callback. It is the URI to which the response of Authentication #5 should be sent back to client #2&lt;/li>
&lt;li>&lt;strong>Access Token:&lt;/strong> Token/Key used by the Client to access all the resources of the owner; based on the authorized permissions.&lt;/li>
&lt;li>&lt;strong>Response Type&lt;/strong>: The type of information the client would like to receive e.g: code (authorization code)&lt;/li>
&lt;li>&lt;strong>Scope:&lt;/strong> Granular permission the client wants acess to like Read Contact, Read Profile basic info.&lt;/li>
&lt;li>&lt;strong>Consent:&lt;/strong> Whether the client can be given permission to access the requested information.&lt;/li>
&lt;li>&lt;strong>ClientID:&lt;/strong> Unique ID of the client, which is used to talk to the authorization server.&lt;/li>
&lt;li>&lt;strong>Client Secret:&lt;/strong> Secret Key - only the client and the auth server shares. Helps Client and Resource Server to share information secretly behind the scenes.&lt;/li>
&lt;/ol>
&lt;h3 id="oauth-flow">OAuth Flow:&lt;/h3>
&lt;h4 id="prerequisite">Prerequisite:&lt;/h4>
&lt;p>Client and the Authorization Server should establish a working relationship. Both the &amp;ldquo;ClientID (AppID)&amp;rdquo; and &amp;ldquo;Client Secret(AppSecret)&amp;rdquo; are generated by the Authorization Server. The client ID would be used to uniquely identify the client. Its the responsibility of the client to ensure the &amp;ldquo;ClientSecret&amp;rdquo; is kept secret and its only know by client and authorization server. As there are the only means for the Auth Server to verify the client.&lt;/p>
&lt;h4 id="actual-flow">Actual Flow:&lt;/h4>
&lt;p>In this example, Yelp would want to authenticate a user based on GMail Authentication, and access Profile/Contacts information.&lt;/p>
&lt;p>&lt;img src="oauth-code-flow.jpg" alt="alt Components" title="OAuth Code Flow">&lt;/p>
&lt;ol>
&lt;li>Client(Yelp) would make a request to Authenticate - to &amp;ldquo;Autorization Server&amp;rdquo; (accounts.google.com); as part of the request:
&lt;ol>
&lt;li>&amp;ldquo;RedirectURI&amp;rdquo; is set to a callback URL of the client &amp;ldquo;yelp.com/callback&amp;rdquo;&lt;/li>
&lt;li>&amp;ldquo;Respose Type&amp;rdquo; is set to &amp;ldquo;Code&amp;rdquo;, so a auth code is sent back as response to 1.a&lt;/li>
&lt;li>&amp;ldquo;Client ID&amp;rdquo; and &amp;ldquo;Scope&amp;rdquo; (Profile/Contacts)&lt;/li>
&lt;/ol>
&lt;/li>
&lt;li>Auth Server pops up a dialog (mostly) - where user gets to enter the GMail credentials (uname/pass) and authenticate&lt;/li>
&lt;li>Auth Server shows up a consent form - list the info access requested (like contacts, profile) - user gets to choose Yes or NO&lt;/li>
&lt;li>Once concent(Yes) is given - an &amp;ldquo;Auth Code&amp;rdquo; is generated and sent back to the the client - to specifiied callback/redirectURI.&lt;/li>
&lt;li>Then, the client sends the &amp;ldquo;Auth Code + ClientID + ClientSecret&amp;rdquo; to &amp;ldquo;Authorization Server&amp;rdquo; and gets back an &amp;ldquo;Access Token&amp;rdquo;&lt;/li>
&lt;li>Using the &amp;ldquo;Access Token&amp;rdquo;, Client can contact the resource server (contacts.google.com) to get the actual resources (profile, contacts) of the user.&lt;/li>
&lt;/ol>
&lt;p>Reference: &lt;a href="https://www.youtube.com/watch?v=aU9RsE4fcRM">https://www.youtube.com/watch?v=aU9RsE4fcRM&lt;/a>&lt;/p></description></item><item><title>WebFinger</title><link>https://jacobaloysious.in/post/tech_webfinger/</link><pubDate>Wed, 01 Dec 2021 00:00:00 +0000</pubDate><guid>https://jacobaloysious.in/post/tech_webfinger/</guid><description>&lt;p>&lt;em>This blog contain just snips - a detailed explanation is available in the Reference links..&lt;/em>&lt;/p>
&lt;h3 id="background">Background:&lt;/h3>
&lt;p>Twitter started the project called
&lt;a href="https://blueskyweb.org/" target="_blank" rel="noopener">Bluesky&lt;/a> with a goal to build a social web. As part of it,
&lt;a href="https://www.w3.org/TR/activitypub/" target="_blank" rel="noopener">ActivityPub&lt;/a> a decentralized social networking protocol was built - which uses WebFinger in the background.&lt;/p>
&lt;h3 id="what-is-webfinger">What is WebFinger:&lt;/h3>
&lt;p>WebFinger is used to discover information about people or other entities on the Internet that are identified by a URI using standard Hypertext Transfer Protocol (HTTP) methods over a secure transport. A WebFinger resource returns a JavaScript Object Notation (JSON) object describing the entity that is queried.&lt;/p>
&lt;h3 id="key-items-to-note">Key Items to Note:&lt;/h3>
&lt;p>WebFinger protocol is used to request information about an entity identified by a query target (a URI).&lt;/p>
&lt;p>A WebFinger request is an HTTPS request to a WebFinger resource.&lt;/p>
&lt;p>A WebFinger resource is a well-known URI&lt;/p>
&lt;p>A WebFinger resource is always given a query target, which is another URI that identifies the entity whose information is sought.
GET requests to a WebFinger resource convey the query target in the &amp;ldquo;resource&amp;rdquo; parameter of the WebFinger URI’s query string&lt;/p>
&lt;p>The path component of a WebFinger URI MUST be the well-known path &lt;strong>&amp;quot;/.well-known/webfinger&amp;rdquo;&lt;/strong>.&lt;/p>
&lt;p>The WebFinger resource returns a JSON Resource Descriptor (JRD) as the resource representation to convey information about an entity on the Internet&lt;/p>
&lt;p>The use of HTTPS is REQUIRED to ensure that information is not modified during transit.
Clients MUST verify that the certificate used on an HTTPS connection is valid and accept a response only if the certificate is valid.&lt;/p>
&lt;p>The Registration Template:&lt;/p>
&lt;ul>
&lt;li>Property Identifier:&lt;/li>
&lt;li>Link Type:&lt;/li>
&lt;li>Description:&lt;/li>
&lt;li>Reference:&lt;/li>
&lt;li>Notes: [optional]
The &amp;ldquo;Property Identifier&amp;rdquo; must be a URI that identifies the property being registered.&lt;/li>
&lt;/ul>
&lt;h4 id="example">Example:&lt;/h4>
&lt;p>Suppose Lia wishes to authenticate with a web site she visits using OpenID Connect. She would provide the web site with her OpenID Connect identifier, say &lt;a href="mailto:lia@example.com">lia@example.com&lt;/a>. The visited web site would perform a WebFinger query looking for the OpenID Connect provider. Since the site is interested in only one particular link relation, the WebFinger resource might utilize the &amp;ldquo;rel&amp;rdquo; parameter&lt;/p>
&lt;p>&lt;strong>Request:&lt;/strong>&lt;/p>
&lt;pre>&lt;code> GET /.well-known/webfinger?
resource=acct%3Alia%40example.com&amp;amp;
rel=http%3A%2F%2Fopenid.net%2Fspecs%2Fconnect%2F1.0%2Fissuer
HTTP/1.1
Host: example.com
&lt;/code>&lt;/pre>
&lt;p>&lt;strong>Response:&lt;/strong>&lt;/p>
&lt;pre>&lt;code> HTTP/1.1 200 OK
Access-Control-Allow-Origin: *
Content-Type: application/jrd+json
{
&amp;quot;subject&amp;quot; : &amp;quot;acct:lia@example.com&amp;quot;,
&amp;quot;links&amp;quot; :
[
{
&amp;quot;rel&amp;quot; : &amp;quot;http://openid.net/specs/connect/1.0/issuer&amp;quot;,
&amp;quot;href&amp;quot; : &amp;quot;https://openid.example.com&amp;quot;
}
]
}
&lt;/code>&lt;/pre>
&lt;p>Since the &amp;ldquo;rel&amp;rdquo; parameter only serves to filter the link relations returned by the resource, other name/value pairs in the response, including any aliases or properties, would be reeturned. Also, since support for the &amp;ldquo;rel&amp;rdquo; parameter is not guaranteed, the client must not assume the &amp;ldquo;links&amp;rdquo; array will contain only the requested link relation.&lt;/p>
&lt;h4 id="take-ways">Take ways:&lt;/h4>
&lt;p>The specification is rather simple as it uses JSON as the data model for all the communication. Almost, all languages provide support for handling JSON. Validation is still not part of the spec - So both validation and handling attack should be the responsiblity for the client.&lt;/p>
&lt;h4 id="references">References:&lt;/h4>
&lt;ul>
&lt;li>
&lt;a href="https://www.rfc-editor.org/rfc/pdfrfc/rfc7033.txt.pdf" target="_blank" rel="noopener">WebFinger Protocol&lt;/a>&lt;/li>
&lt;li>
&lt;a href="https://bluesky-community.net/" target="_blank" rel="noopener">Bluesky&lt;/a>&lt;/li>
&lt;li>
&lt;a href="https://gitlab.com/bluesky-community1/decentralized-ecosystem/-/blob/master/protocols/activitypub.md" target="_blank" rel="noopener">ActivityPub&lt;/a>&lt;/li>
&lt;li>
&lt;a href="https://www.w3.org/TR/activitypub/" target="_blank" rel="noopener">W3 ActivityPub&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>WASM Cloud</title><link>https://jacobaloysious.in/post/tech_wasm_cloud/</link><pubDate>Fri, 26 Nov 2021 00:00:00 +0000</pubDate><guid>https://jacobaloysious.in/post/tech_wasm_cloud/</guid><description>&lt;h4 id="what-is-wasm-cloud-trying-to-solve">What is WASM Cloud trying to solve?&lt;/h4>
&lt;p>Lets ask - what a typical &amp;ldquo;Development Iteration&amp;rdquo; look like?&lt;/p>
&lt;ol>
&lt;li>Start of with your Napkin Drawings - conceptual vision of what is it you want to build&lt;/li>
&lt;li>Initial Prototype, prove that it works&lt;/li>
&lt;li>Then, Figure out how to scale&lt;/li>
&lt;/ol>
&lt;p>The challenge is between #2 and #3 there is more often a &lt;strong>complete rewrite&lt;/strong>. As the author claims- Virtually nothing from prototype is written in a way it is scalable.&lt;/p>
&lt;p>The goal of the project is to &lt;strong>&amp;ldquo;Ship Napkins to Production&amp;rdquo;&lt;/strong> directly.&lt;/p>
&lt;ol>
&lt;li>Draw your conceptual map of how your application should look like&lt;/li>
&lt;li>Fill in the blanks with your business logic and just push it - boom!!&lt;/li>
&lt;li>Don&amp;rsquo;t worry about any of the non-sense in between.&lt;/li>
&lt;li>Remove all the boiler plate and let developer write only Business logic.&lt;/li>
&lt;/ol>
&lt;h4 id="how-is-this-achived">How is this achived?&lt;/h4>
&lt;p>Using the &lt;strong>&amp;ldquo;Actor Model&amp;rdquo;&lt;/strong> and the out of the box boilerplate infrastructure.
Personally, I a fan of Actor Model, as its easy to just write a function which has a single responsibility. If you have ever worked with Threads and IPC(Interprocess communication) - you would know the overheads that come in managing them. With the Actor model you have an input queue, an output queue and the function in between. Developer shall write only the function. Function would read from the input queue, process and write it to the output queue. The infrastructure should take care of running the actor, as a thread or process and also manage the input/output queue, data transfer, serialization etc. One of the popular implementation of Actor model is
&lt;a href="https://akka.io/" target="_blank" rel="noopener">AKKA&lt;/a>.&lt;/p>
&lt;p>From the authors:&lt;/p>
&lt;blockquote>
&lt;p>The creators of wasmCloud believe that we can not only change the way developers build software for the better, but make it easier to secure, deploy, maintain, observe, and upgrade that software as well–all while reducing the amount of boilerplate we have to copy and paste.&lt;/p>
&lt;/blockquote>
&lt;h4 id="example">Example:&lt;/h4>
&lt;p>Lets look at their
&lt;a href="https://wasmcloud.dev/app-dev/create-actor/generate/" target="_blank" rel="noopener">Hello World&lt;/a> example.&lt;/p>
&lt;p>Interesting snippets:&lt;/p>
&lt;pre>&lt;code> #[derive(Debug, Default, Actor, HealthResponder)]
#[services(Actor, HttpServer)]
struct HelloActor {}
/// Implementation of HttpServer trait methods
#[async_trait]
impl HttpServer for HelloActor {
//Actual Impl
}
&lt;/code>&lt;/pre>
&lt;ul>
&lt;li>&lt;em>HelloActor&lt;/em> is the name of your actor&lt;/li>
&lt;li>The two lines with &amp;ldquo;#&amp;rdquo; above the actor name igenerate - at compile time - nearly all of the scaffolding needed to build an actor.&lt;/li>
&lt;li>The &lt;em>HealthResponder&lt;/em> term generates a function that automatically responds to health check queries from the &lt;em>wasmCloud&lt;/em> host.&lt;/li>
&lt;li>The &lt;em>#[services(&amp;hellip;)]&lt;/em> line declares the servicesthat your actor implements, and generates message handling code for those interfaces.&lt;/li>
&lt;li>All actors implement the &lt;em>Actor&lt;/em> interface.&lt;/li>
&lt;li>The &lt;em>HttpServer&lt;/em> entry declares that the actor will also implement that interface, and requires an implementation of that trait’s method: handle_request.&lt;/li>
&lt;/ul>
&lt;h4 id="conclusion">Conclusion:&lt;/h4>
&lt;p>I am fan of distributed computing: designing and writing scalable application. Maybe the concept of wasmCloud doing the heavy lifting sounds easy; but I belive its a very hard problem. Coz: when we actually get our hands dirty with actual application design - challenges like: managing states, versioning, schema management/registry, idempotency, trade offs w.r.t CAP theorm, heterogeneous system etc..all comes in. Also, given what Docker + Kubernetes eco system already provides - finding the right architecture to use wasmCloud would be a challenge. But this is definitely a place lot of things can be improved and room for optimization. Star&amp;rsquo;ed the project for now, will be following it closely.&lt;/p>
&lt;p>Reference:
&lt;a href="https://wasmcloud.dev/" target="_blank" rel="noopener">WASM Cloud&lt;/a>,
&lt;a href="https://github.com/wasmCloud/wasmCloud" target="_blank" rel="noopener">GitHub&lt;/a>&lt;/p></description></item><item><title>Rubber Duck Debugging</title><link>https://jacobaloysious.in/post/tech_rubber_duck_debugging/</link><pubDate>Wed, 24 Nov 2021 00:00:00 +0000</pubDate><guid>https://jacobaloysious.in/post/tech_rubber_duck_debugging/</guid><description>&lt;p>I have been in a similar situation multiple times - where in I was stuck on a problem. To get a solution, I would want to reach out for help. So, I would start to write it down - as it could post it in StackOverflow or send it as an email to my friend/colleague. While I am writing the problem down, I try to set the context and elaborate on the problem statement - after few iteration/reformatting/edits - its strange that I would have found the solution. Oops-What happened???&lt;/p>
&lt;p>Stepping back and looking at it, what had happened is - while we try to explain the problem to someone (in writing) - we try to remove all the clutter and focus on the actual problem - with the intent to keep the &lt;strong>email shorter and digestable components&lt;/strong>, so that the audience/reader is not overwhelmed. But, in the process we have a better clarity of the core (only) problem we are trying to solve. When that clarity comes up, more often we get closer to the solution.&lt;/p>
&lt;p>BTW: I never knew, there was a name for it - &lt;strong>&amp;ldquo;Rubber Duck Debugging&amp;rdquo;&lt;/strong> until I heard it from
&lt;a href="https://twitter.com/shanselman" target="_blank" rel="noopener">scott hanselman&lt;/a> in one of his podcast. And there is a website
&lt;a href="https://rubberduckdebugging.com/" target="_blank" rel="noopener">rubberduckdebugging&lt;/a> for it.&lt;/p>
&lt;p>Snip from the site:&lt;/p>
&lt;blockquote>
&lt;ol>
&lt;li>Place rubber duck on desk and inform it you are just going to go over some code with it, if that’s all right.&lt;/li>
&lt;li>Explain to the duck what your code is supposed to do, and then go into detail and explain your code line by line.&lt;/li>
&lt;li>At some point you will tell the duck what you are doing next and then realise that that is not in fact what you are actually doing. The duck will sit there serenely, happy in the knowledge that it has helped you on your way.&lt;/li>
&lt;/ol>
&lt;/blockquote>
&lt;p>Well, I had to buy one rubber duck, as I wasn&amp;rsquo;t able to negotiate to lease one with my 7yr old daughter 😉&lt;/p>
&lt;p>The next time you get stuck on a hard issue, take a moment and write down exactly whats going wrong as if you were going to explain it to someone else, you may just notice exactly where you are going wrong and even if you dont, at least you have a question all ready for submission to StackOverflow&amp;hellip;&lt;/p></description></item><item><title>Software Enthusiast - Getting Started</title><link>https://jacobaloysious.in/post/tech_getting_started/</link><pubDate>Wed, 24 Nov 2021 00:00:00 +0000</pubDate><guid>https://jacobaloysious.in/post/tech_getting_started/</guid><description>&lt;p>I have been asked this question multiple times. I would like to be a software enthusiast and where do I get started. Thanks to my Friend to help put this together&lt;/p>
&lt;h3 id="books">Books:&lt;/h3>
&lt;h4 id="coding">Coding:&lt;/h4>
&lt;ul>
&lt;li>
&lt;a href="https://www.oreilly.com/library/view/clean-code-a/9780136083238/" target="_blank" rel="noopener">Clean Code: A Handbook of Agile Software Craftsmanship&lt;/a> *&lt;/li>
&lt;/ul>
&lt;h4 id="unit-testing">Unit Testing:&lt;/h4>
&lt;ul>
&lt;li>
&lt;a href="https://www.manning.com/books/the-art-of-unit-testing-second-edition" target="_blank" rel="noopener">The Art of Unit Testing, 2nd Edition&lt;/a> *&lt;/li>
&lt;/ul>
&lt;h4 id="design">Design:&lt;/h4>
&lt;ul>
&lt;li>
&lt;a href="https://www.oreilly.com/library/view/design-patterns-elements/0201633612/" target="_blank" rel="noopener">Design Patterns: Elements of Reusable Object-Oriented Software&lt;/a>*&lt;/li>
&lt;li>
&lt;a href="https://www.oreilly.com/library/view/head-first-design/9781492077992/" target="_blank" rel="noopener">Head First Design Patterns, 2nd Edition&lt;/a> *&lt;/li>
&lt;/ul>
&lt;h4 id="solid-principles">SOLID principles:&lt;/h4>
&lt;ul>
&lt;li>
&lt;a href="https://cleancoders.com/" target="_blank" rel="noopener">Clean Coders: Level up your code&lt;/a> *&lt;/li>
&lt;li>
&lt;a href="https://www.oreilly.com/library/view/clean-architecture-a/9780134494272/" target="_blank" rel="noopener">Clean Architecture: A Craftsman’s Guide to Software Structure and Design&lt;/a> *&lt;/li>
&lt;/ul>
&lt;h4 id="refactoring">Refactoring:&lt;/h4>
&lt;ul>
&lt;li>
&lt;a href="https://www.oreilly.com/library/view/refactoring-improving-the/9780134757681/" target="_blank" rel="noopener">Refactoring: Improving the Design of Existing Code&lt;/a>&lt;/li>
&lt;li>
&lt;a href="https://www.oreilly.com/library/view/working-effectively-with/0131177052/" target="_blank" rel="noopener">Working Effectively with Legacy Code&lt;/a>&lt;/li>
&lt;/ul>
&lt;h4 id="architecture">Architecture:&lt;/h4>
&lt;ul>
&lt;li>
&lt;a href="https://www.oreilly.com/library/view/clean-architecture-a/9780134494272/" target="_blank" rel="noopener">Clean Architecture: A Craftsman’s Guide to Software Structure and Design&lt;/a> *&lt;/li>
&lt;li>
&lt;a href="https://www.oreilly.com/library/view/designing-data-intensive-applications/9781491903063/" target="_blank" rel="noopener">Designing Data-Intensive Applications&lt;/a> *&lt;/li>
&lt;li>
&lt;a href="https://martinfowler.com/architecture/" target="_blank" rel="noopener">Software Architecture Guide&lt;/a>&lt;/li>
&lt;li>
&lt;a href="https://www.oreilly.com/library/view/building-microservices/9781491950340/" target="_blank" rel="noopener">Building Microservices&lt;/a>
&lt;em>*must read books&lt;/em>&lt;/li>
&lt;/ul>
&lt;h2 id="keeping-up-with-ever-evolving-tech-space">Keeping up with ever evolving tech Space:&lt;/h2>
&lt;h3 id="youtube-channels-to-follow">Youtube channels to follow&lt;/h3>
&lt;ul>
&lt;li>
&lt;a href="https://www.youtube.com/c/NDCConferences" target="_blank" rel="noopener">NDC Conferences&lt;/a>&lt;/li>
&lt;li>
&lt;a href="https://www.youtube.com/c/GotoConferences" target="_blank" rel="noopener">GOTO Conferences&lt;/a>&lt;/li>
&lt;li>
&lt;a href="https://www.youtube.com/nctv" target="_blank" rel="noopener">InfoQ&lt;/a>&lt;/li>
&lt;/ul>
&lt;h3 id="twitter-handles-to-follow">Twitter handles to follow:&lt;/h3>
&lt;ul>
&lt;li>
&lt;a href="https://twitter.com/allenholub" target="_blank" rel="noopener">allenholub&lt;/a>&lt;/li>
&lt;li>
&lt;a href="https://twitter.com/ploeh" target="_blank" rel="noopener">ploeh&lt;/a>&lt;/li>
&lt;li>
&lt;a href="https://twitter.com/jbogard" target="_blank" rel="noopener">jbogard&lt;/a>&lt;/li>
&lt;li>
&lt;a href="https://twitter.com/mfeathers" target="_blank" rel="noopener">mfeathers&lt;/a>&lt;/li>
&lt;li>
&lt;a href="https://twitter.com/GeePawHill" target="_blank" rel="noopener">GeePawHill&lt;/a>&lt;/li>
&lt;li>
&lt;a href="https://twitter.com/Grady_Booch" target="_blank" rel="noopener">Grady_Booch&lt;/a>&lt;/li>
&lt;li>
&lt;a href="https://twitter.com/shanselman" target="_blank" rel="noopener">shanselman&lt;/a>&lt;/li>
&lt;li>
&lt;a href="https://twitter.com/KentBeck" target="_blank" rel="noopener">KentBeck&lt;/a>&lt;/li>
&lt;li>
&lt;a href="https://twitter.com/martinfowler" target="_blank" rel="noopener">martinfowler&lt;/a>&lt;/li>
&lt;li>
&lt;a href="https://twitter.com/unclebobmartin" target="_blank" rel="noopener">unclebobmartin&lt;/a>&lt;/li>
&lt;li>
&lt;a href="https://twitter.com/ansonhorton" target="_blank" rel="noopener">Anson Horton&lt;/a>&lt;/li>
&lt;li>
&lt;a href="https://twitter.com/madstorgersen?lang=en" target="_blank" rel="noopener">Mads Torgersen&lt;/a>&lt;/li>
&lt;li>
&lt;a href="https://twitter.com/joboccara" target="_blank" rel="noopener">Jonathan Boccara&lt;/a>&lt;/li>
&lt;li>
&lt;a href="https://twitter.com/thecleancoders" target="_blank" rel="noopener">Clean Coders&lt;/a>&lt;/li>
&lt;li>
&lt;a href="https://twitter.com/davidfowl" target="_blank" rel="noopener">David Fowler&lt;/a>&lt;/li>
&lt;li>
&lt;a href="https://twitter.com/John_Papa" target="_blank" rel="noopener">John Papa&lt;/a>&lt;/li>
&lt;li>
&lt;a href="https://twitter.com/spolsky" target="_blank" rel="noopener">Joel Spolsky&lt;/a>&lt;/li>
&lt;li>
&lt;a href="https://twitter.com/robconery" target="_blank" rel="noopener">Rob Conery&lt;/a>&lt;/li>
&lt;li>
&lt;a href="https://twitter.com/codinghorror" target="_blank" rel="noopener">Jeff Atwood&lt;/a>&lt;/li>
&lt;li>
&lt;a href="https://twitter.com/migueldeicaza" target="_blank" rel="noopener">Miguel “mask and vax” de Icaza&lt;/a>&lt;/li>
&lt;li>
&lt;a href="https://twitter.com/haacked" target="_blank" rel="noopener">Boom Haackalacka&lt;/a>&lt;/li>
&lt;li>
&lt;a href="https://twitter.com/shanselman" target="_blank" rel="noopener">Scott Hanselman&lt;/a>&lt;/li>
&lt;/ul>
&lt;h3 id="podcasts--blogs-to-follow">Podcasts &amp;amp; blogs to follow:&lt;/h3>
&lt;ul>
&lt;li>
&lt;a href="https://www.dataengineeringpodcast.com/" target="_blank" rel="noopener">The Data Engineering Podcast&lt;/a>&lt;/li>
&lt;li>
&lt;a href="https://www.hanselminutes.com/" target="_blank" rel="noopener">Hanselminutes&lt;/a>&lt;/li>
&lt;li>
&lt;a href="https://www.thoughtworks.com/insights/podcasts/technology-podcasts/all-episodes" target="_blank" rel="noopener">Technology podcast: all episodes | Thoughtworks&lt;/a>&lt;/li>
&lt;li>
&lt;a href="https://netflixtechblog.com/" target="_blank" rel="noopener">Netflix TechBlog&lt;/a>&lt;/li>
&lt;li>
&lt;a href="https://www.thoughtworks.com/en-in/radar" target="_blank" rel="noopener">Technology Radar | An opinionated guide to technology frontiers | Thoughtworks&lt;/a>&lt;/li>
&lt;li>
&lt;a href="https://engineering.fb.com/" target="_blank" rel="noopener">Facebook Engineering Blog&lt;/a>&lt;/li>
&lt;li>
&lt;a href="https://engineeringblog.yelp.com/" target="_blank" rel="noopener">Yelp Engineering Blog&lt;/a>&lt;/li>
&lt;li>
&lt;a href="https://eng.uber.com/" target="_blank" rel="noopener">Uber Engineering Blog&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>Reverse ETL</title><link>https://jacobaloysious.in/post/tech_reverse_etl/</link><pubDate>Sun, 14 Nov 2021 00:00:00 +0000</pubDate><guid>https://jacobaloysious.in/post/tech_reverse_etl/</guid><description>&lt;p>While listening to
&lt;a href="https://www.dataengineeringpodcast.com/reverse-etl-and-customer-data-platforms-episode-235/" target="_blank" rel="noopener">Podcast&lt;/a> stumbled upon this term of Reverse-ETL.&lt;/p>
&lt;p>First lets align on a few basic terminologies:&lt;/p>
&lt;h4 id="what-is-a-data-warehouse-dw">What is a Data Warehouse (DW):&lt;/h4>
&lt;p>Data Warehouse centralizes and consolidates data from multiple sources like application log files and transaction applications. Orgainzation use DW to gain valuable insights from data, which can improve decision making by building a historical record of data. It can be considered the single source of truth.&lt;/p>
&lt;h4 id="what-is-etl">What is ETL&lt;/h4>
&lt;p>&lt;img src="ETL.jpg" alt="alt Components" title="ETL">
ETL Stands for &lt;em>Extract Transform and Load&lt;/em>. ETL is a data integration process that combines data from multiple data sources into a single, consistent data store that is loaded into a data warehouse or other target system.&lt;/p>
&lt;h4 id="what-is-reverse-etl">What is Reverse ETL&lt;/h4>
&lt;p>&lt;img src="ReverseETL.jpg" alt="alt Components" title="Reverse ETL">
Reverse ETL is the process of copying data from a central data warehouse to operational systems of record, including but not limited to SaaS tools used for growth, marketing, sales and support.&lt;/p>
&lt;h3 id="discussion">Discussion:&lt;/h3>
&lt;p>Hang On! this doesn&amp;rsquo;t make sense - why should someone do ETL and then Reverse it? Coz, just ETL in itself - is a HUGE infrastructure to both develop and maintain - why would I manage both. Well, as always devil is in the details.&lt;/p>
&lt;p>Lets see an example from
&lt;a href="https://hightouch.io" target="_blank" rel="noopener">Hightouch&lt;/a>
&lt;img src="example.jpg" alt="alt Components" title="Example">&lt;/p>
&lt;p>&lt;strong>Reasoning&lt;/strong>(&lt;em>by Hightouch&lt;/em>):&lt;/p>
&lt;blockquote>
&lt;p>Reverse ETL is necessary because your data warehouse — the platform you bought to eliminate data silos — has ironically become a data silo. Without reverse ETL, your business’s core definitions only live in the warehouse.&lt;/p>
&lt;/blockquote>
&lt;p>More detailed information like use cases are avaialble in this blog
&lt;a href="https://hightouch.io/blog/reverse-etl/" target="_blank" rel="noopener">Reverse ETL&lt;/a>&lt;/p>
&lt;h3 id="conclusion">Conclusion:&lt;/h3>
&lt;p>The intent here seems to bridge the knowlege gap, that comes in due to data warehouse. Say: If a business user is so used to a particular platform/tech like SalesForce, the user would prefer the analystic output/data to be available in their platform. Incase of Data Warehouse, the SalesForce business user should understand and learn the new ways to access the data and its corresponding schema in the DW; and also learn the access mechanisms like scripts, sql etc.. With Reverse ETL, it creates a comfortable/known space for the user as all the &amp;ldquo;Actionable Data&amp;rdquo; from the analytics are available in their platform. So, sure it makes sense.&lt;/p>
&lt;p>Only challenge, I see as an organization is - the effort in maintaining both ETL and Reverse ETL infrastructure. Any changes in the DW schema - should be notified to both the teams and the deliverables should be synced up - to avoid any broken pipelines.&lt;/p>
&lt;p>Reference:
&lt;a href="https://www.oracle.com/in/database/what-is-a-data-warehouse/" target="_blank" rel="noopener">DataWarehouse&lt;/a>&lt;/p></description></item><item><title>Access Control - Casbin</title><link>https://jacobaloysious.in/post/tech_casbin/</link><pubDate>Sun, 31 Oct 2021 00:00:00 +0000</pubDate><guid>https://jacobaloysious.in/post/tech_casbin/</guid><description>&lt;p>I was trying to read about RBAB (Role-Based Access Control) and ABAC(Attribute-Based Access Control); after some reading - I realized, as a software engineer - it&amp;rsquo;s way more easier when you get to read code - So, I started to search for open source project which implements access control infrastructure, and stumbled upon
&lt;a href="https://casbin.org/" target="_blank" rel="noopener">Casbin&lt;/a>.&lt;/p>
&lt;blockquote>
&lt;p>&lt;em>Snip from Readme:&lt;/em> In Casbin, an access control model is abstracted into a CONF file based on the &lt;strong>PERM metamodel (Policy, Effect, Request, Matchers)&lt;/strong>. So switching or upgrading the authorization mechanism for a project is just as simple as modifying a configuration. You can customize your own access control model by combining the available models. For example, you can get RBAC roles and ABAC attributes together inside one model and share one set of policy rules.&lt;/p>
&lt;/blockquote>
&lt;p>The interesting part here is how Casbin - has designed the whole system behind a simple yet flexible configuration file and grammars to describe predicates. Intent of this blog is - just to get you curious about Casbin. Casbin has reasonally good documentation on their site.&lt;/p>
&lt;p>&lt;em>ProTip:&lt;/em> Best way to understand an open source is to start with the test cases, look at each test case - review the context and assert statements.&lt;/p>
&lt;p>Lets look at a simple
&lt;a href="https://github.com/jacobaloysious/pycasbin/blob/master/examples/rbac_model.conf" target="_blank" rel="noopener">RBAC Model Config&lt;/a>&lt;/p>
&lt;pre>&lt;code>[request_definition]
r = sub, obj, act
[policy_definition]
p = sub, obj, act
[role_definition]
g = _, _
[policy_effect]
e = some(where (p.eft == allow))
[matchers]
m = g(r.sub, p.sub) &amp;amp;&amp;amp; r.obj == p.obj &amp;amp;&amp;amp; r.act == p.act
&lt;/code>&lt;/pre>
&lt;p>
&lt;a href="https://github.com/jacobaloysious/pycasbin/blob/master/examples/rbac_policy.csv" target="_blank" rel="noopener">RBAC_Policy.CSV&lt;/a>&lt;/p>
&lt;pre>&lt;code>p, alice, data1, read
p, bob, data2, write
p, data2_admin, data2, read
p, data2_admin, data2, write
g, alice, data2_admin
&lt;/code>&lt;/pre>
&lt;p>Basically there are four parts: Request, Policy, Matcher and Effect.
&lt;a href="https://casbin.org/docs/en/how-it-works" target="_blank" rel="noopener">Ref&lt;/a>&lt;/p>
&lt;p>&lt;strong>Request:&lt;/strong> Defines the parameter &lt;em>name&lt;/em> and &lt;em>order&lt;/em> which we should provide for access control matching function. This enables the code/model to be setup when the data is read from the source.&lt;/p>
&lt;p>&lt;strong>Policy:&lt;/strong> It&amp;rsquo;s the Access stratergy. This is how our internal system is designed. It defines the name and order of the fields in the &lt;em>Policy rule document&lt;/em>.&lt;/p>
&lt;p>&lt;strong>Matchers:&lt;/strong>: This is similar to a predicate function - which shall describe if the request allowed. Rules which help us to match the request and the policy.&lt;/p>
&lt;p>&lt;strong>Effect:&lt;/strong> It&amp;rsquo;s useful, when there are multiple patterns which would match and you would need to make a decision. There are defaults, here you get to override both allow and deny defaults. Refer:
&lt;a href="https://casbin.org/docs/en/syntax-for-models#policy-effect" target="_blank" rel="noopener">PolicyEffect&lt;/a>.&lt;/p>
&lt;h4 id="inheritance">Inheritance:&lt;/h4>
&lt;ul>
&lt;li>If A has role B, B has role C, then A has role C. This transitivity is infinite for now&lt;/li>
&lt;li>Casbin doesn&amp;rsquo;t distinguish role from user in its RBAC. They are all treated as strings.&lt;/li>
&lt;/ul>
&lt;pre>&lt;code>p, data2_admin, data2, read
g, alice, data2_admin
&lt;/code>&lt;/pre>
&lt;p>In the above example,&lt;/p>
&lt;ul>
&lt;li>data2_admin has read access to data2&lt;/li>
&lt;li>alice inherits/is a member of role data2_admin. alice here can be a user, resource or a role.&lt;/li>
&lt;/ul>
&lt;p>
&lt;a href="https://github.com/jacobaloysious/pycasbin/blob/master/tests/rbac/test_role_manager.py" target="_blank" rel="noopener">test_role_manager.py&lt;/a>&lt;/p>
&lt;pre>&lt;code> rm = get_role_manager()
#1
rm.add_link(&amp;quot;u1&amp;quot;, &amp;quot;g1&amp;quot;)
rm.add_link(&amp;quot;u3&amp;quot;, &amp;quot;g2&amp;quot;)
self.assertTrue(rm.has_link(&amp;quot;u1&amp;quot;, &amp;quot;g1&amp;quot;))
self.assertFalse(rm.has_link(&amp;quot;u1&amp;quot;, &amp;quot;g2&amp;quot;))
self.assertCountEqual(rm.get_roles(&amp;quot;u1&amp;quot;), [&amp;quot;g1&amp;quot;])
self.assertCountEqual(rm.get_roles(&amp;quot;u2&amp;quot;), [&amp;quot;g1&amp;quot;])
#2
rm.add_link(&amp;quot;u1&amp;quot;, &amp;quot;g1&amp;quot;, &amp;quot;domain1&amp;quot;)
self.assertTrue(rm.has_link(&amp;quot;u1&amp;quot;, &amp;quot;g1&amp;quot;, &amp;quot;domain1&amp;quot;))
self.assertFalse(rm.has_link(&amp;quot;u1&amp;quot;, &amp;quot;g1&amp;quot;, &amp;quot;domain2&amp;quot;))
&lt;/code>&lt;/pre>
&lt;h4 id="what-casbin-does-not-do">What Casbin does NOT do:&lt;/h4>
&lt;ul>
&lt;li>Authentication (aka verify username and password when a user logs in)&lt;/li>
&lt;li>manage the list of users or roles.&lt;/li>
&lt;/ul>
&lt;p>Reference:
&lt;a href="https://github.com/jacobaloysious/pycasbin" target="_blank" rel="noopener">Casbin&lt;/a>&lt;/p></description></item><item><title>Bitmap Indexed Storage</title><link>https://jacobaloysious.in/post/tech_bitmaped_indexed_storage/</link><pubDate>Sat, 10 Jul 2021 00:00:00 +0000</pubDate><guid>https://jacobaloysious.in/post/tech_bitmaped_indexed_storage/</guid><description>&lt;p>Always been facinated by the ways - data structure&amp;rsquo;s can enable better storage and faster queries.&lt;/p>
&lt;p>BitMapped Indexed Storage - is useful while using &lt;strong>Columnar Databases&lt;/strong> - where per column data is stored together.&lt;/p>
&lt;p>Often the number of &lt;strong>distinct&lt;/strong> values in a column is small compared to the total number of rows. In the below example: column &lt;em>sex&lt;/em> there are only two distinct value &lt;em>Female or Male&lt;/em> in the given table - the cardinality is really low.&lt;/p>
&lt;p>In such cases: We can now take a column with &lt;em>n&lt;/em> distinct values and turn it into &lt;em>n&lt;/em> separate bitmaps: one bitmap for each distinct value, with one bit for each row. The bit is 1 if the row has that value, and 0 if not.&lt;/p>
&lt;p>Picture is worth a thousand words.. here we you go..&lt;/p>
&lt;p>&lt;img src="dbimage.jpg" alt="alt Components" title="Work">&lt;/p>
&lt;p>Fantastic Query performance&amp;hellip; As the amount of data read from disk on to the memory is less - improved Disk Throughput.
As always, solution are for a specific problem. If you wanna do a range query, then this solution might not the best.&lt;/p>
&lt;p>If &lt;em>n&lt;/em> is very small, those bitmaps can be stored with one bit per row. But if &lt;em>n&lt;/em> is bigger, there will be a lot of zeros in most of the bitmaps (&lt;em>sparse&lt;/em>). In that case, the bitmaps can additionally be run-length encoded or might not be recommened.&lt;/p>
&lt;p>Reference:
&lt;a href="https://www.goodreads.com/book/show/23463279-designing-data-intensive-applications" target="_blank" rel="noopener">Designing Data-Intensive Applications, by Martin Kleppmann &lt;/a>&lt;/p></description></item><item><title>Sqlite - Blunders</title><link>https://jacobaloysious.in/post/tech_sqlitedb/</link><pubDate>Sat, 05 Jun 2021 00:00:00 +0000</pubDate><guid>https://jacobaloysious.in/post/tech_sqlitedb/</guid><description>&lt;h3 id="what-is-sqlite">What is Sqlite:&lt;/h3>
&lt;blockquote>
&lt;p>SQLite is a C-language library that implements a small, fast, self-contained, high-reliability, full-featured, SQL database engine. SQLite is the most used database engine in the world.
SQLite is built into all mobile phones and most computers and comes bundled inside countless other applications that people use every day. More Info:
&lt;a href="https://www.sqlite.org/index.html" target="_blank" rel="noopener">SQLite&lt;/a>&lt;/p>
&lt;/blockquote>
&lt;h2 id="my-learning">My Learning:&lt;/h2>
&lt;p>Sqlite is a fantastic embedded database - light weight, easy to use and almost Zero Configuration. I would prefer to call it ZMQ of DataBase 😉&lt;/p>
&lt;h3 id="blunder">Blunder:&lt;/h3>
&lt;p>As part of our design: we had to persist a bunch of parametric information per user session (few MBs). This information is not required outside the user session. But, it would be reused when the user comes back. Session - is local running on a windows box. We decided to use sqlite as our backend persistence mechanism; as there was no installation or specific configuration required to set sqlite up.&lt;/p>
&lt;p>Since there are parallel read write happening, we had to use the
&lt;a href="https://sqlite.org/wal.html" target="_blank" rel="noopener">WAL: Write Ahead Logging&lt;/a> journal mode.&lt;/p>
&lt;p>We did couple of blunders:&lt;/p>
&lt;h4 id="blunder-1---insert-one-at-a-time">Blunder #1 - Insert ONE at a time&lt;/h4>
&lt;ol>
&lt;li>When an action occured - we opened the db connection, inserted record and closed it.&lt;/li>
&lt;li>We didn&amp;rsquo;t want to keep the connection open, throughout the session - as there were chances of software crash - which could potentially corrupt he database.&lt;/li>
&lt;li>But as time went by, the number of actions became HUGE (order of millions - automated). At this point - Opening and closing the connection for every action, started to hurt us (&lt;em>high latency and low throughtput&lt;/em>). Coz, the time taken to open and close a connection (~300ms) was adding up.&lt;/li>
&lt;/ol>
&lt;p>Solution:
&lt;strong>Microbatching&lt;/strong>: Batched about 500 actions together - open the connection once, inserted all 500 actions in one shot and closed - Transaction. Worst case, we would loose less than 500 actions, and that tradeoff was acceptable.&lt;/p>
&lt;h4 id="blunder-2---mounted-drive">Blunder #2 - Mounted Drive:&lt;/h4>
&lt;ol>
&lt;li>The db had to be persisted on a mounted Linux drive (&lt;em>there are reason for this, I won&amp;rsquo;t go into details&lt;/em>).&lt;/li>
&lt;li>As the session started - the app created a sqliteDB directly on the mounted drive and started inserting.&lt;/li>
&lt;li>Since we were using WAL mode - there were temporary files getting created and deleted on the mounted drive during every transaction.&lt;/li>
&lt;li>As time went by - the reliablity of the drive/driver went down, and retries didn&amp;rsquo;t help keep up. The throughput significantly got impacted and multiple errors started showing up.&lt;/li>
&lt;/ol>
&lt;p>&lt;img src="mounted_drive.jpg" alt="alt Components" title="Mounted Drive">&lt;/p>
&lt;p>&lt;strong>Solution:&lt;/strong> During the session, create the db in a &lt;strong>local (temp) drive&lt;/strong>, and end of sesssion copy it into the mounted drive. Similarly, when session restarts - copy from mounted drive on to a new temp location and consume it; and copy it back on completion.&lt;/p>
&lt;p>&lt;img src="temp_drive.jpg" alt="alt Components" title="Temp Drive">&lt;/p>
&lt;blockquote>
&lt;p>Never EVER - read or write an Sqlite Database from a mounted Drive. It usually works, but when it doesn&amp;rsquo;t - its a nightmare.&lt;/p>
&lt;/blockquote>
&lt;h4 id="blunder-3---lazy-load">Blunder #3 - Lazy Load:&lt;/h4>
&lt;ol>
&lt;li>When a specific information was required - we opened the db connection, executed the &amp;ldquo;select&amp;rdquo; query and return the results.&lt;/li>
&lt;li>Again: We didn&amp;rsquo;t want to keep the connection open, throughout the session - as there were chances of software crash - which could potentially corrupt he database.&lt;/li>
&lt;li>The read was implemented in a &amp;ldquo;Lazy - On demand&amp;rdquo; fashion - so that &lt;strong>only&lt;/strong> the minimum required information was read on demand.&lt;/li>
&lt;li>Reading only the required info is an ideal design - why would we ever read more that what is required? GraphQL - is a good example of how to design minimilistic API.&lt;/li>
&lt;li>But the challenge in our (specific) case: was the number of reads - it was about 600reads in less than 50 milliseconds. And each one had to open and close the connection (which takes ~50ms).&lt;/li>
&lt;li>Since it was a high throughput system, the overhead of lazy read was slowing down the entire software.&lt;/li>
&lt;/ol>
&lt;p>Solution: &lt;strong>PreFetch Require Information&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>Since we knew the sequence in which the actions are performed, we redesigned the query to read &lt;strong>&amp;ldquo;more&amp;rdquo;&lt;/strong> records than required.&lt;/li>
&lt;li>Then created an in memory cache [index/map] to hold the data and filter based on Client Query.&lt;/li>
&lt;li>Read from DB would occur only if there is a cache miss.&lt;/li>
&lt;li>The cache was a rolling buffer with LRU (Least Recently Used) and a watermark (memory or count), so that the RAM didn&amp;rsquo;t explode.&lt;/li>
&lt;li>When a new read from DB was required, if the cache is full - we delete the oldest item and read from DB and add it to cache.&lt;/li>
&lt;li>No change to the Client API. Only the Data Acess Layer changed. 😉&lt;/li>
&lt;/ul>
&lt;h3 id="conclusion">Conclusion:&lt;/h3>
&lt;p>As always on hindsight, the solution looks trivial and obvious. Since, we were using Sqlite for the first time and initially didn&amp;rsquo;t reallize the data could grow exponentially - had to learn (Adapt) the hard way. Esp, when these are running in production - you need scheme&amp;rsquo;s to manage old vs new verisons of software (read, backward compatibility).&lt;/p></description></item><item><title>Concurrent Dictionary and Delay - C#</title><link>https://jacobaloysious.in/post/tech_concruent_dictionary/</link><pubDate>Sun, 30 May 2021 00:00:00 +0000</pubDate><guid>https://jacobaloysious.in/post/tech_concruent_dictionary/</guid><description>&lt;p>Reference:
&lt;a href="https://docs.microsoft.com/en-us/dotnet/api/system.collections.concurrent.concurrentdictionary-2?view=net-5.0" target="_blank" rel="noopener">ConcurrentDictionary&lt;/a>&lt;/p>
&lt;p>In a high throuput and low latency enviorment - we had a peice of code in the critical path - which helped in throttling. After version #2.0 of the software was released (internally) - we saw the througput has gone down significantly almost 3X - compared to previous Version #1.0 - 4mins to 11 minutes.&lt;/p>
&lt;p>We looked at the history/change sets between the versions: it was huge, 100s of commits and software was running with 100s of threads - on a 2Milliion LOC software stack. So, we were unsure which peice of code nor thread - was causing the delay. We were all over the place, trying to see if its a system issue or memory trend or some fragmentation vs memory/back pressure vs excess logging etc&amp;hellip;. And, after 3 day (yes: 3days slog) - we found its a delay caused by the &lt;strong>ConcurrentDictionary&lt;/strong> class!!! 😳&lt;/p>
&lt;p>It was an unpleasant suprise, since we didn&amp;rsquo;t expect such a huge delay cause due to a &lt;em>.Net Framework class&lt;/em>.&lt;/p>
&lt;h4 id="1-version-10--4mins">1. Version 1.0: [4mins]&lt;/h4>
&lt;p>One API with its backing data store with one Dictionary.&lt;/p>
&lt;pre>&lt;code> private Dictionary&amp;lt;int, User&amp;gt; userInfos;
public List&amp;lt;User&amp;gt; GetUsers() {
return userInfos.Values;
}
&lt;/code>&lt;/pre>
&lt;h4 id="2-version-20--11-minutes">2. Version 2.0: [11 minutes]&lt;/h4>
&lt;p>In version #2: We changed the data store, which was holding the user info from a simple dictionary to a two level &lt;em>ConcurrentDictionay&lt;/em>.&lt;/p>
&lt;p>There was some obvious reason for using &lt;em>ConcurrentDictionay&lt;/em> - I won&amp;rsquo;t go into the details. But, after changing the data store- we ended up blindly updating all usages to consume from the new data store.&lt;/p>
&lt;p>And the API (GetUsers) implementation was updated, interface remained the same - but: it was in the critical path.&lt;/p>
&lt;pre>&lt;code> private ConcurrentDictionary&amp;lt;int, ConcurrentDictionary&amp;lt;int, User&amp;gt;&amp;gt; userInfos;
public List&amp;lt;User&amp;gt; GetUsers(){
var users = new List&amp;lt;User&amp;gt;();
for(var userGroup in userInfos.Values){
for(var user in userGroup.Values){
users.add(user)
}
return users;
}
}
&lt;/code>&lt;/pre>
&lt;h4 id="3-version-30--4-minutes">3. Version 3.0: [4 minutes]&lt;/h4>
&lt;p>After looking into the usages &lt;strong>&amp;ldquo;in the critical path&amp;rdquo;&lt;/strong> - we only required the count of the active user groups.&lt;/p>
&lt;p>So, we wrote another API to just return the count. With this change, we were able to bring it back the throughput back to #Version 1.0. Huff!! 😌&lt;/p>
&lt;pre>&lt;code> private ConcurrentDictionary&amp;lt;int, ConcurrentDictionary&amp;lt;int, User&amp;gt;&amp;gt; userInfos;
public List&amp;lt;User&amp;gt; GetActiveUserGroups(){
return userInfos.Count;
}
&lt;/code>&lt;/pre>
&lt;h3 id="learning">Learning:&lt;/h3>
&lt;p>On highsight, the fix looks trivial and straight forward. But the challenge was the huge debugging effort to find the root cause. And little did we suspect a .Net Framework class can cause such a huge delay and slow down overall execution time. It was a great learning for us as a team - involved in debugging. BTW, we are talking about a code base of about few Million lines of code.&lt;/p>
&lt;p>BTW: Concurrent dictionary exists for a reason: Usage of it as our data store was the right choice.&lt;/p>
&lt;blockquote>
&lt;p>Represents a thread-safe collection of key/value pairs that can be accessed by multiple threads concurrently.&lt;/p>
&lt;/blockquote>
&lt;p>If a class should be thread safe, then there has to be a lock/Mutex implemented. A lock has its own tradeoff in terms of execution time (Thread Scheduling, Prempt etc..) - so think carefully on its usages.&lt;/p></description></item><item><title>Parallel Processing with Constraints</title><link>https://jacobaloysious.in/post/tech_context_parallel_process/</link><pubDate>Sat, 15 May 2021 00:00:00 +0000</pubDate><guid>https://jacobaloysious.in/post/tech_context_parallel_process/</guid><description>&lt;p>An interesting senario, I recently encountered - have tried to tear down the context to get to the solution early, and avoid nitty gritty (boring) details. Also, example showns only 2 threads.&lt;/p>
&lt;h3 id="context">Context:&lt;/h3>
&lt;p>Different Actions (like: 1,2,3) from Cutomers (like Mr.A, Miss.B..) are pushed into a stream/queue from an external component. &lt;strong>&amp;ldquo;$&amp;rdquo;&lt;/strong> denotes end/last action from a customer.
A thread can 1) Process each action 2) Generate aggregated views for each customer per session - and then write it to the sink. The processing threads are started when the session starts and a user cannot reenter the session once done.&lt;/p>
&lt;p>note: all threads mentioned are background and long running.&lt;/p>
&lt;h3 id="constraints">Constraints:&lt;/h3>
&lt;p>Aggregation can be triggered only after all the actions are processed. i.e processing of action A1, A2, A3 should be done - before aggregation &amp;ldquo;A$&amp;rdquo; of A is triggered.&lt;/p>
&lt;h2 id="1-single-thread">1. Single Thread&lt;/h2>
&lt;p>Well, this is a no brainer. We have one thread which reads (pull) from the stream/queue and process one item from the queue at a time. All constraints can be managed with a global context inside the thread.&lt;/p>
&lt;p>&lt;img src="SingleThread.jpg" alt="alt Components" title="SingleThread">&lt;/p>
&lt;p>Below is a code which runs inside the Thread.&lt;/p>
&lt;pre>&lt;code> # {name, [actions]}
customer = {}
def read_from_stream(name, action):
if action=='$':
aggregate(name)
else:
process(name, action)
def process(name, action)
result = process_the_custom_action(action)
cusomter[name].append(result)
def aggregate(name):
action_results = cutomer[name]
view = gen_agg_view(action_results)
write_to_sink(view)
# remove customer from tracking
del customer[name]
def process_the_custom_action(action):
#Heavy lifting operation
return result
&lt;/code>&lt;/pre>
&lt;h2 id="2-pull-model---parallel-processing">2. Pull Model - Parallel Processing&lt;/h2>
&lt;p>Later we realized, the function &lt;strong>processing_the_custom_action&lt;/strong> is heavy lifting and takes more CPU. So, we were not able to keep up, with the incoming data rate. So, the memory increased drastically and triggers garbage collection and slow down actual code execution.&lt;/p>
&lt;p>So, we decided to introduce multiple threads to read from the queue. Same code as above - executed in multiple threads. Each thread would pop (PULL) one item from the queue, process it and then pop the next item. Just to be explicit: one item from the queue is processed by only one of the threads, but which thread (order) gets which item is not guranteed.&lt;/p>
&lt;p>&lt;img src="MultiThreadPullModel.jpg" alt="alt Components" title="PullModel">&lt;/p>
&lt;p>This design had a major challenge in ensuring the constraints are honored.&lt;/p>
&lt;p>In the above example: Aggregation for customer &amp;ldquo;A$&amp;rdquo; should be triggered only after last action &amp;ldquo;A3&amp;rdquo; is done. But &amp;ldquo;A$&amp;rdquo; and &amp;ldquo;A3&amp;rdquo; are processed in two different threads i.e. A3 by Thread #1; A$ by Thread #2. How would Thread #2 know A3 processing is done?? 😞&lt;/p>
&lt;p>If we really need to go with this approach and also honor the constraints - then there should be &lt;strong>broadcast events fired&lt;/strong> and all threads subscribed to it. Each thread should subscribe and do individual book keeping for all customers being processed across all the threads (dependency across threads, coupling..huff!!) 😰&lt;/p>
&lt;p>&lt;strong>Pros:&lt;/strong>
If any of the thread is killed in between. The data loss would be minimal i.e only the customers already read by that specific thread in the past and not completed. As all NEW data would be processed by one of the threads alive.&lt;/p>
&lt;h2 id="3-push-model---parallel-processing">3. Push Model - Parallel Processing&lt;/h2>
&lt;p>To ensure, we honor the constraint and also enable parallel processing. We introduced a load_balancer, called it a &lt;strong>distributor module&lt;/strong>.&lt;/p>
&lt;p>&lt;img src="MultiThreadPushModel.jpg" alt="alt Components" title="PushModel">&lt;/p>
&lt;h4 id="distributor-module">Distributor Module:&lt;/h4>
&lt;ul>
&lt;li>Would have reference to all the available processing threads.&lt;/li>
&lt;li>Each thread would have a corresponding queue. So, No_Of_Threads equals No_Of_Queueus.&lt;/li>
&lt;li>Each queue would have an index, which range from 0 to No_of_threads.
&lt;blockquote>
&lt;p>0 &amp;lt; Queue_Index &amp;lt; No_Of_Threads&lt;/p>
&lt;/blockquote>
&lt;/li>
&lt;li>Once an item is read - it generates a hash using the &amp;ldquo;name&amp;rdquo; of the customer. (like: SHA256)&lt;/li>
&lt;li>On the HASH we apply a MOD using the No_Of_Threads available and get the queue index
&lt;blockquote>
&lt;p>Queue_Index = hash(customer_name) % No_Of_Threads&lt;/p>
&lt;/blockquote>
&lt;/li>
&lt;li>Push the item to the corresponding queue_index&lt;/li>
&lt;/ul>
&lt;p>With this approach, we ensured - &lt;strong>same customer items are always processed by the same thread&lt;/strong>. So, there are no cross talks across threads or external dependency.
We could increase or decrease the number of threads used (obviously: before a session starts not in-between), based on a configuration or heuristics.&lt;/p>
&lt;pre>&lt;code> #### Distributor Module
def read_from_stream(name, action):
# Generate hashkey using customer name.
# Mod using the number of threads. No_Of_Thread == No_Of_Queues
queue_index = hash(name) % No_Of_Threads
publish(queue_index, name, action)
def publish(queue_index, name, action):
# get the queue reference using the index
queue = queues[queue_index]
# add the item into corresponding queue
queue.push(name, action)
&lt;/code>&lt;/pre>
&lt;p>&lt;strong>Cons:&lt;/strong>
The biggest cons: If a thread is killed for what ever reason. All the customers past and future once - which would be queue/allocated to the thread would be dropped. But in the pull model, it would have dropped only a handful sitting inside (past) the thread. Well we could handle this dynamically - like have a &lt;strong>heart beat&lt;/strong> mechanism and update the &amp;ldquo;No_Of_Thread&amp;rdquo; and get it working - just that it would be another infra.&lt;/p>
&lt;h4 id="conclusion">Conclusion:&lt;/h4>
&lt;p>There are pros and cons to both approaches.&lt;/p>
&lt;blockquote>
&lt;p>More Infra == More Code == More Maintainability == More to Debug == More room for bugs to creep in.&lt;/p>
&lt;/blockquote>
&lt;p>Trying to keep it simple while keeping the tradeoffs minimal is the key. In our case; we went with the push approach - as the chances of a thread crash was minimal and also we added some defensive coding and drop an item safely if required.&lt;/p>
&lt;h4 id="reference">Reference:&lt;/h4>
&lt;ul>
&lt;li>
&lt;a href="https://www.manning.com/books/big-data" target="_blank" rel="noopener">Storm Model, by Nathan Marz&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>Choreography vs Orchestration</title><link>https://jacobaloysious.in/post/tech_choreography/</link><pubDate>Sun, 02 May 2021 00:00:00 +0000</pubDate><guid>https://jacobaloysious.in/post/tech_choreography/</guid><description>&lt;p>I have been getting into this debate quite often; which approach is preferred - Choreography vs Orchestrator. As always, it depends - Here is my take on it&amp;hellip;&lt;/p>
&lt;h3 id="modules">Modules:&lt;/h3>
&lt;ul>
&lt;li>&lt;strong>App:&lt;/strong> Like an UI module, where user trigger&amp;rsquo;s an action like:Create an account&lt;/li>
&lt;li>&lt;strong>Action #N:&lt;/strong> Specific modules, which has one specific action to be performed like: sending email, mail atm cards - once an account is created.&lt;/li>
&lt;li>&lt;strong>Coordinator:&lt;/strong> Module, which handles/triggers all the downstream actions (#n) - when an event is recived from App.&lt;/li>
&lt;/ul>
&lt;h1 id="orchestrator">Orchestrator&lt;/h1>
&lt;p>&lt;img src="orchestration.jpg" alt="alt Components" title="Orchestrator">&lt;/p>
&lt;p>Here the coordinator is the orchestrator - which has the knowledge of the business rules - like: what are the set of actions that need to be performed when a specific event occured. If you observe, the arrows are pointing from coordinator towards actions. So, the coordinator need to understand the details of each and every API (per Actions) - their base URL/REST end points. And if each of them takes in differnt params - then all this information would sit inside the coordinator module.&lt;/p>
&lt;p>The cooridnator has become the central brain. And now it has tight coupling with all the Actions. And if a new action should be added, we will have changes to the coordinator module. After a point of time - this module will become the &amp;ldquo;God like&amp;rdquo; service. And every new Actions team would have to come to coordinator team (more meetings/discussion/followps) - to have their action integrated with the coordinator. Any changes to the coordinator module would become extremely hard - as there is so much coupling. And in addition failures of each action should be handled with special case - adding more and more actions specific details/code into the coordinator module.&lt;/p>
&lt;p>On deployment and version management: If there are specific changes to an Actions - URI or params. Either the action module - can never depricate their existing uri and params, as it is used by coordinator module. Or, coordinator module would have to understand the versions (of action) and act accordingly. Both can get a bit too messy. And choosing one or either of them - would require some tradeoffs (debates) to be made somewhere.&lt;/p>
&lt;p>BTW: When we are just starting of - coordinator might be the easiest way to get things done (read, move faster). But as we scale out - we should look for approaches which would enable better decoupling.&lt;/p>
&lt;p>&lt;strong>Pros:&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>Better control in coordinating the Action modules&lt;/li>
&lt;li>Handling failures would be easiers - in one place&lt;/li>
&lt;li>One place to look at the flow chart&lt;/li>
&lt;li>Good for transaction driven and places where actions are not idempotenent&lt;/li>
&lt;/ul>
&lt;h1 id="choreography">Choreography:&lt;/h1>
&lt;p>&lt;img src="choreography.jpg" alt="alt Components" title="Choreography">&lt;/p>
&lt;p>In this approach, lets take a broker like KAFKA as an example. When a new event is created its publised to a KAFKA queue with a specific TOPIC. There could be multiple subscribers (actions) - which would subscribe to the topic. The subscribers have their specific logics and subscibe to specific topics. At any point of time, any new actions can be added into the system without the knowledge of the App module. Look at the red arrows - there are NO arrow pointing from App towards specific Actions - so app doesn&amp;rsquo;t have any dependency on Action. We could also have custom subscribers (Actions) - which would run a pipeline for the data science team - this would enable us to generate observability data on the loads and trends.&lt;/p>
&lt;p>Deploy: In this approach both app and action moudles can be indeopendently deployed and upgraded. The schema registry would have the contract details - topic vs the payloads.&lt;/p>
&lt;p>&lt;strong>Schema Registry:&lt;/strong>
Every payload schema: should be published in the schema repository. We can use libraries like &amp;ldquo;Protocol Buffers&amp;rdquo; or &amp;ldquo;Apache Avro&amp;rdquo; for describing payload schemas.&lt;/p>
&lt;p>&lt;strong>Cons:&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>Handling errors might become very tricky. You would need an external monitoring infrastructure to track this.&lt;/li>
&lt;li>If there are depenencies across actions - then you should think twice on using this pattern. Though there are ways to solve it - like: using sub topics.&lt;/li>
&lt;li>Debugging can become a bit more hard. Needs distributed tracing to correlate/debug.&lt;/li>
&lt;/ul>
&lt;h1 id="conclusion">Conclusion:&lt;/h1>
&lt;p>Personal preference is to lean towards choreographed approach - as they are more loosely coupled and are more flexibble and amenable to change. Again: it all depends on your use case - if you are building a low latency system - which should catch errors and react to it really fast based on lots of buisinees logic, then orchestrator might be a better approach.&lt;/p>
&lt;h2 id="references">References:&lt;/h2>
&lt;p>
&lt;a href="https://samnewman.io/books/building_microservices/" target="_blank" rel="noopener">Book : Building Microservices by Sam Newman&lt;/a>&lt;/p></description></item><item><title>Feature Toggle - Nightmares</title><link>https://jacobaloysious.in/post/tech_feature_toggle/</link><pubDate>Sat, 01 May 2021 00:00:00 +0000</pubDate><guid>https://jacobaloysious.in/post/tech_feature_toggle/</guid><description>&lt;p>We were working on a custom algorithm to optimally grab images given a list of area. Every image grabbed is written into an object store with a generated hashkey. The haskkey can be used to query the recorded data - when we would like to play them back.&lt;/p>
&lt;p>Software was a monolith and the algorithm was shipped with it. So, any new fixes would be shipped with the new software versions and user would be able to playback the same recorded data.&lt;/p>
&lt;pre>&lt;code>def customAlgorithm():
locations = []
# actual algo...
return locations;
&lt;/code>&lt;/pre>
&lt;p>A day came when we - rolled out a bug fix in the algorithm. But, we broke the backward compatiblity 😟. Users had lots of existing recorded data which they wanted to playback. Since the algorithm had changed - the generated &lt;strong>hashkeys&lt;/strong> didn&amp;rsquo;t match the ones in the object store.&lt;/p>
&lt;p>We thought its proabaly a one off scenario: so we added a feature toggle - user will have to &lt;strong>manually&lt;/strong> (pain) figure out - which version the data was recorded on and update the toggle config (xml) and rerun.&lt;/p>
&lt;pre>&lt;code>def customAlgorithm():
locs = []
if version == '1.0':
locs = oldAlgo()
else:
locs = newAlgo()
return locs
&lt;/code>&lt;/pre>
&lt;p>And - Well there was another bug fix:&lt;/p>
&lt;pre>&lt;code>def customAlgorithm():
locs = []
if version == '1.0':
locs = Algo_1_0()
else if version == ''2.0':
locs = Algo_2_0()
else:
locs = latestAlgo()
return locs
&lt;/code>&lt;/pre>
&lt;p>And yeah the story went on for one more iteration and we had to STOP!!!! 😓 It was already becoming a nightmare to mantain the code.&lt;/p>
&lt;p>It wasn&amp;rsquo;t just manually figuring out the toggle which was creating pain - but to enable code reuse, the algorthm had to be refactored multiple times - so that parts of the algorithm can be reused across versions. In addition to that the number of test suites we had to manage had gone up significantly, as there were multiple users each one using different version - and oh yeah there were version specific fixes released as patches 😟&lt;/p>
&lt;p>Finally we decided to write all the &lt;strong>metadata&lt;/strong> - related to recording - into an embedded database. And on playback we get the information from the database and not by re-running the code/algorithm.&lt;/p>
&lt;p>On hindsight: the database feels like it an obvious solution, right? well the devil is in the details - in our case it about effort(read, time). The playback infrastructure based on database took ~8 weeks to build -first version. It had to handle numerous (10s) use cases. Previously, we only had one flow to be maintained i.e the flow which ran the use cases + algo shipped. But now we have to manage &lt;strong>TWO&lt;/strong> different flows - one for recording and one for playback (x) No_Of_Use_Cases.&lt;/p>
&lt;p>Its almost 3yrs since we rolled out this solution to production. When we look back it was a very good decision to build two different workflows 😍. Code is more structured and maintainable.&lt;/p>
&lt;p>At times - short term goal/fix takes priority. But, its always good to step back and look at the pain points and have an item in your backlog to find a better solution. The priority should be driven based on feedback: remember the 80/20 rule&lt;/p>
&lt;blockquote>
&lt;p>80 percent of customers only use 20 percent of the features in the software they’ve bought.&lt;/p>
&lt;/blockquote>
&lt;p>BTW: the database schema has changed a lot - with the addition of multiple use cases. Which also needs version management 😉 And evey new use case discussion now has two parts to be discussed - some problems are good to have (read, tradeoffs).&lt;/p>
&lt;p>Ref:
&lt;a href="https://martinfowler.com/articles/feature-toggles.html" target="_blank" rel="noopener">Feature Toggle&lt;/a>&lt;/p></description></item><item><title>Side Cars</title><link>https://jacobaloysious.in/post/tech_sidecars/</link><pubDate>Sat, 01 May 2021 00:00:00 +0000</pubDate><guid>https://jacobaloysious.in/post/tech_sidecars/</guid><description>&lt;h2 id="idea">Idea&lt;/h2>
&lt;p>Add additional functionality into an existing application, with minimal or zero code changes to the exiting application. Also, the package can be consume by any software - irrespective of the language or framework (heterogeneous) it is written in.&lt;/p>
&lt;h2 id="how">How?&lt;/h2>
&lt;p>Using side cars pattern - : I/O(
&lt;a href="https://zeromq.org" target="_blank" rel="noopener">ZMQ&lt;/a>) + Topic (intent) + Data(
&lt;a href="https://developers.google.com/protocol-buffers" target="_blank" rel="noopener">ProtoBuf&lt;/a>) + Impl(Subscriber of topic).&lt;/p>
&lt;p>&lt;img src="sidecar.jpg" alt="alt Components" title="SideCarBase Window Images">&lt;/p>
&lt;p>DAPR framework from Microsoft Azure team – uses a similar design - runs side car. Dapr is a portable, event-driven runtime that makes it easy for developers to build resilient, micro service stateless and stateful applications that run on the cloud and edge and embraces the diversity of languages and developer frameworks.&lt;/p>
&lt;p>We could use the Side Car for adding functionalities like resource monitoring, logging, log aggregation, configurations, observability (telemetry /feature trends) etc.. Once we get the sidecar installed - we could keep adding new functionality as it independently developed and deployed. The features in the sidecar could be turned on or off based on options.&lt;/p>
&lt;p>Say as an organization: we decided to use cloud solutions for telemetry and internal tools decide to push data to the cloud - this solution might need a security layer - which would implement both gov and corp policies. This common implemenation of policies could be sitting inside the side cars and can be deployed on to all machine where the internal tools runs. And any update made to the policies would be transparent to the application - as the side cars would be independently deployed and maintained.&lt;/p>
&lt;h2 id="how-is-it-different-from-a-microservice-with-rest-api">How is it different from a Microservice with REST API?&lt;/h2>
&lt;p>Microservice doesn’t really care about the client. But here the side car is tightly coupled and tracks everything about the main application – including say: killing the application if the resource used is above the threshold - so that the tool PC doesn’t require a restart.&lt;/p>
&lt;h2 id="where-should-it-be-used">Where should it be used?&lt;/h2>
&lt;p>Only for non-critical operation - as the latency will be high.&lt;/p>
&lt;h3 id="reference">Reference:&lt;/h3>
&lt;ul>
&lt;li>
&lt;a href="https://dapr.io/" target="_blank" rel="noopener">DAPR&lt;/a>&lt;/li>
&lt;li>
&lt;a href="https://www.magalix.com/blog/the-sidecar-pattern" target="_blank" rel="noopener">Blog&lt;/a>&lt;/li>
&lt;li>
&lt;a href="https://docs.microsoft.com/en-us/azure/architecture/patterns/sidecar" target="_blank" rel="noopener">MSSideCar&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>MongoDB - Hybrid Schema</title><link>https://jacobaloysious.in/post/tech_mogodb-hybridschemadesign/</link><pubDate>Sun, 14 Mar 2021 00:00:00 +0000</pubDate><guid>https://jacobaloysious.in/post/tech_mogodb-hybridschemadesign/</guid><description>&lt;p>I recently ran into a situation where in we have a session and each Session produces a lot of messages (sync) and each message has an associated timestamp and order.
This is similar to a blog post (session) and its comments (messages).&lt;/p>
&lt;p>Since the messages are unbounded (not sure how many would be produced). In my initial design - I had a separate collection for Messages. Each Session would have one corresponding MessageW (wrapper) document and each MesssageW document would have an array of messages (acutal messages).&lt;/p>
&lt;p>With all the available data I had - the size of the MessageW document never went above 1MB - so we were all good. But unfortunately, we had a new use case which broke this assumption 😟. The session was running for hours and there was tons of messages getting generated and yes - we hit the 16MB ceiling 😓.&lt;/p>
&lt;p>Obvious solution was to create to create a new one doucment for every new message. So, that we never hit the size limit (16MB) of a mongoDB document. But, since we are looking at unbounded message - there could huge (million message docs) number of documents.&lt;/p>
&lt;p>So, we choose to go with the &lt;strong>Hybrid Approach&lt;/strong>&lt;/p>
&lt;p>In the hybrid approach, we go with our initial option - where we have a MessageW (wrapper) document - which has an array of Messages. Then we add a constrain on the document i.e. the size of the array should not be more than a given arbitary number. For every new message we increment the &lt;em>count&lt;/em>. If the number count crossed the constraint - then create a new MessageW document, inc the page number and also update the Session (&lt;em>NumberOfMsgPages&lt;/em>). The reader would get the &lt;em>NumberOfMsgPages&lt;/em> and read backwards - until he hit page 0. Voila!! 😍&lt;/p>
&lt;p>&lt;strong>Important Items to note:&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>Session Object : will have a &lt;em>NumberOfMsgPages&lt;/em> property&lt;/li>
&lt;li>MessageW Object: would have a &lt;em>Page&lt;/em> and &lt;em>Count&lt;/em> property.&lt;/li>
&lt;/ul>
&lt;p>&lt;strong>Operators:&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>
&lt;a href="https://docs.mongodb.com/manual/reference/operator/update/inc/" target="_blank" rel="noopener">$inc&lt;/a>&lt;/li>
&lt;li>
&lt;a href="https://docs.mongodb.com/manual/reference/operator/update/set/" target="_blank" rel="noopener">$set&lt;/a>&lt;/li>
&lt;li>
&lt;a href="https://docs.mongodb.com/manual/reference/operator/query/lt/" target="_blank" rel="noopener">$lt&lt;/a>&lt;/li>
&lt;li>
&lt;a href="https://docs.mongodb.com/manual/reference/method/db.collection.update/#update-upsert" target="_blank" rel="noopener">upsert&lt;/a>&lt;/li>
&lt;/ul>
&lt;p>Since: A code is worth a thousand words. Here is the code 😊&lt;/p>
&lt;pre>&lt;code> public void InsertMessage(Message message, ObjectId parentId)
{
var builder = Builders&amp;lt;Message&amp;gt;.Filter;
// Find MessageObject: given parentId and CurrentPageNo
// And: Constraint: Number of messages is less than 1000.
var filter = builder.Eq(&amp;quot;parentId&amp;quot;, parentId)
&amp;amp; builder.Lt(&amp;quot;Count&amp;quot;, 1000)
&amp;amp; builder.Eq(&amp;quot;Page&amp;quot;, CurrentMessagePageCount);
// Try and Insert into existing Message Document array And Increment the Count.
var update = Builders&amp;lt;Message&amp;gt;.Update
.AddToSet(x =&amp;gt; x.Messages, message)
.Inc(&amp;quot;Count&amp;quot;, 1);
// If the constraint with &amp;quot;Count&amp;quot; matches.
// ModifiedCount would be greater than Zero
var result = MsgCollection.UpdateOne(filter, update);
if (result.IsAcknowledged)
{
if(result.ModifiedCount== 0)
{
// Increment the Page count
CurrentMessagePageCount += 1;
// Update: NumberOfMsgPages in Session Collection
var sessionFilter = Builders&amp;lt;MySession&amp;gt;.Filter.Eq(&amp;quot;_id&amp;quot;, parentId);
var updateSession = Builders&amp;lt;MySession&amp;gt;.Update
.Inc(&amp;quot;NumberOfMsgPages&amp;quot;, 1);
SessionsCollection.UpdateOne(sessionFilter, updateSession);
// Find MessageObject: given parentId and CurrentPageNo-which is obviously not found.
// So, we will use &amp;quot;IsUpsert=true&amp;quot; to add a new document.
// And: Number of messages is less than 20.
var filter2 = builder.Eq(&amp;quot;parentId&amp;quot;, parentId)
&amp;amp; builder.Eq(&amp;quot;Page&amp;quot;, CurrentMessagePageCount);
// Try: Insert into existing Message And Increment the Count
var update2 = Builders&amp;lt;Message&amp;gt;.Update
.AddToSet(x =&amp;gt; x.Messages, message)
.Set(&amp;quot;Page&amp;quot;, CurrentMessagePageCount)
.Inc(&amp;quot;Count&amp;quot;, 1);
MsgCollection.UpdateOne(filter2, update2, new UpdateOptions() { IsUpsert = true });
}
}
}
&lt;/code>&lt;/pre>
&lt;p>A obvious reaction after reading the code is - Ok, whats the big deal here?&lt;/p>
&lt;p>Lets take a code walk through: (think how would you achive this in your traditional SQL DB&amp;rsquo;s)&lt;/p>
&lt;ol>
&lt;li>
&lt;p>Code never check if the MessageW document &lt;strong>exits&lt;/strong> before Update.&lt;/p>
&lt;ul>
&lt;li>How did it work: &lt;strong>&lt;em>&amp;ldquo;$IsUpsert=true&amp;rdquo;&lt;/em>&lt;/strong> is the magic word.&lt;/li>
&lt;li>&lt;em>$IsUpsert=True&lt;/em>: MongoDB would internally insert a &lt;strong>new&lt;/strong> document - if the document doesn&amp;rsquo;t exists.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>Updating &lt;strong>counter&lt;/strong> is happening inside MongoDB:&lt;/p>
&lt;ul>
&lt;li>&lt;strong>&lt;em>$inc&lt;/em>&lt;/strong> mongoDB operator - increments a specific field - in our case &lt;em>count&lt;/em>&lt;/li>
&lt;li>Hence, Client doesn&amp;rsquo;t have to read the document to know the actual count and then increment/update.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>Constraint Check:&lt;/p>
&lt;ul>
&lt;li>&lt;strong>&lt;em>$lt&lt;/em>&lt;/strong> MongoDB operator would handle the constraints.&lt;/li>
&lt;li>Update goes through if the constraint is met.&lt;/li>
&lt;li>As part of the result, we could check if any doc was modified&lt;/li>
&lt;li>Cool part - the second half of the code runs only once in 1000 inserts&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ol>
&lt;p>How did we come up with the arbitary constraint number? well, I dont think we have a thumb rule here. Since we don&amp;rsquo;t track the actual document size - you would have to look at your data and come up with the sizing.&lt;/p>
&lt;p>BTW, I am new to MongoDB and learning. If there are better approaches - feel free to write back to me. Thanks.&lt;/p>
&lt;p>Reference:
&lt;a href="https://www.oreilly.com/library/view/mongodb-applied-design/9781449340056/" target="_blank" rel="noopener">MongoDB Applied Design Patterns, by Rick Copeland&lt;/a>&lt;/p></description></item><item><title>MongoDB Tit Bits</title><link>https://jacobaloysious.in/post/tech_mogodb-titbits/</link><pubDate>Sun, 07 Mar 2021 00:00:00 +0000</pubDate><guid>https://jacobaloysious.in/post/tech_mogodb-titbits/</guid><description>&lt;p>While I am getting started with MongoDB for my new project. Here are the few quick notes - based on my readings so far:&lt;/p>
&lt;p>One Key Takeaway:&lt;/p>
&lt;blockquote>
&lt;p>Data accessed together should be stored together&lt;/p>
&lt;/blockquote>
&lt;h4 id="wiredtiger">WiredTiger:&lt;/h4>
&lt;ul>
&lt;li>
&lt;a href="https://docs.mongodb.com/manual/core/wiredtiger/" target="_blank" rel="noopener">WiredTiger&lt;/a> is the default storage engine for mongo db&lt;/li>
&lt;li>It stores documents and indexes on disk&lt;/li>
&lt;li>In memory cache stores some doc and frequency used index - working set
&lt;ul>
&lt;li>50% of (RAM -1 GB) Or, 256MB&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ul>
&lt;h4 id="massive-arrays">Massive Arrays:&lt;/h4>
&lt;ul>
&lt;li>Max Document size is 16MB&lt;/li>
&lt;li>Index performance on arrays decreases as array size increases&lt;/li>
&lt;li>
&lt;a href="https://www.mongodb.com/blog/post/building-with-patterns-the-extended-reference-pattern" target="_blank" rel="noopener">Extended Reference pattern&lt;/a>: where we duplicate some and not all data&lt;/li>
&lt;/ul>
&lt;h4 id="index">Index:&lt;/h4>
&lt;ul>
&lt;li>Each index is atleast 8KB&lt;/li>
&lt;li>Index take up storage - One File for each collection and one file for each Index (WiredTiger impl)&lt;/li>
&lt;li>Write performance as index needs to updated&lt;/li>
&lt;li>Limit each collection to 50 Index max&lt;/li>
&lt;li>Do: Add index for frequently supported queries - improves read performance&lt;/li>
&lt;li>Don’t: Create unnecessary indexes - reduce performance and takes up space&lt;/li>
&lt;/ul>
&lt;h4 id="bloated-documents">Bloated Documents:&lt;/h4>
&lt;ul>
&lt;li>Do: Data accessed together should be stored together&lt;/li>
&lt;li>Don’t: bloat your document with related data that is not accessed to gether&lt;/li>
&lt;li>Data that is related to each other should NOT necessarily stored together&lt;/li>
&lt;li>Remove bloat from frequently used documents - it can be inmemory wire tiger cache&lt;/li>
&lt;li>Data Duplication is OK (depends!)
&lt;ul>
&lt;li>Summary document and Details document - ref via links&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ul>
&lt;h4 id="case-incentive-query">Case Incentive Query:&lt;/h4>
&lt;ul>
&lt;li>
&lt;a href="https://docs.mongodb.com/manual/reference/operator/query/regex/" target="_blank" rel="noopener">$regex&lt;/a> queries are case &lt;em>insensitive&lt;/em> but not performant&lt;/li>
&lt;li>Non-$regex queries are case &lt;em>sensitive&lt;/em>&lt;/li>
&lt;li>
&lt;a href="https://docs.mongodb.com/manual/reference/collation/" target="_blank" rel="noopener">Collation&lt;/a>:
&lt;ul>
&lt;li>Language specific rules for MongoDB for string comparison&lt;/li>
&lt;li>Strength ranges from 1-5&lt;/li>
&lt;li>Strength 1-2 will give you case insensitive&lt;/li>
&lt;li>Query: {&amp;ldquo;first_name&amp;rdquo;: {$regex: /Jacob/i }} - case insensitive&lt;/li>
&lt;li>Query: {&amp;ldquo;first_name&amp;rdquo;: &amp;ldquo;Jacob&amp;rdquo;} - case sensitive&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ul>
&lt;h4 id="accessing---separate-data-together">Accessing - Separate data together:&lt;/h4>
&lt;ul>
&lt;li>
&lt;a href="https://docs.mongodb.com/manual/reference/operator/aggregation/lookup/" target="_blank" rel="noopener">$lookup&lt;/a>:
&lt;ul>
&lt;li>Is used to join data from more than one collection&lt;/li>
&lt;li>Great for rarely used queries or analytical queries (batch run overnight)&lt;/li>
&lt;li>Very slow and resource intensive&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ul>
&lt;h4 id="upserthttpswwwmongodbtutorialorgmongodb-crudmongodb-upsert">
&lt;a href="https://www.mongodbtutorial.org/mongodb-crud/mongodb-upsert/" target="_blank" rel="noopener">Upsert&lt;/a>:&lt;/h4>
&lt;ul>
&lt;li>Upsert is a combination of update and insert. Upsert performs two functions:
&lt;ul>
&lt;li>Update data if there is a matching document.&lt;/li>
&lt;li>Insert a new document in case there is no document matches the query criteria.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>Personally: I found this very interesting, as for my use cases - I dont need to track if the document already exist.&lt;/li>
&lt;/ul>
&lt;p>I understand - the blog looks a bit navive (read, nothing interseting), sry I am just getting started. BTW, I plan to write a follow blog - deep dive on my specific use cases and data modelling.&lt;/p>
&lt;h4 id="reference">Reference:&lt;/h4>
&lt;ul>
&lt;li>
&lt;a href="https://www.youtube.com/channel/UCK_m2976Yvbx-TyDLw7n1WA" target="_blank" rel="noopener">MongoDB Channel&lt;/a>&lt;/li>
&lt;li>
&lt;a href="https://www.mongodbtutorial.org/" target="_blank" rel="noopener">MongoDB Tutorial&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>Docker - DotNet 4.8 - Getting Started</title><link>https://jacobaloysious.in/post/tech_docker_getting_started/</link><pubDate>Wed, 30 Dec 2020 00:00:00 +0000</pubDate><guid>https://jacobaloysious.in/post/tech_docker_getting_started/</guid><description>&lt;p>Source Code:
&lt;a href="https://github.com/jacobaloysious/docker-try-net48" target="_blank" rel="noopener">docker-try-net48&lt;/a>&lt;/p>
&lt;p>&lt;strong>Goal:&lt;/strong> Develop a .Net48 application - coding would happen in the host while its run inside the docker - live&lt;/p>
&lt;p>&lt;em>Prerequisite:&lt;/em>
&lt;a href="https://docs.docker.com/docker-for-windows/install/" target="_blank" rel="noopener">Docker Desktop for windows&lt;/a> is up and running and you are using Windows Containers&lt;/p>
&lt;p>There are tons of documentation online, this is just me getting started.&lt;/p>
&lt;h2 id="base-image">Base Image:&lt;/h2>
&lt;p>There are two base images supported officially by Microsoft for .Net windows application.&lt;/p>
&lt;ul>
&lt;li>Windows Server Core&lt;/li>
&lt;li>Windows Nano Server&lt;/li>
&lt;/ul>
&lt;p>&lt;img src="DotNetContainer.jpg" alt="alt Components" title="Base Window Images">&lt;/p>
&lt;p>In this example: we are gona work on a .Net Framework 4.8 - console application. We would be using the
&lt;a href="https://github.com/microsoft/dotnet-framework-docker/blob/master/src/runtime/4.8/windowsservercore-ltsc2019/Dockerfile" target="_blank" rel="noopener">&lt;strong>DotNet Framework 4.8&lt;/strong>&lt;/a> image which internally uses
&lt;a href="https://hub.docker.com/_/microsoft-windows-servercore" target="_blank" rel="noopener">&lt;strong>servercore:ltsc2019-amd64&lt;/strong>&lt;/a> as base image.&lt;/p>
&lt;p>Let first pull the image, into our local image cache. This would take a while as the size is huge - get yourself a coffee.&lt;/p>
&lt;pre>&lt;code>$docker pull mcr.microsoft.com/dotnet/framework/runtime:4.8
&lt;/code>&lt;/pre>
&lt;p>Once pulled (image is available in your local cache) - you are gona see an entry with Tag &lt;strong>4.8&lt;/strong> in image list&lt;/p>
&lt;pre>&lt;code>$docker image ls
&lt;/code>&lt;/pre>
&lt;p>&lt;img src="PullDotNetImage.jpg" alt="alt Components" title="Pull DotNet Image">&lt;/p>
&lt;h2 id="sample-app">Sample App:&lt;/h2>
&lt;p>Here is a sample app to demostrate the usage. Clone the
&lt;a href="https://github.com/jacobaloysious/docker-try-net48" target="_blank" rel="noopener">repository&lt;/a>. Open the solution &lt;em>jacapp.sln&lt;/em> in VS2019 and compile it. Its a simple app which starts two threads and exits after 10 seconds&lt;/p>
&lt;ul>
&lt;li>One thread - print the count and the thread ID - to the &lt;em>console&lt;/em> every second&lt;/li>
&lt;li>Second thread - write the same count and thread ID - into an &lt;em>output text file&lt;/em> every second&lt;/li>
&lt;/ul>
&lt;p>&lt;em>note:&lt;/em> the output file would be on a volume mounted drive with the host&lt;/p>
&lt;p>&lt;img src="reposcreenshot.jpg" alt="alt Components" title="Repo Content">&lt;/p>
&lt;h2 id="dockerfile">Dockerfile:&lt;/h2>
&lt;p>Let&amp;rsquo;s look at the contents of the dockerfile:&lt;/p>
&lt;ol>
&lt;li>Specify &lt;strong>Base image&lt;/strong> we plan to use - in our case we are gona use the image (4.8 Runtime) we have already downloaded (pulled)&lt;/li>
&lt;li>Specify the &lt;strong>Working Directory&lt;/strong> where all the commands would be run. Think of it as running &lt;code>$CD /d c:/myapp&lt;/code>, before the &lt;em>EntryPoint&lt;/em> is exectuted&lt;/li>
&lt;li>Specify the &lt;strong>Command&lt;/strong> to be executed inside the working directory. In our case its the console app, so we run the output exe &lt;em>jacapp.exe&lt;/em>&lt;/li>
&lt;/ol>
&lt;p>&lt;img src="docker-file.jpg" alt="alt Components" title="Dockerfile Content">&lt;/p>
&lt;h2 id="build-image">Build Image:&lt;/h2>
&lt;p>Let&amp;rsquo;s build a docker image. Run the command from the directory which has the &lt;code>Dockerfile&lt;/code>&lt;/p>
&lt;pre>&lt;code>$docker build -t testapp .
&lt;/code>&lt;/pre>
&lt;p>&lt;img src="docker-build-image.jpg" alt="alt Components" title="Dockerfile Content">&lt;/p>
&lt;p>&lt;img src="test-app-image.jpg" alt="alt Components" title="Test App Image">&lt;/p>
&lt;h2 id="run-image">Run Image:&lt;/h2>
&lt;p>Items to note:&lt;/p>
&lt;ul>
&lt;li>&amp;ndash;name: name of the container image instance&lt;/li>
&lt;li>-v: volume mount; here we mount the output &amp;ldquo;bin&amp;rdquo; directory with a specific directory inside the container &amp;ldquo;c:\myapp&amp;quot; .&lt;/li>
&lt;li>-rm: remove the container instance once the program exits&lt;/li>
&lt;/ul>
&lt;pre>&lt;code>$docker run --name jacinstance1 --rm -v D:\dev\dkr\docker-try-net48\jacapp\bin\Debug:C:\myapp\ testapp
&lt;/code>&lt;/pre>
&lt;h4 id="instance-of-the-containter-running">Instance of the containter running:&lt;/h4>
&lt;p>&lt;img src="jac-instance.jpg" alt="alt Components" title="Run Console Output">&lt;/p>
&lt;h4 id="console-output">Console output:&lt;/h4>
&lt;p>&lt;img src="run-console-output.jpg" alt="alt Components" title="Run Console Output">&lt;/p>
&lt;h4 id="contents-of-the-file-writted-on-the-mounted-drive">Contents of the file writted on the mounted drive:&lt;/h4>
&lt;p>&lt;img src="run-output-file-content.jpg" alt="alt Components" title="Run Console Output">&lt;/p>
&lt;p>Well thats it! We have our application running inside a container - Now we could edit your project (try updating the console output text), compile and directly &lt;code>docker run&lt;/code> the image i.e. NO docker image re-build required!!&lt;/p></description></item><item><title>Code Reviews</title><link>https://jacobaloysious.in/post/tech_code-reviews/</link><pubDate>Sat, 10 Oct 2020 00:00:00 +0000</pubDate><guid>https://jacobaloysious.in/post/tech_code-reviews/</guid><description>&lt;p>While working in a development team; being part of the code review is not an optional thing. Depending on the size of the org/team and the # of features in parallel - I am sure, we will get multiple code review requests in a week.&lt;/p>
&lt;p>I often ask myself, can I provide valuable feedbacks on every review I am part of? Most certainly NO! lets be honest here.&lt;/p>
&lt;p>Then the next question is - why are you added into the review? well I could think of two reasons:&lt;/p>
&lt;ul>
&lt;li>FYI: Let the team know I made this change; there could be depenencies (another developer; working on similar files)&lt;/li>
&lt;li>Status update (stakeholders)&lt;/li>
&lt;/ul>
&lt;h3 id="not-an-active-reviewer">Not an active reviewer:&lt;/h3>
&lt;p>If you think, you don&amp;rsquo;t have the right context to contribute. It&amp;rsquo;s good to be explict and call out - &lt;strong>Sorry, I don&amp;rsquo;t have enough context to add value. I would leave the review to the experts&lt;/strong> and recommend the author to mark you as &lt;strong>Observer&lt;/strong> instead.&lt;/p>
&lt;p>Note: code review is not a place to train; where you discuss and use it as a learning form.&lt;/p>
&lt;h3 id="add-value">Add Value:&lt;/h3>
&lt;p>This is critical and most important objective of a code review. Pointing out each and every spelling mistake and intendation fix - might not add much value; in which case - propose them to run it through a FxCop/style/code analyser utility.&lt;/p>
&lt;p>You absolutely need to be aware of the context before providing feedback 👀. If you are not sure about the specifics, setup a meeting and request the developer to walk you through. At the end of the day - &lt;strong>code is the ground truth&lt;/strong> (irrespective of what was aligned). Few example of real Value could be:&lt;/p>
&lt;ol>
&lt;li>Pointing out specific use cases not handled&lt;/li>
&lt;li>Usage of design principles (SOLID) - coupling/cohesion etc&lt;/li>
&lt;li>Adding: Validation, Logging, Tracablility etc&amp;hellip;&lt;/li>
&lt;li>Handling: Errors, Exception, Leaks, Performance etc..&lt;/li>
&lt;li>Code Coverage, appropriate tests (note: test should also be as clean/maintainable as SUT)&lt;/li>
&lt;/ol>
&lt;h3 id="assumptions">Assumptions:&lt;/h3>
&lt;p>This could at times come back back and bite; I have been there 😕. If you are not sure about a specfic change again: &lt;strong>call out explicitly and confirm - if your understanding is accurate&lt;/strong>.&lt;/p>
&lt;p>As authors - we do want our code to be non ambiguous. But, since there is always a context and depenency in the code - its hard to describe everything. Maybe better naming could help? well: 😏&lt;/p>
&lt;blockquote>
&lt;p>There are only two hard things in Computer Science: cache invalidation and naming things.&amp;ndash; Phil Karlton&lt;/p>
&lt;/blockquote>
&lt;h3 id="no-condescending-words">No Condescending words&lt;/h3>
&lt;p>Thanks to
&lt;a href="https://www.michaelagreiler.com/" target="_blank" rel="noopener">Doctor McKayla&lt;/a> for this.&lt;/p>
&lt;blockquote>
&lt;p>Words such as “just”, “easy”, “only”, or “obvious” can come across belittling and condescending. It’s a good practice to remove those words from your feedback. Most of the time, they do not add any value&amp;hellip;.. research shows that people have a tendency to interpret written language in a negative way.&lt;/p>
&lt;/blockquote>
&lt;p>Personally, I am cautious to ensure my review comments looks like a conversation - than a direct command.&lt;/p>
&lt;h3 id="more-the-number-of-reviewers">More the number of Reviewers:&lt;/h3>
&lt;p>I remember receiving emails like &amp;ldquo;please review my change set&amp;rdquo; sent to a DL which had 30+ developers.
Do you think all 30 members would have all the required context or would &lt;strong>you ensure&lt;/strong> all 30 will follow up and get the context and then do the reivew? Less likely! 🤔&lt;/p>
&lt;p>Maybe there is a perception that more the reviewers; more the # of valuable feedback. But, what if its the other way - every one assumes its someone else responsiblity - zero feedbacks 😟.&lt;/p>
&lt;h3 id="separate-feature-addtion-and-refactoring">Separate Feature Addtion and Refactoring:&lt;/h3>
&lt;p>Many at time its intriguing to refactor the code while we are working on a feature. We should - its always a good practise to &lt;em>clean up&lt;/em> and &lt;em>make it better&lt;/em>.&lt;/p>
&lt;p>My only argument here is: If you have both - the feature implemetation and refactoring in the same changeset (esp on a large code base, spanning files/dependencies) - It becomes a challenge to follow the &lt;strong>actual functional&lt;/strong> change.&lt;/p>
&lt;p>Recomendation: split them into two changeset like
&lt;a href="https://www.michaelagreiler.com/stacked-pull-requests/" target="_blank" rel="noopener">StackedPullRequest&lt;/a>: Personally would prefer (2) followed by (1)&lt;/p>
&lt;ol>
&lt;li>Only refactoring. No feature addtion&lt;/li>
&lt;li>Only feature addition&lt;/li>
&lt;/ol>
&lt;h3 id="unsure-of-the-reviewers">Unsure of the reviewers?&lt;/h3>
&lt;p>If you are already part of the team which maintains the code; no brainer. You are here since you are not sure - simplest tip &lt;strong>Go for history&lt;/strong>.
I have done this more than once; say making a change in deploy scripts maintained by operational team. Look for atleast 2 developers based on change history (again: don&amp;rsquo;t stop with one) of the file(s); secondly: make sure you look at their org structure and see if they belong to the operational team (If I get to deliver and someone else sends me a new review request 😑); finally: just to be sure add their solid line into it (why: well, you are unsure if you would get a response 😜).&lt;/p>
&lt;h3 id="approval-enforced">Approval Enforced:&lt;/h3>
&lt;p>Few companies have strict code review rules like: only a pull request that has 2 or more approvals - would be merged into mainline. I feel this culture/process is awesome; as there is definetly a sens of responsibility and ownership on the part of the reviewers - at the end of the day again (would say it multiple times) &lt;strong>&amp;ldquo;Code is the Ground Truth&amp;rdquo;&lt;/strong> (irrespective; what ever the feature/design document says).&lt;/p>
&lt;h2 id="conclusion">Conclusion:&lt;/h2>
&lt;p>I personally belive, Code review are not optional. Anyday - another pair of eyes would give you a different prespective. While, we as developers are more focused on getting thing done, there are chances of scenarios getting overlooked - effective code review enable us to bridge this gap. This defintely requires commitment and trust between authors and reviewers.&lt;/p>
&lt;p>Building a culture toward delivering quality code with code review as pivot is critical.&lt;/p>
&lt;p>Check out:
&lt;a href="https://www.michaelagreiler.com/" target="_blank" rel="noopener">Doctor McKayla&lt;/a>; there are lots of good content on this topics.&lt;/p></description></item><item><title>Apache Airflow and Regression Monitoring</title><link>https://jacobaloysious.in/post/tech_airflow_reg_monitor/</link><pubDate>Fri, 02 Oct 2020 00:00:00 +0000</pubDate><guid>https://jacobaloysious.in/post/tech_airflow_reg_monitor/</guid><description>&lt;h2 id="introduction">Introduction:&lt;/h2>
&lt;p>Airflow is a platform to programmatically author, schedule and monitor workflows or data pipelines. It was originally developed and open sourced by Airbnb, later joined Apache Software foundation’s incubation program in 2016. Workflow is a sequence of tasks defined around Directed Acyclic Graph(DAGs) – which could be started on a schedule or triggered by an event or using Command line interface. Airflow pipelines are configuration as code (Python), allowing for dynamic pipeline generation. This allows for writing code that instantiate pipelines dynamically.&lt;/p>
&lt;h2 id="components">Components:&lt;/h2>
&lt;p>&lt;img src="airflow_component.jpg" alt="alt Components" title="Airflow Components">&lt;/p>
&lt;h4 id="metadata-db">Metadata DB:&lt;/h4>
&lt;p>Stores information&amp;rsquo;s like job status and task instance status.&lt;/p>
&lt;h4 id="scheduler">Scheduler:&lt;/h4>
&lt;p>Airflow scheduler executes your tasks on an array of workers while following the specified dependencies. The scheduler is the brains behind setting up the workflows in airflow. The execution time begins at DAG start date and repeat every schedule interval.&lt;/p>
&lt;h4 id="web-interface-ui">Web Interface (UI):&lt;/h4>
&lt;p>Airflow ships with a Flask app that tracks all the defined workflows and lets you easily change, start or stop them. The rich user interface makes it easy to visualize pipelines running in production, monitor progress and troubleshoot issues.&lt;/p>
&lt;h4 id="cli">CLI:&lt;/h4>
&lt;p>Airflow has a very rich command line interface that allows to test, run, backfill, describe and clear parts of your DAGs&lt;/p>
&lt;h2 id="concepts">Concepts:&lt;/h2>
&lt;h4 id="dag">DAG:&lt;/h4>
&lt;p>A DAG is the container that is used to organize tasks in a way that reflects their relationship, dependencies and set their execution context and order.&lt;/p>
&lt;h4 id="operators">Operators:&lt;/h4>
&lt;p>Operators are the worker that run the tasks. Workflows are defined by creating a DAG of operators. They are broadly classified into three – Sensors, Operators and Transfers. Airflow provides many prebuild operators for many common tasks and new operators can be created by inheriting BaseOperator class.&lt;/p>
&lt;h4 id="tasks">Tasks:&lt;/h4>
&lt;p>Once an operator is instantiated, its is referred to as a “task”. Each task is user defined and responsible for performing a specific operation in the workflow. Instantiating a task requires providing a unique task_id and DAG container. Task can be python function or external scripts that could be invoked.&lt;/p>
&lt;h2 id="example">Example:&lt;/h2>
&lt;p>&lt;img src="example.jpg" alt="alt Example" title="Example">&lt;/p>
&lt;p>In the example, we show case - how Airflow could be used to express a workflow that can be used to generate the statistics/ report as part of end-to-end regression test suit; which involves multiple systems to work together. A traditional approach would use something very basic like bunch of batch scripts w/o CRON. But the challenge is - it would very easily get tangled and developer would spend a lot of time to figure out where the log files are or what failed and why/who owns what. Airflow helps solves this problem by helping in orchestrating your processes, managing the logs and really good dashboard with visualization of what failed and much more information.&lt;/p>
&lt;p>&lt;img src="code_snippet.jpg" alt="alt CodeSnippet" title="Code Snippt">&lt;/p>
&lt;h2 id="references">References:&lt;/h2>
&lt;ul>
&lt;li>Airflow : &lt;a href="https://airflow.apache.org">https://airflow.apache.org&lt;/a>&lt;/li>
&lt;li>Luigi: &lt;a href="https://luigi.readthedocs.io/en/stable/index.html">https://luigi.readthedocs.io/en/stable/index.html&lt;/a>&lt;/li>
&lt;li>Blog: &lt;a href="https://medium.com/airbnb-engineering/airflow-a-workflow-management-platform-46318b977fd8">https://medium.com/airbnb-engineering/airflow-a-workflow-management-platform-46318b977fd8&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>Python Plugins with Topics</title><link>https://jacobaloysious.in/post/tech_python_plugins/</link><pubDate>Sun, 13 Sep 2020 00:00:00 +0000</pubDate><guid>https://jacobaloysious.in/post/tech_python_plugins/</guid><description>&lt;p>Source Code:
&lt;a href="https://github.com/jacobaloysious/pyplugins" target="_blank" rel="noopener">PyPlugins&lt;/a>&lt;/p>
&lt;p>Any infrastructure should always have the capability to extend itself. It would be better if the functionality is added by a contributor - who is not part of the core team. And it doesn&amp;rsquo;t get in the way of core components - development, compilation and deployment.&lt;/p>
&lt;p>The concept of plugin has been around there for quite a while - Visual Studio/VS Code all has plugins (aka extensions). Basic idea here is to add new functionalities - by just deploying a new dll, jar or py modules.&lt;/p>
&lt;p>In this proposal using python, we have a root folder named &lt;strong>Plugins&lt;/strong> - the infra would enumerate the Plugins directory to add functions. The functionalities register themselves with a &lt;strong>Key&lt;/strong> - let’s call them &lt;strong>TOPICS&lt;/strong>.&lt;/p>
&lt;p>Well why &lt;strong>Topics&lt;/strong>? I am borrowing this idea from Messaging Queue infra like
&lt;a href="https://zeromq.org/" target="_blank" rel="noopener">ZMQ&lt;/a> and
&lt;a href="https://kafka.apache.org/" target="_blank" rel="noopener">Kafka&lt;/a>&amp;hellip; So, that we could create a Topic to Function mapping - and we would be able to map caller to a MQ subscriber.&lt;/p>
&lt;p>Let me walk through each component:&lt;/p>
&lt;h4 id="base-class">Base class&lt;/h4>
&lt;p>Base class that each plugin must inherit from; this class exposes couple of items 1) List of Topics 2) Execute Method - which your plugin should implement.&lt;/p>
&lt;pre>&lt;code>class IPlugin(object):
def __init__(self):
self.description = 'UNKNOWN'
self.topics = []
def execute(self, topic, argument):
&amp;quot;&amp;quot;&amp;quot;The method that we expect all plugins to implement. This is the
method that our framework will call
&amp;quot;&amp;quot;&amp;quot;
raise NotImplementedError
&lt;/code>&lt;/pre>
&lt;h4 id="example-plugin--calculate">Example Plugin : Calculate&lt;/h4>
&lt;p>Calculate Plugin - exposes two functionalities: &lt;em>add&lt;/em> and &lt;em>subtract&lt;/em>. For the client its exposed as two topics. The execute function takes in topic and the argument. Based on the topic the respective plugin could dispatch it to sub-functions within the plugin.&lt;/p>
&lt;pre>&lt;code>class CalculatorPlugin(IPlugin):
def __init__(self):
self.description = 'Calculator'
self.topics = ['Add', 'Subtract']
def execute(self, topic, args):
if topic == &amp;quot;Add&amp;quot;:
return self.add(args)
raise Exception (f'Topic: {topic} has no mapping function')
def add(self, args):
count = 0
for index in range(0, len(args)):
count += args[index]
return count
&lt;/code>&lt;/pre>
&lt;h4 id="service-discovery">Service Discovery:&lt;/h4>
&lt;p>The infrastructure would enumerate the plugin base_directory and try find sub class of &lt;strong>IPlugin&lt;/strong>.
Create instance of the sub_class and register to the store: &lt;code>MAP&amp;lt;topic, instance&amp;gt;&lt;/code>. Infra should be able to directly call the &lt;strong>execute&lt;/strong> API, on the instance.&lt;/p>
&lt;pre>&lt;code>class ServiceDiscovery(object):
def __init__(self, plugin_package_dir='plugins'):
self.plugin_package_base_dir = plugin_package_dir
self.plugin_topic_instance_map = {}
self.enumerate_packages()
def enumerate_packages(self, package):
&amp;quot;&amp;quot;&amp;quot;Recursively walk the supplied package to retrieve all plugins
&amp;quot;&amp;quot;&amp;quot;
imported_package = __import__(package, fromlist=['foo'])
for _, pluginname, ispkg in pkgutil.iter_modules(imported_package.__path__, imported_package.__name__ + '.'):
if not ispkg:
plugin_module = __import__(pluginname, fromlist=['foo'])
clsmembers = inspect.getmembers(plugin_module, inspect.isclass)
for (_, c) in clsmembers:
# Only add classes that are a sub class of Plugin, but NOT Plugin itself
if issubclass(c, IPlugin) &amp;amp; (c is not IPlugin):
print(f' Found plugin class: {c.__module__}.{c.__name__}')
cls_instance = c()
for topic in cls_instance.topics:
print(f' Registering Topics: {topic}')
self.plugin_topic_instance_map[topic] = cls_instance
&lt;/code>&lt;/pre>
&lt;h4 id="execution">Execution&lt;/h4>
&lt;p>Now that we have a &lt;code>Map&amp;lt;Topic,instance&amp;gt;&lt;/code>. When a call comes in - it would have a topic and the args. Using the Map, we could get the corresponding instance and call by passing in both the topic and args. This is similar to delegate (&lt;strong>C#&lt;/strong>) or function pointers(in &lt;strong>C&lt;/strong>).&lt;/p>
&lt;pre>&lt;code>class ServiceDiscovery(object):
def __init__(self, plugin_package_dir='plugins'):
...
self.plugin_topic_instance_map = {}
def execute(self, topic, argument):
if topic not in self.plugin_topic_instance_map:
raise Exception (f'Topic: {topic} is not registered')
return self.plugin_topic_instance_map[topic].execute(topic, argument)
&lt;/code>&lt;/pre>
&lt;h4 id="unit-test">Unit Test:&lt;/h4>
&lt;p>Writing unit test is not optional. Well, I am a fan of TDD 😉&lt;/p>
&lt;pre>&lt;code>def test_cal_plugin_add_func(self):
# Arrange
ser_dis = ServiceDiscovery()
# Action
result = ser_dis.execute(&amp;quot;Add&amp;quot;, [1, 2])
# Assert
self.assertEqual(result, 3)
&lt;/code>&lt;/pre>
&lt;h4 id="deployment">Deployment:&lt;/h4>
&lt;p>Adding a new plugin should be as simple as&lt;/p>
&lt;ul>
&lt;li>Copy and paste a new directory under the Plugin base directory&lt;/li>
&lt;li>Directory should have a class which implements &lt;code>IPlugin&lt;/code>&lt;/li>
&lt;/ul>
&lt;h4 id="conclusion">Conclusion:&lt;/h4>
&lt;p>Building a comprehensive plugin infrastructure is non-trivial; look at Visual Studio - you could override pretty much anything, starting from adding intellisese to a new compiler tool chain. Here, we are just look at a small tip - to get started - on having a python based plugin. Always starting off any infra project with the idea of extension in mind - is good to ensure cleaner responsibility separation.&lt;/p>
&lt;p>From the &lt;strong>SOLID&lt;/strong> principle : &lt;strong>O&lt;/strong> -&amp;gt; our software should be Opened for extension but closed for modifications 😍&lt;/p></description></item><item><title>Version control your install directory - GIT</title><link>https://jacobaloysious.in/post/tech_version_install_dir_git/</link><pubDate>Sun, 05 Jul 2020 00:00:00 +0000</pubDate><guid>https://jacobaloysious.in/post/tech_version_install_dir_git/</guid><description>&lt;h3 id="what-problem-are-we-trying-to-solve">What problem are we trying to Solve?&lt;/h3>
&lt;p>Context: Desktop software installed on client computer, inside a private network and no access to internet.&lt;/p>
&lt;h4 id="use-cases">Use Cases:&lt;/h4>
&lt;ul>
&lt;li>Shipping and applying multiple patches for a given version of the software.&lt;/li>
&lt;li>Reverting a patch&lt;/li>
&lt;li>Keeping track of patches installed&lt;/li>
&lt;li>Check for sanity of the installation and tack &lt;strong>manual&lt;/strong> edits (configuration files etc..)&lt;/li>
&lt;li>User can apply a temporary patch on a server (i.e. common machine) and test, once done revert&lt;/li>
&lt;/ul>
&lt;h3 id="how-are-we-trying-to-solve">How are we trying to solve?&lt;/h3>
&lt;p>Bring the content of the install directory under version control. We will be using GIT version control, to explain how it could be achived.&lt;/p>
&lt;blockquote>
&lt;p>PreRequisite &amp;ldquo;git&amp;rdquo; should be installed and available in the path&lt;/p>
&lt;/blockquote>
&lt;h3 id="why-use-git">Why use GIT?&lt;/h3>
&lt;p>
&lt;a href="https://git-scm.com/" target="_blank" rel="noopener">Git&lt;/a> is a distributed version control system designed to handle everything from small to very large projects with speed and efficiency.&lt;/p>
&lt;p>So, with Git any &lt;strong>local folder&lt;/strong> can be converted into a &lt;strong>git repository&lt;/strong> by just running the &lt;strong>init&lt;/strong> command.&lt;/p>
&lt;pre>&lt;code>D:\Foo\1.0.0&amp;gt;git init
Initialized empty Git repository in D:/Foo/1.0.0/.git/
&lt;/code>&lt;/pre>
&lt;p>Now your installed directory is version controlled. You can add files, commit, create branch(s), switch branches etc&amp;hellip; Since Git is distributed - &lt;strong>N0 Server nor Internet required&lt;/strong> 😄&lt;/p>
&lt;p>At any point of time, you could use &lt;strong>status&lt;/strong> to check if the install directory has any &lt;strong>modified&lt;/strong>, &lt;strong>new&lt;/strong> or &lt;strong>deleted&lt;/strong> files.&lt;/p>
&lt;pre>&lt;code>D:\Foo\1.0.0&amp;gt;git status
On branch base_install
Changes not staged for commit:
(use &amp;quot;git add &amp;lt;file&amp;gt;...&amp;quot; to update what will be committed)
(use &amp;quot;git checkout -- &amp;lt;file&amp;gt;...&amp;quot; to discard changes in working directory)
modified: config/params.toml
Untracked files:
(use &amp;quot;git add &amp;lt;file&amp;gt;...&amp;quot; to include in what will be committed)
config/menus.toml
config/languages.toml
&lt;/code>&lt;/pre>
&lt;p>You could use &lt;strong>git diff&lt;/strong> command to compare changes across two branches like &lt;strong>base_install&lt;/strong> vs &lt;strong>patch_1&lt;/strong>&lt;/p>
&lt;pre>&lt;code>D:\Foo\1.0.0&amp;gt;git diff base_install patch_1
diff --git a/Config/params.toml
index b32de60..e8e1694 100644
--- a/Config/params.toml
+++ b/Config/params.toml
@@ -29,7 +29,7 @@
- main_menu = {align = &amp;quot;l&amp;quot;, show_logo = true}
+ main_menu = {align = &amp;quot;l&amp;quot;, show_logo = false}
&lt;/code>&lt;/pre>
&lt;p>Creating a branch is a trivial operation. As a user you could&lt;/p>
&lt;ul>
&lt;li>Create a new (personal) branch from current (master) branch&lt;/li>
&lt;li>Apply the patch files (edit or new) -&amp;gt; Commit your changes to your &lt;em>personal&lt;/em> branch&lt;/li>
&lt;li>Run your tests&lt;/li>
&lt;li>Once you are done, switch to the original (master) branch&lt;/li>
&lt;/ul>
&lt;p>If you observe - &lt;strong>you don&amp;rsquo;t have to delete your patch&lt;/strong> - at any point of time when you have server time you could come back and switch to your branch&amp;hellip;&lt;/p>
&lt;pre>&lt;code>D:\Foo\1.0.0&amp;gt;git checkout jac_logo_patch
Switched to branch 'jac_logo_patch'
&lt;/code>&lt;/pre>
&lt;h2 id="installer-workflow-and-scripts-sequence">Installer workflow and Scripts sequence&amp;hellip;&lt;/h2>
&lt;h3 id="post-install">POST INSTALL&lt;/h3>
&lt;ol>
&lt;li>Installer should copy this file into the root of destination directory and execute&lt;/li>
&lt;li>Example: &amp;ldquo;D:\Foo\&amp;laquo;_Build_Number_&amp;raquo;&amp;rdquo;&lt;/li>
&lt;li>Run this file post installation&lt;/li>
&lt;/ol>
&lt;h4 id="steps">Steps:&lt;/h4>
&lt;ol>
&lt;li>Initialize git.&lt;/li>
&lt;li>Create a .gitignore file.&lt;/li>
&lt;li>Add contents into .gitignore.&lt;/li>
&lt;li>Create a branch &lt;strong>base_Install&lt;/strong>.&lt;/li>
&lt;li>Commit all contents into git repo with msg &lt;em>Base Install Commit&lt;/em>.&lt;/li>
&lt;/ol>
&lt;pre>&lt;code class="language-bash">@echo off
echo Initializing Git
call git init
echo Add git ignore files
call touch .gitignore
echo Add files to .git ignore for installer cmd files
call PostInstall.cmd &amp;gt;&amp;gt; .gitignore
echo Add all the files and create a initial commit named Base Install. This would take a while!!
call git add -A &amp;amp;&amp;amp; git commit -m &amp;quot;Base Install Commit&amp;quot;
echo Creating a new branch base_install
call git branch base_install
&lt;/code>&lt;/pre>
&lt;h3 id="patch-installer">PATCH INSTALLER&lt;/h3>
&lt;h4 id="pre-install">PRE INSTALL&lt;/h4>
&lt;p>Copy this file into the root of destination directory - D:\Foo\&amp;laquo;Build_Number&amp;raquo;&amp;rdquo;&lt;/p>
&lt;ol>
&lt;li>Check if branch exists&lt;/li>
&lt;li>Add all existing UNTRACKED FILES into the current branch&lt;/li>
&lt;li>Add add a commit message &lt;strong>PrePatch_&amp;lt;PATCH_NAME&amp;gt;&lt;/strong>&lt;/li>
&lt;li>Add the current branch to a known file &lt;strong>LastKnownWorkingBranch&lt;/strong>; useful for un-installation&lt;/li>
&lt;li>Create a &lt;em>new branch&lt;/em> &lt;strong>PATCH_&amp;lt;PATCH_NAME&amp;gt;&lt;/strong>&lt;/li>
&lt;li>Switch to new branch with PATCH_&amp;lt;PATCH_NAME&amp;gt;&lt;/li>
&lt;li>Apply the patch using &lt;strong>git am&lt;/strong>. Note: &lt;strong>am&lt;/strong> would allow you to sign off an applied patch. This maybe useful for future reference.&lt;/li>
&lt;li>Add all existing UNTRACKED FILES into new/current branch i.e. PATCH_&amp;lt;PATCH_NAME&amp;gt;&lt;/li>
&lt;li>Done - we have a new branch with all patches&lt;/li>
&lt;/ol>
&lt;pre>&lt;code class="language-bash">REM TODO: This could be an enviornment variable
set patchName=Patch_1
set branchName=Patch_%patchName%
echo Check if git installed
call git --version
if ERRORLEVEL 1 (
echo Git not installed!!!
goto :Cleanup
)
echo Check if valid git repository
call git rev-parse --is-inside-work-tree
if ERRORLEVEL 1 (
echo Not a valid git repo : %cd%
REM TODO - Should we create a new git repo? or fall back scripts to use .keep solution
goto :Cleanup
)
set commitMsg=&amp;quot;PrePatch_%patchName%&amp;quot;
echo Add all un-tracked files and create a
call git add -A &amp;amp;&amp;amp; git commit -m %commitMsg%
echo Add the current branch name into a temporary file - LastKnownWorkingBranch
call git symbolic-ref --short HEAD &amp;gt; LastKnownWorkingBranch
echo Check if branch exits
git rev-parse --verify %branchName%
if ERRORLEVEL 0 (
echo Branch exists : %branchName%
)
echo Create a new branch
git branch %branchName%
echo Checkout the new branch
git checkout %branchName%
echo Apply the .git patch
git am --signoff &amp;lt; %patchName%.patch
:Cleanup
echo exit
&lt;/code>&lt;/pre>
&lt;p>User can always go back to the base install by switching branches.&lt;/p>
&lt;h3 id="post-install-1">POST INSTALL&lt;/h3>
&lt;h4 id="steps-1">Steps&lt;/h4>
&lt;ol>
&lt;li>Now we are in the new branch for the Patch&lt;/li>
&lt;li>And install script has already installed all the patch files&lt;/li>
&lt;li>Just commit all the changes and we are good to go!!&lt;/li>
&lt;li>Patch installation done!!&lt;/li>
&lt;/ol>
&lt;pre>&lt;code class="language-bash">REM TODO: This should be an enviornment variable
set patchName=Patch_1
set branchName=Patch_%patchName%
set commitMsg=&amp;quot;Patch_%patchName%&amp;quot;
echo Add all un-tracked files and create a
call git add -A &amp;amp;&amp;amp; git commit -m %commitMsg%
echo Patch installed and avaialble in branch %branchName%
&lt;/code>&lt;/pre>
&lt;h3 id="patch-un-installer">PATCH UN-INSTALLER&lt;/h3>
&lt;h4 id="steps-2">Steps&lt;/h4>
&lt;ol>
&lt;li>Read the &lt;strong>lastKnownWorkingBranch&lt;/strong> from a file&lt;/li>
&lt;li>git checkout lastKnownWorkingBranch&lt;/li>
&lt;li>Voila thats it!!&lt;/li>
&lt;/ol>
&lt;blockquote>
&lt;p>&lt;strong>Note:&lt;/strong> File &amp;ldquo;lastKnownWorkingBranch&amp;rdquo; file should be under .gitignore (?) so that its shared across branches and its updated only by the patch installer.&lt;/p>
&lt;/blockquote>
&lt;pre>&lt;code class="language-bash">for /f &amp;quot;delims=&amp;quot; %%a in ('type LastKnownWorkingBranch') do (
set lastKnownWorkingBranch=%%a
break
)
echo lastKnownWorkingBranch: %lastKnownWorkingBranch%
echo Checkout the lastKnownWorkingBranch: %lastKnownWorkingBranch%
git checkout %lastKnownWorkingBranch%
echo Voila we have reverted the previous install
&lt;/code>&lt;/pre>
&lt;h2 id="advantages">ADVANTAGES&lt;/h2>
&lt;ul>
&lt;li>User could compare what has changed across branches aka patches&lt;/li>
&lt;li>User could always go back to base_install at any point of time&lt;/li>
&lt;li>Files are never deleted&lt;/li>
&lt;li>Uninstall is just switching a branch - no file deletes.&lt;/li>
&lt;li>Any number of branches can be created for &lt;strong>personal&lt;/strong> testing.&lt;/li>
&lt;/ul></description></item><item><title>Book - My Good Reads, Technical</title><link>https://jacobaloysious.in/post/book_list-tech-books/</link><pubDate>Sun, 14 Jun 2020 00:00:00 +0000</pubDate><guid>https://jacobaloysious.in/post/book_list-tech-books/</guid><description>&lt;h2 id="podcasts">Podcasts&lt;/h2>
&lt;ul>
&lt;li>
&lt;a href="https://www.hanselminutes.com/" target="_blank" rel="noopener">Hanselminutes&lt;/a>, by Scott Hanselman&lt;/li>
&lt;li>
&lt;a href="https://www.dataengineeringpodcast.com/" target="_blank" rel="noopener">Data Engineering&lt;/a>, by Tobias Macey&lt;/li>
&lt;li>
&lt;a href="https://talkpython.fm/" target="_blank" rel="noopener">Talk Python To Me&lt;/a>, by Michael Kennedy&lt;/li>
&lt;li>
&lt;a href="https://pythonbytes.fm/" target="_blank" rel="noopener">Python Bytes&lt;/a>, by Michael Kennedy and Brian Okken&lt;/li>
&lt;li>
&lt;a href="https://www.dotnetrocks.com/" target="_blank" rel="noopener">.Net Rocks&lt;/a>, by Carl Franklin and Richard Campbell&lt;/li>
&lt;/ul>
&lt;h2 id="in-progress">In Progress&lt;/h2>
&lt;ul>
&lt;li>
&lt;a href="https://www.shroffpublishers.com/books/9789351100744/" target="_blank" rel="noopener">MongoDB Applied Design Patterns&lt;/a>&lt;/li>
&lt;li>
&lt;a href="https://www.oreilly.com/library/view/designing-data-intensive-applications/9781491903063/" target="_blank" rel="noopener">Designing Data-Intensive Applications&lt;/a>, by Martin Kleppmann&lt;/li>
&lt;/ul>
&lt;h2 id="done">Done&lt;/h2>
&lt;ul>
&lt;li>
&lt;a href="https://www.manning.com/books/big-data" target="_blank" rel="noopener">Big Data: Lamda Architecture&lt;/a>, by Nathan Marz and James Warren&lt;/li>
&lt;li>
&lt;a href="https://www.goodreads.com/book/show/36411996-docker-deep-dive" target="_blank" rel="noopener">Docker Deep Dive&lt;/a>, by Nigel Poulton&lt;/li>
&lt;li>
&lt;a href="https://www.goodreads.com/book/show/35516296-chaos-engineering" target="_blank" rel="noopener">Chaos Engineering&lt;/a> by Casey Rosenthal&lt;/li>
&lt;li>
&lt;a href="https://www.manning.com/books/c-sharp-in-depth-fourth-edition" target="_blank" rel="noopener">C# in Depth&lt;/a>, by Jon Skeet&lt;/li>
&lt;li>
&lt;a href="https://git-scm.com/book/en/v2" target="_blank" rel="noopener">Pro Git&lt;/a>, by Scott Chacon and Ben Straub&lt;/li>
&lt;li>
&lt;a href="https://www.oreilly.com/library/view/clean-code/9780136083238/" target="_blank" rel="noopener">Clean Code&lt;/a>, by Robert C Martin&lt;/li>
&lt;li>
&lt;a href="https://www.oreilly.com/library/view/clean-architecture-a/9780134494272/" target="_blank" rel="noopener">Clean Architecture&lt;/a>, by Robert C Martin&lt;/li>
&lt;li>
&lt;a href="https://www.oreilly.com/library/view/working-effectively-with/0131177052/" target="_blank" rel="noopener">Working Effectively with Legacy Code&lt;/a>, by Michael Feathers&lt;/li>
&lt;/ul></description></item><item><title>Tech - My Website</title><link>https://jacobaloysious.in/post/tech_my-website/</link><pubDate>Wed, 20 May 2020 00:00:00 +0000</pubDate><guid>https://jacobaloysious.in/post/tech_my-website/</guid><description>&lt;p>I registered my domain name on
&lt;a href="https://in.godaddy.com/" target="_blank" rel="noopener">GoDaddy&lt;/a> couple of years back (2018). I can&amp;rsquo;t really remember the motivation to get one registered.
I almost forgot about this, until I got a reminder to renew.&lt;/p>
&lt;p>Thanks to
&lt;a href="https://www.hanselman.com/" target="_blank" rel="noopener">Scott&lt;/a>, I realized the importance of key stroke - the nubmer of keystroke in your life time are finite. So if you worked on something interesting - why waste it in an email which you are never sure the recipient may or not read. Instead, write it in a blog and it would be there forever and could potentially help someone new in future.&lt;/p>
&lt;p>On hindsight - this website should have ideally taken less than half a day to setup, but I ended up spending almost 2 days to set the first version. Well I was just getting my hands dirty.&lt;/p>
&lt;p>This is a static website:&lt;/p>
&lt;ol>
&lt;li>Original Content on
&lt;a href="https://github.com/jacobaloysious/" target="_blank" rel="noopener">GitHub&lt;/a> - my private repo&lt;/li>
&lt;li>Static files Generated using
&lt;a href="https://gohugo.io/" target="_blank" rel="noopener">HUGO Extended&lt;/a>&lt;/li>
&lt;li>Themes from
&lt;a href="https://themes.gohugo.io/academia-hugo/" target="_blank" rel="noopener">Academia&lt;/a>&lt;/li>
&lt;li>Static files Hosted on
&lt;a href="https://aws.amazon.com/s3" target="_blank" rel="noopener">AWS S3&lt;/a>&lt;/li>
&lt;li>Domain DNS
&lt;a href="https://aws.amazon.com/route53/" target="_blank" rel="noopener">AWS Route S3&lt;/a>&lt;/li>
&lt;li>Continous Deployment
&lt;a href="https://github.com/features/actions" target="_blank" rel="noopener">Github Actions&lt;/a>&lt;/li>
&lt;/ol>
&lt;h3 id="godaddy">Godaddy&lt;/h3>
&lt;p>This is a very trivial step, go to
&lt;a href="https://in.godaddy.com/" target="_blank" rel="noopener">GoDaddy&lt;/a>. Search if your expected domain name is available. If yes, just follow the steps that show up.
Note: There are many providers GoDaddy is just one of them. I dont have a good reason why I chose GoDaddy or I haven&amp;rsquo;t seen a good reason to try other providers (read, lazy me).&lt;/p>
&lt;h3 id="hugo">Hugo&lt;/h3>
&lt;p>Hugo again has extensive documentation (bit overwhelming at times) on setting up.
Every theme you donwload come with an
&lt;a href="https://github.com/gcushen/hugo-academic/tree/master/exampleSite" target="_blank" rel="noopener">example site&lt;/a>, all you need to do is just start deleting content.&lt;/p>
&lt;p>One item I really like is the &lt;strong>live reload&lt;/strong> - when &lt;strong>hugo server&lt;/strong> is running (locally) - the website would be auto deployed and page reloaded with new content as and when you save your file.&lt;/p>
&lt;p>&lt;strong>Tip:&lt;/strong> Theme I downloaded was using Hugo &lt;strong>Extended&lt;/strong> and not the default one. It took me almost half a day to figure this out :-( debugging through css generation failures etc..&lt;/p>
&lt;h3 id="aws-configuration">AWS Configuration&lt;/h3>
&lt;p>This article walks you through pretty much all the steps. If you are getting started, recomend to skip the cloundfront part:
&lt;a href="https://medium.com/faun/how-to-host-your-static-website-with-s3-cloudfront-and-set-up-an-ssl-certificate-9ee48cd701f9" target="_blank" rel="noopener">how-to-host-your-static-website-with-s3&lt;/a>&lt;/p>
&lt;h3 id="continous-deploy-actions">Continous Deploy (Actions)&lt;/h3>
&lt;p>This was my favourite part, I am personally a CI/CD enthusiast. I wanted to deploy my website every time I push changes into my GitRepo.&lt;/p>
&lt;p>I used
&lt;a href="https://github.com/marketplace/actions/s3-sync" target="_blank" rel="noopener">S3 Sync Actions&lt;/a> from marketplace. It has a few additional steps like generting &lt;em>AWS_ACCESS_KEY_ID&lt;/em> and &lt;em>AWS_SECRET_ACCESS_KEY&lt;/em> to get it configured.&lt;/p>
&lt;p>&lt;strong>Important: Secure you keys, by setting it up as secure env variable in github - so that it doesn&amp;rsquo;t show up in console log&lt;/strong>&lt;/p>
&lt;p>Use my icon for the website:
Every theme comes with its own default icon. Since I have referenced the theme as a GIT Submodule - as part of CD/workflow build is was always overwritten.
Did a minor hack - I checked in my icon into my repo and as part of build replaced the default theme icons with mine before build step.&lt;/p>
&lt;p>Below is the GitHub Action I stitched together.&lt;/p>
&lt;pre>&lt;code> name: CD
on:
push:
branches:
- master
jobs:
deploy:
name: deploy-website
runs-on: ubuntu-latest
continue-on-error: true
steps:
- name: Checkout master
uses: actions/checkout@master
- name: Update theme
run: git submodule update --init --recursive
- name: Setup hugo #https://github.com/peaceiris/actions-hugo
uses: peaceiris/actions-hugo@v2
with:
extended: true #This website is based on Hugo-Extended
hugo-version: &amp;quot;0.70.0&amp;quot; #We could also use latest
- name: copy my icon
run: cp -f ./assets/images/icon.png ./themes/academic/assets/images/icon.png
- name: Build Website
run: hugo --minify
- name: Publish To AWS S3
uses: jakejarvis/s3-sync-action@master
with:
args: --acl public-read --follow-symlinks --delete --exclude '.git/*'
env:
AWS_S3_BUCKET: ${{ secrets.AWS_S3_BUCKET }}
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
AWS_REGION: 'ap-south-1' # optional: defaults to us-east-1
SOURCE_DIR: 'public' # Req: hugo gen - static files into this dir
&lt;/code>&lt;/pre></description></item></channel></rss>